mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 06:44:06 +02:00
Merge branch 'master' of github.com:worklist/hifi into qt-for-portaudio
This commit is contained in:
commit
9db662afaa
26 changed files with 1038 additions and 2451 deletions
879
animation-server/src/AnimationServer.cpp
Normal file
879
animation-server/src/AnimationServer.cpp
Normal file
|
@ -0,0 +1,879 @@
|
|||
//
|
||||
// AnimationServer.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 12/5/2013.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <EnvironmentData.h>
|
||||
#include <NodeList.h>
|
||||
#include <NodeTypes.h>
|
||||
#include <OctalCode.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <JurisdictionListener.h>
|
||||
#include <SceneUtils.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <VoxelEditPacketSender.h>
|
||||
#include <VoxelTree.h>
|
||||
|
||||
#include "AnimationServer.h"
|
||||
|
||||
bool shouldShowPacketsPerSecond = false; // do we want to debug packets per second
|
||||
bool includeBillboard = true;
|
||||
bool includeBorderTracer = true;
|
||||
bool includeMovingBug = true;
|
||||
bool includeBlinkingVoxel = false;
|
||||
bool includeDanceFloor = true;
|
||||
bool buildStreet = false;
|
||||
bool nonThreadedPacketSender = false;
|
||||
int packetsPerSecond = PacketSender::DEFAULT_PACKETS_PER_SECOND;
|
||||
bool waitForVoxelServer = true;
|
||||
|
||||
|
||||
const int ANIMATION_LISTEN_PORT = 40107;
|
||||
int ANIMATE_FPS = 60;
|
||||
double ANIMATE_FPS_IN_MILLISECONDS = 1000.0/ANIMATE_FPS; // determines FPS from our desired FPS
|
||||
int ANIMATE_VOXELS_INTERVAL_USECS = (ANIMATE_FPS_IN_MILLISECONDS * 1000.0); // converts from milliseconds to usecs
|
||||
|
||||
|
||||
int PROCESSING_FPS = 60;
|
||||
double PROCESSING_FPS_IN_MILLISECONDS = 1000.0/PROCESSING_FPS; // determines FPS from our desired FPS
|
||||
int FUDGE_USECS = 650; // a little bit of fudge to actually do some processing
|
||||
int PROCESSING_INTERVAL_USECS = (PROCESSING_FPS_IN_MILLISECONDS * 1000.0) - FUDGE_USECS; // converts from milliseconds to usecs
|
||||
|
||||
bool wantLocalDomain = false;
|
||||
|
||||
unsigned long packetsSent = 0;
|
||||
unsigned long bytesSent = 0;
|
||||
|
||||
JurisdictionListener* jurisdictionListener = NULL;
|
||||
VoxelEditPacketSender* voxelEditPacketSender = NULL;
|
||||
pthread_t animateVoxelThread;
|
||||
|
||||
glm::vec3 rotatePoint(glm::vec3 point, float angle) {
|
||||
// First, create the quaternion based on this angle of rotation
|
||||
glm::quat q(glm::vec3(0, -angle, 0));
|
||||
|
||||
// Next, create a rotation matrix from that quaternion
|
||||
glm::mat4 rotation = glm::mat4_cast(q);
|
||||
|
||||
// Transform the original vectors by the rotation matrix to get the new vectors
|
||||
glm::vec4 quatPoint(point.x, point.y, point.z, 0);
|
||||
glm::vec4 newPoint = quatPoint * rotation;
|
||||
|
||||
return glm::vec3(newPoint.x, newPoint.y, newPoint.z);
|
||||
}
|
||||
|
||||
|
||||
const float BUG_VOXEL_SIZE = 0.0625f / TREE_SCALE;
|
||||
glm::vec3 bugPosition = glm::vec3(BUG_VOXEL_SIZE * 20.0, BUG_VOXEL_SIZE * 30.0, BUG_VOXEL_SIZE * 20.0);
|
||||
glm::vec3 bugDirection = glm::vec3(0, 0, 1);
|
||||
const int VOXELS_PER_BUG = 18;
|
||||
glm::vec3 bugPathCenter = glm::vec3(0.25f,0.15f,0.25f); // glm::vec3(BUG_VOXEL_SIZE * 150.0, BUG_VOXEL_SIZE * 30.0, BUG_VOXEL_SIZE * 150.0);
|
||||
float bugPathRadius = 0.2f; //BUG_VOXEL_SIZE * 140.0;
|
||||
float bugPathTheta = 0.0 * PI_OVER_180;
|
||||
float bugRotation = 0.0 * PI_OVER_180;
|
||||
float bugAngleDelta = 0.2 * PI_OVER_180;
|
||||
bool moveBugInLine = false;
|
||||
|
||||
class BugPart {
|
||||
public:
|
||||
glm::vec3 partLocation;
|
||||
unsigned char partColor[3];
|
||||
|
||||
BugPart(const glm::vec3& location, unsigned char red, unsigned char green, unsigned char blue ) {
|
||||
partLocation = location;
|
||||
partColor[0] = red;
|
||||
partColor[1] = green;
|
||||
partColor[2] = blue;
|
||||
}
|
||||
};
|
||||
|
||||
const BugPart bugParts[VOXELS_PER_BUG] = {
|
||||
|
||||
// tail
|
||||
BugPart(glm::vec3( 0, 0, -3), 51, 51, 153) ,
|
||||
BugPart(glm::vec3( 0, 0, -2), 51, 51, 153) ,
|
||||
BugPart(glm::vec3( 0, 0, -1), 51, 51, 153) ,
|
||||
|
||||
// body
|
||||
BugPart(glm::vec3( 0, 0, 0), 255, 200, 0) ,
|
||||
BugPart(glm::vec3( 0, 0, 1), 255, 200, 0) ,
|
||||
|
||||
// head
|
||||
BugPart(glm::vec3( 0, 0, 2), 200, 0, 0) ,
|
||||
|
||||
// eyes
|
||||
BugPart(glm::vec3( 1, 0, 3), 64, 64, 64) ,
|
||||
BugPart(glm::vec3(-1, 0, 3), 64, 64, 64) ,
|
||||
|
||||
// wings
|
||||
BugPart(glm::vec3( 3, 1, 1), 0, 153, 0) ,
|
||||
BugPart(glm::vec3( 2, 1, 1), 0, 153, 0) ,
|
||||
BugPart(glm::vec3( 1, 0, 1), 0, 153, 0) ,
|
||||
BugPart(glm::vec3(-1, 0, 1), 0, 153, 0) ,
|
||||
BugPart(glm::vec3(-2, 1, 1), 0, 153, 0) ,
|
||||
BugPart(glm::vec3(-3, 1, 1), 0, 153, 0) ,
|
||||
|
||||
|
||||
BugPart(glm::vec3( 2, -1, 0), 153, 200, 0) ,
|
||||
BugPart(glm::vec3( 1, -1, 0), 153, 200, 0) ,
|
||||
BugPart(glm::vec3(-1, -1, 0), 153, 200, 0) ,
|
||||
BugPart(glm::vec3(-2, -1, 0), 153, 200, 0) ,
|
||||
};
|
||||
|
||||
static void renderMovingBug() {
|
||||
VoxelDetail details[VOXELS_PER_BUG];
|
||||
|
||||
// Generate voxels for where bug used to be
|
||||
for (int i = 0; i < VOXELS_PER_BUG; i++) {
|
||||
details[i].s = BUG_VOXEL_SIZE;
|
||||
|
||||
glm::vec3 partAt = bugParts[i].partLocation * BUG_VOXEL_SIZE * (bugDirection.x < 0 ? -1.0f : 1.0f);
|
||||
glm::vec3 rotatedPartAt = rotatePoint(partAt, bugRotation);
|
||||
glm::vec3 offsetPartAt = rotatedPartAt + bugPosition;
|
||||
|
||||
details[i].x = offsetPartAt.x;
|
||||
details[i].y = offsetPartAt.y;
|
||||
details[i].z = offsetPartAt.z;
|
||||
|
||||
details[i].red = bugParts[i].partColor[0];
|
||||
details[i].green = bugParts[i].partColor[1];
|
||||
details[i].blue = bugParts[i].partColor[2];
|
||||
}
|
||||
|
||||
// send the "erase message" first...
|
||||
PACKET_TYPE message = PACKET_TYPE_VOXEL_ERASE;
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details);
|
||||
|
||||
// Move the bug...
|
||||
if (moveBugInLine) {
|
||||
bugPosition.x += (bugDirection.x * BUG_VOXEL_SIZE);
|
||||
bugPosition.y += (bugDirection.y * BUG_VOXEL_SIZE);
|
||||
bugPosition.z += (bugDirection.z * BUG_VOXEL_SIZE);
|
||||
|
||||
// Check boundaries
|
||||
if (bugPosition.z > 1.0) {
|
||||
bugDirection.z = -1;
|
||||
}
|
||||
if (bugPosition.z < BUG_VOXEL_SIZE) {
|
||||
bugDirection.z = 1;
|
||||
}
|
||||
} else {
|
||||
|
||||
//printf("bugPathCenter=(%f,%f,%f)\n", bugPathCenter.x, bugPathCenter.y, bugPathCenter.z);
|
||||
|
||||
bugPathTheta += bugAngleDelta; // move slightly
|
||||
bugRotation -= bugAngleDelta; // rotate slightly
|
||||
|
||||
// If we loop past end of circle, just reset back into normal range
|
||||
if (bugPathTheta > (360.0f * PI_OVER_180)) {
|
||||
bugPathTheta = 0;
|
||||
bugRotation = 0;
|
||||
}
|
||||
|
||||
float x = bugPathCenter.x + bugPathRadius * cos(bugPathTheta);
|
||||
float z = bugPathCenter.z + bugPathRadius * sin(bugPathTheta);
|
||||
float y = bugPathCenter.y;
|
||||
|
||||
bugPosition = glm::vec3(x, y, z);
|
||||
//printf("bugPathTheta=%f\n", bugPathTheta);
|
||||
//printf("bugRotation=%f\n", bugRotation);
|
||||
}
|
||||
|
||||
//printf("bugPosition=(%f,%f,%f)\n", bugPosition.x, bugPosition.y, bugPosition.z);
|
||||
//printf("bugDirection=(%f,%f,%f)\n", bugDirection.x, bugDirection.y, bugDirection.z);
|
||||
// would be nice to add some randomness here...
|
||||
|
||||
// Generate voxels for where bug is going to
|
||||
for (int i = 0; i < VOXELS_PER_BUG; i++) {
|
||||
details[i].s = BUG_VOXEL_SIZE;
|
||||
|
||||
glm::vec3 partAt = bugParts[i].partLocation * BUG_VOXEL_SIZE * (bugDirection.x < 0 ? -1.0f : 1.0f);
|
||||
glm::vec3 rotatedPartAt = rotatePoint(partAt, bugRotation);
|
||||
glm::vec3 offsetPartAt = rotatedPartAt + bugPosition;
|
||||
|
||||
details[i].x = offsetPartAt.x;
|
||||
details[i].y = offsetPartAt.y;
|
||||
details[i].z = offsetPartAt.z;
|
||||
|
||||
details[i].red = bugParts[i].partColor[0];
|
||||
details[i].green = bugParts[i].partColor[1];
|
||||
details[i].blue = bugParts[i].partColor[2];
|
||||
}
|
||||
|
||||
// send the "create message" ...
|
||||
message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE;
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details);
|
||||
}
|
||||
|
||||
|
||||
float intensity = 0.5f;
|
||||
float intensityIncrement = 0.1f;
|
||||
const float MAX_INTENSITY = 1.0f;
|
||||
const float MIN_INTENSITY = 0.5f;
|
||||
const float BEACON_SIZE = 0.25f / TREE_SCALE; // approximately 1/4th meter
|
||||
|
||||
static void sendVoxelBlinkMessage() {
|
||||
VoxelDetail detail;
|
||||
detail.s = BEACON_SIZE;
|
||||
|
||||
glm::vec3 position = glm::vec3(0, 0, detail.s);
|
||||
|
||||
detail.x = detail.s * floor(position.x / detail.s);
|
||||
detail.y = detail.s * floor(position.y / detail.s);
|
||||
detail.z = detail.s * floor(position.z / detail.s);
|
||||
|
||||
::intensity += ::intensityIncrement;
|
||||
if (::intensity >= MAX_INTENSITY) {
|
||||
::intensity = MAX_INTENSITY;
|
||||
::intensityIncrement = -::intensityIncrement;
|
||||
}
|
||||
if (::intensity <= MIN_INTENSITY) {
|
||||
::intensity = MIN_INTENSITY;
|
||||
::intensityIncrement = -::intensityIncrement;
|
||||
}
|
||||
|
||||
detail.red = 255 * ::intensity;
|
||||
detail.green = 0 * ::intensity;
|
||||
detail.blue = 0 * ::intensity;
|
||||
|
||||
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE;
|
||||
|
||||
::voxelEditPacketSender->sendVoxelEditMessage(message, detail);
|
||||
}
|
||||
|
||||
bool stringOfLightsInitialized = false;
|
||||
int currentLight = 0;
|
||||
int lightMovementDirection = 1;
|
||||
const int SEGMENT_COUNT = 4;
|
||||
const int LIGHTS_PER_SEGMENT = 80;
|
||||
const int LIGHT_COUNT = LIGHTS_PER_SEGMENT * SEGMENT_COUNT;
|
||||
glm::vec3 stringOfLights[LIGHT_COUNT];
|
||||
unsigned char offColor[3] = { 240, 240, 240 };
|
||||
unsigned char onColor[3] = { 0, 255, 255 };
|
||||
const float STRING_OF_LIGHTS_SIZE = 0.125f / TREE_SCALE; // approximately 1/8th meter
|
||||
|
||||
static void sendBlinkingStringOfLights() {
|
||||
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
|
||||
float lightScale = STRING_OF_LIGHTS_SIZE;
|
||||
static VoxelDetail details[LIGHTS_PER_SEGMENT];
|
||||
|
||||
// first initialized the string of lights if needed...
|
||||
if (!stringOfLightsInitialized) {
|
||||
for (int segment = 0; segment < SEGMENT_COUNT; segment++) {
|
||||
for (int indexInSegment = 0; indexInSegment < LIGHTS_PER_SEGMENT; indexInSegment++) {
|
||||
|
||||
int i = (segment * LIGHTS_PER_SEGMENT) + indexInSegment;
|
||||
|
||||
// four different segments on sides of initial platform
|
||||
switch (segment) {
|
||||
case 0:
|
||||
// along x axis
|
||||
stringOfLights[i] = glm::vec3(indexInSegment * lightScale, 0, 0);
|
||||
break;
|
||||
case 1:
|
||||
// parallel to Z axis at outer X edge
|
||||
stringOfLights[i] = glm::vec3(LIGHTS_PER_SEGMENT * lightScale, 0, indexInSegment * lightScale);
|
||||
break;
|
||||
case 2:
|
||||
// parallel to X axis at outer Z edge
|
||||
stringOfLights[i] = glm::vec3((LIGHTS_PER_SEGMENT-indexInSegment) * lightScale, 0,
|
||||
LIGHTS_PER_SEGMENT * lightScale);
|
||||
break;
|
||||
case 3:
|
||||
// on Z axis
|
||||
stringOfLights[i] = glm::vec3(0, 0, (LIGHTS_PER_SEGMENT-indexInSegment) * lightScale);
|
||||
break;
|
||||
}
|
||||
|
||||
details[indexInSegment].s = STRING_OF_LIGHTS_SIZE;
|
||||
details[indexInSegment].x = stringOfLights[i].x;
|
||||
details[indexInSegment].y = stringOfLights[i].y;
|
||||
details[indexInSegment].z = stringOfLights[i].z;
|
||||
|
||||
details[indexInSegment].red = offColor[0];
|
||||
details[indexInSegment].green = offColor[1];
|
||||
details[indexInSegment].blue = offColor[2];
|
||||
}
|
||||
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, LIGHTS_PER_SEGMENT, (VoxelDetail*)&details);
|
||||
}
|
||||
stringOfLightsInitialized = true;
|
||||
} else {
|
||||
// turn off current light
|
||||
details[0].x = stringOfLights[currentLight].x;
|
||||
details[0].y = stringOfLights[currentLight].y;
|
||||
details[0].z = stringOfLights[currentLight].z;
|
||||
details[0].red = offColor[0];
|
||||
details[0].green = offColor[1];
|
||||
details[0].blue = offColor[2];
|
||||
|
||||
// move current light...
|
||||
// if we're at the end of our string, then change direction
|
||||
if (currentLight == LIGHT_COUNT-1) {
|
||||
lightMovementDirection = -1;
|
||||
}
|
||||
if (currentLight == 0) {
|
||||
lightMovementDirection = 1;
|
||||
}
|
||||
currentLight += lightMovementDirection;
|
||||
|
||||
// turn on new current light
|
||||
details[1].x = stringOfLights[currentLight].x;
|
||||
details[1].y = stringOfLights[currentLight].y;
|
||||
details[1].z = stringOfLights[currentLight].z;
|
||||
details[1].red = onColor[0];
|
||||
details[1].green = onColor[1];
|
||||
details[1].blue = onColor[2];
|
||||
|
||||
// send both changes in same message
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, 2, (VoxelDetail*)&details);
|
||||
}
|
||||
}
|
||||
|
||||
bool danceFloorInitialized = false;
|
||||
const float DANCE_FLOOR_LIGHT_SIZE = 1.0f / TREE_SCALE; // approximately 1 meter
|
||||
const int DANCE_FLOOR_LENGTH = 10;
|
||||
const int DANCE_FLOOR_WIDTH = 10;
|
||||
glm::vec3 danceFloorPosition(100.0f / TREE_SCALE, 30.0f / TREE_SCALE, 10.0f / TREE_SCALE);
|
||||
glm::vec3 danceFloorLights[DANCE_FLOOR_LENGTH][DANCE_FLOOR_WIDTH];
|
||||
unsigned char danceFloorOffColor[3] = { 240, 240, 240 };
|
||||
const int DANCE_FLOOR_COLORS = 6;
|
||||
|
||||
unsigned char danceFloorOnColorA[DANCE_FLOOR_COLORS][3] = {
|
||||
{ 255, 0, 0 }, { 0, 255, 0 }, { 0, 0, 255 },
|
||||
{ 0, 191, 255 }, { 0, 250, 154 }, { 255, 69, 0 },
|
||||
};
|
||||
unsigned char danceFloorOnColorB[DANCE_FLOOR_COLORS][3] = {
|
||||
{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } ,
|
||||
{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }
|
||||
};
|
||||
float danceFloorGradient = 0.5f;
|
||||
const float BEATS_PER_MINUTE = 118.0f;
|
||||
const float SECONDS_PER_MINUTE = 60.0f;
|
||||
const float FRAMES_PER_BEAT = (SECONDS_PER_MINUTE * ANIMATE_FPS) / BEATS_PER_MINUTE;
|
||||
float danceFloorGradientIncrement = 1.0f / FRAMES_PER_BEAT;
|
||||
const float DANCE_FLOOR_MAX_GRADIENT = 1.0f;
|
||||
const float DANCE_FLOOR_MIN_GRADIENT = 0.0f;
|
||||
const int DANCE_FLOOR_VOXELS_PER_PACKET = 100;
|
||||
const int PACKETS_PER_DANCE_FLOOR = DANCE_FLOOR_VOXELS_PER_PACKET / (DANCE_FLOOR_WIDTH * DANCE_FLOOR_LENGTH);
|
||||
int danceFloorColors[DANCE_FLOOR_WIDTH][DANCE_FLOOR_LENGTH];
|
||||
|
||||
void sendDanceFloor() {
|
||||
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
|
||||
float lightScale = DANCE_FLOOR_LIGHT_SIZE;
|
||||
static VoxelDetail details[DANCE_FLOOR_VOXELS_PER_PACKET];
|
||||
|
||||
// first initialized the billboard of lights if needed...
|
||||
if (!::danceFloorInitialized) {
|
||||
for (int i = 0; i < DANCE_FLOOR_WIDTH; i++) {
|
||||
for (int j = 0; j < DANCE_FLOOR_LENGTH; j++) {
|
||||
|
||||
int randomColorIndex = randIntInRange(-DANCE_FLOOR_COLORS, DANCE_FLOOR_COLORS);
|
||||
::danceFloorColors[i][j] = randomColorIndex;
|
||||
::danceFloorLights[i][j] = ::danceFloorPosition +
|
||||
glm::vec3(i * DANCE_FLOOR_LIGHT_SIZE, 0, j * DANCE_FLOOR_LIGHT_SIZE);
|
||||
}
|
||||
}
|
||||
::danceFloorInitialized = true;
|
||||
}
|
||||
|
||||
::danceFloorGradient += ::danceFloorGradientIncrement;
|
||||
|
||||
if (::danceFloorGradient >= DANCE_FLOOR_MAX_GRADIENT) {
|
||||
::danceFloorGradient = DANCE_FLOOR_MAX_GRADIENT;
|
||||
::danceFloorGradientIncrement = -::danceFloorGradientIncrement;
|
||||
}
|
||||
if (::danceFloorGradient <= DANCE_FLOOR_MIN_GRADIENT) {
|
||||
::danceFloorGradient = DANCE_FLOOR_MIN_GRADIENT;
|
||||
::danceFloorGradientIncrement = -::danceFloorGradientIncrement;
|
||||
}
|
||||
|
||||
for (int i = 0; i < DANCE_FLOOR_LENGTH; i++) {
|
||||
for (int j = 0; j < DANCE_FLOOR_WIDTH; j++) {
|
||||
|
||||
int nthVoxel = ((i * DANCE_FLOOR_WIDTH) + j);
|
||||
int item = nthVoxel % DANCE_FLOOR_VOXELS_PER_PACKET;
|
||||
|
||||
::danceFloorLights[i][j] = ::danceFloorPosition +
|
||||
glm::vec3(i * DANCE_FLOOR_LIGHT_SIZE, 0, j * DANCE_FLOOR_LIGHT_SIZE);
|
||||
|
||||
details[item].s = lightScale;
|
||||
details[item].x = ::danceFloorLights[i][j].x;
|
||||
details[item].y = ::danceFloorLights[i][j].y;
|
||||
details[item].z = ::danceFloorLights[i][j].z;
|
||||
|
||||
if (danceFloorColors[i][j] > 0) {
|
||||
int color = danceFloorColors[i][j] - 1;
|
||||
details[item].red = (::danceFloorOnColorA[color][0] +
|
||||
((::danceFloorOnColorB[color][0] - ::danceFloorOnColorA[color][0])
|
||||
* ::danceFloorGradient));
|
||||
details[item].green = (::danceFloorOnColorA[color][1] +
|
||||
((::danceFloorOnColorB[color][1] - ::danceFloorOnColorA[color][1])
|
||||
* ::danceFloorGradient));
|
||||
details[item].blue = (::danceFloorOnColorA[color][2] +
|
||||
((::danceFloorOnColorB[color][2] - ::danceFloorOnColorA[color][2])
|
||||
* ::danceFloorGradient));
|
||||
} else if (::danceFloorColors[i][j] < 0) {
|
||||
int color = -(::danceFloorColors[i][j] + 1);
|
||||
details[item].red = (::danceFloorOnColorB[color][0] +
|
||||
((::danceFloorOnColorA[color][0] - ::danceFloorOnColorB[color][0])
|
||||
* ::danceFloorGradient));
|
||||
details[item].green = (::danceFloorOnColorB[color][1] +
|
||||
((::danceFloorOnColorA[color][1] - ::danceFloorOnColorB[color][1])
|
||||
* ::danceFloorGradient));
|
||||
details[item].blue = (::danceFloorOnColorB[color][2] +
|
||||
((::danceFloorOnColorA[color][2] - ::danceFloorOnColorB[color][2])
|
||||
* ::danceFloorGradient));
|
||||
} else {
|
||||
int color = 0;
|
||||
details[item].red = (::danceFloorOnColorB[color][0] +
|
||||
((::danceFloorOnColorA[color][0] - ::danceFloorOnColorB[color][0])
|
||||
* ::danceFloorGradient));
|
||||
details[item].green = (::danceFloorOnColorB[color][1] +
|
||||
((::danceFloorOnColorA[color][1] - ::danceFloorOnColorB[color][1])
|
||||
* ::danceFloorGradient));
|
||||
details[item].blue = (::danceFloorOnColorB[color][2] +
|
||||
((::danceFloorOnColorA[color][2] - ::danceFloorOnColorB[color][2])
|
||||
* ::danceFloorGradient));
|
||||
}
|
||||
|
||||
if (item == DANCE_FLOOR_VOXELS_PER_PACKET - 1) {
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, DANCE_FLOOR_VOXELS_PER_PACKET, (VoxelDetail*)&details);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool billboardInitialized = false;
|
||||
const int BILLBOARD_HEIGHT = 9;
|
||||
const int BILLBOARD_WIDTH = 45;
|
||||
glm::vec3 billboardPosition((0.125f / TREE_SCALE),(0.125f / TREE_SCALE),0);
|
||||
glm::vec3 billboardLights[BILLBOARD_HEIGHT][BILLBOARD_WIDTH];
|
||||
unsigned char billboardOffColor[3] = { 240, 240, 240 };
|
||||
unsigned char billboardOnColorA[3] = { 0, 0, 255 };
|
||||
unsigned char billboardOnColorB[3] = { 0, 255, 0 };
|
||||
float billboardGradient = 0.5f;
|
||||
float billboardGradientIncrement = 0.01f;
|
||||
const float BILLBOARD_MAX_GRADIENT = 1.0f;
|
||||
const float BILLBOARD_MIN_GRADIENT = 0.0f;
|
||||
const float BILLBOARD_LIGHT_SIZE = 0.125f / TREE_SCALE; // approximately 1/8 meter per light
|
||||
const int VOXELS_PER_PACKET = 81;
|
||||
const int PACKETS_PER_BILLBOARD = VOXELS_PER_PACKET / (BILLBOARD_HEIGHT * BILLBOARD_WIDTH);
|
||||
|
||||
|
||||
// top to bottom...
|
||||
bool billboardMessage[BILLBOARD_HEIGHT][BILLBOARD_WIDTH] = {
|
||||
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
|
||||
{ 0,0,1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,1,1,1,1,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,0,0 },
|
||||
{ 0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,1,1,1,0,0,0,0,0 },
|
||||
{ 0,0,1,1,1,1,0,1,0,1,1,1,0,1,1,1,0,0,1,1,1,0,0,0,0,1,1,1,0,1,1,1,0,1,0,1,0,0,1,0,0,1,0,1,0 },
|
||||
{ 0,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,1,0,1,0 },
|
||||
{ 0,0,1,0,0,1,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,1,1,0,1,1,1,0,1,0,1,0,0,1,0,0,1,1,1,0 },
|
||||
{ 0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0 },
|
||||
{ 0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0 },
|
||||
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
|
||||
};
|
||||
|
||||
static void sendBillboard() {
|
||||
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
|
||||
float lightScale = BILLBOARD_LIGHT_SIZE;
|
||||
static VoxelDetail details[VOXELS_PER_PACKET];
|
||||
|
||||
// first initialized the billboard of lights if needed...
|
||||
if (!billboardInitialized) {
|
||||
for (int i = 0; i < BILLBOARD_HEIGHT; i++) {
|
||||
for (int j = 0; j < BILLBOARD_WIDTH; j++) {
|
||||
|
||||
billboardLights[i][j] = billboardPosition + glm::vec3(j * lightScale, (float)((BILLBOARD_HEIGHT - i) * lightScale), 0);
|
||||
}
|
||||
}
|
||||
billboardInitialized = true;
|
||||
}
|
||||
|
||||
::billboardGradient += ::billboardGradientIncrement;
|
||||
|
||||
if (::billboardGradient >= BILLBOARD_MAX_GRADIENT) {
|
||||
::billboardGradient = BILLBOARD_MAX_GRADIENT;
|
||||
::billboardGradientIncrement = -::billboardGradientIncrement;
|
||||
}
|
||||
if (::billboardGradient <= BILLBOARD_MIN_GRADIENT) {
|
||||
::billboardGradient = BILLBOARD_MIN_GRADIENT;
|
||||
::billboardGradientIncrement = -::billboardGradientIncrement;
|
||||
}
|
||||
|
||||
for (int i = 0; i < BILLBOARD_HEIGHT; i++) {
|
||||
for (int j = 0; j < BILLBOARD_WIDTH; j++) {
|
||||
|
||||
int nthVoxel = ((i * BILLBOARD_WIDTH) + j);
|
||||
int item = nthVoxel % VOXELS_PER_PACKET;
|
||||
|
||||
billboardLights[i][j] = billboardPosition + glm::vec3(j * lightScale, (float)((BILLBOARD_HEIGHT - i) * lightScale), 0);
|
||||
|
||||
details[item].s = lightScale;
|
||||
details[item].x = billboardLights[i][j].x;
|
||||
details[item].y = billboardLights[i][j].y;
|
||||
details[item].z = billboardLights[i][j].z;
|
||||
|
||||
if (billboardMessage[i][j]) {
|
||||
details[item].red = (billboardOnColorA[0] + ((billboardOnColorB[0] - billboardOnColorA[0]) * ::billboardGradient));
|
||||
details[item].green = (billboardOnColorA[1] + ((billboardOnColorB[1] - billboardOnColorA[1]) * ::billboardGradient));
|
||||
details[item].blue = (billboardOnColorA[2] + ((billboardOnColorB[2] - billboardOnColorA[2]) * ::billboardGradient));
|
||||
} else {
|
||||
details[item].red = billboardOffColor[0];
|
||||
details[item].green = billboardOffColor[1];
|
||||
details[item].blue = billboardOffColor[2];
|
||||
}
|
||||
|
||||
if (item == VOXELS_PER_PACKET - 1) {
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_PACKET, (VoxelDetail*)&details);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_VOXEL_SET_DESTRUCTIVE; // we're a bully!
|
||||
static VoxelDetail details[BRICKS_PER_PACKET];
|
||||
|
||||
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) {
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, BRICKS_PER_PACKET, (VoxelDetail*)&details);
|
||||
}
|
||||
}
|
||||
}
|
||||
roadInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
double start = 0;
|
||||
|
||||
|
||||
void* animateVoxels(void* args) {
|
||||
|
||||
uint64_t lastAnimateTime = 0;
|
||||
uint64_t lastProcessTime = 0;
|
||||
int processesPerAnimate = 0;
|
||||
|
||||
bool firstTime = true;
|
||||
|
||||
qDebug() << "Setting PPS to " << ::packetsPerSecond << "\n";
|
||||
::voxelEditPacketSender->setPacketsPerSecond(::packetsPerSecond);
|
||||
|
||||
qDebug() << "PPS set to " << ::voxelEditPacketSender->getPacketsPerSecond() << "\n";
|
||||
|
||||
while (true) {
|
||||
|
||||
// If we're asked to wait for voxel servers, and there isn't one available yet, then
|
||||
// let the voxelEditPacketSender process and move on.
|
||||
if (::waitForVoxelServer && !::voxelEditPacketSender->voxelServersExist()) {
|
||||
if (::nonThreadedPacketSender) {
|
||||
::voxelEditPacketSender->process();
|
||||
}
|
||||
} else {
|
||||
if (firstTime) {
|
||||
lastAnimateTime = usecTimestampNow();
|
||||
firstTime = false;
|
||||
}
|
||||
lastProcessTime = usecTimestampNow();
|
||||
|
||||
int packetsStarting = 0;
|
||||
int packetsEnding = 0;
|
||||
|
||||
// The while loop will be running at PROCESSING_FPS, but we only want to call these animation functions at
|
||||
// ANIMATE_FPS. So we check out last animate time and only call these if we've elapsed that time.
|
||||
uint64_t now = usecTimestampNow();
|
||||
uint64_t animationElapsed = now - lastAnimateTime;
|
||||
int withinAnimationTarget = ANIMATE_VOXELS_INTERVAL_USECS - animationElapsed;
|
||||
const int CLOSE_ENOUGH_TO_ANIMATE = 2000; // approximately 2 ms
|
||||
|
||||
int animateLoopsPerAnimate = 0;
|
||||
while (withinAnimationTarget < CLOSE_ENOUGH_TO_ANIMATE) {
|
||||
processesPerAnimate = 0;
|
||||
animateLoopsPerAnimate++;
|
||||
|
||||
lastAnimateTime = now;
|
||||
packetsStarting = ::voxelEditPacketSender->packetsToSendCount();
|
||||
|
||||
// some animations
|
||||
//sendVoxelBlinkMessage();
|
||||
|
||||
if (::includeBillboard) {
|
||||
sendBillboard();
|
||||
}
|
||||
if (::includeBorderTracer) {
|
||||
sendBlinkingStringOfLights();
|
||||
}
|
||||
if (::includeMovingBug) {
|
||||
renderMovingBug();
|
||||
}
|
||||
if (::includeBlinkingVoxel) {
|
||||
sendVoxelBlinkMessage();
|
||||
}
|
||||
if (::includeDanceFloor) {
|
||||
sendDanceFloor();
|
||||
}
|
||||
|
||||
if (::buildStreet) {
|
||||
doBuildStreet();
|
||||
}
|
||||
|
||||
packetsEnding = ::voxelEditPacketSender->packetsToSendCount();
|
||||
|
||||
if (animationElapsed > ANIMATE_VOXELS_INTERVAL_USECS) {
|
||||
animationElapsed -= ANIMATE_VOXELS_INTERVAL_USECS; // credit ourselves one animation frame
|
||||
} else {
|
||||
animationElapsed = 0;
|
||||
}
|
||||
withinAnimationTarget = ANIMATE_VOXELS_INTERVAL_USECS - animationElapsed;
|
||||
|
||||
::voxelEditPacketSender->releaseQueuedMessages();
|
||||
}
|
||||
|
||||
if (::nonThreadedPacketSender) {
|
||||
::voxelEditPacketSender->process();
|
||||
}
|
||||
processesPerAnimate++;
|
||||
|
||||
if (::shouldShowPacketsPerSecond) {
|
||||
float lifetimeSeconds = ::voxelEditPacketSender->getLifetimeInSeconds();
|
||||
int targetPPS = ::voxelEditPacketSender->getPacketsPerSecond();
|
||||
float lifetimePPS = ::voxelEditPacketSender->getLifetimePPS();
|
||||
float lifetimeBPS = ::voxelEditPacketSender->getLifetimeBPS();
|
||||
uint64_t totalPacketsSent = ::voxelEditPacketSender->getLifetimePacketsSent();
|
||||
uint64_t totalBytesSent = ::voxelEditPacketSender->getLifetimeBytesSent();
|
||||
|
||||
float lifetimePPSQueued = ::voxelEditPacketSender->getLifetimePPSQueued();
|
||||
float lifetimeBPSQueued = ::voxelEditPacketSender->getLifetimeBPSQueued();
|
||||
uint64_t totalPacketsQueued = ::voxelEditPacketSender->getLifetimePacketsQueued();
|
||||
uint64_t totalBytesQueued = ::voxelEditPacketSender->getLifetimeBytesQueued();
|
||||
|
||||
uint64_t packetsPending = ::voxelEditPacketSender->packetsToSendCount();
|
||||
|
||||
printf("lifetime=%f secs packetsSent=%lld, bytesSent=%lld targetPPS=%d pps=%f bps=%f\n",
|
||||
lifetimeSeconds, totalPacketsSent, totalBytesSent, targetPPS, lifetimePPS, lifetimeBPS);
|
||||
printf("packetsPending=%lld packetsQueued=%lld, bytesQueued=%lld ppsQueued=%f bpsQueued=%f\n",
|
||||
packetsPending, totalPacketsQueued, totalBytesQueued, lifetimePPSQueued, lifetimeBPSQueued);
|
||||
}
|
||||
}
|
||||
// dynamically sleep until we need to fire off the next set of voxels
|
||||
uint64_t usecToSleep = PROCESSING_INTERVAL_USECS - (usecTimestampNow() - lastProcessTime);
|
||||
if (usecToSleep > PROCESSING_INTERVAL_USECS) {
|
||||
usecToSleep = PROCESSING_INTERVAL_USECS;
|
||||
}
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_exit(0);
|
||||
}
|
||||
|
||||
AnimationServer::AnimationServer(int &argc, char **argv) :
|
||||
QCoreApplication(argc, argv)
|
||||
{
|
||||
::start = usecTimestampNow();
|
||||
|
||||
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_ANIMATION_SERVER, ANIMATION_LISTEN_PORT);
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
// Handle Local Domain testing with the --local command line
|
||||
const char* NON_THREADED_PACKETSENDER = "--NonThreadedPacketSender";
|
||||
::nonThreadedPacketSender = cmdOptionExists(argc, (const char**) argv, NON_THREADED_PACKETSENDER);
|
||||
printf("nonThreadedPacketSender=%s\n", debug::valueOf(::nonThreadedPacketSender));
|
||||
|
||||
// Handle Local Domain testing with the --local command line
|
||||
const char* NO_BILLBOARD = "--NoBillboard";
|
||||
::includeBillboard = !cmdOptionExists(argc, (const char**) argv, NO_BILLBOARD);
|
||||
printf("includeBillboard=%s\n", debug::valueOf(::includeBillboard));
|
||||
|
||||
const char* NO_BORDER_TRACER = "--NoBorderTracer";
|
||||
::includeBorderTracer = !cmdOptionExists(argc, (const char**) argv, NO_BORDER_TRACER);
|
||||
printf("includeBorderTracer=%s\n", debug::valueOf(::includeBorderTracer));
|
||||
|
||||
const char* NO_MOVING_BUG = "--NoMovingBug";
|
||||
::includeMovingBug = !cmdOptionExists(argc, (const char**) argv, NO_MOVING_BUG);
|
||||
printf("includeMovingBug=%s\n", debug::valueOf(::includeMovingBug));
|
||||
|
||||
const char* INCLUDE_BLINKING_VOXEL = "--includeBlinkingVoxel";
|
||||
::includeBlinkingVoxel = cmdOptionExists(argc, (const char**) argv, INCLUDE_BLINKING_VOXEL);
|
||||
printf("includeBlinkingVoxel=%s\n", debug::valueOf(::includeBlinkingVoxel));
|
||||
|
||||
const char* NO_DANCE_FLOOR = "--NoDanceFloor";
|
||||
::includeDanceFloor = !cmdOptionExists(argc, (const char**) argv, NO_DANCE_FLOOR);
|
||||
printf("includeDanceFloor=%s\n", debug::valueOf(::includeDanceFloor));
|
||||
|
||||
const char* BUILD_STREET = "--BuildStreet";
|
||||
::buildStreet = cmdOptionExists(argc, (const char**) argv, BUILD_STREET);
|
||||
printf("buildStreet=%s\n", debug::valueOf(::buildStreet));
|
||||
|
||||
// Handle Local Domain testing with the --local command line
|
||||
const char* showPPS = "--showPPS";
|
||||
::shouldShowPacketsPerSecond = cmdOptionExists(argc, (const char**) argv, showPPS);
|
||||
|
||||
// Handle Local Domain testing with the --local command line
|
||||
const char* local = "--local";
|
||||
::wantLocalDomain = cmdOptionExists(argc, (const char**) argv,local);
|
||||
if (::wantLocalDomain) {
|
||||
printf("Local Domain MODE!\n");
|
||||
nodeList->setDomainIPToLocalhost();
|
||||
}
|
||||
|
||||
const char* domainHostname = getCmdOption(argc, (const char**) argv, "--domain");
|
||||
if (domainHostname) {
|
||||
NodeList::getInstance()->setDomainHostname(domainHostname);
|
||||
}
|
||||
|
||||
const char* packetsPerSecondCommand = getCmdOption(argc, (const char**) argv, "--pps");
|
||||
if (packetsPerSecondCommand) {
|
||||
::packetsPerSecond = atoi(packetsPerSecondCommand);
|
||||
}
|
||||
printf("packetsPerSecond=%d\n",packetsPerSecond);
|
||||
|
||||
const char* animateFPSCommand = getCmdOption(argc, (const char**) argv, "--AnimateFPS");
|
||||
const char* animateIntervalCommand = getCmdOption(argc, (const char**) argv, "--AnimateInterval");
|
||||
if (animateFPSCommand || animateIntervalCommand) {
|
||||
if (animateIntervalCommand) {
|
||||
::ANIMATE_FPS_IN_MILLISECONDS = atoi(animateIntervalCommand);
|
||||
::ANIMATE_VOXELS_INTERVAL_USECS = (ANIMATE_FPS_IN_MILLISECONDS * 1000.0); // converts from milliseconds to usecs
|
||||
::ANIMATE_FPS = PacketSender::USECS_PER_SECOND / ::ANIMATE_VOXELS_INTERVAL_USECS;
|
||||
} else {
|
||||
::ANIMATE_FPS = atoi(animateFPSCommand);
|
||||
::ANIMATE_FPS_IN_MILLISECONDS = 1000.0/ANIMATE_FPS; // determines FPS from our desired FPS
|
||||
::ANIMATE_VOXELS_INTERVAL_USECS = (ANIMATE_FPS_IN_MILLISECONDS * 1000.0); // converts from milliseconds to usecs
|
||||
}
|
||||
}
|
||||
printf("ANIMATE_FPS=%d\n",ANIMATE_FPS);
|
||||
printf("ANIMATE_VOXELS_INTERVAL_USECS=%d\n",ANIMATE_VOXELS_INTERVAL_USECS);
|
||||
|
||||
const char* processingFPSCommand = getCmdOption(argc, (const char**) argv, "--ProcessingFPS");
|
||||
const char* processingIntervalCommand = getCmdOption(argc, (const char**) argv, "--ProcessingInterval");
|
||||
if (processingFPSCommand || processingIntervalCommand) {
|
||||
if (processingIntervalCommand) {
|
||||
::PROCESSING_FPS_IN_MILLISECONDS = atoi(processingIntervalCommand);
|
||||
::PROCESSING_INTERVAL_USECS = ::PROCESSING_FPS_IN_MILLISECONDS * 1000.0;
|
||||
::PROCESSING_FPS = PacketSender::USECS_PER_SECOND / ::PROCESSING_INTERVAL_USECS;
|
||||
} else {
|
||||
::PROCESSING_FPS = atoi(processingFPSCommand);
|
||||
::PROCESSING_FPS_IN_MILLISECONDS = 1000.0/PROCESSING_FPS; // determines FPS from our desired FPS
|
||||
::PROCESSING_INTERVAL_USECS = (PROCESSING_FPS_IN_MILLISECONDS * 1000.0) - FUDGE_USECS; // converts from milliseconds to usecs
|
||||
}
|
||||
}
|
||||
printf("PROCESSING_FPS=%d\n",PROCESSING_FPS);
|
||||
printf("PROCESSING_INTERVAL_USECS=%d\n",PROCESSING_INTERVAL_USECS);
|
||||
|
||||
nodeList->linkedDataCreateCallback = NULL; // do we need a callback?
|
||||
|
||||
// Create our JurisdictionListener so we'll know where to send edit packets
|
||||
::jurisdictionListener = new JurisdictionListener();
|
||||
if (::jurisdictionListener) {
|
||||
::jurisdictionListener->initialize(true);
|
||||
}
|
||||
|
||||
// Create out VoxelEditPacketSender
|
||||
::voxelEditPacketSender = new VoxelEditPacketSender;
|
||||
::voxelEditPacketSender->initialize(!::nonThreadedPacketSender);
|
||||
|
||||
if (::jurisdictionListener) {
|
||||
::voxelEditPacketSender->setVoxelServerJurisdictions(::jurisdictionListener->getJurisdictions());
|
||||
}
|
||||
if (::nonThreadedPacketSender) {
|
||||
::voxelEditPacketSender->setProcessCallIntervalHint(PROCESSING_INTERVAL_USECS);
|
||||
}
|
||||
|
||||
srand((unsigned)time(0));
|
||||
|
||||
|
||||
pthread_create(&::animateVoxelThread, NULL, animateVoxels, NULL);
|
||||
|
||||
NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1);
|
||||
|
||||
QTimer* domainServerTimer = new QTimer(this);
|
||||
connect(domainServerTimer, SIGNAL(timeout()), nodeList, SLOT(sendDomainServerCheckIn()));
|
||||
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
|
||||
|
||||
QTimer* silentNodeTimer = new QTimer(this);
|
||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
|
||||
connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(readPendingDatagrams()));
|
||||
}
|
||||
|
||||
void AnimationServer::readPendingDatagrams() {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
static int receivedBytes = 0;
|
||||
static unsigned char packetData[MAX_PACKET_SIZE];
|
||||
static HifiSockAddr nodeSockAddr;
|
||||
|
||||
// Nodes sending messages to us...
|
||||
while (nodeList->getNodeSocket().hasPendingDatagrams()
|
||||
&& (receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
|
||||
nodeSockAddr.getAddressPointer(),
|
||||
nodeSockAddr.getPortPointer())) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
|
||||
if (packetData[0] == PACKET_TYPE_JURISDICTION) {
|
||||
if (::jurisdictionListener) {
|
||||
::jurisdictionListener->queueReceivedPacket(nodeSockAddr, packetData, receivedBytes);
|
||||
}
|
||||
}
|
||||
NodeList::getInstance()->processNodeData(nodeSockAddr, packetData, receivedBytes);
|
||||
}
|
||||
}
|
||||
|
||||
AnimationServer::~AnimationServer() {
|
||||
pthread_join(animateVoxelThread, NULL);
|
||||
|
||||
if (::jurisdictionListener) {
|
||||
::jurisdictionListener->terminate();
|
||||
delete ::jurisdictionListener;
|
||||
}
|
||||
|
||||
if (::voxelEditPacketSender) {
|
||||
::voxelEditPacketSender->terminate();
|
||||
delete ::voxelEditPacketSender;
|
||||
}
|
||||
}
|
24
animation-server/src/AnimationServer.h
Normal file
24
animation-server/src/AnimationServer.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// AnimationServer.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 12/5/2013.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__AnimationServer__
|
||||
#define __hifi__AnimationServer__
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
class AnimationServer : public QCoreApplication {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AnimationServer(int &argc, char **argv);
|
||||
~AnimationServer();
|
||||
private slots:
|
||||
void readPendingDatagrams();
|
||||
};
|
||||
|
||||
|
||||
#endif /* defined(__hifi__AnimationServer__) */
|
|
@ -6,884 +6,10 @@
|
|||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#include <EnvironmentData.h>
|
||||
#include <NodeList.h>
|
||||
#include <NodeTypes.h>
|
||||
#include <OctalCode.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <JurisdictionListener.h>
|
||||
#include <SceneUtils.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <VoxelEditPacketSender.h>
|
||||
#include <VoxelTree.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#include "Systime.h"
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
|
||||
bool shouldShowPacketsPerSecond = false; // do we want to debug packets per second
|
||||
bool includeBillboard = true;
|
||||
bool includeBorderTracer = true;
|
||||
bool includeMovingBug = true;
|
||||
bool includeBlinkingVoxel = false;
|
||||
bool includeDanceFloor = true;
|
||||
bool buildStreet = false;
|
||||
bool nonThreadedPacketSender = false;
|
||||
int packetsPerSecond = PacketSender::DEFAULT_PACKETS_PER_SECOND;
|
||||
bool waitForVoxelServer = true;
|
||||
#include "AnimationServer.h"
|
||||
|
||||
|
||||
const int ANIMATION_LISTEN_PORT = 40107;
|
||||
int ANIMATE_FPS = 60;
|
||||
double ANIMATE_FPS_IN_MILLISECONDS = 1000.0/ANIMATE_FPS; // determines FPS from our desired FPS
|
||||
int ANIMATE_VOXELS_INTERVAL_USECS = (ANIMATE_FPS_IN_MILLISECONDS * 1000.0); // converts from milliseconds to usecs
|
||||
|
||||
|
||||
int PROCESSING_FPS = 60;
|
||||
double PROCESSING_FPS_IN_MILLISECONDS = 1000.0/PROCESSING_FPS; // determines FPS from our desired FPS
|
||||
int FUDGE_USECS = 650; // a little bit of fudge to actually do some processing
|
||||
int PROCESSING_INTERVAL_USECS = (PROCESSING_FPS_IN_MILLISECONDS * 1000.0) - FUDGE_USECS; // converts from milliseconds to usecs
|
||||
|
||||
bool wantLocalDomain = false;
|
||||
|
||||
unsigned long packetsSent = 0;
|
||||
unsigned long bytesSent = 0;
|
||||
|
||||
|
||||
JurisdictionListener* jurisdictionListener = NULL;
|
||||
VoxelEditPacketSender* voxelEditPacketSender = NULL;
|
||||
|
||||
|
||||
glm::vec3 rotatePoint(glm::vec3 point, float angle) {
|
||||
// First, create the quaternion based on this angle of rotation
|
||||
glm::quat q(glm::vec3(0, -angle, 0));
|
||||
|
||||
// Next, create a rotation matrix from that quaternion
|
||||
glm::mat4 rotation = glm::mat4_cast(q);
|
||||
|
||||
// Transform the original vectors by the rotation matrix to get the new vectors
|
||||
glm::vec4 quatPoint(point.x, point.y, point.z, 0);
|
||||
glm::vec4 newPoint = quatPoint * rotation;
|
||||
|
||||
return glm::vec3(newPoint.x, newPoint.y, newPoint.z);
|
||||
}
|
||||
|
||||
|
||||
const float BUG_VOXEL_SIZE = 0.0625f / TREE_SCALE;
|
||||
glm::vec3 bugPosition = glm::vec3(BUG_VOXEL_SIZE * 20.0, BUG_VOXEL_SIZE * 30.0, BUG_VOXEL_SIZE * 20.0);
|
||||
glm::vec3 bugDirection = glm::vec3(0, 0, 1);
|
||||
const int VOXELS_PER_BUG = 18;
|
||||
glm::vec3 bugPathCenter = glm::vec3(0.25f,0.15f,0.25f); // glm::vec3(BUG_VOXEL_SIZE * 150.0, BUG_VOXEL_SIZE * 30.0, BUG_VOXEL_SIZE * 150.0);
|
||||
float bugPathRadius = 0.2f; //BUG_VOXEL_SIZE * 140.0;
|
||||
float bugPathTheta = 0.0 * PI_OVER_180;
|
||||
float bugRotation = 0.0 * PI_OVER_180;
|
||||
float bugAngleDelta = 0.2 * PI_OVER_180;
|
||||
bool moveBugInLine = false;
|
||||
|
||||
class BugPart {
|
||||
public:
|
||||
glm::vec3 partLocation;
|
||||
unsigned char partColor[3];
|
||||
|
||||
BugPart(const glm::vec3& location, unsigned char red, unsigned char green, unsigned char blue ) {
|
||||
partLocation = location;
|
||||
partColor[0] = red;
|
||||
partColor[1] = green;
|
||||
partColor[2] = blue;
|
||||
}
|
||||
};
|
||||
|
||||
const BugPart bugParts[VOXELS_PER_BUG] = {
|
||||
|
||||
// tail
|
||||
BugPart(glm::vec3( 0, 0, -3), 51, 51, 153) ,
|
||||
BugPart(glm::vec3( 0, 0, -2), 51, 51, 153) ,
|
||||
BugPart(glm::vec3( 0, 0, -1), 51, 51, 153) ,
|
||||
|
||||
// body
|
||||
BugPart(glm::vec3( 0, 0, 0), 255, 200, 0) ,
|
||||
BugPart(glm::vec3( 0, 0, 1), 255, 200, 0) ,
|
||||
|
||||
// head
|
||||
BugPart(glm::vec3( 0, 0, 2), 200, 0, 0) ,
|
||||
|
||||
// eyes
|
||||
BugPart(glm::vec3( 1, 0, 3), 64, 64, 64) ,
|
||||
BugPart(glm::vec3(-1, 0, 3), 64, 64, 64) ,
|
||||
|
||||
// wings
|
||||
BugPart(glm::vec3( 3, 1, 1), 0, 153, 0) ,
|
||||
BugPart(glm::vec3( 2, 1, 1), 0, 153, 0) ,
|
||||
BugPart(glm::vec3( 1, 0, 1), 0, 153, 0) ,
|
||||
BugPart(glm::vec3(-1, 0, 1), 0, 153, 0) ,
|
||||
BugPart(glm::vec3(-2, 1, 1), 0, 153, 0) ,
|
||||
BugPart(glm::vec3(-3, 1, 1), 0, 153, 0) ,
|
||||
|
||||
|
||||
BugPart(glm::vec3( 2, -1, 0), 153, 200, 0) ,
|
||||
BugPart(glm::vec3( 1, -1, 0), 153, 200, 0) ,
|
||||
BugPart(glm::vec3(-1, -1, 0), 153, 200, 0) ,
|
||||
BugPart(glm::vec3(-2, -1, 0), 153, 200, 0) ,
|
||||
};
|
||||
|
||||
static void renderMovingBug() {
|
||||
VoxelDetail details[VOXELS_PER_BUG];
|
||||
|
||||
// Generate voxels for where bug used to be
|
||||
for (int i = 0; i < VOXELS_PER_BUG; i++) {
|
||||
details[i].s = BUG_VOXEL_SIZE;
|
||||
|
||||
glm::vec3 partAt = bugParts[i].partLocation * BUG_VOXEL_SIZE * (bugDirection.x < 0 ? -1.0f : 1.0f);
|
||||
glm::vec3 rotatedPartAt = rotatePoint(partAt, bugRotation);
|
||||
glm::vec3 offsetPartAt = rotatedPartAt + bugPosition;
|
||||
|
||||
details[i].x = offsetPartAt.x;
|
||||
details[i].y = offsetPartAt.y;
|
||||
details[i].z = offsetPartAt.z;
|
||||
|
||||
details[i].red = bugParts[i].partColor[0];
|
||||
details[i].green = bugParts[i].partColor[1];
|
||||
details[i].blue = bugParts[i].partColor[2];
|
||||
}
|
||||
|
||||
// send the "erase message" first...
|
||||
PACKET_TYPE message = PACKET_TYPE_VOXEL_ERASE;
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details);
|
||||
|
||||
// Move the bug...
|
||||
if (moveBugInLine) {
|
||||
bugPosition.x += (bugDirection.x * BUG_VOXEL_SIZE);
|
||||
bugPosition.y += (bugDirection.y * BUG_VOXEL_SIZE);
|
||||
bugPosition.z += (bugDirection.z * BUG_VOXEL_SIZE);
|
||||
|
||||
// Check boundaries
|
||||
if (bugPosition.z > 1.0) {
|
||||
bugDirection.z = -1;
|
||||
}
|
||||
if (bugPosition.z < BUG_VOXEL_SIZE) {
|
||||
bugDirection.z = 1;
|
||||
}
|
||||
} else {
|
||||
|
||||
//printf("bugPathCenter=(%f,%f,%f)\n", bugPathCenter.x, bugPathCenter.y, bugPathCenter.z);
|
||||
|
||||
bugPathTheta += bugAngleDelta; // move slightly
|
||||
bugRotation -= bugAngleDelta; // rotate slightly
|
||||
|
||||
// If we loop past end of circle, just reset back into normal range
|
||||
if (bugPathTheta > (360.0f * PI_OVER_180)) {
|
||||
bugPathTheta = 0;
|
||||
bugRotation = 0;
|
||||
}
|
||||
|
||||
float x = bugPathCenter.x + bugPathRadius * cos(bugPathTheta);
|
||||
float z = bugPathCenter.z + bugPathRadius * sin(bugPathTheta);
|
||||
float y = bugPathCenter.y;
|
||||
|
||||
bugPosition = glm::vec3(x, y, z);
|
||||
//printf("bugPathTheta=%f\n", bugPathTheta);
|
||||
//printf("bugRotation=%f\n", bugRotation);
|
||||
}
|
||||
|
||||
//printf("bugPosition=(%f,%f,%f)\n", bugPosition.x, bugPosition.y, bugPosition.z);
|
||||
//printf("bugDirection=(%f,%f,%f)\n", bugDirection.x, bugDirection.y, bugDirection.z);
|
||||
// would be nice to add some randomness here...
|
||||
|
||||
// Generate voxels for where bug is going to
|
||||
for (int i = 0; i < VOXELS_PER_BUG; i++) {
|
||||
details[i].s = BUG_VOXEL_SIZE;
|
||||
|
||||
glm::vec3 partAt = bugParts[i].partLocation * BUG_VOXEL_SIZE * (bugDirection.x < 0 ? -1.0f : 1.0f);
|
||||
glm::vec3 rotatedPartAt = rotatePoint(partAt, bugRotation);
|
||||
glm::vec3 offsetPartAt = rotatedPartAt + bugPosition;
|
||||
|
||||
details[i].x = offsetPartAt.x;
|
||||
details[i].y = offsetPartAt.y;
|
||||
details[i].z = offsetPartAt.z;
|
||||
|
||||
details[i].red = bugParts[i].partColor[0];
|
||||
details[i].green = bugParts[i].partColor[1];
|
||||
details[i].blue = bugParts[i].partColor[2];
|
||||
}
|
||||
|
||||
// send the "create message" ...
|
||||
message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE;
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details);
|
||||
}
|
||||
|
||||
|
||||
float intensity = 0.5f;
|
||||
float intensityIncrement = 0.1f;
|
||||
const float MAX_INTENSITY = 1.0f;
|
||||
const float MIN_INTENSITY = 0.5f;
|
||||
const float BEACON_SIZE = 0.25f / TREE_SCALE; // approximately 1/4th meter
|
||||
|
||||
static void sendVoxelBlinkMessage() {
|
||||
VoxelDetail detail;
|
||||
detail.s = BEACON_SIZE;
|
||||
|
||||
glm::vec3 position = glm::vec3(0, 0, detail.s);
|
||||
|
||||
detail.x = detail.s * floor(position.x / detail.s);
|
||||
detail.y = detail.s * floor(position.y / detail.s);
|
||||
detail.z = detail.s * floor(position.z / detail.s);
|
||||
|
||||
::intensity += ::intensityIncrement;
|
||||
if (::intensity >= MAX_INTENSITY) {
|
||||
::intensity = MAX_INTENSITY;
|
||||
::intensityIncrement = -::intensityIncrement;
|
||||
}
|
||||
if (::intensity <= MIN_INTENSITY) {
|
||||
::intensity = MIN_INTENSITY;
|
||||
::intensityIncrement = -::intensityIncrement;
|
||||
}
|
||||
|
||||
detail.red = 255 * ::intensity;
|
||||
detail.green = 0 * ::intensity;
|
||||
detail.blue = 0 * ::intensity;
|
||||
|
||||
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE;
|
||||
|
||||
::voxelEditPacketSender->sendVoxelEditMessage(message, detail);
|
||||
}
|
||||
|
||||
bool stringOfLightsInitialized = false;
|
||||
int currentLight = 0;
|
||||
int lightMovementDirection = 1;
|
||||
const int SEGMENT_COUNT = 4;
|
||||
const int LIGHTS_PER_SEGMENT = 80;
|
||||
const int LIGHT_COUNT = LIGHTS_PER_SEGMENT * SEGMENT_COUNT;
|
||||
glm::vec3 stringOfLights[LIGHT_COUNT];
|
||||
unsigned char offColor[3] = { 240, 240, 240 };
|
||||
unsigned char onColor[3] = { 0, 255, 255 };
|
||||
const float STRING_OF_LIGHTS_SIZE = 0.125f / TREE_SCALE; // approximately 1/8th meter
|
||||
|
||||
static void sendBlinkingStringOfLights() {
|
||||
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
|
||||
float lightScale = STRING_OF_LIGHTS_SIZE;
|
||||
static VoxelDetail details[LIGHTS_PER_SEGMENT];
|
||||
|
||||
// first initialized the string of lights if needed...
|
||||
if (!stringOfLightsInitialized) {
|
||||
for (int segment = 0; segment < SEGMENT_COUNT; segment++) {
|
||||
for (int indexInSegment = 0; indexInSegment < LIGHTS_PER_SEGMENT; indexInSegment++) {
|
||||
|
||||
int i = (segment * LIGHTS_PER_SEGMENT) + indexInSegment;
|
||||
|
||||
// four different segments on sides of initial platform
|
||||
switch (segment) {
|
||||
case 0:
|
||||
// along x axis
|
||||
stringOfLights[i] = glm::vec3(indexInSegment * lightScale, 0, 0);
|
||||
break;
|
||||
case 1:
|
||||
// parallel to Z axis at outer X edge
|
||||
stringOfLights[i] = glm::vec3(LIGHTS_PER_SEGMENT * lightScale, 0, indexInSegment * lightScale);
|
||||
break;
|
||||
case 2:
|
||||
// parallel to X axis at outer Z edge
|
||||
stringOfLights[i] = glm::vec3((LIGHTS_PER_SEGMENT-indexInSegment) * lightScale, 0,
|
||||
LIGHTS_PER_SEGMENT * lightScale);
|
||||
break;
|
||||
case 3:
|
||||
// on Z axis
|
||||
stringOfLights[i] = glm::vec3(0, 0, (LIGHTS_PER_SEGMENT-indexInSegment) * lightScale);
|
||||
break;
|
||||
}
|
||||
|
||||
details[indexInSegment].s = STRING_OF_LIGHTS_SIZE;
|
||||
details[indexInSegment].x = stringOfLights[i].x;
|
||||
details[indexInSegment].y = stringOfLights[i].y;
|
||||
details[indexInSegment].z = stringOfLights[i].z;
|
||||
|
||||
details[indexInSegment].red = offColor[0];
|
||||
details[indexInSegment].green = offColor[1];
|
||||
details[indexInSegment].blue = offColor[2];
|
||||
}
|
||||
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, LIGHTS_PER_SEGMENT, (VoxelDetail*)&details);
|
||||
}
|
||||
stringOfLightsInitialized = true;
|
||||
} else {
|
||||
// turn off current light
|
||||
details[0].x = stringOfLights[currentLight].x;
|
||||
details[0].y = stringOfLights[currentLight].y;
|
||||
details[0].z = stringOfLights[currentLight].z;
|
||||
details[0].red = offColor[0];
|
||||
details[0].green = offColor[1];
|
||||
details[0].blue = offColor[2];
|
||||
|
||||
// move current light...
|
||||
// if we're at the end of our string, then change direction
|
||||
if (currentLight == LIGHT_COUNT-1) {
|
||||
lightMovementDirection = -1;
|
||||
}
|
||||
if (currentLight == 0) {
|
||||
lightMovementDirection = 1;
|
||||
}
|
||||
currentLight += lightMovementDirection;
|
||||
|
||||
// turn on new current light
|
||||
details[1].x = stringOfLights[currentLight].x;
|
||||
details[1].y = stringOfLights[currentLight].y;
|
||||
details[1].z = stringOfLights[currentLight].z;
|
||||
details[1].red = onColor[0];
|
||||
details[1].green = onColor[1];
|
||||
details[1].blue = onColor[2];
|
||||
|
||||
// send both changes in same message
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, 2, (VoxelDetail*)&details);
|
||||
}
|
||||
}
|
||||
|
||||
bool danceFloorInitialized = false;
|
||||
const float DANCE_FLOOR_LIGHT_SIZE = 1.0f / TREE_SCALE; // approximately 1 meter
|
||||
const int DANCE_FLOOR_LENGTH = 10;
|
||||
const int DANCE_FLOOR_WIDTH = 10;
|
||||
glm::vec3 danceFloorPosition(100.0f / TREE_SCALE, 30.0f / TREE_SCALE, 10.0f / TREE_SCALE);
|
||||
glm::vec3 danceFloorLights[DANCE_FLOOR_LENGTH][DANCE_FLOOR_WIDTH];
|
||||
unsigned char danceFloorOffColor[3] = { 240, 240, 240 };
|
||||
const int DANCE_FLOOR_COLORS = 6;
|
||||
|
||||
unsigned char danceFloorOnColorA[DANCE_FLOOR_COLORS][3] = {
|
||||
{ 255, 0, 0 }, { 0, 255, 0 }, { 0, 0, 255 },
|
||||
{ 0, 191, 255 }, { 0, 250, 154 }, { 255, 69, 0 },
|
||||
};
|
||||
unsigned char danceFloorOnColorB[DANCE_FLOOR_COLORS][3] = {
|
||||
{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } ,
|
||||
{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }
|
||||
};
|
||||
float danceFloorGradient = 0.5f;
|
||||
const float BEATS_PER_MINUTE = 118.0f;
|
||||
const float SECONDS_PER_MINUTE = 60.0f;
|
||||
const float FRAMES_PER_BEAT = (SECONDS_PER_MINUTE * ANIMATE_FPS) / BEATS_PER_MINUTE;
|
||||
float danceFloorGradientIncrement = 1.0f / FRAMES_PER_BEAT;
|
||||
const float DANCE_FLOOR_MAX_GRADIENT = 1.0f;
|
||||
const float DANCE_FLOOR_MIN_GRADIENT = 0.0f;
|
||||
const int DANCE_FLOOR_VOXELS_PER_PACKET = 100;
|
||||
const int PACKETS_PER_DANCE_FLOOR = DANCE_FLOOR_VOXELS_PER_PACKET / (DANCE_FLOOR_WIDTH * DANCE_FLOOR_LENGTH);
|
||||
int danceFloorColors[DANCE_FLOOR_WIDTH][DANCE_FLOOR_LENGTH];
|
||||
|
||||
void sendDanceFloor() {
|
||||
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
|
||||
float lightScale = DANCE_FLOOR_LIGHT_SIZE;
|
||||
static VoxelDetail details[DANCE_FLOOR_VOXELS_PER_PACKET];
|
||||
|
||||
// first initialized the billboard of lights if needed...
|
||||
if (!::danceFloorInitialized) {
|
||||
for (int i = 0; i < DANCE_FLOOR_WIDTH; i++) {
|
||||
for (int j = 0; j < DANCE_FLOOR_LENGTH; j++) {
|
||||
|
||||
int randomColorIndex = randIntInRange(-DANCE_FLOOR_COLORS, DANCE_FLOOR_COLORS);
|
||||
::danceFloorColors[i][j] = randomColorIndex;
|
||||
::danceFloorLights[i][j] = ::danceFloorPosition +
|
||||
glm::vec3(i * DANCE_FLOOR_LIGHT_SIZE, 0, j * DANCE_FLOOR_LIGHT_SIZE);
|
||||
}
|
||||
}
|
||||
::danceFloorInitialized = true;
|
||||
}
|
||||
|
||||
::danceFloorGradient += ::danceFloorGradientIncrement;
|
||||
|
||||
if (::danceFloorGradient >= DANCE_FLOOR_MAX_GRADIENT) {
|
||||
::danceFloorGradient = DANCE_FLOOR_MAX_GRADIENT;
|
||||
::danceFloorGradientIncrement = -::danceFloorGradientIncrement;
|
||||
}
|
||||
if (::danceFloorGradient <= DANCE_FLOOR_MIN_GRADIENT) {
|
||||
::danceFloorGradient = DANCE_FLOOR_MIN_GRADIENT;
|
||||
::danceFloorGradientIncrement = -::danceFloorGradientIncrement;
|
||||
}
|
||||
|
||||
for (int i = 0; i < DANCE_FLOOR_LENGTH; i++) {
|
||||
for (int j = 0; j < DANCE_FLOOR_WIDTH; j++) {
|
||||
|
||||
int nthVoxel = ((i * DANCE_FLOOR_WIDTH) + j);
|
||||
int item = nthVoxel % DANCE_FLOOR_VOXELS_PER_PACKET;
|
||||
|
||||
::danceFloorLights[i][j] = ::danceFloorPosition +
|
||||
glm::vec3(i * DANCE_FLOOR_LIGHT_SIZE, 0, j * DANCE_FLOOR_LIGHT_SIZE);
|
||||
|
||||
details[item].s = lightScale;
|
||||
details[item].x = ::danceFloorLights[i][j].x;
|
||||
details[item].y = ::danceFloorLights[i][j].y;
|
||||
details[item].z = ::danceFloorLights[i][j].z;
|
||||
|
||||
if (danceFloorColors[i][j] > 0) {
|
||||
int color = danceFloorColors[i][j] - 1;
|
||||
details[item].red = (::danceFloorOnColorA[color][0] +
|
||||
((::danceFloorOnColorB[color][0] - ::danceFloorOnColorA[color][0])
|
||||
* ::danceFloorGradient));
|
||||
details[item].green = (::danceFloorOnColorA[color][1] +
|
||||
((::danceFloorOnColorB[color][1] - ::danceFloorOnColorA[color][1])
|
||||
* ::danceFloorGradient));
|
||||
details[item].blue = (::danceFloorOnColorA[color][2] +
|
||||
((::danceFloorOnColorB[color][2] - ::danceFloorOnColorA[color][2])
|
||||
* ::danceFloorGradient));
|
||||
} else if (::danceFloorColors[i][j] < 0) {
|
||||
int color = -(::danceFloorColors[i][j] + 1);
|
||||
details[item].red = (::danceFloorOnColorB[color][0] +
|
||||
((::danceFloorOnColorA[color][0] - ::danceFloorOnColorB[color][0])
|
||||
* ::danceFloorGradient));
|
||||
details[item].green = (::danceFloorOnColorB[color][1] +
|
||||
((::danceFloorOnColorA[color][1] - ::danceFloorOnColorB[color][1])
|
||||
* ::danceFloorGradient));
|
||||
details[item].blue = (::danceFloorOnColorB[color][2] +
|
||||
((::danceFloorOnColorA[color][2] - ::danceFloorOnColorB[color][2])
|
||||
* ::danceFloorGradient));
|
||||
} else {
|
||||
int color = 0;
|
||||
details[item].red = (::danceFloorOnColorB[color][0] +
|
||||
((::danceFloorOnColorA[color][0] - ::danceFloorOnColorB[color][0])
|
||||
* ::danceFloorGradient));
|
||||
details[item].green = (::danceFloorOnColorB[color][1] +
|
||||
((::danceFloorOnColorA[color][1] - ::danceFloorOnColorB[color][1])
|
||||
* ::danceFloorGradient));
|
||||
details[item].blue = (::danceFloorOnColorB[color][2] +
|
||||
((::danceFloorOnColorA[color][2] - ::danceFloorOnColorB[color][2])
|
||||
* ::danceFloorGradient));
|
||||
}
|
||||
|
||||
if (item == DANCE_FLOOR_VOXELS_PER_PACKET - 1) {
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, DANCE_FLOOR_VOXELS_PER_PACKET, (VoxelDetail*)&details);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool billboardInitialized = false;
|
||||
const int BILLBOARD_HEIGHT = 9;
|
||||
const int BILLBOARD_WIDTH = 45;
|
||||
glm::vec3 billboardPosition((0.125f / TREE_SCALE),(0.125f / TREE_SCALE),0);
|
||||
glm::vec3 billboardLights[BILLBOARD_HEIGHT][BILLBOARD_WIDTH];
|
||||
unsigned char billboardOffColor[3] = { 240, 240, 240 };
|
||||
unsigned char billboardOnColorA[3] = { 0, 0, 255 };
|
||||
unsigned char billboardOnColorB[3] = { 0, 255, 0 };
|
||||
float billboardGradient = 0.5f;
|
||||
float billboardGradientIncrement = 0.01f;
|
||||
const float BILLBOARD_MAX_GRADIENT = 1.0f;
|
||||
const float BILLBOARD_MIN_GRADIENT = 0.0f;
|
||||
const float BILLBOARD_LIGHT_SIZE = 0.125f / TREE_SCALE; // approximately 1/8 meter per light
|
||||
const int VOXELS_PER_PACKET = 81;
|
||||
const int PACKETS_PER_BILLBOARD = VOXELS_PER_PACKET / (BILLBOARD_HEIGHT * BILLBOARD_WIDTH);
|
||||
|
||||
|
||||
// top to bottom...
|
||||
bool billboardMessage[BILLBOARD_HEIGHT][BILLBOARD_WIDTH] = {
|
||||
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
|
||||
{ 0,0,1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,1,1,1,1,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,0,0 },
|
||||
{ 0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,1,1,1,0,0,0,0,0 },
|
||||
{ 0,0,1,1,1,1,0,1,0,1,1,1,0,1,1,1,0,0,1,1,1,0,0,0,0,1,1,1,0,1,1,1,0,1,0,1,0,0,1,0,0,1,0,1,0 },
|
||||
{ 0,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,0,1,0,0,1,0,1,0 },
|
||||
{ 0,0,1,0,0,1,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,1,1,0,1,1,1,0,1,0,1,0,0,1,0,0,1,1,1,0 },
|
||||
{ 0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0 },
|
||||
{ 0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0 },
|
||||
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
|
||||
};
|
||||
|
||||
static void sendBillboard() {
|
||||
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
|
||||
float lightScale = BILLBOARD_LIGHT_SIZE;
|
||||
static VoxelDetail details[VOXELS_PER_PACKET];
|
||||
|
||||
// first initialized the billboard of lights if needed...
|
||||
if (!billboardInitialized) {
|
||||
for (int i = 0; i < BILLBOARD_HEIGHT; i++) {
|
||||
for (int j = 0; j < BILLBOARD_WIDTH; j++) {
|
||||
|
||||
billboardLights[i][j] = billboardPosition + glm::vec3(j * lightScale, (float)((BILLBOARD_HEIGHT - i) * lightScale), 0);
|
||||
}
|
||||
}
|
||||
billboardInitialized = true;
|
||||
}
|
||||
|
||||
::billboardGradient += ::billboardGradientIncrement;
|
||||
|
||||
if (::billboardGradient >= BILLBOARD_MAX_GRADIENT) {
|
||||
::billboardGradient = BILLBOARD_MAX_GRADIENT;
|
||||
::billboardGradientIncrement = -::billboardGradientIncrement;
|
||||
}
|
||||
if (::billboardGradient <= BILLBOARD_MIN_GRADIENT) {
|
||||
::billboardGradient = BILLBOARD_MIN_GRADIENT;
|
||||
::billboardGradientIncrement = -::billboardGradientIncrement;
|
||||
}
|
||||
|
||||
for (int i = 0; i < BILLBOARD_HEIGHT; i++) {
|
||||
for (int j = 0; j < BILLBOARD_WIDTH; j++) {
|
||||
|
||||
int nthVoxel = ((i * BILLBOARD_WIDTH) + j);
|
||||
int item = nthVoxel % VOXELS_PER_PACKET;
|
||||
|
||||
billboardLights[i][j] = billboardPosition + glm::vec3(j * lightScale, (float)((BILLBOARD_HEIGHT - i) * lightScale), 0);
|
||||
|
||||
details[item].s = lightScale;
|
||||
details[item].x = billboardLights[i][j].x;
|
||||
details[item].y = billboardLights[i][j].y;
|
||||
details[item].z = billboardLights[i][j].z;
|
||||
|
||||
if (billboardMessage[i][j]) {
|
||||
details[item].red = (billboardOnColorA[0] + ((billboardOnColorB[0] - billboardOnColorA[0]) * ::billboardGradient));
|
||||
details[item].green = (billboardOnColorA[1] + ((billboardOnColorB[1] - billboardOnColorA[1]) * ::billboardGradient));
|
||||
details[item].blue = (billboardOnColorA[2] + ((billboardOnColorB[2] - billboardOnColorA[2]) * ::billboardGradient));
|
||||
} else {
|
||||
details[item].red = billboardOffColor[0];
|
||||
details[item].green = billboardOffColor[1];
|
||||
details[item].blue = billboardOffColor[2];
|
||||
}
|
||||
|
||||
if (item == VOXELS_PER_PACKET - 1) {
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_PACKET, (VoxelDetail*)&details);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_VOXEL_SET_DESTRUCTIVE; // we're a bully!
|
||||
static VoxelDetail details[BRICKS_PER_PACKET];
|
||||
|
||||
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) {
|
||||
::voxelEditPacketSender->queueVoxelEditMessages(message, BRICKS_PER_PACKET, (VoxelDetail*)&details);
|
||||
}
|
||||
}
|
||||
}
|
||||
roadInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
double start = 0;
|
||||
|
||||
|
||||
void* animateVoxels(void* args) {
|
||||
|
||||
uint64_t lastAnimateTime = 0;
|
||||
uint64_t lastProcessTime = 0;
|
||||
int processesPerAnimate = 0;
|
||||
|
||||
bool firstTime = true;
|
||||
|
||||
std::cout << "Setting PPS to " << ::packetsPerSecond << "\n";
|
||||
::voxelEditPacketSender->setPacketsPerSecond(::packetsPerSecond);
|
||||
|
||||
std::cout << "PPS set to " << ::voxelEditPacketSender->getPacketsPerSecond() << "\n";
|
||||
|
||||
while (true) {
|
||||
|
||||
// If we're asked to wait for voxel servers, and there isn't one available yet, then
|
||||
// let the voxelEditPacketSender process and move on.
|
||||
if (::waitForVoxelServer && !::voxelEditPacketSender->voxelServersExist()) {
|
||||
if (::nonThreadedPacketSender) {
|
||||
::voxelEditPacketSender->process();
|
||||
}
|
||||
} else {
|
||||
if (firstTime) {
|
||||
lastAnimateTime = usecTimestampNow();
|
||||
firstTime = false;
|
||||
}
|
||||
lastProcessTime = usecTimestampNow();
|
||||
|
||||
int packetsStarting = 0;
|
||||
int packetsEnding = 0;
|
||||
|
||||
// The while loop will be running at PROCESSING_FPS, but we only want to call these animation functions at
|
||||
// ANIMATE_FPS. So we check out last animate time and only call these if we've elapsed that time.
|
||||
uint64_t now = usecTimestampNow();
|
||||
uint64_t animationElapsed = now - lastAnimateTime;
|
||||
int withinAnimationTarget = ANIMATE_VOXELS_INTERVAL_USECS - animationElapsed;
|
||||
const int CLOSE_ENOUGH_TO_ANIMATE = 2000; // approximately 2 ms
|
||||
|
||||
int animateLoopsPerAnimate = 0;
|
||||
while (withinAnimationTarget < CLOSE_ENOUGH_TO_ANIMATE) {
|
||||
processesPerAnimate = 0;
|
||||
animateLoopsPerAnimate++;
|
||||
|
||||
lastAnimateTime = now;
|
||||
packetsStarting = ::voxelEditPacketSender->packetsToSendCount();
|
||||
|
||||
// some animations
|
||||
//sendVoxelBlinkMessage();
|
||||
|
||||
if (::includeBillboard) {
|
||||
sendBillboard();
|
||||
}
|
||||
if (::includeBorderTracer) {
|
||||
sendBlinkingStringOfLights();
|
||||
}
|
||||
if (::includeMovingBug) {
|
||||
renderMovingBug();
|
||||
}
|
||||
if (::includeBlinkingVoxel) {
|
||||
sendVoxelBlinkMessage();
|
||||
}
|
||||
if (::includeDanceFloor) {
|
||||
sendDanceFloor();
|
||||
}
|
||||
|
||||
if (::buildStreet) {
|
||||
doBuildStreet();
|
||||
}
|
||||
|
||||
packetsEnding = ::voxelEditPacketSender->packetsToSendCount();
|
||||
|
||||
if (animationElapsed > ANIMATE_VOXELS_INTERVAL_USECS) {
|
||||
animationElapsed -= ANIMATE_VOXELS_INTERVAL_USECS; // credit ourselves one animation frame
|
||||
} else {
|
||||
animationElapsed = 0;
|
||||
}
|
||||
withinAnimationTarget = ANIMATE_VOXELS_INTERVAL_USECS - animationElapsed;
|
||||
|
||||
::voxelEditPacketSender->releaseQueuedMessages();
|
||||
}
|
||||
|
||||
if (::nonThreadedPacketSender) {
|
||||
::voxelEditPacketSender->process();
|
||||
}
|
||||
processesPerAnimate++;
|
||||
|
||||
if (::shouldShowPacketsPerSecond) {
|
||||
float lifetimeSeconds = ::voxelEditPacketSender->getLifetimeInSeconds();
|
||||
int targetPPS = ::voxelEditPacketSender->getPacketsPerSecond();
|
||||
float lifetimePPS = ::voxelEditPacketSender->getLifetimePPS();
|
||||
float lifetimeBPS = ::voxelEditPacketSender->getLifetimeBPS();
|
||||
uint64_t totalPacketsSent = ::voxelEditPacketSender->getLifetimePacketsSent();
|
||||
uint64_t totalBytesSent = ::voxelEditPacketSender->getLifetimeBytesSent();
|
||||
|
||||
float lifetimePPSQueued = ::voxelEditPacketSender->getLifetimePPSQueued();
|
||||
float lifetimeBPSQueued = ::voxelEditPacketSender->getLifetimeBPSQueued();
|
||||
uint64_t totalPacketsQueued = ::voxelEditPacketSender->getLifetimePacketsQueued();
|
||||
uint64_t totalBytesQueued = ::voxelEditPacketSender->getLifetimeBytesQueued();
|
||||
|
||||
uint64_t packetsPending = ::voxelEditPacketSender->packetsToSendCount();
|
||||
|
||||
printf("lifetime=%f secs packetsSent=%lld, bytesSent=%lld targetPPS=%d pps=%f bps=%f\n",
|
||||
lifetimeSeconds, totalPacketsSent, totalBytesSent, targetPPS, lifetimePPS, lifetimeBPS);
|
||||
printf("packetsPending=%lld packetsQueued=%lld, bytesQueued=%lld ppsQueued=%f bpsQueued=%f\n",
|
||||
packetsPending, totalPacketsQueued, totalBytesQueued, lifetimePPSQueued, lifetimeBPSQueued);
|
||||
}
|
||||
}
|
||||
// dynamically sleep until we need to fire off the next set of voxels
|
||||
uint64_t usecToSleep = PROCESSING_INTERVAL_USECS - (usecTimestampNow() - lastProcessTime);
|
||||
if (usecToSleep > PROCESSING_INTERVAL_USECS) {
|
||||
usecToSleep = PROCESSING_INTERVAL_USECS;
|
||||
}
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_exit(0);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char * argv[])
|
||||
{
|
||||
::start = usecTimestampNow();
|
||||
|
||||
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_ANIMATION_SERVER, ANIMATION_LISTEN_PORT);
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
// Handle Local Domain testing with the --local command line
|
||||
const char* NON_THREADED_PACKETSENDER = "--NonThreadedPacketSender";
|
||||
::nonThreadedPacketSender = cmdOptionExists(argc, argv, NON_THREADED_PACKETSENDER);
|
||||
printf("nonThreadedPacketSender=%s\n", debug::valueOf(::nonThreadedPacketSender));
|
||||
|
||||
// Handle Local Domain testing with the --local command line
|
||||
const char* NO_BILLBOARD = "--NoBillboard";
|
||||
::includeBillboard = !cmdOptionExists(argc, argv, NO_BILLBOARD);
|
||||
printf("includeBillboard=%s\n", debug::valueOf(::includeBillboard));
|
||||
|
||||
const char* NO_BORDER_TRACER = "--NoBorderTracer";
|
||||
::includeBorderTracer = !cmdOptionExists(argc, argv, NO_BORDER_TRACER);
|
||||
printf("includeBorderTracer=%s\n", debug::valueOf(::includeBorderTracer));
|
||||
|
||||
const char* NO_MOVING_BUG = "--NoMovingBug";
|
||||
::includeMovingBug = !cmdOptionExists(argc, argv, NO_MOVING_BUG);
|
||||
printf("includeMovingBug=%s\n", debug::valueOf(::includeMovingBug));
|
||||
|
||||
const char* INCLUDE_BLINKING_VOXEL = "--includeBlinkingVoxel";
|
||||
::includeBlinkingVoxel = cmdOptionExists(argc, argv, INCLUDE_BLINKING_VOXEL);
|
||||
printf("includeBlinkingVoxel=%s\n", debug::valueOf(::includeBlinkingVoxel));
|
||||
|
||||
const char* NO_DANCE_FLOOR = "--NoDanceFloor";
|
||||
::includeDanceFloor = !cmdOptionExists(argc, argv, NO_DANCE_FLOOR);
|
||||
printf("includeDanceFloor=%s\n", debug::valueOf(::includeDanceFloor));
|
||||
|
||||
const char* BUILD_STREET = "--BuildStreet";
|
||||
::buildStreet = cmdOptionExists(argc, argv, BUILD_STREET);
|
||||
printf("buildStreet=%s\n", debug::valueOf(::buildStreet));
|
||||
|
||||
// Handle Local Domain testing with the --local command line
|
||||
const char* showPPS = "--showPPS";
|
||||
::shouldShowPacketsPerSecond = cmdOptionExists(argc, argv, showPPS);
|
||||
|
||||
// Handle Local Domain testing with the --local command line
|
||||
const char* local = "--local";
|
||||
::wantLocalDomain = cmdOptionExists(argc, argv,local);
|
||||
if (::wantLocalDomain) {
|
||||
printf("Local Domain MODE!\n");
|
||||
nodeList->setDomainIPToLocalhost();
|
||||
}
|
||||
|
||||
const char* domainHostname = getCmdOption(argc, argv, "--domain");
|
||||
if (domainHostname) {
|
||||
NodeList::getInstance()->setDomainHostname(domainHostname);
|
||||
}
|
||||
|
||||
const char* packetsPerSecondCommand = getCmdOption(argc, argv, "--pps");
|
||||
if (packetsPerSecondCommand) {
|
||||
::packetsPerSecond = atoi(packetsPerSecondCommand);
|
||||
}
|
||||
printf("packetsPerSecond=%d\n",packetsPerSecond);
|
||||
|
||||
const char* animateFPSCommand = getCmdOption(argc, argv, "--AnimateFPS");
|
||||
const char* animateIntervalCommand = getCmdOption(argc, argv, "--AnimateInterval");
|
||||
if (animateFPSCommand || animateIntervalCommand) {
|
||||
if (animateIntervalCommand) {
|
||||
::ANIMATE_FPS_IN_MILLISECONDS = atoi(animateIntervalCommand);
|
||||
::ANIMATE_VOXELS_INTERVAL_USECS = (ANIMATE_FPS_IN_MILLISECONDS * 1000.0); // converts from milliseconds to usecs
|
||||
::ANIMATE_FPS = PacketSender::USECS_PER_SECOND / ::ANIMATE_VOXELS_INTERVAL_USECS;
|
||||
} else {
|
||||
::ANIMATE_FPS = atoi(animateFPSCommand);
|
||||
::ANIMATE_FPS_IN_MILLISECONDS = 1000.0/ANIMATE_FPS; // determines FPS from our desired FPS
|
||||
::ANIMATE_VOXELS_INTERVAL_USECS = (ANIMATE_FPS_IN_MILLISECONDS * 1000.0); // converts from milliseconds to usecs
|
||||
}
|
||||
}
|
||||
printf("ANIMATE_FPS=%d\n",ANIMATE_FPS);
|
||||
printf("ANIMATE_VOXELS_INTERVAL_USECS=%d\n",ANIMATE_VOXELS_INTERVAL_USECS);
|
||||
|
||||
const char* processingFPSCommand = getCmdOption(argc, argv, "--ProcessingFPS");
|
||||
const char* processingIntervalCommand = getCmdOption(argc, argv, "--ProcessingInterval");
|
||||
if (processingFPSCommand || processingIntervalCommand) {
|
||||
if (processingIntervalCommand) {
|
||||
::PROCESSING_FPS_IN_MILLISECONDS = atoi(processingIntervalCommand);
|
||||
::PROCESSING_INTERVAL_USECS = ::PROCESSING_FPS_IN_MILLISECONDS * 1000.0;
|
||||
::PROCESSING_FPS = PacketSender::USECS_PER_SECOND / ::PROCESSING_INTERVAL_USECS;
|
||||
} else {
|
||||
::PROCESSING_FPS = atoi(processingFPSCommand);
|
||||
::PROCESSING_FPS_IN_MILLISECONDS = 1000.0/PROCESSING_FPS; // determines FPS from our desired FPS
|
||||
::PROCESSING_INTERVAL_USECS = (PROCESSING_FPS_IN_MILLISECONDS * 1000.0) - FUDGE_USECS; // converts from milliseconds to usecs
|
||||
}
|
||||
}
|
||||
printf("PROCESSING_FPS=%d\n",PROCESSING_FPS);
|
||||
printf("PROCESSING_INTERVAL_USECS=%d\n",PROCESSING_INTERVAL_USECS);
|
||||
|
||||
if (cmdOptionExists(argc, argv, "--quickExit")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
nodeList->linkedDataCreateCallback = NULL; // do we need a callback?
|
||||
nodeList->startSilentNodeRemovalThread();
|
||||
|
||||
// Create our JurisdictionListener so we'll know where to send edit packets
|
||||
::jurisdictionListener = new JurisdictionListener();
|
||||
if (::jurisdictionListener) {
|
||||
::jurisdictionListener->initialize(true);
|
||||
}
|
||||
|
||||
// Create out VoxelEditPacketSender
|
||||
::voxelEditPacketSender = new VoxelEditPacketSender;
|
||||
::voxelEditPacketSender->initialize(!::nonThreadedPacketSender);
|
||||
|
||||
if (::jurisdictionListener) {
|
||||
::voxelEditPacketSender->setVoxelServerJurisdictions(::jurisdictionListener->getJurisdictions());
|
||||
}
|
||||
if (::nonThreadedPacketSender) {
|
||||
::voxelEditPacketSender->setProcessCallIntervalHint(PROCESSING_INTERVAL_USECS);
|
||||
}
|
||||
|
||||
srand((unsigned)time(0));
|
||||
|
||||
pthread_t animateVoxelThread;
|
||||
pthread_create(&animateVoxelThread, NULL, animateVoxels, NULL);
|
||||
|
||||
HifiSockAddr nodeSockAddr;
|
||||
|
||||
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
|
||||
ssize_t receivedBytes;
|
||||
|
||||
timeval lastDomainServerCheckIn = {};
|
||||
NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1);
|
||||
|
||||
// loop to send to nodes requesting data
|
||||
while (true) {
|
||||
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
|
||||
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
|
||||
gettimeofday(&lastDomainServerCheckIn, NULL);
|
||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
}
|
||||
|
||||
// Nodes sending messages to us...
|
||||
if (nodeList->getNodeSocket().hasPendingDatagrams()
|
||||
&& (receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
|
||||
nodeSockAddr.getAddressPointer(),
|
||||
nodeSockAddr.getPortPointer())) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
|
||||
if (packetData[0] == PACKET_TYPE_JURISDICTION) {
|
||||
if (::jurisdictionListener) {
|
||||
::jurisdictionListener->queueReceivedPacket(nodeSockAddr, packetData, receivedBytes);
|
||||
}
|
||||
}
|
||||
NodeList::getInstance()->processNodeData(nodeSockAddr, packetData, receivedBytes);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_join(animateVoxelThread, NULL);
|
||||
|
||||
if (::jurisdictionListener) {
|
||||
::jurisdictionListener->terminate();
|
||||
delete ::jurisdictionListener;
|
||||
}
|
||||
|
||||
if (::voxelEditPacketSender) {
|
||||
::voxelEditPacketSender->terminate();
|
||||
delete ::voxelEditPacketSender;
|
||||
}
|
||||
|
||||
return 0;
|
||||
int main(int argc, char * argv[]) {
|
||||
AnimationServer animationServer(argc, argv);
|
||||
return animationServer.exec();
|
||||
}
|
||||
|
|
|
@ -22,6 +22,12 @@
|
|||
|
||||
const int RESTART_HOLD_TIME_MSECS = 5 * 1000;
|
||||
|
||||
void signalhandler(int sig){
|
||||
if (sig == SIGINT) {
|
||||
qApp->quit();
|
||||
}
|
||||
}
|
||||
|
||||
DomainServer* DomainServer::domainServerInstance = NULL;
|
||||
|
||||
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||
|
@ -35,6 +41,8 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
{
|
||||
DomainServer::setDomainServerInstance(this);
|
||||
|
||||
signal(SIGINT, signalhandler);
|
||||
|
||||
const char CUSTOM_PORT_OPTION[] = "-p";
|
||||
const char* customPortString = getCmdOption(argc, (const char**) argv, CUSTOM_PORT_OPTION);
|
||||
unsigned short domainServerPort = customPortString ? atoi(customPortString) : DEFAULT_DOMAIN_SERVER_PORT;
|
||||
|
@ -88,10 +96,8 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
// fire a single shot timer to add static assignments back into the queue after a restart
|
||||
QTimer::singleShot(RESTART_HOLD_TIME_MSECS, this, SLOT(addStaticAssignmentsBackToQueueAfterRestart()));
|
||||
}
|
||||
|
||||
void DomainServer::exit(int retCode) {
|
||||
cleanup();
|
||||
|
||||
connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup()));
|
||||
}
|
||||
|
||||
void DomainServer::readAvailableDatagrams() {
|
||||
|
@ -774,8 +780,6 @@ void DomainServer::addStaticAssignmentsBackToQueueAfterRestart() {
|
|||
}
|
||||
|
||||
void DomainServer::cleanup() {
|
||||
qDebug() << "cleanup called!\n";
|
||||
|
||||
_staticAssignmentFile.unmap(_staticAssignmentFileData);
|
||||
_staticAssignmentFile.close();
|
||||
}
|
|
@ -50,8 +50,6 @@ private:
|
|||
const QUuid& checkInUUI);
|
||||
void addReleasedAssignmentBackToQueue(Assignment* releasedAssignment);
|
||||
|
||||
void cleanup();
|
||||
|
||||
unsigned char* addNodeToBroadcastPacket(unsigned char* currentPosition, Node* nodeToAdd);
|
||||
|
||||
QMutex _assignmentQueueMutex;
|
||||
|
@ -69,6 +67,7 @@ private:
|
|||
private slots:
|
||||
void readAvailableDatagrams();
|
||||
void addStaticAssignmentsBackToQueueAfterRestart();
|
||||
void cleanup();
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__DomainServer__) */
|
||||
|
|
|
@ -79,8 +79,10 @@ const int MIRROR_VIEW_TOP_PADDING = 5;
|
|||
const int MIRROR_VIEW_LEFT_PADDING = 10;
|
||||
const int MIRROR_VIEW_WIDTH = 265;
|
||||
const int MIRROR_VIEW_HEIGHT = 215;
|
||||
const float MAX_ZOOM_DISTANCE = 0.3f;
|
||||
const float MIN_ZOOM_DISTANCE = 2.0f;
|
||||
const float MIRROR_FULLSCREEN_DISTANCE = 0.2f;
|
||||
const float MIRROR_REARVIEW_DISTANCE = 0.3f;
|
||||
const float MIRROR_REARVIEW_BODY_DISTANCE = 1.f;
|
||||
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) {
|
||||
fprintf(stdout, "%s", message.toLocal8Bit().constData());
|
||||
|
@ -150,7 +152,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
listenPort = atoi(portStr);
|
||||
}
|
||||
|
||||
NodeList::createInstance(NODE_TYPE_AGENT, listenPort);
|
||||
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AGENT, listenPort);
|
||||
|
||||
// put the audio processing on a separate thread
|
||||
QThread* audioThread = new QThread(this);
|
||||
|
@ -160,10 +162,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
|
||||
audioThread->start();
|
||||
|
||||
NodeList::getInstance()->addHook(&_voxels);
|
||||
NodeList::getInstance()->addHook(this);
|
||||
NodeList::getInstance()->addDomainListener(this);
|
||||
NodeList::getInstance()->addDomainListener(&_voxels);
|
||||
nodeList->addHook(&_voxels);
|
||||
nodeList->addHook(this);
|
||||
nodeList->addDomainListener(this);
|
||||
nodeList->addDomainListener(&_voxels);
|
||||
|
||||
// network receive thread and voxel parsing thread are both controlled by the --nonblocking command line
|
||||
_enableProcessVoxelsThread = _enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking");
|
||||
|
@ -196,7 +198,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_voxelsFilename = getCmdOption(argc, constArgv, "-i");
|
||||
|
||||
// the callback for our instance of NodeList is attachNewHeadToNode
|
||||
NodeList::getInstance()->linkedDataCreateCallback = &attachNewHeadToNode;
|
||||
nodeList->linkedDataCreateCallback = &attachNewHeadToNode;
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA WsaData;
|
||||
|
@ -205,10 +207,11 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
|
||||
// tell the NodeList instance who to tell the domain server we care about
|
||||
const char nodeTypesOfInterest[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_VOXEL_SERVER};
|
||||
NodeList::getInstance()->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest));
|
||||
nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest));
|
||||
|
||||
// start the nodeList threads
|
||||
NodeList::getInstance()->startSilentNodeRemovalThread();
|
||||
QTimer* silentNodeTimer = new QTimer(this);
|
||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
|
||||
_networkAccessManager = new QNetworkAccessManager(this);
|
||||
QNetworkDiskCache* cache = new QNetworkDiskCache(_networkAccessManager);
|
||||
|
@ -369,16 +372,18 @@ void Application::paintGL() {
|
|||
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation());
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing
|
||||
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
|
||||
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation());
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_myCamera.setTightness(0.0f);
|
||||
_myCamera.setDistance(MAX_ZOOM_DISTANCE);
|
||||
_myCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition());
|
||||
float headHeight = _myAvatar.getHead().calculateAverageEyePosition().y - _myAvatar.getPosition().y;
|
||||
_myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar.getScale());
|
||||
_myCamera.setTargetPosition(_myAvatar.getPosition() + glm::vec3(0, headHeight, 0));
|
||||
_myCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f)));
|
||||
}
|
||||
|
||||
|
||||
// Update camera position
|
||||
_myCamera.update( 1.f/_fps );
|
||||
|
||||
|
@ -430,10 +435,10 @@ void Application::paintGL() {
|
|||
|
||||
bool eyeRelativeCamera = false;
|
||||
if (_rearMirrorTools->getZoomLevel() == BODY) {
|
||||
_mirrorCamera.setDistance(MIN_ZOOM_DISTANCE);
|
||||
_mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar.getScale());
|
||||
_mirrorCamera.setTargetPosition(_myAvatar.getChestJointPosition());
|
||||
} else { // HEAD zoom level
|
||||
_mirrorCamera.setDistance(MAX_ZOOM_DISTANCE);
|
||||
_mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar.getScale());
|
||||
if (_myAvatar.getSkeletonModel().isActive() && _myAvatar.getHead().getFaceModel().isActive()) {
|
||||
// as a hack until we have a better way of dealing with coordinate precision issues, reposition the
|
||||
// face/body so that the average eye position lies at the origin
|
||||
|
@ -1410,13 +1415,17 @@ void Application::processAvatarURLsMessage(unsigned char* packetData, size_t dat
|
|||
if (!avatar) {
|
||||
return;
|
||||
}
|
||||
QDataStream in(QByteArray((char*)packetData, dataBytes));
|
||||
QUrl voxelURL;
|
||||
in >> voxelURL;
|
||||
|
||||
// invoke the set URL functions on the simulate/render thread
|
||||
QMetaObject::invokeMethod(avatar->getVoxels(), "setVoxelURL", Q_ARG(QUrl, voxelURL));
|
||||
// PER Note: message is no longer processed but used to trigger
|
||||
// Dataserver lookup - redesign this to instantly ask the
|
||||
// dataserver on first receipt of other avatar UUID, and also
|
||||
// don't ask over and over again. Instead use this message to
|
||||
// Tell the other avatars that your dataserver data has
|
||||
// changed.
|
||||
|
||||
//QDataStream in(QByteArray((char*)packetData, dataBytes));
|
||||
//QUrl voxelURL;
|
||||
//in >> voxelURL;
|
||||
|
||||
// use this timing to as the data-server for an updated mesh for this avatar (if we have UUID)
|
||||
DataServerClient::getValuesForKeysAndUUID(QStringList() << DataServerKey::FaceMeshURL << DataServerKey::SkeletonURL,
|
||||
avatar->getUUID());
|
||||
|
@ -1791,9 +1800,6 @@ void Application::init() {
|
|||
_voxels.setDisableFastVoxelPipeline(false);
|
||||
_voxels.init();
|
||||
|
||||
|
||||
Avatar::sendAvatarURLsMessage(_myAvatar.getVoxels()->getVoxelURL());
|
||||
|
||||
_palette.init(_glWidget->width(), _glWidget->height());
|
||||
_palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0);
|
||||
_palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelDeleteMode), 0, 1);
|
||||
|
@ -2328,20 +2334,6 @@ void Application::updateTransmitter(float deltaTime) {
|
|||
if (_voxels.findRayIntersection(_transmitterPickStart, direction, detail, distance, face)) {
|
||||
minDistance = min(minDistance, distance);
|
||||
}
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
node->lock();
|
||||
if (node->getLinkedData() != NULL) {
|
||||
Avatar *avatar = (Avatar*)node->getLinkedData();
|
||||
if (!avatar->isInitialized()) {
|
||||
avatar->init();
|
||||
}
|
||||
if (avatar->findRayIntersection(_transmitterPickStart, direction, distance)) {
|
||||
minDistance = min(minDistance, distance);
|
||||
}
|
||||
}
|
||||
node->unlock();
|
||||
}
|
||||
_transmitterPickEnd = _transmitterPickStart + direction * minDistance;
|
||||
|
||||
} else {
|
||||
|
@ -2571,12 +2563,11 @@ void Application::updateAvatar(float deltaTime) {
|
|||
controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString,
|
||||
nodeTypesOfInterest, sizeof(nodeTypesOfInterest));
|
||||
|
||||
// once in a while, send my urls
|
||||
const float AVATAR_URLS_SEND_INTERVAL = 1.0f; // seconds
|
||||
const float AVATAR_URLS_SEND_INTERVAL = 1.0f;
|
||||
if (shouldDo(AVATAR_URLS_SEND_INTERVAL, deltaTime)) {
|
||||
Avatar::sendAvatarURLsMessage(_myAvatar.getVoxels()->getVoxelURL());
|
||||
QUrl empty;
|
||||
Avatar::sendAvatarURLsMessage(empty);
|
||||
}
|
||||
|
||||
// Update _viewFrustum with latest camera and view frustum data...
|
||||
// NOTE: we get this from the view frustum, to make it simpler, since the
|
||||
// loadViewFrumstum() method will get the correct details from the camera
|
||||
|
@ -3004,6 +2995,13 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
}
|
||||
}
|
||||
|
||||
// render the ambient occlusion effect if enabled
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AmbientOcclusion)) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... AmbientOcclusion...");
|
||||
_ambientOcclusionEffect.render();
|
||||
}
|
||||
|
||||
// restore default, white specular
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR);
|
||||
|
||||
|
@ -3087,13 +3085,6 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
renderWorldBox();
|
||||
}
|
||||
|
||||
// render the ambient occlusion effect if enabled
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AmbientOcclusion)) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... AmbientOcclusion...");
|
||||
_ambientOcclusionEffect.render();
|
||||
}
|
||||
|
||||
// brad's frustum for debugging
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum) && whichCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
|
|
|
@ -317,15 +317,8 @@ Menu::Menu() :
|
|||
QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AvatarAsBalls);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollisionProxies);
|
||||
|
||||
addActionToQMenuAndActionHash(avatarOptionsMenu,
|
||||
MenuOption::VoxelMode,
|
||||
0,
|
||||
appInstance->getAvatar()->getVoxels(),
|
||||
SLOT(cycleMode()));
|
||||
|
||||
addActionToQMenuAndActionHash(avatarOptionsMenu,
|
||||
MenuOption::FaceMode,
|
||||
0,
|
||||
|
@ -781,11 +774,7 @@ void Menu::editPreferences() {
|
|||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
layout->addLayout(form, 1);
|
||||
|
||||
QLineEdit* avatarURL = new QLineEdit(applicationInstance->getAvatar()->getVoxels()->getVoxelURL().toString());
|
||||
avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
form->addRow("Avatar URL:", avatarURL);
|
||||
|
||||
|
||||
QString faceURLString = applicationInstance->getProfile()->getFaceModelURL().toString();
|
||||
QLineEdit* faceURLEdit = new QLineEdit(faceURLString);
|
||||
faceURLEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
|
@ -868,12 +857,7 @@ void Menu::editPreferences() {
|
|||
DataServerClient::putValueForKey(DataServerKey::SkeletonURL,
|
||||
skeletonModelURL.toString().toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
QUrl avatarVoxelURL(avatarURL->text());
|
||||
applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL);
|
||||
|
||||
Avatar::sendAvatarURLsMessage(avatarVoxelURL);
|
||||
|
||||
|
||||
applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum());
|
||||
|
||||
_maxVoxels = maxVoxels->value();
|
||||
|
|
|
@ -147,7 +147,6 @@ namespace MenuOption {
|
|||
const QString AboutApp = "About Interface";
|
||||
const QString AmbientOcclusion = "Ambient Occlusion";
|
||||
const QString Avatars = "Avatars";
|
||||
const QString AvatarAsBalls = "Avatar as Balls";
|
||||
const QString Atmosphere = "Atmosphere";
|
||||
const QString AutomaticallyAuditTree = "Automatically Audit Tree Stats";
|
||||
const QString Bandwidth = "Bandwidth Display";
|
||||
|
|
|
@ -519,33 +519,6 @@ void renderNudgeGuide(float voxelX, float voxelY, float voxelZ, float voxelS) {
|
|||
glEnd();
|
||||
}
|
||||
|
||||
void renderDiskShadow(glm::vec3 position, glm::vec3 upDirection, float radius, float darkness) {
|
||||
|
||||
glColor4f(0.0f, 0.0f, 0.0f, darkness);
|
||||
|
||||
int num = 20;
|
||||
float y = 0.001f;
|
||||
float x2 = 0.0f;
|
||||
float z2 = radius;
|
||||
float x1;
|
||||
float z1;
|
||||
|
||||
glBegin(GL_TRIANGLES);
|
||||
|
||||
for (int i=1; i<num+1; i++) {
|
||||
x1 = x2;
|
||||
z1 = z2;
|
||||
float r = ((float)i / (float)num) * PIf * 2.0;
|
||||
x2 = radius * sin(r);
|
||||
z2 = radius * cos(r);
|
||||
|
||||
glVertex3f(position.x, y, position.z );
|
||||
glVertex3f(position.x + x1, y, position.z + z1);
|
||||
glVertex3f(position.x + x2, y, position.z + z2);
|
||||
}
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -77,8 +77,6 @@ void renderNudgeGuide(float voxelX, float voxelY, float voxelZ, float voxelS);
|
|||
|
||||
void renderCollisionOverlay(int width, int height, float magnitude);
|
||||
|
||||
void renderDiskShadow(glm::vec3 position, glm::vec3 upDirection, float radius, float darkness);
|
||||
|
||||
void renderOrientationDirections( glm::vec3 position, const glm::quat& orientation, float size );
|
||||
|
||||
void renderSphereOutline(glm::vec3 position, float radius, int numSides, glm::vec3 cameraPosition);
|
||||
|
|
|
@ -48,7 +48,6 @@ const float HEAD_MAX_PITCH = 45;
|
|||
const float HEAD_MIN_PITCH = -45;
|
||||
const float HEAD_MAX_YAW = 85;
|
||||
const float HEAD_MIN_YAW = -85;
|
||||
const float PERIPERSONAL_RADIUS = 1.0f;
|
||||
const float AVATAR_BRAKING_STRENGTH = 40.0f;
|
||||
const float MOUSE_RAY_TOUCH_RANGE = 0.01f;
|
||||
const float FLOATING_HEIGHT = 0.13f;
|
||||
|
@ -61,7 +60,7 @@ const float SKIN_COLOR[] = {1.0, 0.84, 0.66};
|
|||
const float DARK_SKIN_COLOR[] = {0.9, 0.78, 0.63};
|
||||
const int NUM_BODY_CONE_SIDES = 9;
|
||||
const float chatMessageScale = 0.0015;
|
||||
const float chatMessageHeight = 0.20;
|
||||
const float chatMessageHeight = 0.4f;
|
||||
|
||||
void Avatar::sendAvatarURLsMessage(const QUrl& voxelURL) {
|
||||
QByteArray message;
|
||||
|
@ -97,7 +96,6 @@ Avatar::Avatar(Node* owningNode) :
|
|||
_mouseRayDirection(0.0f, 0.0f, 0.0f),
|
||||
_isCollisionsOn(true),
|
||||
_leadingAvatar(NULL),
|
||||
_voxels(this),
|
||||
_moving(false),
|
||||
_initialized(false),
|
||||
_handHoldingPosition(0.0f, 0.0f, 0.0f),
|
||||
|
@ -113,17 +111,13 @@ Avatar::Avatar(Node* owningNode) :
|
|||
|
||||
_skeleton.initialize();
|
||||
|
||||
initializeBodyBalls();
|
||||
|
||||
_height = _skeleton.getHeight() + _bodyBall[BODY_BALL_LEFT_HEEL].radius + _bodyBall[BODY_BALL_HEAD_BASE].radius;
|
||||
_height = _skeleton.getHeight();
|
||||
|
||||
_maxArmLength = _skeleton.getArmLength();
|
||||
_pelvisStandingHeight = _skeleton.getPelvisStandingHeight() + _bodyBall[BODY_BALL_LEFT_HEEL].radius;
|
||||
_pelvisFloatingHeight = _skeleton.getPelvisFloatingHeight() + _bodyBall[BODY_BALL_LEFT_HEEL].radius;
|
||||
_pelvisStandingHeight = _skeleton.getPelvisStandingHeight();
|
||||
_pelvisFloatingHeight = _skeleton.getPelvisFloatingHeight();
|
||||
_pelvisToHeadLength = _skeleton.getPelvisToHeadLength();
|
||||
|
||||
_avatarTouch.setReachableRadius(PERIPERSONAL_RADIUS);
|
||||
|
||||
if (BALLS_ON) {
|
||||
_balls = new Balls(100);
|
||||
} else {
|
||||
|
@ -132,126 +126,6 @@ Avatar::Avatar(Node* owningNode) :
|
|||
}
|
||||
|
||||
|
||||
void Avatar::initializeBodyBalls() {
|
||||
|
||||
_ballSpringsInitialized = false; //this gets set to true on the first update pass...
|
||||
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
_bodyBall[b].parentJoint = AVATAR_JOINT_NULL;
|
||||
_bodyBall[b].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[b].position = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[b].velocity = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[b].radius = 0.0;
|
||||
_bodyBall[b].touchForce = 0.0;
|
||||
_bodyBall[b].isCollidable = true;
|
||||
_bodyBall[b].jointTightness = BODY_SPRING_DEFAULT_TIGHTNESS;
|
||||
}
|
||||
|
||||
// specify the radius of each ball
|
||||
_bodyBall[BODY_BALL_PELVIS].radius = BODY_BALL_RADIUS_PELVIS;
|
||||
_bodyBall[BODY_BALL_TORSO].radius = BODY_BALL_RADIUS_TORSO;
|
||||
_bodyBall[BODY_BALL_CHEST].radius = BODY_BALL_RADIUS_CHEST;
|
||||
_bodyBall[BODY_BALL_NECK_BASE].radius = BODY_BALL_RADIUS_NECK_BASE;
|
||||
_bodyBall[BODY_BALL_HEAD_BASE].radius = BODY_BALL_RADIUS_HEAD_BASE;
|
||||
_bodyBall[BODY_BALL_LEFT_COLLAR].radius = BODY_BALL_RADIUS_LEFT_COLLAR;
|
||||
_bodyBall[BODY_BALL_LEFT_SHOULDER].radius = BODY_BALL_RADIUS_LEFT_SHOULDER;
|
||||
_bodyBall[BODY_BALL_LEFT_ELBOW].radius = BODY_BALL_RADIUS_LEFT_ELBOW;
|
||||
_bodyBall[BODY_BALL_LEFT_WRIST].radius = BODY_BALL_RADIUS_LEFT_WRIST;
|
||||
_bodyBall[BODY_BALL_LEFT_FINGERTIPS].radius = BODY_BALL_RADIUS_LEFT_FINGERTIPS;
|
||||
_bodyBall[BODY_BALL_RIGHT_COLLAR].radius = BODY_BALL_RADIUS_RIGHT_COLLAR;
|
||||
_bodyBall[BODY_BALL_RIGHT_SHOULDER].radius = BODY_BALL_RADIUS_RIGHT_SHOULDER;
|
||||
_bodyBall[BODY_BALL_RIGHT_ELBOW].radius = BODY_BALL_RADIUS_RIGHT_ELBOW;
|
||||
_bodyBall[BODY_BALL_RIGHT_WRIST].radius = BODY_BALL_RADIUS_RIGHT_WRIST;
|
||||
_bodyBall[BODY_BALL_RIGHT_FINGERTIPS].radius = BODY_BALL_RADIUS_RIGHT_FINGERTIPS;
|
||||
_bodyBall[BODY_BALL_LEFT_HIP].radius = BODY_BALL_RADIUS_LEFT_HIP;
|
||||
_bodyBall[BODY_BALL_LEFT_KNEE].radius = BODY_BALL_RADIUS_LEFT_KNEE;
|
||||
_bodyBall[BODY_BALL_LEFT_HEEL].radius = BODY_BALL_RADIUS_LEFT_HEEL;
|
||||
_bodyBall[BODY_BALL_LEFT_TOES].radius = BODY_BALL_RADIUS_LEFT_TOES;
|
||||
_bodyBall[BODY_BALL_RIGHT_HIP].radius = BODY_BALL_RADIUS_RIGHT_HIP;
|
||||
_bodyBall[BODY_BALL_RIGHT_KNEE].radius = BODY_BALL_RADIUS_RIGHT_KNEE;
|
||||
_bodyBall[BODY_BALL_RIGHT_HEEL].radius = BODY_BALL_RADIUS_RIGHT_HEEL;
|
||||
_bodyBall[BODY_BALL_RIGHT_TOES].radius = BODY_BALL_RADIUS_RIGHT_TOES;
|
||||
|
||||
|
||||
// specify the parent joint for each ball
|
||||
_bodyBall[BODY_BALL_PELVIS].parentJoint = AVATAR_JOINT_PELVIS;
|
||||
_bodyBall[BODY_BALL_TORSO].parentJoint = AVATAR_JOINT_TORSO;
|
||||
_bodyBall[BODY_BALL_CHEST].parentJoint = AVATAR_JOINT_CHEST;
|
||||
_bodyBall[BODY_BALL_NECK_BASE].parentJoint = AVATAR_JOINT_NECK_BASE;
|
||||
_bodyBall[BODY_BALL_HEAD_BASE].parentJoint = AVATAR_JOINT_HEAD_BASE;
|
||||
_bodyBall[BODY_BALL_HEAD_TOP].parentJoint = AVATAR_JOINT_HEAD_TOP;
|
||||
_bodyBall[BODY_BALL_LEFT_COLLAR].parentJoint = AVATAR_JOINT_LEFT_COLLAR;
|
||||
_bodyBall[BODY_BALL_LEFT_SHOULDER].parentJoint = AVATAR_JOINT_LEFT_SHOULDER;
|
||||
_bodyBall[BODY_BALL_LEFT_ELBOW].parentJoint = AVATAR_JOINT_LEFT_ELBOW;
|
||||
_bodyBall[BODY_BALL_LEFT_WRIST].parentJoint = AVATAR_JOINT_LEFT_WRIST;
|
||||
_bodyBall[BODY_BALL_LEFT_FINGERTIPS].parentJoint = AVATAR_JOINT_LEFT_FINGERTIPS;
|
||||
_bodyBall[BODY_BALL_RIGHT_COLLAR].parentJoint = AVATAR_JOINT_RIGHT_COLLAR;
|
||||
_bodyBall[BODY_BALL_RIGHT_SHOULDER].parentJoint = AVATAR_JOINT_RIGHT_SHOULDER;
|
||||
_bodyBall[BODY_BALL_RIGHT_ELBOW].parentJoint = AVATAR_JOINT_RIGHT_ELBOW;
|
||||
_bodyBall[BODY_BALL_RIGHT_WRIST].parentJoint = AVATAR_JOINT_RIGHT_WRIST;
|
||||
_bodyBall[BODY_BALL_RIGHT_FINGERTIPS].parentJoint = AVATAR_JOINT_RIGHT_FINGERTIPS;
|
||||
_bodyBall[BODY_BALL_LEFT_HIP].parentJoint = AVATAR_JOINT_LEFT_HIP;
|
||||
_bodyBall[BODY_BALL_LEFT_KNEE].parentJoint = AVATAR_JOINT_LEFT_KNEE;
|
||||
_bodyBall[BODY_BALL_LEFT_HEEL].parentJoint = AVATAR_JOINT_LEFT_HEEL;
|
||||
_bodyBall[BODY_BALL_LEFT_TOES].parentJoint = AVATAR_JOINT_LEFT_TOES;
|
||||
_bodyBall[BODY_BALL_RIGHT_HIP].parentJoint = AVATAR_JOINT_RIGHT_HIP;
|
||||
_bodyBall[BODY_BALL_RIGHT_KNEE].parentJoint = AVATAR_JOINT_RIGHT_KNEE;
|
||||
_bodyBall[BODY_BALL_RIGHT_HEEL].parentJoint = AVATAR_JOINT_RIGHT_HEEL;
|
||||
_bodyBall[BODY_BALL_RIGHT_TOES].parentJoint = AVATAR_JOINT_RIGHT_TOES;
|
||||
|
||||
// specify the parent offset for each ball
|
||||
_bodyBall[BODY_BALL_PELVIS].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_TORSO].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_CHEST].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_NECK_BASE].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_HEAD_BASE].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_HEAD_TOP].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_LEFT_COLLAR].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_LEFT_SHOULDER].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_LEFT_ELBOW].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_LEFT_WRIST].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_LEFT_FINGERTIPS].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_RIGHT_COLLAR].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_RIGHT_SHOULDER].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_RIGHT_ELBOW].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_RIGHT_WRIST].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_RIGHT_FINGERTIPS].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_LEFT_HIP].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_LEFT_KNEE].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_LEFT_HEEL].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_LEFT_TOES].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_RIGHT_HIP].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_RIGHT_KNEE].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_RIGHT_HEEL].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
_bodyBall[BODY_BALL_RIGHT_TOES].parentOffset = glm::vec3(0.0, 0.0, 0.0);
|
||||
|
||||
// specify the parent BALL for each ball
|
||||
_bodyBall[BODY_BALL_PELVIS].parentBall = BODY_BALL_NULL;
|
||||
_bodyBall[BODY_BALL_TORSO].parentBall = BODY_BALL_PELVIS;
|
||||
_bodyBall[BODY_BALL_CHEST].parentBall = BODY_BALL_TORSO;
|
||||
_bodyBall[BODY_BALL_NECK_BASE].parentBall = BODY_BALL_CHEST;
|
||||
_bodyBall[BODY_BALL_HEAD_BASE].parentBall = BODY_BALL_NECK_BASE;
|
||||
_bodyBall[BODY_BALL_HEAD_TOP].parentBall = BODY_BALL_HEAD_BASE;
|
||||
_bodyBall[BODY_BALL_LEFT_COLLAR].parentBall = BODY_BALL_CHEST;
|
||||
_bodyBall[BODY_BALL_LEFT_SHOULDER].parentBall = BODY_BALL_LEFT_COLLAR;
|
||||
_bodyBall[BODY_BALL_LEFT_ELBOW].parentBall = BODY_BALL_LEFT_SHOULDER;
|
||||
_bodyBall[BODY_BALL_LEFT_WRIST].parentBall = BODY_BALL_LEFT_ELBOW;
|
||||
_bodyBall[BODY_BALL_LEFT_FINGERTIPS].parentBall = BODY_BALL_LEFT_WRIST;
|
||||
_bodyBall[BODY_BALL_RIGHT_COLLAR].parentBall = BODY_BALL_CHEST;
|
||||
_bodyBall[BODY_BALL_RIGHT_SHOULDER].parentBall = BODY_BALL_RIGHT_COLLAR;
|
||||
_bodyBall[BODY_BALL_RIGHT_ELBOW].parentBall = BODY_BALL_RIGHT_SHOULDER;
|
||||
_bodyBall[BODY_BALL_RIGHT_WRIST].parentBall = BODY_BALL_RIGHT_ELBOW;
|
||||
_bodyBall[BODY_BALL_RIGHT_FINGERTIPS].parentBall = BODY_BALL_RIGHT_WRIST;
|
||||
_bodyBall[BODY_BALL_LEFT_HIP].parentBall = BODY_BALL_PELVIS;
|
||||
_bodyBall[BODY_BALL_LEFT_KNEE].parentBall = BODY_BALL_LEFT_HIP;
|
||||
_bodyBall[BODY_BALL_LEFT_HEEL].parentBall = BODY_BALL_LEFT_KNEE;
|
||||
_bodyBall[BODY_BALL_LEFT_TOES].parentBall = BODY_BALL_LEFT_HEEL;
|
||||
_bodyBall[BODY_BALL_RIGHT_HIP].parentBall = BODY_BALL_PELVIS;
|
||||
_bodyBall[BODY_BALL_RIGHT_KNEE].parentBall = BODY_BALL_RIGHT_HIP;
|
||||
_bodyBall[BODY_BALL_RIGHT_HEEL].parentBall = BODY_BALL_RIGHT_KNEE;
|
||||
_bodyBall[BODY_BALL_RIGHT_TOES].parentBall = BODY_BALL_RIGHT_HEEL;
|
||||
}
|
||||
|
||||
Avatar::~Avatar() {
|
||||
_headData = NULL;
|
||||
_handData = NULL;
|
||||
|
@ -267,7 +141,6 @@ void Avatar::init() {
|
|||
_head.init();
|
||||
_hand.init();
|
||||
_skeletonModel.init();
|
||||
_voxels.init();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
|
@ -295,22 +168,18 @@ void Avatar::follow(Avatar* leadingAvatar) {
|
|||
}
|
||||
|
||||
void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
||||
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
|
||||
|
||||
if (_leadingAvatar && !_leadingAvatar->getOwningNode()->isAlive()) {
|
||||
follow(NULL);
|
||||
}
|
||||
|
||||
|
||||
if (_scale != _newScale) {
|
||||
setScale(_newScale);
|
||||
}
|
||||
|
||||
// copy velocity so we can use it later for acceleration
|
||||
glm::vec3 oldVelocity = getVelocity();
|
||||
|
||||
|
||||
// update balls
|
||||
if (_balls) {
|
||||
_balls->moveOrigin(_position);
|
||||
|
@ -325,7 +194,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
|
||||
// update torso rotation based on head lean
|
||||
_skeleton.joint[AVATAR_JOINT_TORSO].rotation = glm::quat(glm::radians(glm::vec3(
|
||||
_head.getLeanForward(), 0.0f, _head.getLeanSideways())));
|
||||
_head.getLeanForward(), 0.0f, _head.getLeanSideways())));
|
||||
|
||||
// apply joint data (if any) to skeleton
|
||||
bool enableHandMovement = true;
|
||||
|
@ -338,73 +207,24 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
|
||||
// update avatar skeleton
|
||||
_skeleton.update(deltaTime, getOrientation(), _position);
|
||||
|
||||
//determine the lengths of the body springs now that we have updated the skeleton at least once
|
||||
if (!_ballSpringsInitialized) {
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
|
||||
glm::vec3 targetPosition
|
||||
= _skeleton.joint[_bodyBall[b].parentJoint].position
|
||||
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
|
||||
|
||||
glm::vec3 parentTargetPosition
|
||||
= _skeleton.joint[_bodyBall[b].parentJoint].position
|
||||
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
|
||||
|
||||
_bodyBall[b].springLength = glm::length(targetPosition - parentTargetPosition);
|
||||
}
|
||||
|
||||
_ballSpringsInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
// if this is not my avatar, then hand position comes from transmitted data
|
||||
_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = _handPosition;
|
||||
|
||||
|
||||
//update the movement of the hand and process handshaking with other avatars...
|
||||
updateHandMovementAndTouching(deltaTime, enableHandMovement);
|
||||
_avatarTouch.simulate(deltaTime);
|
||||
|
||||
// update body balls
|
||||
updateBodyBalls(deltaTime);
|
||||
|
||||
//apply the head lean values to the ball positions...
|
||||
if (USING_HEAD_LEAN) {
|
||||
if (fabs(_head.getLeanSideways() + _head.getLeanForward()) > 0.0f) {
|
||||
glm::vec3 headLean =
|
||||
right * _head.getLeanSideways() +
|
||||
front * _head.getLeanForward();
|
||||
|
||||
_bodyBall[BODY_BALL_TORSO].position += headLean * 0.1f;
|
||||
_bodyBall[BODY_BALL_CHEST].position += headLean * 0.4f;
|
||||
_bodyBall[BODY_BALL_NECK_BASE].position += headLean * 0.7f;
|
||||
_bodyBall[BODY_BALL_HEAD_BASE].position += headLean * 1.0f;
|
||||
|
||||
_bodyBall[BODY_BALL_LEFT_COLLAR].position += headLean * 0.6f;
|
||||
_bodyBall[BODY_BALL_LEFT_SHOULDER].position += headLean * 0.6f;
|
||||
_bodyBall[BODY_BALL_LEFT_ELBOW].position += headLean * 0.2f;
|
||||
_bodyBall[BODY_BALL_LEFT_WRIST].position += headLean * 0.1f;
|
||||
_bodyBall[BODY_BALL_LEFT_FINGERTIPS].position += headLean * 0.0f;
|
||||
|
||||
_bodyBall[BODY_BALL_RIGHT_COLLAR].position += headLean * 0.6f;
|
||||
_bodyBall[BODY_BALL_RIGHT_SHOULDER].position += headLean * 0.6f;
|
||||
_bodyBall[BODY_BALL_RIGHT_ELBOW].position += headLean * 0.2f;
|
||||
_bodyBall[BODY_BALL_RIGHT_WRIST].position += headLean * 0.1f;
|
||||
_bodyBall[BODY_BALL_RIGHT_FINGERTIPS].position += headLean * 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
_hand.simulate(deltaTime, false);
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||
glm::vec3 headPosition;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls) || !_skeletonModel.getHeadPosition(headPosition)) {
|
||||
headPosition = _bodyBall[BODY_BALL_HEAD_BASE].position;
|
||||
}
|
||||
_skeletonModel.getHeadPosition(headPosition);
|
||||
_head.setPosition(headPosition);
|
||||
_head.setScale(_scale);
|
||||
_head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]));
|
||||
_head.simulate(deltaTime, false);
|
||||
|
||||
|
||||
// use speed and angular velocity to determine walking vs. standing
|
||||
if (_speed + fabs(_bodyYawDelta) > 0.2) {
|
||||
_mode = AVATAR_MODE_WALKING;
|
||||
|
@ -412,12 +232,12 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
_mode = AVATAR_MODE_INTERACTING;
|
||||
}
|
||||
|
||||
// update position by velocity, and subtract the change added earlier for gravity
|
||||
// update position by velocity, and subtract the change added earlier for gravity
|
||||
_position += _velocity * deltaTime;
|
||||
|
||||
// Zero thrust out now that we've added it to velocity in this frame
|
||||
_thrust = glm::vec3(0, 0, 0);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Avatar::setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction) {
|
||||
|
@ -443,9 +263,6 @@ static TextRenderer* textRenderer() {
|
|||
}
|
||||
|
||||
void Avatar::render(bool forceRenderHead) {
|
||||
|
||||
// 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);
|
||||
|
||||
{
|
||||
// glow when moving in the distance
|
||||
|
@ -486,7 +303,7 @@ void Avatar::render(bool forceRenderHead) {
|
|||
}
|
||||
glPushMatrix();
|
||||
|
||||
glm::vec3 chatPosition = _bodyBall[BODY_BALL_HEAD_BASE].position + getBodyUpDirection() * chatMessageHeight * _scale;
|
||||
glm::vec3 chatPosition = getPosition() + getBodyUpDirection() * chatMessageHeight * _scale;
|
||||
glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z);
|
||||
glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation();
|
||||
glm::vec3 chatAxis = glm::axis(chatRotation);
|
||||
|
@ -521,90 +338,7 @@ void Avatar::render(bool forceRenderHead) {
|
|||
}
|
||||
}
|
||||
|
||||
void Avatar::resetBodyBalls() {
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
|
||||
glm::vec3 targetPosition
|
||||
= _skeleton.joint[_bodyBall[b].parentJoint].position
|
||||
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
|
||||
|
||||
_bodyBall[b].position = targetPosition; // put ball on target position
|
||||
_bodyBall[b].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::updateBodyBalls(float deltaTime) {
|
||||
// Check for a large repositioning, and re-initialize balls if this has happened
|
||||
const float BEYOND_BODY_SPRING_RANGE = _scale * 2.f;
|
||||
if (glm::length(_position - _bodyBall[BODY_BALL_PELVIS].position) > BEYOND_BODY_SPRING_RANGE) {
|
||||
resetBodyBalls();
|
||||
}
|
||||
glm::quat orientation = getOrientation();
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
|
||||
glm::vec3 springVector;
|
||||
float length = 0.0f;
|
||||
if (_ballSpringsInitialized) {
|
||||
|
||||
// apply spring forces
|
||||
springVector = _bodyBall[b].position;
|
||||
|
||||
if (b == BODY_BALL_PELVIS) {
|
||||
springVector -= _position;
|
||||
} else {
|
||||
springVector -= _bodyBall[_bodyBall[b].parentBall].position;
|
||||
}
|
||||
|
||||
length = glm::length(springVector);
|
||||
|
||||
if (length > 0.0f) { // to avoid divide by zero
|
||||
glm::vec3 springDirection = springVector / length;
|
||||
|
||||
float force = (length - _skeleton.joint[b].length) * BODY_SPRING_FORCE * deltaTime;
|
||||
_bodyBall[b].velocity -= springDirection * force;
|
||||
|
||||
if (_bodyBall[b].parentBall != BODY_BALL_NULL) {
|
||||
_bodyBall[_bodyBall[b].parentBall].velocity += springDirection * force;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply tightness force - (causing ball position to be close to skeleton joint position)
|
||||
glm::vec3 targetPosition
|
||||
= _skeleton.joint[_bodyBall[b].parentJoint].position
|
||||
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
|
||||
|
||||
_bodyBall[b].velocity += (targetPosition - _bodyBall[b].position) * _bodyBall[b].jointTightness * deltaTime;
|
||||
|
||||
// apply decay
|
||||
float decay = 1.0 - BODY_SPRING_DECAY * deltaTime;
|
||||
if (decay > 0.0) {
|
||||
_bodyBall[b].velocity *= decay;
|
||||
} else {
|
||||
_bodyBall[b].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
// update position by velocity...
|
||||
_bodyBall[b].position += _bodyBall[b].velocity * deltaTime;
|
||||
|
||||
// update rotation
|
||||
const float SMALL_SPRING_LENGTH = 0.001f; // too-small springs can change direction rapidly
|
||||
if (_skeleton.joint[b].parent == AVATAR_JOINT_NULL || length < SMALL_SPRING_LENGTH) {
|
||||
_bodyBall[b].rotation = orientation * _skeleton.joint[_bodyBall[b].parentJoint].absoluteBindPoseRotation;
|
||||
} else {
|
||||
glm::vec3 parentDirection = _bodyBall[ _bodyBall[b].parentBall ].rotation * JOINT_DIRECTION;
|
||||
_bodyBall[b].rotation = rotationBetween(parentDirection, springVector) *
|
||||
_bodyBall[ _bodyBall[b].parentBall ].rotation;
|
||||
}
|
||||
}
|
||||
|
||||
// copy the head's rotation
|
||||
_bodyBall[BODY_BALL_HEAD_BASE].rotation = _bodyBall[BODY_BALL_HEAD_TOP].rotation = _head.getOrientation();
|
||||
_bodyBall[BODY_BALL_HEAD_BASE].position = _bodyBall[BODY_BALL_NECK_BASE].position +
|
||||
_bodyBall[BODY_BALL_HEAD_BASE].rotation * _skeleton.joint[BODY_BALL_HEAD_BASE].bindPosePosition;
|
||||
_bodyBall[BODY_BALL_HEAD_TOP].position = _bodyBall[BODY_BALL_HEAD_BASE].position +
|
||||
_bodyBall[BODY_BALL_HEAD_TOP].rotation * _skeleton.joint[BODY_BALL_HEAD_TOP].bindPosePosition;
|
||||
}
|
||||
|
||||
// returns true if the Leap controls any of the avatar's hands.
|
||||
bool Avatar::updateLeapHandPositions() {
|
||||
|
@ -665,7 +399,7 @@ void Avatar::updateArmIKAndConstraints(float deltaTime, AvatarJointID fingerTipJ
|
|||
float distance = glm::length(armVector);
|
||||
|
||||
// don't let right hand get dragged beyond maximum arm length...
|
||||
float armLength = (_skeletonModel.isActive() && !Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls)) ?
|
||||
float armLength = _skeletonModel.isActive() ?
|
||||
_skeletonModel.getRightArmLength() : _skeleton.getArmLength();
|
||||
const float ARM_RETRACTION = 0.75f;
|
||||
float retractedArmLength = armLength * ARM_RETRACTION;
|
||||
|
@ -709,79 +443,19 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
|||
return glm::angleAxis(angle * proportion, axis);
|
||||
}
|
||||
|
||||
float Avatar::getBallRenderAlpha(int ball, bool forceRenderHead) const {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
|
||||
void Avatar::renderBody(bool forceRenderHead) {
|
||||
|
||||
if (_head.getVideoFace().isFullFrame()) {
|
||||
// Render the full-frame video
|
||||
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, forceRenderHead);
|
||||
if (alpha > 0.0f) {
|
||||
_head.getVideoFace().render(1.0f);
|
||||
}
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls)) {
|
||||
// Render the body as balls and cones
|
||||
glm::vec3 skinColor, darkSkinColor;
|
||||
getSkinColors(skinColor, darkSkinColor);
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
float alpha = getBallRenderAlpha(b, forceRenderHead);
|
||||
|
||||
// When we have leap hands, hide part of the arms.
|
||||
if (_hand.getNumPalms() > 0) {
|
||||
if (b == BODY_BALL_LEFT_FINGERTIPS
|
||||
|| 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) {
|
||||
_head.render(alpha, true);
|
||||
}
|
||||
} else if (alpha > 0.0f) {
|
||||
// Render the body ball sphere
|
||||
glColor3f(skinColor.r + _bodyBall[b].touchForce * 0.3f,
|
||||
skinColor.g - _bodyBall[b].touchForce * 0.2f,
|
||||
skinColor.b - _bodyBall[b].touchForce * 0.1f);
|
||||
|
||||
if ((b != BODY_BALL_HEAD_TOP )
|
||||
&& (b != BODY_BALL_HEAD_BASE )) {
|
||||
glPushMatrix();
|
||||
glTranslatef(_bodyBall[b].position.x, _bodyBall[b].position.y, _bodyBall[b].position.z);
|
||||
glutSolidSphere(_bodyBall[b].radius, 20.0f, 20.0f);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
// Render the cone connecting this ball to its parent
|
||||
if (_bodyBall[b].parentBall != BODY_BALL_NULL) {
|
||||
if ((b != BODY_BALL_HEAD_TOP)
|
||||
&& (b != BODY_BALL_HEAD_BASE)
|
||||
&& (b != BODY_BALL_PELVIS)
|
||||
&& (b != BODY_BALL_TORSO)
|
||||
&& (b != BODY_BALL_CHEST)
|
||||
&& (b != BODY_BALL_LEFT_COLLAR)
|
||||
&& (b != BODY_BALL_LEFT_SHOULDER)
|
||||
&& (b != BODY_BALL_RIGHT_COLLAR)
|
||||
&& (b != BODY_BALL_RIGHT_SHOULDER)) {
|
||||
glColor3fv((const GLfloat*)&darkSkinColor);
|
||||
|
||||
float r2 = _bodyBall[b].radius * 0.8;
|
||||
renderJointConnectingCone(_bodyBall[_bodyBall[b].parentBall].position, _bodyBall[b].position, r2, r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_head.getVideoFace().render(1.0f);
|
||||
} else {
|
||||
// Render the body's voxels and head
|
||||
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, forceRenderHead);
|
||||
if (alpha > 0.0f) {
|
||||
if (!_skeletonModel.render(alpha)) {
|
||||
_voxels.render(false);
|
||||
}
|
||||
_head.render(alpha, false);
|
||||
}
|
||||
glm::vec3 pos = getPosition();
|
||||
//printf("Render other at %.3f, %.2f, %.2f\n", pos.x, pos.y, pos.z);
|
||||
_skeletonModel.render(1.0f);
|
||||
_head.render(1.0f, false);
|
||||
}
|
||||
_hand.render(false);
|
||||
}
|
||||
|
@ -789,33 +463,13 @@ void Avatar::renderBody(bool forceRenderHead) {
|
|||
void Avatar::getSkinColors(glm::vec3& lighter, glm::vec3& darker) {
|
||||
lighter = glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]);
|
||||
darker = glm::vec3(DARK_SKIN_COLOR[0], DARK_SKIN_COLOR[1], DARK_SKIN_COLOR[2]);
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls) && _head.getFaceModel().isActive()) {
|
||||
if (_head.getFaceModel().isActive()) {
|
||||
lighter = glm::vec3(_head.getFaceModel().computeAverageColor());
|
||||
const float SKIN_DARKENING = 0.9f;
|
||||
darker = lighter * SKIN_DARKENING;
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const {
|
||||
position = _bodyBall[jointID].position;
|
||||
rotation = _bodyBall[jointID].rotation;
|
||||
}
|
||||
|
||||
bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
float minDistance = FLT_MAX;
|
||||
for (int i = 0; i < NUM_AVATAR_BODY_BALLS; i++) {
|
||||
float distance;
|
||||
if (rayIntersectsSphere(origin, direction, _bodyBall[i].position, _bodyBall[i].radius, distance)) {
|
||||
minDistance = min(minDistance, distance);
|
||||
}
|
||||
}
|
||||
if (minDistance == FLT_MAX) {
|
||||
return false;
|
||||
}
|
||||
distance = minDistance;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Avatar::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
glm::vec3& penetration, int skeletonSkipIndex) {
|
||||
bool didPenetrate = false;
|
||||
|
@ -929,37 +583,11 @@ void Avatar::setScale(const float scale) {
|
|||
|
||||
_skeleton.setScale(_scale);
|
||||
|
||||
// specify the new radius of each ball
|
||||
_bodyBall[BODY_BALL_PELVIS].radius = _scale * BODY_BALL_RADIUS_PELVIS;
|
||||
_bodyBall[BODY_BALL_TORSO].radius = _scale * BODY_BALL_RADIUS_TORSO;
|
||||
_bodyBall[BODY_BALL_CHEST].radius = _scale * BODY_BALL_RADIUS_CHEST;
|
||||
_bodyBall[BODY_BALL_NECK_BASE].radius = _scale * BODY_BALL_RADIUS_NECK_BASE;
|
||||
_bodyBall[BODY_BALL_HEAD_BASE].radius = _scale * BODY_BALL_RADIUS_HEAD_BASE;
|
||||
_bodyBall[BODY_BALL_LEFT_COLLAR].radius = _scale * BODY_BALL_RADIUS_LEFT_COLLAR;
|
||||
_bodyBall[BODY_BALL_LEFT_SHOULDER].radius = _scale * BODY_BALL_RADIUS_LEFT_SHOULDER;
|
||||
_bodyBall[BODY_BALL_LEFT_ELBOW].radius = _scale * BODY_BALL_RADIUS_LEFT_ELBOW;
|
||||
_bodyBall[BODY_BALL_LEFT_WRIST].radius = _scale * BODY_BALL_RADIUS_LEFT_WRIST;
|
||||
_bodyBall[BODY_BALL_LEFT_FINGERTIPS].radius = _scale * BODY_BALL_RADIUS_LEFT_FINGERTIPS;
|
||||
_bodyBall[BODY_BALL_RIGHT_COLLAR].radius = _scale * BODY_BALL_RADIUS_RIGHT_COLLAR;
|
||||
_bodyBall[BODY_BALL_RIGHT_SHOULDER].radius = _scale * BODY_BALL_RADIUS_RIGHT_SHOULDER;
|
||||
_bodyBall[BODY_BALL_RIGHT_ELBOW].radius = _scale * BODY_BALL_RADIUS_RIGHT_ELBOW;
|
||||
_bodyBall[BODY_BALL_RIGHT_WRIST].radius = _scale * BODY_BALL_RADIUS_RIGHT_WRIST;
|
||||
_bodyBall[BODY_BALL_RIGHT_FINGERTIPS].radius = _scale * BODY_BALL_RADIUS_RIGHT_FINGERTIPS;
|
||||
_bodyBall[BODY_BALL_LEFT_HIP].radius = _scale * BODY_BALL_RADIUS_LEFT_HIP;
|
||||
_bodyBall[BODY_BALL_LEFT_KNEE].radius = _scale * BODY_BALL_RADIUS_LEFT_KNEE;
|
||||
_bodyBall[BODY_BALL_LEFT_HEEL].radius = _scale * BODY_BALL_RADIUS_LEFT_HEEL;
|
||||
_bodyBall[BODY_BALL_LEFT_TOES].radius = _scale * BODY_BALL_RADIUS_LEFT_TOES;
|
||||
_bodyBall[BODY_BALL_RIGHT_HIP].radius = _scale * BODY_BALL_RADIUS_RIGHT_HIP;
|
||||
_bodyBall[BODY_BALL_RIGHT_KNEE].radius = _scale * BODY_BALL_RADIUS_RIGHT_KNEE;
|
||||
_bodyBall[BODY_BALL_RIGHT_HEEL].radius = _scale * BODY_BALL_RADIUS_RIGHT_HEEL;
|
||||
_bodyBall[BODY_BALL_RIGHT_TOES].radius = _scale * BODY_BALL_RADIUS_RIGHT_TOES;
|
||||
|
||||
_height = _skeleton.getHeight() + _bodyBall[BODY_BALL_LEFT_HEEL].radius + _bodyBall[BODY_BALL_HEAD_BASE].radius;
|
||||
_height = _skeleton.getHeight();
|
||||
|
||||
_maxArmLength = _skeleton.getArmLength();
|
||||
_pelvisStandingHeight = _skeleton.getPelvisStandingHeight() + _bodyBall[BODY_BALL_LEFT_HEEL].radius;
|
||||
_pelvisFloatingHeight = _skeleton.getPelvisFloatingHeight() + _bodyBall[BODY_BALL_LEFT_HEEL].radius;
|
||||
_pelvisStandingHeight = _skeleton.getPelvisStandingHeight();
|
||||
_pelvisFloatingHeight = _skeleton.getPelvisFloatingHeight();
|
||||
_pelvisToHeadLength = _skeleton.getPelvisToHeadLength();
|
||||
_avatarTouch.setReachableRadius(_scale * PERIPERSONAL_RADIUS);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
|
||||
#include <AvatarData.h>
|
||||
|
||||
#include "AvatarTouch.h"
|
||||
#include "AvatarVoxelSystem.h"
|
||||
#include "Balls.h"
|
||||
#include "Hand.h"
|
||||
#include "Head.h"
|
||||
|
@ -160,20 +158,9 @@ public:
|
|||
Hand& getHand() { return _hand; }
|
||||
glm::quat getOrientation() const;
|
||||
glm::quat getWorldAlignedOrientation() const;
|
||||
AvatarVoxelSystem* getVoxels() { return &_voxels; }
|
||||
|
||||
void getSkinColors(glm::vec3& lighter, glm::vec3& darker);
|
||||
|
||||
// Get the position/rotation of a single body ball
|
||||
void getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const;
|
||||
|
||||
/// Checks for an intersection between the described ray and any of the avatar's body balls.
|
||||
/// \param origin the origin of the ray
|
||||
/// \param direction the unit direction vector
|
||||
/// \param[out] distance the variable in which to store the distance to intersection
|
||||
/// \return whether or not the ray intersected
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
/// Checks for penetration between the described sphere and the avatar.
|
||||
/// \param penetratorCenter the center of the penetration test sphere
|
||||
/// \param penetratorRadius the radius of the penetration test sphere
|
||||
|
@ -218,7 +205,6 @@ protected:
|
|||
SkeletonModel _skeletonModel;
|
||||
bool _ballSpringsInitialized;
|
||||
float _bodyYawDelta;
|
||||
AvatarBall _bodyBall[ NUM_AVATAR_BODY_BALLS ];
|
||||
AvatarMode _mode;
|
||||
glm::vec3 _velocity;
|
||||
glm::vec3 _thrust;
|
||||
|
@ -229,14 +215,12 @@ protected:
|
|||
float _scale;
|
||||
float _height;
|
||||
Balls* _balls;
|
||||
AvatarTouch _avatarTouch;
|
||||
glm::vec3 _worldUpDirection;
|
||||
glm::vec3 _mouseRayOrigin;
|
||||
glm::vec3 _mouseRayDirection;
|
||||
bool _isCollisionsOn;
|
||||
Avatar* _leadingAvatar;
|
||||
float _stringLength;
|
||||
AvatarVoxelSystem _voxels;
|
||||
|
||||
bool _moving; ///< set when position is changing
|
||||
|
||||
|
@ -245,7 +229,6 @@ protected:
|
|||
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
void updateBodyBalls(float deltaTime);
|
||||
bool updateLeapHandPositions();
|
||||
void updateArmIKAndConstraints(float deltaTime, AvatarJointID fingerTipJointID);
|
||||
void setScale(const float scale);
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
//
|
||||
// AvatarTouch.cpp
|
||||
// interface
|
||||
//
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
|
||||
#include <iostream>
|
||||
#include <glm/glm.hpp>
|
||||
#include <SharedUtil.h>
|
||||
#include "AvatarTouch.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "Util.h"
|
||||
|
||||
const float THREAD_RADIUS = 0.007;
|
||||
const float HANDS_CLOSE_ENOUGH_TO_GRASP = 0.2;
|
||||
const float AVATAR_FACING_THRESHOLD = -0.5f; // (-1 to 1) (larger value indicates narrower angle of influence
|
||||
|
||||
AvatarTouch::AvatarTouch() {
|
||||
|
||||
_myHandPosition = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
_yourHandPosition = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
_myBodyPosition = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
_yourBodyPosition = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
_vectorBetweenHands = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
_myHandState = HAND_STATE_NULL;
|
||||
_yourHandState = HAND_STATE_NULL;
|
||||
_reachableRadius = 0.0f;
|
||||
_weAreHoldingHands = false;
|
||||
_canReachToOtherAvatar = false;
|
||||
_handsCloseEnoughToGrasp = false;
|
||||
_hasInteractingOther = false;
|
||||
|
||||
for (int p=0; p<NUM_PARTICLE_POINTS; p++) {
|
||||
_point[p] = glm::vec3(0.0, 0.0, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarTouch::simulate (float deltaTime) {
|
||||
|
||||
_canReachToOtherAvatar = false; // default
|
||||
|
||||
if (_hasInteractingOther) {
|
||||
|
||||
glm::vec3 vectorBetweenBodies = _yourBodyPosition - _myBodyPosition;
|
||||
float distanceBetweenBodies = glm::length(vectorBetweenBodies);
|
||||
|
||||
//KEEP THIS - it is another variation that we are considering getting rid of
|
||||
//the following code take into account of the two avatars are facing each other
|
||||
/*
|
||||
glm::vec3 directionBetweenBodies = vectorBetweenBodies / distanceBetweenBodies;
|
||||
|
||||
bool facingEachOther = false;
|
||||
|
||||
glm::vec3 myFront = _myOrientation * AVATAR_FRONT;
|
||||
glm::vec3 yourFront = _yourOrientation * AVATAR_FRONT;
|
||||
|
||||
if (( glm::dot(myFront, yourFront ) < -AVATAR_FACING_THRESHOLD) // we're facing each other
|
||||
&& ( glm::dot(myFront, directionBetweenBodies ) > AVATAR_FACING_THRESHOLD)) { // I'm facing you
|
||||
facingEachOther = true;
|
||||
}
|
||||
*/
|
||||
|
||||
if (distanceBetweenBodies < _reachableRadius)
|
||||
{
|
||||
_canReachToOtherAvatar = true;
|
||||
|
||||
_vectorBetweenHands = _yourHandPosition - _myHandPosition;
|
||||
|
||||
float distanceBetweenHands = glm::length(_vectorBetweenHands);
|
||||
if (distanceBetweenHands < HANDS_CLOSE_ENOUGH_TO_GRASP) {
|
||||
_handsCloseEnoughToGrasp = true;
|
||||
} else {
|
||||
_handsCloseEnoughToGrasp = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AvatarTouch::render(glm::vec3 cameraPosition) {
|
||||
|
||||
if (_canReachToOtherAvatar) {
|
||||
|
||||
//show circle indicating that we can reach out to each other...
|
||||
glColor4f(0.3, 0.4, 0.5, 0.5);
|
||||
glm::vec3 p(_yourBodyPosition);
|
||||
p.y = 0.0005f;
|
||||
renderCircle(p, _reachableRadius, glm::vec3(0.0f, 1.0f, 0.0f), 30);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AvatarTouch::renderBeamBetweenHands() {
|
||||
|
||||
glm::vec3 v1(_myHandPosition);
|
||||
glm::vec3 v2(_yourHandPosition);
|
||||
|
||||
glLineWidth(3.0);
|
||||
glColor4f(0.9f, 0.9f, 0.1f, 0.6);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
glVertex3f(v1.x, v1.y, v1.z);
|
||||
glVertex3f(v2.x, v2.y, v2.z);
|
||||
glEnd();
|
||||
|
||||
glColor3f(0.5f, 0.3f, 0.0f);
|
||||
for (int p=0; p<NUM_PARTICLE_POINTS; p++) {
|
||||
|
||||
_point[p] = _myHandPosition + _vectorBetweenHands * ((float)p / (float)NUM_PARTICLE_POINTS);
|
||||
_point[p].x += randFloatInRange(-THREAD_RADIUS, THREAD_RADIUS);
|
||||
_point[p].y += randFloatInRange(-THREAD_RADIUS, THREAD_RADIUS);
|
||||
_point[p].z += randFloatInRange(-THREAD_RADIUS, THREAD_RADIUS);
|
||||
|
||||
glBegin(GL_POINTS);
|
||||
glVertex3f(_point[p].x, _point[p].y, _point[p].z);
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
//
|
||||
// AvatarTouch.h
|
||||
// interface
|
||||
//
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__AvatarTouch__
|
||||
#define __interface__AvatarTouch__
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <AvatarData.h>
|
||||
|
||||
enum AvatarHandState
|
||||
{
|
||||
HAND_STATE_NULL = 0,
|
||||
HAND_STATE_OPEN,
|
||||
HAND_STATE_GRASPING,
|
||||
HAND_STATE_POINTING,
|
||||
NUM_HAND_STATES
|
||||
};
|
||||
|
||||
class AvatarTouch {
|
||||
public:
|
||||
|
||||
AvatarTouch();
|
||||
|
||||
void simulate(float deltaTime);
|
||||
void render(glm::vec3 cameraPosition);
|
||||
|
||||
void setHasInteractingOther(bool hasInteractingOther) { _hasInteractingOther = hasInteractingOther;}
|
||||
void setMyHandPosition (glm::vec3 position ) { _myHandPosition = position;}
|
||||
void setYourHandPosition (glm::vec3 position ) { _yourHandPosition = position;}
|
||||
void setMyOrientation (glm::quat orientation ) { _myOrientation = orientation;}
|
||||
void setYourOrientation (glm::quat orientation ) { _yourOrientation = orientation;}
|
||||
void setMyBodyPosition (glm::vec3 position ) { _myBodyPosition = position;}
|
||||
void setYourBodyPosition (glm::vec3 position ) { _yourBodyPosition = position;}
|
||||
void setMyHandState (int state ) { _myHandState = state;}
|
||||
void setYourHandState (int state ) { _yourHandState = state;}
|
||||
void setReachableRadius (float radius ) { _reachableRadius = radius;}
|
||||
void setHoldingHands (bool holding ) { _weAreHoldingHands = holding;}
|
||||
|
||||
bool getAbleToReachOtherAvatar () const {return _canReachToOtherAvatar; }
|
||||
bool getHandsCloseEnoughToGrasp() const {return _handsCloseEnoughToGrasp;}
|
||||
bool getHoldingHands () const {return _weAreHoldingHands; }
|
||||
|
||||
private:
|
||||
|
||||
static const int NUM_PARTICLE_POINTS = 100;
|
||||
|
||||
bool _hasInteractingOther;
|
||||
bool _weAreHoldingHands;
|
||||
glm::vec3 _point [NUM_PARTICLE_POINTS];
|
||||
glm::vec3 _myBodyPosition;
|
||||
glm::vec3 _yourBodyPosition;
|
||||
glm::vec3 _myHandPosition;
|
||||
glm::vec3 _yourHandPosition;
|
||||
glm::quat _myOrientation;
|
||||
glm::quat _yourOrientation;
|
||||
glm::vec3 _vectorBetweenHands;
|
||||
int _myHandState;
|
||||
int _yourHandState;
|
||||
bool _canReachToOtherAvatar;
|
||||
bool _handsCloseEnoughToGrasp;
|
||||
float _reachableRadius;
|
||||
|
||||
void renderBeamBetweenHands();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,339 +0,0 @@
|
|||
//
|
||||
// AvatarVoxelSystem.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/31/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include <GeometryUtil.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Avatar.h"
|
||||
#include "AvatarVoxelSystem.h"
|
||||
#include "renderer/ProgramObject.h"
|
||||
|
||||
const float AVATAR_TREE_SCALE = 1.0f;
|
||||
const int MAX_VOXELS_PER_AVATAR = 10000;
|
||||
const int BONE_ELEMENTS_PER_VOXEL = BONE_ELEMENTS_PER_VERTEX * VERTICES_PER_VOXEL;
|
||||
|
||||
AvatarVoxelSystem::AvatarVoxelSystem(Avatar* avatar) :
|
||||
VoxelSystem(AVATAR_TREE_SCALE, MAX_VOXELS_PER_AVATAR),
|
||||
_initialized(false),
|
||||
_mode(0),
|
||||
_avatar(avatar),
|
||||
_voxelReply(0) {
|
||||
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
moveToThread(Application::getInstance()->thread());
|
||||
}
|
||||
|
||||
AvatarVoxelSystem::~AvatarVoxelSystem() {
|
||||
if (_initialized) {
|
||||
delete[] _readBoneIndicesArray;
|
||||
delete[] _readBoneWeightsArray;
|
||||
delete[] _writeBoneIndicesArray;
|
||||
delete[] _writeBoneWeightsArray;
|
||||
|
||||
glDeleteBuffers(1, &_vboBoneIndicesID);
|
||||
glDeleteBuffers(1, &_vboBoneWeightsID);
|
||||
}
|
||||
}
|
||||
|
||||
ProgramObject AvatarVoxelSystem::_skinProgram;
|
||||
int AvatarVoxelSystem::_boneMatricesLocation;
|
||||
int AvatarVoxelSystem::_boneIndicesLocation;
|
||||
int AvatarVoxelSystem::_boneWeightsLocation;
|
||||
|
||||
void AvatarVoxelSystem::init() {
|
||||
if (_initialized) {
|
||||
qDebug("[ERROR] AvatarVoxelSystem is already initialized.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
VoxelSystem::init();
|
||||
|
||||
// prep the data structures for incoming voxel data
|
||||
_writeBoneIndicesArray = new GLubyte[BONE_ELEMENTS_PER_VOXEL * _maxVoxels];
|
||||
_readBoneIndicesArray = new GLubyte[BONE_ELEMENTS_PER_VOXEL * _maxVoxels];
|
||||
|
||||
_writeBoneWeightsArray = new GLfloat[BONE_ELEMENTS_PER_VOXEL * _maxVoxels];
|
||||
_readBoneWeightsArray = new GLfloat[BONE_ELEMENTS_PER_VOXEL * _maxVoxels];
|
||||
|
||||
// VBO for the boneIndicesArray
|
||||
glGenBuffers(1, &_vboBoneIndicesID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneIndicesID);
|
||||
glBufferData(GL_ARRAY_BUFFER, BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);
|
||||
|
||||
// VBO for the boneWeightsArray
|
||||
glGenBuffers(1, &_vboBoneWeightsID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneWeightsID);
|
||||
glBufferData(GL_ARRAY_BUFFER, BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);
|
||||
|
||||
// load our skin program if this is the first avatar system to initialize
|
||||
if (!_skinProgram.isLinked()) {
|
||||
_skinProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/skin_voxels.vert");
|
||||
_skinProgram.link();
|
||||
}
|
||||
|
||||
_boneMatricesLocation = _skinProgram.uniformLocation("boneMatrices");
|
||||
_boneIndicesLocation = _skinProgram.attributeLocation("boneIndices");
|
||||
_boneWeightsLocation = _skinProgram.attributeLocation("boneWeights");
|
||||
|
||||
VoxelTreeElement::removeUpdateHook(this); // we don't want this
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::removeOutOfView() {
|
||||
// no-op for now
|
||||
}
|
||||
|
||||
class Mode {
|
||||
public:
|
||||
|
||||
bool bindVoxelsTogether;
|
||||
int maxBonesPerBind;
|
||||
bool includeBonesOutsideBindRadius;
|
||||
};
|
||||
|
||||
const Mode MODES[] = {
|
||||
{ false, BONE_ELEMENTS_PER_VERTEX, false }, // original
|
||||
{ false, 1, true }, // one bone per vertex
|
||||
{ true, 1, true }, // one bone per voxel
|
||||
{ true, BONE_ELEMENTS_PER_VERTEX, false } }; // four bones per voxel
|
||||
|
||||
void AvatarVoxelSystem::cycleMode() {
|
||||
_mode = (_mode + 1) % (sizeof(MODES) / sizeof(MODES[0]));
|
||||
qDebug("Voxeltar bind mode %d.\n", _mode);
|
||||
|
||||
// rebind
|
||||
QUrl url = _voxelURL;
|
||||
setVoxelURL(QUrl());
|
||||
setVoxelURL(url);
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::setVoxelURL(const QUrl& url) {
|
||||
// don't restart the download if it's the same URL
|
||||
if (_voxelURL == url) {
|
||||
return;
|
||||
}
|
||||
|
||||
// cancel any current download
|
||||
if (_voxelReply != 0) {
|
||||
delete _voxelReply;
|
||||
_voxelReply = 0;
|
||||
}
|
||||
|
||||
killLocalVoxels();
|
||||
|
||||
// remember the URL
|
||||
_voxelURL = url;
|
||||
|
||||
// handle "file://" urls...
|
||||
if (url.isLocalFile()) {
|
||||
QString pathString = url.path();
|
||||
QByteArray pathAsAscii = pathString.toLocal8Bit();
|
||||
const char* path = pathAsAscii.data();
|
||||
readFromSVOFile(path);
|
||||
return;
|
||||
}
|
||||
|
||||
// load the URL data asynchronously
|
||||
if (!url.isValid()) {
|
||||
return;
|
||||
}
|
||||
_voxelReply = Application::getInstance()->getNetworkAccessManager()->get(QNetworkRequest(url));
|
||||
connect(_voxelReply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleVoxelDownloadProgress(qint64,qint64)));
|
||||
connect(_voxelReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleVoxelReplyError()));
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||
float voxelScale, const nodeColor& color) {
|
||||
VoxelSystem::updateArraysDetails(nodeIndex, startVertex, voxelScale, color);
|
||||
|
||||
GLubyte* writeBoneIndicesAt = _writeBoneIndicesArray + (nodeIndex * BONE_ELEMENTS_PER_VOXEL);
|
||||
GLfloat* writeBoneWeightsAt = _writeBoneWeightsArray + (nodeIndex * BONE_ELEMENTS_PER_VOXEL);
|
||||
|
||||
if (MODES[_mode].bindVoxelsTogether) {
|
||||
BoneIndices boneIndices;
|
||||
glm::vec4 boneWeights;
|
||||
computeBoneIndicesAndWeights(startVertex + glm::vec3(voxelScale, voxelScale, voxelScale) * 0.5f,
|
||||
boneIndices, boneWeights);
|
||||
for (int i = 0; i < VERTICES_PER_VOXEL; i++) {
|
||||
for (int j = 0; j < BONE_ELEMENTS_PER_VERTEX; j++) {
|
||||
*(writeBoneIndicesAt + i * BONE_ELEMENTS_PER_VERTEX + j) = boneIndices[j];
|
||||
*(writeBoneWeightsAt + i * BONE_ELEMENTS_PER_VERTEX + j) = boneWeights[j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < VERTICES_PER_VOXEL; i++) {
|
||||
BoneIndices boneIndices;
|
||||
glm::vec4 boneWeights;
|
||||
computeBoneIndicesAndWeights(computeVoxelVertex(startVertex, voxelScale, i), boneIndices, boneWeights);
|
||||
for (int j = 0; j < BONE_ELEMENTS_PER_VERTEX; j++) {
|
||||
*(writeBoneIndicesAt + i * BONE_ELEMENTS_PER_VERTEX + j) = boneIndices[j];
|
||||
*(writeBoneWeightsAt + i * BONE_ELEMENTS_PER_VERTEX + j) = boneWeights[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
|
||||
VoxelSystem::copyWrittenDataSegmentToReadArrays(segmentStart, segmentEnd);
|
||||
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readBoneIndicesAt = _readBoneIndicesArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
GLubyte* writeBoneIndicesAt = _writeBoneIndicesArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
memcpy(readBoneIndicesAt, writeBoneIndicesAt, segmentSizeBytes);
|
||||
|
||||
segmentStartAt = segmentStart * BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat);
|
||||
segmentSizeBytes = segmentLength * BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readBoneWeightsAt = _readBoneWeightsArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
GLfloat* writeBoneWeightsAt = _writeBoneWeightsArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
memcpy(readBoneWeightsAt, writeBoneWeightsAt, segmentSizeBytes);
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
|
||||
VoxelSystem::updateVBOSegment(segmentStart, segmentEnd);
|
||||
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * BONE_ELEMENTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readBoneIndicesFrom = _readBoneIndicesArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneIndicesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readBoneIndicesFrom);
|
||||
|
||||
segmentStartAt = segmentStart * BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat);
|
||||
segmentSizeBytes = segmentLength * BONE_ELEMENTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readBoneWeightsFrom = _readBoneWeightsArray + (segmentStart * BONE_ELEMENTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneWeightsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readBoneWeightsFrom);
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::applyScaleAndBindProgram(bool texture) {
|
||||
_skinProgram.bind();
|
||||
|
||||
// the base matrix includes centering and scale
|
||||
QMatrix4x4 baseMatrix;
|
||||
baseMatrix.scale(_treeScale);
|
||||
baseMatrix.translate(-0.5f, -0.5f, -0.5f);
|
||||
|
||||
// bone matrices include joint transforms
|
||||
QMatrix4x4 boneMatrices[NUM_AVATAR_JOINTS];
|
||||
for (int i = 0; i < NUM_AVATAR_JOINTS; i++) {
|
||||
glm::vec3 position;
|
||||
glm::quat orientation;
|
||||
_avatar->getBodyBallTransform((AvatarJointID)i, position, orientation);
|
||||
boneMatrices[i].translate(position.x, position.y, position.z);
|
||||
orientation = orientation * glm::inverse(_avatar->getSkeleton().joint[i].absoluteBindPoseRotation);
|
||||
boneMatrices[i].rotate(QQuaternion(orientation.w, orientation.x, orientation.y, orientation.z));
|
||||
const glm::vec3& bindPosition = _avatar->getSkeleton().joint[i].absoluteBindPosePosition;
|
||||
boneMatrices[i].translate(-bindPosition.x, -bindPosition.y, -bindPosition.z);
|
||||
boneMatrices[i] *= baseMatrix;
|
||||
}
|
||||
_skinProgram.setUniformValueArray(_boneMatricesLocation, boneMatrices, NUM_AVATAR_JOINTS);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneIndicesID);
|
||||
glVertexAttribPointer(_boneIndicesLocation, BONE_ELEMENTS_PER_VERTEX, GL_UNSIGNED_BYTE, false, 0, 0);
|
||||
_skinProgram.enableAttributeArray(_boneIndicesLocation);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboBoneWeightsID);
|
||||
_skinProgram.setAttributeBuffer(_boneWeightsLocation, GL_FLOAT, 0, BONE_ELEMENTS_PER_VERTEX);
|
||||
_skinProgram.enableAttributeArray(_boneWeightsLocation);
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::removeScaleAndReleaseProgram(bool texture) {
|
||||
_skinProgram.release();
|
||||
_skinProgram.disableAttributeArray(_boneIndicesLocation);
|
||||
_skinProgram.disableAttributeArray(_boneWeightsLocation);
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::handleVoxelDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
||||
// for now, just wait until we have the full business
|
||||
if (bytesReceived < bytesTotal) {
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray entirety = _voxelReply->readAll();
|
||||
_voxelReply->disconnect(this);
|
||||
_voxelReply->deleteLater();
|
||||
_voxelReply = 0;
|
||||
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS);
|
||||
_tree->readBitstreamToTree((unsigned char*)entirety.data(), entirety.size(), args);
|
||||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
|
||||
void AvatarVoxelSystem::handleVoxelReplyError() {
|
||||
qDebug("%s\n", _voxelReply->errorString().toLocal8Bit().constData());
|
||||
|
||||
_voxelReply->disconnect(this);
|
||||
_voxelReply->deleteLater();
|
||||
_voxelReply = 0;
|
||||
}
|
||||
|
||||
class IndexDistance {
|
||||
public:
|
||||
IndexDistance(GLubyte index = AVATAR_JOINT_PELVIS, float distance = FLT_MAX) : index(index), distance(distance) { }
|
||||
|
||||
GLubyte index;
|
||||
float distance;
|
||||
};
|
||||
|
||||
void AvatarVoxelSystem::computeBoneIndicesAndWeights(const glm::vec3& vertex, BoneIndices& indices, glm::vec4& weights) const {
|
||||
// transform into joint space
|
||||
glm::vec3 jointVertex = (vertex - glm::vec3(0.5f, 0.5f, 0.5f)) * AVATAR_TREE_SCALE;
|
||||
|
||||
// find the nearest four joints (TODO: use a better data structure for the pose positions to speed this up)
|
||||
IndexDistance nearest[BONE_ELEMENTS_PER_VERTEX];
|
||||
const Skeleton& skeleton = _avatar->getSkeleton();
|
||||
for (int i = 0; i < NUM_AVATAR_JOINTS; i++) {
|
||||
AvatarJointID parent = skeleton.joint[i].parent;
|
||||
float distance = glm::length(computeVectorFromPointToSegment(jointVertex,
|
||||
skeleton.joint[parent == AVATAR_JOINT_NULL ? i : parent].absoluteBindPosePosition,
|
||||
skeleton.joint[i].absoluteBindPosePosition));
|
||||
if (!MODES[_mode].includeBonesOutsideBindRadius && distance > skeleton.joint[i].bindRadius) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < BONE_ELEMENTS_PER_VERTEX; j++) {
|
||||
if (distance < nearest[j].distance) {
|
||||
// move the rest of the indices down
|
||||
for (int k = BONE_ELEMENTS_PER_VERTEX - 1; k > j; k--) {
|
||||
nearest[k] = nearest[k - 1];
|
||||
}
|
||||
nearest[j] = IndexDistance(i, distance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compute the weights based on inverse distance
|
||||
float totalWeight = 0.0f;
|
||||
for (int i = 0; i < MODES[_mode].maxBonesPerBind; i++) {
|
||||
indices[i] = nearest[i].index;
|
||||
if (nearest[i].distance != FLT_MAX) {
|
||||
weights[i] = 1.0f / glm::max(nearest[i].distance, EPSILON);
|
||||
totalWeight += weights[i];
|
||||
|
||||
} else {
|
||||
weights[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// if it's not attached to anything, consider it attached to the hip
|
||||
if (totalWeight == 0.0f) {
|
||||
weights[0] = 1.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
// ortherwise, normalize the weights
|
||||
for (int i = 0; i < BONE_ELEMENTS_PER_VERTEX; i++) {
|
||||
weights[i] /= totalWeight;
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
//
|
||||
// AvatarVoxelSystem.h
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/31/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__AvatarVoxelSystem__
|
||||
#define __interface__AvatarVoxelSystem__
|
||||
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
#include "VoxelSystem.h"
|
||||
|
||||
const int BONE_ELEMENTS_PER_VERTEX = 4;
|
||||
typedef GLubyte BoneIndices[BONE_ELEMENTS_PER_VERTEX];
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
class Avatar;
|
||||
|
||||
class AvatarVoxelSystem : public VoxelSystem {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AvatarVoxelSystem(Avatar* avatar);
|
||||
virtual ~AvatarVoxelSystem();
|
||||
|
||||
virtual void init();
|
||||
|
||||
virtual void removeOutOfView();
|
||||
|
||||
Q_INVOKABLE void setVoxelURL(const QUrl& url);
|
||||
const QUrl& getVoxelURL() const { return _voxelURL; }
|
||||
|
||||
public slots:
|
||||
|
||||
void cycleMode();
|
||||
|
||||
protected:
|
||||
|
||||
virtual void updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||
float voxelScale, const nodeColor& color);
|
||||
virtual void copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||
virtual void updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd);
|
||||
virtual void applyScaleAndBindProgram(bool texture);
|
||||
virtual void removeScaleAndReleaseProgram(bool texture);
|
||||
|
||||
private slots:
|
||||
|
||||
void handleVoxelDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||
void handleVoxelReplyError();
|
||||
|
||||
private:
|
||||
|
||||
void computeBoneIndicesAndWeights(const glm::vec3& vertex, BoneIndices& indices, glm::vec4& weights) const;
|
||||
|
||||
bool _initialized;
|
||||
int _mode;
|
||||
|
||||
Avatar* _avatar;
|
||||
|
||||
QUrl _voxelURL;
|
||||
|
||||
GLubyte* _readBoneIndicesArray;
|
||||
GLfloat* _readBoneWeightsArray;
|
||||
GLubyte* _writeBoneIndicesArray;
|
||||
GLfloat* _writeBoneWeightsArray;
|
||||
|
||||
GLuint _vboBoneIndicesID;
|
||||
GLuint _vboBoneWeightsID;
|
||||
|
||||
QNetworkReply* _voxelReply;
|
||||
|
||||
static ProgramObject _skinProgram;
|
||||
static int _boneMatricesLocation;
|
||||
static int _boneIndicesLocation;
|
||||
static int _boneWeightsLocation;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__AvatarVoxelSystem__) */
|
|
@ -24,6 +24,7 @@
|
|||
#include "ParticleSystem.h"
|
||||
#include "world.h"
|
||||
#include "devices/SerialInterface.h"
|
||||
#include "VoxelSystem.h"
|
||||
|
||||
class Avatar;
|
||||
class ProgramObject;
|
||||
|
|
|
@ -250,9 +250,8 @@ void Head::simulate(float deltaTime, bool isMine) {
|
|||
calculateGeometry();
|
||||
|
||||
// the blend face may have custom eye meshes
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls)) {
|
||||
_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition);
|
||||
}
|
||||
_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition);
|
||||
|
||||
}
|
||||
|
||||
void Head::calculateGeometry() {
|
||||
|
|
|
@ -32,7 +32,6 @@ const float COLLISION_RADIUS_SCALAR = 1.2; // pertains to avatar-to-avatar colli
|
|||
const float COLLISION_BALL_FORCE = 200.0; // pertains to avatar-to-avatar collisions
|
||||
const float COLLISION_BODY_FORCE = 30.0; // pertains to avatar-to-avatar collisions
|
||||
const float COLLISION_RADIUS_SCALE = 0.125f;
|
||||
const float PERIPERSONAL_RADIUS = 1.0f;
|
||||
const float MOUSE_RAY_TOUCH_RANGE = 0.01f;
|
||||
const bool USING_HEAD_LEAN = false;
|
||||
const float SKIN_COLOR[] = {1.0, 0.84, 0.66};
|
||||
|
@ -46,7 +45,6 @@ MyAvatar::MyAvatar(Node* owningNode) :
|
|||
_shouldJump(false),
|
||||
_gravity(0.0f, -1.0f, 0.0f),
|
||||
_distanceToNearestAvatar(std::numeric_limits<float>::max()),
|
||||
_interactingOther(NULL),
|
||||
_elapsedTimeMoving(0.0f),
|
||||
_elapsedTimeStopped(0.0f),
|
||||
_elapsedTimeSinceCollision(0.0f),
|
||||
|
@ -77,8 +75,6 @@ void MyAvatar::setMoveTarget(const glm::vec3 moveTarget) {
|
|||
void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
|
||||
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
|
||||
// Update movement timers
|
||||
_elapsedTimeSinceCollision += deltaTime;
|
||||
|
@ -115,9 +111,6 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
// calculate speed
|
||||
_speed = glm::length(_velocity);
|
||||
|
||||
// figure out if the mouse cursor is over any body spheres...
|
||||
checkForMouseRayTouching();
|
||||
|
||||
// update balls
|
||||
if (_balls) {
|
||||
_balls->moveOrigin(_position);
|
||||
|
@ -143,30 +136,8 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
enableHandMovement &= (it->jointID != AVATAR_JOINT_RIGHT_WRIST);
|
||||
}
|
||||
|
||||
// update avatar skeleton
|
||||
_skeleton.update(deltaTime, getOrientation(), _position);
|
||||
|
||||
// determine the lengths of the body springs now that we have updated the skeleton at least once
|
||||
if (!_ballSpringsInitialized) {
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
|
||||
glm::vec3 targetPosition
|
||||
= _skeleton.joint[_bodyBall[b].parentJoint].position
|
||||
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
|
||||
|
||||
glm::vec3 parentTargetPosition
|
||||
= _skeleton.joint[_bodyBall[b].parentJoint].position
|
||||
+ _skeleton.joint[_bodyBall[b].parentJoint].rotation * _bodyBall[b].parentOffset;
|
||||
|
||||
_bodyBall[b].springLength = glm::length(targetPosition - parentTargetPosition);
|
||||
}
|
||||
|
||||
_ballSpringsInitialized = true;
|
||||
}
|
||||
|
||||
// update the movement of the hand and process handshaking with other avatars...
|
||||
updateHandMovementAndTouching(deltaTime, enableHandMovement);
|
||||
_avatarTouch.simulate(deltaTime);
|
||||
|
||||
// apply gravity
|
||||
// For gravity, always move the avatar by the amount driven by gravity, so that the collision
|
||||
|
@ -193,9 +164,6 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
updateAvatarCollisions(deltaTime);
|
||||
}
|
||||
|
||||
// update body balls
|
||||
updateBodyBalls(deltaTime);
|
||||
|
||||
// add thrust to velocity
|
||||
_velocity += _thrust * deltaTime;
|
||||
|
||||
|
@ -238,30 +206,9 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
applyDamping(deltaTime, _velocity, linearDamping, SQUARED_DAMPING_STRENGTH);
|
||||
}
|
||||
|
||||
// pitch and roll the body as a function of forward speed and turning delta
|
||||
const float HIGH_VELOCITY = 10.f;
|
||||
if (glm::length(_velocity) < HIGH_VELOCITY) {
|
||||
const float BODY_PITCH_WHILE_WALKING = -20.0;
|
||||
const float BODY_ROLL_WHILE_TURNING = 0.2;
|
||||
float forwardComponentOfVelocity = glm::dot(getBodyFrontDirection(), _velocity);
|
||||
orientation = orientation * glm::quat(glm::radians(glm::vec3(
|
||||
BODY_PITCH_WHILE_WALKING * deltaTime * forwardComponentOfVelocity, 0.0f,
|
||||
BODY_ROLL_WHILE_TURNING * deltaTime * _speed * _bodyYawDelta)));
|
||||
}
|
||||
|
||||
// these forces keep the body upright...
|
||||
const float BODY_UPRIGHT_FORCE = _scale * 10.0;
|
||||
float tiltDecay = BODY_UPRIGHT_FORCE * deltaTime;
|
||||
if (tiltDecay > 1.0f) {
|
||||
tiltDecay = 1.0f;
|
||||
}
|
||||
|
||||
// update the euler angles
|
||||
setOrientation(orientation);
|
||||
|
||||
//the following will be used to make the avatar upright no matter what gravity is
|
||||
setOrientation(computeRotationFromBodyToWorldUp(tiltDecay) * orientation);
|
||||
|
||||
// Compute instantaneous acceleration
|
||||
float forwardAcceleration = glm::length(glm::dot(getBodyFrontDirection(), getVelocity() - oldVelocity)) / deltaTime;
|
||||
const float ACCELERATION_PITCH_DECAY = 0.4f;
|
||||
|
@ -295,44 +242,6 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
}
|
||||
}
|
||||
|
||||
//apply the head lean values to the ball positions...
|
||||
if (USING_HEAD_LEAN) {
|
||||
if (fabs(_head.getLeanSideways() + _head.getLeanForward()) > 0.0f) {
|
||||
glm::vec3 headLean =
|
||||
right * _head.getLeanSideways() +
|
||||
front * _head.getLeanForward();
|
||||
|
||||
_bodyBall[BODY_BALL_TORSO].position += headLean * 0.1f;
|
||||
_bodyBall[BODY_BALL_CHEST].position += headLean * 0.4f;
|
||||
_bodyBall[BODY_BALL_NECK_BASE].position += headLean * 0.7f;
|
||||
_bodyBall[BODY_BALL_HEAD_BASE].position += headLean * 1.0f;
|
||||
|
||||
_bodyBall[BODY_BALL_LEFT_COLLAR].position += headLean * 0.6f;
|
||||
_bodyBall[BODY_BALL_LEFT_SHOULDER].position += headLean * 0.6f;
|
||||
_bodyBall[BODY_BALL_LEFT_ELBOW].position += headLean * 0.2f;
|
||||
_bodyBall[BODY_BALL_LEFT_WRIST].position += headLean * 0.1f;
|
||||
_bodyBall[BODY_BALL_LEFT_FINGERTIPS].position += headLean * 0.0f;
|
||||
|
||||
_bodyBall[BODY_BALL_RIGHT_COLLAR].position += headLean * 0.6f;
|
||||
_bodyBall[BODY_BALL_RIGHT_SHOULDER].position += headLean * 0.6f;
|
||||
_bodyBall[BODY_BALL_RIGHT_ELBOW].position += headLean * 0.2f;
|
||||
_bodyBall[BODY_BALL_RIGHT_WRIST].position += headLean * 0.1f;
|
||||
_bodyBall[BODY_BALL_RIGHT_FINGERTIPS].position += headLean * 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
_hand.simulate(deltaTime, true);
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||
glm::vec3 headPosition;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls) || !_skeletonModel.getHeadPosition(headPosition)) {
|
||||
headPosition = _bodyBall[BODY_BALL_HEAD_BASE].position;
|
||||
}
|
||||
_head.setPosition(headPosition);
|
||||
_head.setScale(_scale);
|
||||
_head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]));
|
||||
_head.simulate(deltaTime, true);
|
||||
|
||||
const float WALKING_SPEED_THRESHOLD = 0.2f;
|
||||
// use speed and angular velocity to determine walking vs. standing
|
||||
if (_speed + fabs(_bodyYawDelta) > WALKING_SPEED_THRESHOLD) {
|
||||
|
@ -364,6 +273,18 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
|
||||
_position += _velocity * deltaTime;
|
||||
|
||||
// update avatar skeleton and simulate hand and head
|
||||
_skeleton.update(deltaTime, getOrientation(), _position);
|
||||
_hand.simulate(deltaTime, true);
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||
glm::vec3 headPosition;
|
||||
_skeletonModel.getHeadPosition(headPosition);
|
||||
_head.setPosition(headPosition);
|
||||
_head.setScale(_scale);
|
||||
_head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]));
|
||||
_head.simulate(deltaTime, true);
|
||||
|
||||
// Zero thrust out now that we've added it to velocity in this frame
|
||||
_thrust = glm::vec3(0, 0, 0);
|
||||
|
||||
|
@ -498,16 +419,10 @@ static TextRenderer* textRenderer() {
|
|||
}
|
||||
|
||||
void MyAvatar::render(bool forceRenderHead) {
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
// render body
|
||||
renderBody(forceRenderHead);
|
||||
|
||||
// if this is my avatar, then render my interactions with the other avatar
|
||||
_avatarTouch.render(Application::getInstance()->getCamera()->getPosition());
|
||||
|
||||
// Render the balls
|
||||
if (_balls) {
|
||||
glPushMatrix();
|
||||
|
@ -523,7 +438,7 @@ void MyAvatar::render(bool forceRenderHead) {
|
|||
}
|
||||
glPushMatrix();
|
||||
|
||||
glm::vec3 chatPosition = _bodyBall[BODY_BALL_HEAD_BASE].position + getBodyUpDirection() * chatMessageHeight * _scale;
|
||||
glm::vec3 chatPosition = getPosition() + getBodyUpDirection() * chatMessageHeight * _scale;
|
||||
glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z);
|
||||
glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation();
|
||||
glm::vec3 chatAxis = glm::axis(chatRotation);
|
||||
|
@ -571,7 +486,6 @@ void MyAvatar::saveData(QSettings* settings) {
|
|||
settings->setValue("position_y", _position.y);
|
||||
settings->setValue("position_z", _position.z);
|
||||
|
||||
settings->setValue("voxelURL", _voxels.getVoxelURL());
|
||||
settings->setValue("pupilDilation", _head.getPupilDilation());
|
||||
|
||||
settings->setValue("leanScale", _leanScale);
|
||||
|
@ -594,7 +508,6 @@ void MyAvatar::loadData(QSettings* settings) {
|
|||
_position.y = loadSetting(settings, "position_y", 0.0f);
|
||||
_position.z = loadSetting(settings, "position_z", 0.0f);
|
||||
|
||||
_voxels.setVoxelURL(settings->value("voxelURL").toUrl());
|
||||
_head.setPupilDilation(settings->value("pupilDilation", 0.0f).toFloat());
|
||||
|
||||
_leanScale = loadSetting(settings, "leanScale", 0.05f);
|
||||
|
@ -621,93 +534,15 @@ glm::vec3 MyAvatar::getEyeLevelPosition() const {
|
|||
glm::vec3(0.0f, _pelvisToHeadLength + _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_UP_OFFSET, 0.0f);
|
||||
}
|
||||
|
||||
float MyAvatar::getBallRenderAlpha(int ball, bool forceRenderHead) const {
|
||||
const float RENDER_OPAQUE_OUTSIDE = _scale * 0.25f; // render opaque if greater than this distance
|
||||
const float DO_NOT_RENDER_INSIDE = _scale * 0.25f; // do not render if less than this distance
|
||||
float distanceToCamera = glm::length(Application::getInstance()->getCamera()->getPosition() - _bodyBall[ball].position);
|
||||
return (forceRenderHead) ? 1.0f : glm::clamp(
|
||||
(distanceToCamera - DO_NOT_RENDER_INSIDE) / (RENDER_OPAQUE_OUTSIDE - DO_NOT_RENDER_INSIDE), 0.f, 1.f);
|
||||
}
|
||||
|
||||
void MyAvatar::renderBody(bool forceRenderHead) {
|
||||
|
||||
if (_head.getVideoFace().isFullFrame()) {
|
||||
// Render the full-frame video
|
||||
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, forceRenderHead);
|
||||
if (alpha > 0.0f) {
|
||||
_head.getVideoFace().render(1.0f);
|
||||
}
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls)) {
|
||||
// Render the body as balls and cones
|
||||
glm::vec3 skinColor, darkSkinColor;
|
||||
getSkinColors(skinColor, darkSkinColor);
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
float alpha = getBallRenderAlpha(b, forceRenderHead);
|
||||
|
||||
// When we have leap hands, hide part of the arms.
|
||||
if (_hand.getNumPalms() > 0) {
|
||||
if (b == BODY_BALL_LEFT_FINGERTIPS
|
||||
|| 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) {
|
||||
_head.render(alpha, true);
|
||||
}
|
||||
} else if (alpha > 0.0f) {
|
||||
// Render the body ball sphere
|
||||
if (b == BODY_BALL_RIGHT_ELBOW
|
||||
|| b == BODY_BALL_RIGHT_WRIST
|
||||
|| b == BODY_BALL_RIGHT_FINGERTIPS ) {
|
||||
glColor3f(skinColor.r + _bodyBall[b].touchForce * 0.3f,
|
||||
skinColor.g - _bodyBall[b].touchForce * 0.2f,
|
||||
skinColor.b - _bodyBall[b].touchForce * 0.1f);
|
||||
} else {
|
||||
glColor4f(skinColor.r + _bodyBall[b].touchForce * 0.3f,
|
||||
skinColor.g - _bodyBall[b].touchForce * 0.2f,
|
||||
skinColor.b - _bodyBall[b].touchForce * 0.1f,
|
||||
alpha);
|
||||
}
|
||||
|
||||
if ((b != BODY_BALL_HEAD_TOP )
|
||||
&& (b != BODY_BALL_HEAD_BASE )) {
|
||||
glPushMatrix();
|
||||
glTranslatef(_bodyBall[b].position.x, _bodyBall[b].position.y, _bodyBall[b].position.z);
|
||||
glutSolidSphere(_bodyBall[b].radius, 20.0f, 20.0f);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
// Render the cone connecting this ball to its parent
|
||||
if (_bodyBall[b].parentBall != BODY_BALL_NULL) {
|
||||
if ((b != BODY_BALL_HEAD_TOP)
|
||||
&& (b != BODY_BALL_HEAD_BASE)
|
||||
&& (b != BODY_BALL_PELVIS)
|
||||
&& (b != BODY_BALL_TORSO)
|
||||
&& (b != BODY_BALL_CHEST)
|
||||
&& (b != BODY_BALL_LEFT_COLLAR)
|
||||
&& (b != BODY_BALL_LEFT_SHOULDER)
|
||||
&& (b != BODY_BALL_RIGHT_COLLAR)
|
||||
&& (b != BODY_BALL_RIGHT_SHOULDER)) {
|
||||
glColor3fv((const GLfloat*)&darkSkinColor);
|
||||
|
||||
float r2 = _bodyBall[b].radius * 0.8;
|
||||
|
||||
renderJointConnectingCone(_bodyBall[_bodyBall[b].parentBall].position, _bodyBall[b].position, r2, r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Render the body's voxels and head
|
||||
if (!_skeletonModel.render(1.0f)) {
|
||||
_voxels.render(false);
|
||||
}
|
||||
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, forceRenderHead);
|
||||
if (alpha > 0.0f) {
|
||||
_head.render(alpha, false);
|
||||
}
|
||||
_skeletonModel.render(1.0f);
|
||||
_head.render(1.0f, false);
|
||||
}
|
||||
_hand.render(true);
|
||||
}
|
||||
|
@ -882,90 +717,6 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMov
|
|||
pointing = true;
|
||||
}
|
||||
|
||||
_avatarTouch.setMyBodyPosition(_position);
|
||||
_avatarTouch.setMyOrientation(orientation);
|
||||
|
||||
float closestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
_interactingOther = NULL;
|
||||
|
||||
//loop through all the other avatars for potential interactions...
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
|
||||
Avatar *otherAvatar = (Avatar *)node->getLinkedData();
|
||||
|
||||
// test whether shoulders are close enough to allow for reaching to touch hands
|
||||
glm::vec3 v(_position - otherAvatar->_position);
|
||||
float distance = glm::length(v);
|
||||
if (distance < closestDistance) {
|
||||
closestDistance = distance;
|
||||
|
||||
if (distance < _scale * PERIPERSONAL_RADIUS) {
|
||||
_interactingOther = otherAvatar;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_interactingOther) {
|
||||
|
||||
_avatarTouch.setHasInteractingOther(true);
|
||||
_avatarTouch.setYourBodyPosition(_interactingOther->_position);
|
||||
_avatarTouch.setYourHandPosition(_interactingOther->_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].position);
|
||||
_avatarTouch.setYourOrientation (_interactingOther->getOrientation());
|
||||
_avatarTouch.setYourHandState(_interactingOther->_handState);
|
||||
|
||||
//if hand-holding is initiated by either avatar, turn on hand-holding...
|
||||
if (_avatarTouch.getHandsCloseEnoughToGrasp()) {
|
||||
if ((_handState == HAND_STATE_GRASPING ) || (_interactingOther->_handState == HAND_STATE_GRASPING)) {
|
||||
if (!_avatarTouch.getHoldingHands())
|
||||
{
|
||||
_avatarTouch.setHoldingHands(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 vectorFromMyHandToYourHand
|
||||
(
|
||||
_interactingOther->_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position -
|
||||
_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position
|
||||
);
|
||||
|
||||
float distanceBetweenOurHands = glm::length(vectorFromMyHandToYourHand);
|
||||
|
||||
// if neither of us are grasping, turn off hand-holding
|
||||
if ((_handState != HAND_STATE_GRASPING ) && (_interactingOther->_handState != HAND_STATE_GRASPING)) {
|
||||
_avatarTouch.setHoldingHands(false);
|
||||
}
|
||||
|
||||
//if holding hands, apply the appropriate forces
|
||||
if (_avatarTouch.getHoldingHands()) {
|
||||
_skeleton.joint[AVATAR_JOINT_RIGHT_FINGERTIPS ].position +=
|
||||
(_interactingOther->_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position
|
||||
- _skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position) * 0.5f;
|
||||
|
||||
const float MAX_FORCE = 1.0f;
|
||||
const float FORCE_RATIO = 10.0f;
|
||||
|
||||
if (distanceBetweenOurHands > 0.3) {
|
||||
float force = min(MAX_FORCE, FORCE_RATIO * deltaTime);
|
||||
_velocity += vectorFromMyHandToYourHand * force;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_avatarTouch.setHasInteractingOther(false);
|
||||
}
|
||||
|
||||
enableHandMovement |= updateLeapHandPositions();
|
||||
|
||||
//constrain right arm length and re-adjust elbow position as it bends
|
||||
// NOTE - the following must be called on all avatars - not just _isMine
|
||||
if (enableHandMovement) {
|
||||
updateArmIKAndConstraints(deltaTime, AVATAR_JOINT_RIGHT_FINGERTIPS);
|
||||
updateArmIKAndConstraints(deltaTime, AVATAR_JOINT_LEFT_FINGERTIPS);
|
||||
}
|
||||
|
||||
//Set right hand position and state to be transmitted, and also tell AvatarTouch about it
|
||||
setHandPosition(_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position);
|
||||
|
||||
|
@ -976,9 +727,6 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMov
|
|||
} else {
|
||||
_handState = HAND_STATE_NULL;
|
||||
}
|
||||
|
||||
_avatarTouch.setMyHandState(_handState);
|
||||
_avatarTouch.setMyHandPosition(_bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].position);
|
||||
}
|
||||
|
||||
void MyAvatar::updateCollisionWithEnvironment(float deltaTime) {
|
||||
|
@ -1076,74 +824,13 @@ void MyAvatar::updateAvatarCollisions(float deltaTime) {
|
|||
NodeList* nodeList = NodeList::getInstance();
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
|
||||
Avatar *otherAvatar = (Avatar *)node->getLinkedData();
|
||||
|
||||
// check if the bounding spheres of the two avatars are colliding
|
||||
glm::vec3 vectorBetweenBoundingSpheres(_position - otherAvatar->_position);
|
||||
|
||||
if (glm::length(vectorBetweenBoundingSpheres) < _height * ONE_HALF + otherAvatar->_height * ONE_HALF) {
|
||||
// apply forces from collision
|
||||
applyCollisionWithOtherAvatar(otherAvatar, deltaTime);
|
||||
}
|
||||
// test other avatar hand position for proximity
|
||||
glm::vec3 v(_skeleton.joint[ AVATAR_JOINT_RIGHT_SHOULDER ].position);
|
||||
v -= otherAvatar->getPosition();
|
||||
|
||||
float distance = glm::length(v);
|
||||
if (distance < _distanceToNearestAvatar) {
|
||||
_distanceToNearestAvatar = distance;
|
||||
}
|
||||
//Avatar *otherAvatar = (Avatar *)node->getLinkedData();
|
||||
//
|
||||
// Placeholder: Add code here when we want to add Avatar<->Avatar collision stuff
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// detect collisions with other avatars and respond
|
||||
void MyAvatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTime) {
|
||||
|
||||
// for now, don't collide if we have a new skeleton
|
||||
if (_skeletonModel.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 bodyPushForce = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
// loop through the body balls of each avatar to check for every possible collision
|
||||
for (int b = 1; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
if (_bodyBall[b].isCollidable) {
|
||||
|
||||
for (int o = b+1; o < NUM_AVATAR_BODY_BALLS; o++) {
|
||||
if (otherAvatar->_bodyBall[o].isCollidable) {
|
||||
|
||||
glm::vec3 vectorBetweenBalls(_bodyBall[b].position - otherAvatar->_bodyBall[o].position);
|
||||
float distanceBetweenBalls = glm::length(vectorBetweenBalls);
|
||||
|
||||
if (distanceBetweenBalls > 0.0) { // to avoid divide by zero
|
||||
float combinedRadius = _bodyBall[b].radius + otherAvatar->_bodyBall[o].radius;
|
||||
|
||||
// check for collision
|
||||
if (distanceBetweenBalls < combinedRadius * COLLISION_RADIUS_SCALAR) {
|
||||
glm::vec3 directionVector = vectorBetweenBalls / distanceBetweenBalls;
|
||||
|
||||
// push balls away from each other and apply friction
|
||||
float penetration = 1.0f - (distanceBetweenBalls / (combinedRadius * COLLISION_RADIUS_SCALAR));
|
||||
|
||||
glm::vec3 ballPushForce = directionVector * COLLISION_BALL_FORCE * penetration * deltaTime;
|
||||
bodyPushForce += directionVector * COLLISION_BODY_FORCE * penetration * deltaTime;
|
||||
|
||||
_bodyBall[b].velocity += ballPushForce;
|
||||
otherAvatar->_bodyBall[o].velocity -= ballPushForce;
|
||||
|
||||
}// check for collision
|
||||
} // to avoid divide by zero
|
||||
} // o loop
|
||||
} // collidable
|
||||
} // b loop
|
||||
} // collidable
|
||||
|
||||
// apply force on the whole body
|
||||
_velocity += bodyPushForce;
|
||||
}
|
||||
|
||||
class SortedAvatar {
|
||||
public:
|
||||
Avatar* avatar;
|
||||
|
@ -1262,23 +949,6 @@ void MyAvatar::setGravity(glm::vec3 gravity) {
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::checkForMouseRayTouching() {
|
||||
|
||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
||||
|
||||
glm::vec3 directionToBodySphere = glm::normalize(_bodyBall[b].position - _mouseRayOrigin);
|
||||
float dot = glm::dot(directionToBodySphere, _mouseRayDirection);
|
||||
|
||||
float range = _bodyBall[b].radius * MOUSE_RAY_TOUCH_RANGE;
|
||||
|
||||
if (dot > (1.0f - range)) {
|
||||
_bodyBall[b].touchForce = (dot - (1.0f - range)) / range;
|
||||
} else {
|
||||
_bodyBall[b].touchForce = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setOrientation(const glm::quat& orientation) {
|
||||
glm::vec3 eulerAngles = safeEulerAngles(orientation);
|
||||
_bodyPitch = eulerAngles.x;
|
||||
|
|
|
@ -13,6 +13,15 @@
|
|||
|
||||
#include "Avatar.h"
|
||||
|
||||
enum AvatarHandState
|
||||
{
|
||||
HAND_STATE_NULL = 0,
|
||||
HAND_STATE_OPEN,
|
||||
HAND_STATE_GRASPING,
|
||||
HAND_STATE_POINTING,
|
||||
NUM_HAND_STATES
|
||||
};
|
||||
|
||||
class MyAvatar : public Avatar {
|
||||
public:
|
||||
MyAvatar(Node* owningNode = NULL);
|
||||
|
@ -67,7 +76,6 @@ private:
|
|||
float _driveKeys[MAX_DRIVE_KEYS];
|
||||
glm::vec3 _gravity;
|
||||
float _distanceToNearestAvatar; // How close is the nearest avatar?
|
||||
Avatar* _interactingOther;
|
||||
float _elapsedTimeMoving; // Timers to drive camera transitions when moving
|
||||
float _elapsedTimeStopped;
|
||||
float _elapsedTimeSinceCollision;
|
||||
|
@ -80,7 +88,6 @@ private:
|
|||
int _moveTargetStepCounter;
|
||||
|
||||
// private methods
|
||||
float getBallRenderAlpha(int ball, bool forceRenderHead) const;
|
||||
void renderBody(bool forceRenderHead);
|
||||
void updateThrust(float deltaTime, Transmitter * transmitter);
|
||||
void updateHandMovementAndTouching(float deltaTime, bool enableHandMovement);
|
||||
|
@ -89,9 +96,7 @@ private:
|
|||
void updateCollisionWithVoxels(float deltaTime);
|
||||
void applyHardCollision(const glm::vec3& penetration, float elasticity, float damping);
|
||||
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
|
||||
void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime );
|
||||
void updateChatCircle(float deltaTime);
|
||||
void checkForMouseRayTouching();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -61,6 +61,7 @@ void SkeletonModel::simulate(float deltaTime) {
|
|||
}
|
||||
|
||||
bool SkeletonModel::render(float alpha) {
|
||||
|
||||
if (_jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ GlowEffect::GlowEffect()
|
|||
: _initialized(false),
|
||||
_renderMode(DIFFUSE_ADD_MODE),
|
||||
_isOddFrame(false),
|
||||
_isFirstFrame(true),
|
||||
_intensity(0.0f) {
|
||||
}
|
||||
|
||||
|
@ -166,16 +167,21 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) {
|
|||
}
|
||||
newDiffusedFBO->bind();
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, oldDiffusedFBO->texture());
|
||||
if (_isFirstFrame) {
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
_diffuseProgram->bind();
|
||||
QSize size = Application::getInstance()->getGLWidget()->size();
|
||||
_diffuseProgram->setUniformValue(_diffusionScaleLocation, 1.0f / size.width(), 1.0f / size.height());
|
||||
} else {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, oldDiffusedFBO->texture());
|
||||
|
||||
_diffuseProgram->bind();
|
||||
QSize size = Application::getInstance()->getGLWidget()->size();
|
||||
_diffuseProgram->setUniformValue(_diffusionScaleLocation, 1.0f / size.width(), 1.0f / size.height());
|
||||
|
||||
renderFullscreenQuad();
|
||||
renderFullscreenQuad();
|
||||
|
||||
_diffuseProgram->release();
|
||||
_diffuseProgram->release();
|
||||
}
|
||||
|
||||
newDiffusedFBO->release();
|
||||
|
||||
|
@ -221,7 +227,7 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) {
|
|||
maybeRelease(destFBO);
|
||||
|
||||
} else { // _renderMode == BLUR_PERSIST_ADD_MODE
|
||||
// render the secondary to the tertiary with horizontal blur and persistence
|
||||
// render the secondary to the tertiary with vertical blur and persistence
|
||||
QOpenGLFramebufferObject* tertiaryFBO =
|
||||
Application::getInstance()->getTextureCache()->getTertiaryFramebufferObject();
|
||||
tertiaryFBO->bind();
|
||||
|
@ -229,7 +235,7 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) {
|
|||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE_MINUS_CONSTANT_ALPHA, GL_CONSTANT_ALPHA);
|
||||
const float PERSISTENCE_SMOOTHING = 0.9f;
|
||||
glBlendColor(0.0f, 0.0f, 0.0f, PERSISTENCE_SMOOTHING);
|
||||
glBlendColor(0.0f, 0.0f, 0.0f, _isFirstFrame ? 0.0f : PERSISTENCE_SMOOTHING);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, secondaryFBO->texture());
|
||||
|
||||
|
@ -270,6 +276,8 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) {
|
|||
glDepthMask(GL_TRUE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
_isFirstFrame = false;
|
||||
|
||||
return destFBO;
|
||||
}
|
||||
|
||||
|
@ -292,6 +300,7 @@ void GlowEffect::cycleRenderMode() {
|
|||
qDebug() << "Glow mode: Diffuse/add\n";
|
||||
break;
|
||||
}
|
||||
_isFirstFrame = true;
|
||||
}
|
||||
|
||||
Glower::Glower(float amount) {
|
||||
|
|
|
@ -66,6 +66,7 @@ private:
|
|||
|
||||
bool _isEmpty; ///< set when nothing in the scene is currently glowing
|
||||
bool _isOddFrame; ///< controls the alternation between texture targets in diffuse add mode
|
||||
bool _isFirstFrame; ///< for persistent modes, notes whether this is the first frame rendered
|
||||
|
||||
float _intensity;
|
||||
QStack<float> _intensityStack;
|
||||
|
|
|
@ -615,7 +615,6 @@ void OctreeServer::run() {
|
|||
nodeList->addHook(this);
|
||||
nodeList->linkedDataCreateCallback = &OctreeServer::attachQueryNodeToNode;
|
||||
|
||||
nodeList->startSilentNodeRemovalThread();
|
||||
srand((unsigned)time(0));
|
||||
|
||||
const char* VERBOSE_DEBUG = "--verboseDebug";
|
||||
|
|
|
@ -23,12 +23,6 @@
|
|||
#include "SharedUtil.h"
|
||||
#include "UUID.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
const char SOLO_NODE_TYPES[2] = {
|
||||
NODE_TYPE_AVATAR_MIXER,
|
||||
NODE_TYPE_AUDIO_MIXER
|
||||
|
@ -37,9 +31,6 @@ const char SOLO_NODE_TYPES[2] = {
|
|||
const QString DEFAULT_DOMAIN_HOSTNAME = "root.highfidelity.io";
|
||||
const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102;
|
||||
|
||||
bool silentNodeThreadStopFlag = false;
|
||||
bool pingUnknownNodeThreadStopFlag = false;
|
||||
|
||||
NodeList* NodeList::_sharedInstance = NULL;
|
||||
|
||||
NodeList* NodeList::createInstance(char ownerType, unsigned short int socketListenPort) {
|
||||
|
@ -83,9 +74,6 @@ NodeList::~NodeList() {
|
|||
delete _nodeTypesOfInterest;
|
||||
|
||||
clear();
|
||||
|
||||
// stop the spawned threads, if they were started
|
||||
stopSilentNodeRemovalThread();
|
||||
}
|
||||
|
||||
void NodeList::setDomainHostname(const QString& domainHostname) {
|
||||
|
@ -802,56 +790,6 @@ void NodeList::removeSilentNodes() {
|
|||
}
|
||||
}
|
||||
|
||||
void* removeSilentNodesAndSleep(void *args) {
|
||||
NodeList* nodeList = (NodeList*) args;
|
||||
uint64_t checkTimeUsecs = 0;
|
||||
int sleepTime = 0;
|
||||
|
||||
while (!::silentNodeThreadStopFlag) {
|
||||
|
||||
checkTimeUsecs = usecTimestampNow();
|
||||
|
||||
nodeList->removeSilentNodes();
|
||||
|
||||
sleepTime = NODE_SILENCE_THRESHOLD_USECS - (usecTimestampNow() - checkTimeUsecs);
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
Sleep( static_cast<int>(1000.0f*sleepTime) );
|
||||
|
||||
#else
|
||||
|
||||
if (sleepTime > 0) {
|
||||
usleep(sleepTime);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
pthread_exit(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void NodeList::startSilentNodeRemovalThread() {
|
||||
if (!::silentNodeThreadStopFlag) {
|
||||
pthread_create(&removeSilentNodesThread, NULL, removeSilentNodesAndSleep, (void*) this);
|
||||
} else {
|
||||
qDebug("Refusing to start silent node removal thread from previously failed join.\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void NodeList::stopSilentNodeRemovalThread() {
|
||||
::silentNodeThreadStopFlag = true;
|
||||
int joinResult = pthread_join(removeSilentNodesThread, NULL);
|
||||
|
||||
if (joinResult == 0) {
|
||||
::silentNodeThreadStopFlag = false;
|
||||
} else {
|
||||
qDebug("Silent node removal thread join failed with %d. Will not restart.\n", joinResult);
|
||||
}
|
||||
}
|
||||
|
||||
const QString QSETTINGS_GROUP_NAME = "NodeList";
|
||||
const QString DOMAIN_SERVER_SETTING_KEY = "domainServerHostname";
|
||||
|
||||
|
|
|
@ -21,10 +21,6 @@
|
|||
#include "Node.h"
|
||||
#include "NodeTypes.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "pthread.h"
|
||||
#endif
|
||||
|
||||
const int MAX_NUM_NODES = 10000;
|
||||
const int NODES_PER_BUCKET = 100;
|
||||
|
||||
|
@ -126,9 +122,6 @@ public:
|
|||
|
||||
Node* soloNodeOfType(char nodeType);
|
||||
|
||||
void startSilentNodeRemovalThread();
|
||||
void stopSilentNodeRemovalThread();
|
||||
|
||||
void loadData(QSettings* settings);
|
||||
void saveData(QSettings* settings);
|
||||
|
||||
|
@ -170,8 +163,6 @@ private:
|
|||
char _ownerType;
|
||||
char* _nodeTypesOfInterest;
|
||||
QUuid _ownerUUID;
|
||||
pthread_t removeSilentNodesThread;
|
||||
pthread_t checkInWithDomainServerThread;
|
||||
int _numNoReplyDomainCheckIns;
|
||||
HifiSockAddr _assignmentServerSocket;
|
||||
HifiSockAddr _publicSockAddr;
|
||||
|
|
Loading…
Reference in a new issue