mirror of
https://github.com/JulianGro/overte.git
synced 2025-08-08 09:27:35 +02:00
merge fix
This commit is contained in:
commit
56e90323f9
69 changed files with 899 additions and 547 deletions
|
@ -31,11 +31,11 @@ ENDIF(APPLE)
|
|||
# targets not supported on windows
|
||||
if (NOT WIN32)
|
||||
add_subdirectory(animation-server)
|
||||
add_subdirectory(data-server)
|
||||
endif (NOT WIN32)
|
||||
|
||||
# targets on all platforms
|
||||
add_subdirectory(assignment-client)
|
||||
add_subdirectory(data-server)
|
||||
add_subdirectory(domain-server)
|
||||
add_subdirectory(interface)
|
||||
add_subdirectory(pairing-server)
|
||||
|
|
|
@ -832,7 +832,7 @@ void AnimationServer::readPendingDatagrams() {
|
|||
&& (receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
|
||||
nodeSockAddr.getAddressPointer(),
|
||||
nodeSockAddr.getPortPointer())) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
packetVersionMatch(packetData, nodeSockAddr)) {
|
||||
|
||||
if (packetData[0] == PACKET_TYPE_JURISDICTION) {
|
||||
int headerBytes = numBytesForPacketHeader(packetData);
|
||||
|
|
|
@ -111,7 +111,7 @@ void AssignmentClient::readPendingDatagrams() {
|
|||
if ((receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
|
||||
senderSockAddr.getAddressPointer(),
|
||||
senderSockAddr.getPortPointer()))
|
||||
&& packetVersionMatch(packetData)) {
|
||||
&& packetVersionMatch(packetData, senderSockAddr)) {
|
||||
|
||||
if (_currentAssignment) {
|
||||
// have the threaded current assignment handle this datagram
|
||||
|
|
|
@ -30,7 +30,8 @@ const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000;
|
|||
AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) :
|
||||
ThreadedAssignment(dataBuffer, numBytes)
|
||||
{
|
||||
|
||||
// make sure we hear about node kills so we can tell the other nodes
|
||||
connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled);
|
||||
}
|
||||
|
||||
unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) {
|
||||
|
@ -46,7 +47,7 @@ unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *no
|
|||
|
||||
void attachAvatarDataToNode(Node* newNode) {
|
||||
if (newNode->getLinkedData() == NULL) {
|
||||
newNode->setLinkedData(new AvatarData(newNode));
|
||||
newNode->setLinkedData(new AvatarData());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,6 +117,22 @@ void broadcastAvatarData() {
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
|
||||
if (killedNode->getType() == NODE_TYPE_AGENT
|
||||
&& killedNode->getLinkedData()) {
|
||||
// this was an avatar we were sending to other people
|
||||
// send a kill packet for it to our other nodes
|
||||
unsigned char packetData[MAX_PACKET_SIZE];
|
||||
int numHeaderBytes = populateTypeAndVersion(packetData, PACKET_TYPE_KILL_AVATAR);
|
||||
|
||||
QByteArray rfcUUID = killedNode->getUUID().toRfc4122();
|
||||
memcpy(packetData + numHeaderBytes, rfcUUID.constData(), rfcUUID.size());
|
||||
|
||||
NodeList::getInstance()->broadcastToNodes(packetData, numHeaderBytes + NUM_BYTES_RFC4122_UUID,
|
||||
QSet<NODE_TYPE>() << NODE_TYPE_AGENT);
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
@ -136,7 +153,10 @@ void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSoc
|
|||
}
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_KILL_NODE:
|
||||
case PACKET_TYPE_KILL_AVATAR: {
|
||||
nodeList->processKillNode(dataByteArray);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// hand this off to the NodeList
|
||||
nodeList->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
|
||||
|
|
|
@ -19,6 +19,8 @@ public:
|
|||
public slots:
|
||||
/// runs the avatar mixer
|
||||
void run();
|
||||
|
||||
void nodeKilled(SharedNodePointer killedNode);
|
||||
|
||||
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
|
||||
};
|
||||
|
|
|
@ -54,14 +54,13 @@ void DataServer::readPendingDatagrams() {
|
|||
qint64 receivedBytes = 0;
|
||||
static unsigned char packetData[MAX_PACKET_SIZE];
|
||||
|
||||
QHostAddress senderAddress;
|
||||
quint16 senderPort;
|
||||
HifiSockAddr senderSockAddr;
|
||||
|
||||
while (_socket.hasPendingDatagrams() &&
|
||||
(receivedBytes = _socket.readDatagram(reinterpret_cast<char*>(packetData), MAX_PACKET_SIZE,
|
||||
&senderAddress, &senderPort))) {
|
||||
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()))) {
|
||||
if ((packetData[0] == PACKET_TYPE_DATA_SERVER_PUT || packetData[0] == PACKET_TYPE_DATA_SERVER_GET) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
packetVersionMatch(packetData, senderSockAddr)) {
|
||||
|
||||
int readBytes = numBytesForPacketHeader(packetData);
|
||||
|
||||
|
@ -126,7 +125,7 @@ void DataServer::readPendingDatagrams() {
|
|||
// which is the sent packet with the header replaced
|
||||
packetData[0] = PACKET_TYPE_DATA_SERVER_CONFIRM;
|
||||
_socket.writeDatagram(reinterpret_cast<char*>(packetData), receivedBytes,
|
||||
senderAddress, senderPort);
|
||||
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
}
|
||||
|
||||
freeReplyObject(reply);
|
||||
|
@ -190,7 +189,7 @@ void DataServer::readPendingDatagrams() {
|
|||
|
||||
// reply back with the send packet
|
||||
_socket.writeDatagram(reinterpret_cast<char*>(packetData), numSendPacketBytes,
|
||||
senderAddress, senderPort);
|
||||
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ void DomainServer::readAvailableDatagrams() {
|
|||
if ((receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
|
||||
senderSockAddr.getAddressPointer(),
|
||||
senderSockAddr.getPortPointer()))
|
||||
&& packetVersionMatch((unsigned char*) packetData)) {
|
||||
&& packetVersionMatch((unsigned char*) packetData, senderSockAddr)) {
|
||||
if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_TYPE_DOMAIN_LIST_REQUEST) {
|
||||
// this is an RFD or domain list request packet, and there is a version match
|
||||
|
||||
|
@ -532,7 +532,7 @@ void DomainServer::addReleasedAssignmentBackToQueue(Assignment* releasedAssignme
|
|||
void DomainServer::nodeKilled(SharedNodePointer node) {
|
||||
// if this node has linked data it was from an assignment
|
||||
if (node->getLinkedData()) {
|
||||
Assignment* nodeAssignment = (Assignment*) node->getLinkedData();
|
||||
Assignment* nodeAssignment = (Assignment*) node->getLinkedData();
|
||||
|
||||
addReleasedAssignmentBackToQueue(nodeAssignment);
|
||||
}
|
||||
|
|
128
examples/findParticleExample.js
Normal file
128
examples/findParticleExample.js
Normal file
|
@ -0,0 +1,128 @@
|
|||
//
|
||||
// editParticleExample.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/24/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates "finding" particles
|
||||
//
|
||||
|
||||
var iteration = 0;
|
||||
|
||||
var particleA = Particles.addParticle(
|
||||
{
|
||||
position: { x: 2, y: 0, z: 2 },
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
radius : 0.1,
|
||||
color: { red: 0, green: 255, blue: 0 }
|
||||
});
|
||||
|
||||
var particleB = Particles.addParticle(
|
||||
{
|
||||
position: { x: 5, y: 0, z: 5 },
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
radius : 0.1,
|
||||
color: { red: 0, green: 255, blue: 255 }
|
||||
});
|
||||
|
||||
var searchAt = { x: 0, y: 0, z: 0};
|
||||
var moveSearch = { x: 0.1, y: 0, z: 0.1};
|
||||
var searchRadius = 1;
|
||||
var searchRadiusChange = 0;
|
||||
|
||||
print("particleA.creatorTokenID = " + particleA.creatorTokenID);
|
||||
print("particleB.creatorTokenID = " + particleB.creatorTokenID);
|
||||
|
||||
|
||||
function scriptEnding() {
|
||||
print("calling Particles.deleteParticle()");
|
||||
Particles.deleteParticle(particleA);
|
||||
Particles.deleteParticle(particleB);
|
||||
}
|
||||
|
||||
function printProperties(properties) {
|
||||
for (var property in properties) {
|
||||
if (properties.hasOwnProperty(property)) {
|
||||
if (property == "position" ||
|
||||
property == "gravity" ||
|
||||
property == "velocity") {
|
||||
print(property +": " + properties[property].x + ", " + properties[property].y + ", " + properties[property].z);
|
||||
} else if (property == "color") {
|
||||
print(property +": " + properties[property].red + ", "
|
||||
+ properties[property].green + ", " + properties[property].blue);
|
||||
} else {
|
||||
print(property +": " + properties[property])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findParticles() {
|
||||
|
||||
// run for a while, then clean up
|
||||
// stop it...
|
||||
if (iteration >= 100) {
|
||||
print("calling Agent.stop()");
|
||||
Agent.stop();
|
||||
}
|
||||
|
||||
print("--------------------------");
|
||||
print("iteration =" + iteration);
|
||||
iteration++;
|
||||
|
||||
// Check to see if we've been notified of the actual identity of the particles we created
|
||||
if (!particleA.isKnownID) {
|
||||
var identifyA = Particles.identifyParticle(particleA);
|
||||
if (identifyA.isKnownID) {
|
||||
particleA = identifyA;
|
||||
print(">>>> identified particleA.id = " + particleA.id);
|
||||
}
|
||||
}
|
||||
if (!particleB.isKnownID) {
|
||||
var identifyB = Particles.identifyParticle(particleB);
|
||||
if (identifyB.isKnownID) {
|
||||
particleB = identifyB;
|
||||
print(">>>> identified particleB.id = " + particleB.id);
|
||||
}
|
||||
}
|
||||
|
||||
// also check to see if we can "find" particles...
|
||||
print("searching for particles at:" + searchAt.x + ", " + searchAt.y + ", " + searchAt.z + " radius:" + searchRadius);
|
||||
var foundParticles = Particles.findParticles(searchAt, searchRadius);
|
||||
print("found this many particles: "+ foundParticles.length);
|
||||
for (var i = 0; i < foundParticles.length; i++) {
|
||||
print(" particle[" + i + "].id:" + foundParticles[i].id);
|
||||
if (foundParticles[i].id == particleA.id) {
|
||||
print(">>>> found particleA!!");
|
||||
var propertiesA = Particles.getParticleProperties(particleA);
|
||||
printProperties(propertiesA);
|
||||
}
|
||||
if (foundParticles[i].id == particleB.id) {
|
||||
print(">>>> found particleB!!");
|
||||
}
|
||||
}
|
||||
// move search
|
||||
searchAt.x += moveSearch.x;
|
||||
searchAt.y += moveSearch.y;
|
||||
searchAt.z += moveSearch.z;
|
||||
searchRadius += searchRadiusChange;
|
||||
|
||||
// after we've searched for 80 iterations, change our search mechanism to be from the center with expanding radius
|
||||
if (iteration == 80) {
|
||||
searchAt = { x: 3.5, y: 0, z: 3.5};
|
||||
moveSearch = { x: 0, y: 0, z: 0};
|
||||
searchRadius = 0.5;
|
||||
searchRadiusChange = 0.5;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Agent.willSendVisualDataCallback.connect(findParticles);
|
||||
|
||||
// register our scriptEnding callback
|
||||
Agent.scriptEnding.connect(scriptEnding);
|
|
@ -8,11 +8,9 @@
|
|||
function vLength(v) {
|
||||
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
|
||||
}
|
||||
|
||||
function printVector(v) {
|
||||
print(v.x + ", " + v.y + ", " + v.z + "\n");
|
||||
}
|
||||
|
||||
// Create a random vector with individual lengths between a,b
|
||||
function randVector(a, b) {
|
||||
var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) };
|
||||
|
@ -69,49 +67,53 @@ if (which < 0.2) {
|
|||
size = 0.15;
|
||||
}
|
||||
|
||||
var startPosition = { x: 0, y: 0, z: 0 };
|
||||
var targetPosition = { x: 0, y: 0, z: 0 };
|
||||
|
||||
var range = 0.7; // Over what distance in meters do you want your bird to fly around
|
||||
var startTimeInSeconds = new Date().getTime() / 1000;
|
||||
|
||||
var birdLifetime = 20; // lifetime of the bird in seconds!
|
||||
var position = { x: 0, y: 0, z: 0 };
|
||||
var targetPosition = { x: 0, y: 0, z: 0 };
|
||||
var range = 1.0; // Over what distance in meters do you want your bird to fly around
|
||||
var frame = 0;
|
||||
var moving = false;
|
||||
var tweeting = 0;
|
||||
var moved = false;
|
||||
|
||||
var CHANCE_OF_MOVING = 0.02;
|
||||
var CHANCE_OF_TWEETING = 0.01;
|
||||
var START_HEIGHT_ABOVE_ME = 2.0;
|
||||
|
||||
var CHANCE_OF_MOVING = 0.05;
|
||||
var CHANCE_OF_TWEETING = 0.05;
|
||||
var START_HEIGHT_ABOVE_ME = 1.5;
|
||||
var myPosition = MyAvatar.position;
|
||||
|
||||
var properties = {
|
||||
lifetime: birdLifetime,
|
||||
position: { x: myPosition.x, y: myPosition.y + START_HEIGHT_ABOVE_ME, z: myPosition.z },
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
radius : 0.1,
|
||||
color: { red: 0,
|
||||
green: 255,
|
||||
blue: 0 },
|
||||
lifetime: 60
|
||||
green: 255,
|
||||
blue: 0 }
|
||||
};
|
||||
|
||||
position = properties.position;
|
||||
|
||||
var range = 1.0; // Distance around avatar where I can move
|
||||
|
||||
// Create the actual bird
|
||||
var particleID = Particles.addParticle(properties);
|
||||
|
||||
function moveBird() {
|
||||
|
||||
// check to see if we've been running long enough that our bird is dead
|
||||
var nowTimeInSeconds = new Date().getTime() / 1000;
|
||||
if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) {
|
||||
print("our bird is dying, stop our script");
|
||||
Agent.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
myPosition = MyAvatar.position;
|
||||
myPosition.y += START_HEIGHT_ABOVE_ME;
|
||||
frame++;
|
||||
frame++;
|
||||
if (frame % 3 == 0) {
|
||||
// Tweeting behavior
|
||||
if (tweeting == 0) {
|
||||
if (Math.random() < CHANCE_OF_TWEETING) {
|
||||
//print("tweet!" + "\n");
|
||||
var options = new AudioInjectionOptions();
|
||||
var options = new AudioInjectionOptions();
|
||||
options.position = position;
|
||||
options.volume = 0.75;
|
||||
Audio.playSound(tweet, options);
|
||||
|
@ -125,6 +127,25 @@ function moveBird() {
|
|||
if (Math.random() < CHANCE_OF_MOVING) {
|
||||
targetPosition = randVector(- range, range);
|
||||
targetPosition = vPlus(targetPosition, myPosition);
|
||||
|
||||
if (targetPosition.x < 0) {
|
||||
targetPosition.x = 0;
|
||||
}
|
||||
if (targetPosition.y < 0) {
|
||||
targetPosition.y = 0;
|
||||
}
|
||||
if (targetPosition.z < 0) {
|
||||
targetPosition.z = 0;
|
||||
}
|
||||
if (targetPosition.x > TREE_SCALE) {
|
||||
targetPosition.x = TREE_SCALE;
|
||||
}
|
||||
if (targetPosition.y > TREE_SCALE) {
|
||||
targetPosition.y = TREE_SCALE;
|
||||
}
|
||||
if (targetPosition.z > TREE_SCALE) {
|
||||
targetPosition.z = TREE_SCALE;
|
||||
}
|
||||
//printVector(position);
|
||||
moving = true;
|
||||
}
|
||||
|
@ -141,17 +162,16 @@ function moveBird() {
|
|||
if (moved || (tweeting > 0)) {
|
||||
if (tweeting > 0) {
|
||||
var newProperties = {
|
||||
position: position,
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
|
||||
position: position,
|
||||
|
||||
radius : size * 1.5,
|
||||
color: { red: Math.random() * 255, green: 0, blue: 0 }
|
||||
};
|
||||
} else {
|
||||
var newProperties = {
|
||||
position: position,
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
|
||||
position: position,
|
||||
radius : size,
|
||||
color: { red: color.r, green: color.g, blue: color.b }
|
||||
};
|
||||
|
@ -162,11 +182,5 @@ function moveBird() {
|
|||
}
|
||||
}
|
||||
|
||||
function killBird() {
|
||||
deleteParticle(particleID);
|
||||
}
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Agent.willSendVisualDataCallback.connect(moveBird);
|
||||
Agent.scriptEnding.connect(killBird);
|
||||
|
||||
|
|
|
@ -116,6 +116,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_voxelImporter(_window),
|
||||
_wantToKillLocalVoxels(false),
|
||||
_audioScope(256, 200, true),
|
||||
_avatarManager(),
|
||||
_profile(QString()),
|
||||
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
|
||||
_mouseX(0),
|
||||
|
@ -138,8 +139,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_nudgeStarted(false),
|
||||
_lookingAlongX(false),
|
||||
_lookingAwayFromOrigin(true),
|
||||
_lookatTargetAvatar(NULL),
|
||||
_lookatIndicatorScale(1.0f),
|
||||
_chatEntryOn(false),
|
||||
_audio(&_audioScope, STARTUP_JITTER_SAMPLES),
|
||||
_enableProcessVoxelsThread(true),
|
||||
|
@ -220,9 +219,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
// Voxel File.
|
||||
_voxelsFilename = getCmdOption(argc, constArgv, "-i");
|
||||
|
||||
// the callback for our instance of NodeList is attachNewHeadToNode
|
||||
nodeList->linkedDataCreateCallback = &attachNewHeadToNode;
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA WsaData;
|
||||
int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData);
|
||||
|
@ -294,7 +290,7 @@ Application::~Application() {
|
|||
_settings->sync();
|
||||
|
||||
// let the avatar mixer know we're out
|
||||
NodeList::getInstance()->sendKillNode(QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
|
||||
MyAvatar::sendKillAvatar();
|
||||
|
||||
// ask the datagram processing thread to quit and wait until it is done
|
||||
_nodeThread->quit();
|
||||
|
@ -1160,8 +1156,8 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
|
|||
if (activeWindow() == _window) {
|
||||
// orbit behavior
|
||||
if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) {
|
||||
if (_lookatTargetAvatar) {
|
||||
_myAvatar.orbit(_lookatTargetAvatar->getPosition(), deltaX, deltaY);
|
||||
if (_avatarManager.getLookAtTargetAvatar()) {
|
||||
_myAvatar.orbit(_avatarManager.getLookAtTargetAvatar()->getPosition(), deltaX, deltaY);
|
||||
return;
|
||||
}
|
||||
if (_isHoverVoxel) {
|
||||
|
@ -1212,7 +1208,7 @@ void Application::mousePressEvent(QMouseEvent* event) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!_palette.isActive() && (!_isHoverVoxel || _lookatTargetAvatar)) {
|
||||
if (!_palette.isActive() && (!_isHoverVoxel || _avatarManager.getLookAtTargetAvatar())) {
|
||||
// disable for now
|
||||
// _pieMenu.mousePressEvent(_mouseX, _mouseY);
|
||||
}
|
||||
|
@ -1846,40 +1842,6 @@ const float MAX_AVATAR_EDIT_VELOCITY = 1.0f;
|
|||
const float MAX_VOXEL_EDIT_DISTANCE = 50.0f;
|
||||
const float HEAD_SPHERE_RADIUS = 0.07f;
|
||||
|
||||
static QUuid DEFAULT_NODE_ID_REF;
|
||||
|
||||
void Application::updateLookatTargetAvatar(glm::vec3& eyePosition) {
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()");
|
||||
|
||||
if (!_mousePressed) {
|
||||
_lookatTargetAvatar = findLookatTargetAvatar(eyePosition, DEFAULT_NODE_ID_REF);
|
||||
}
|
||||
}
|
||||
|
||||
Avatar* Application::findLookatTargetAvatar(glm::vec3& eyePosition, QUuid& nodeUUID = DEFAULT_NODE_ID_REF) {
|
||||
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
|
||||
Avatar* avatar = (Avatar*)node->getLinkedData();
|
||||
float distance;
|
||||
|
||||
if (avatar->findRayIntersection(_mouseRayOrigin, _mouseRayDirection, distance)) {
|
||||
// rescale to compensate for head embiggening
|
||||
eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) *
|
||||
(avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot();
|
||||
|
||||
_lookatIndicatorScale = avatar->getHead().getScale();
|
||||
_lookatOtherPosition = avatar->getHead().getPosition();
|
||||
nodeUUID = avatar->getOwningNode()->getUUID();
|
||||
return avatar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool Application::isLookingAtMyAvatar(Avatar* avatar) {
|
||||
glm::vec3 theirLookat = avatar->getHead().getLookAtPosition();
|
||||
glm::vec3 myHeadPosition = _myAvatar.getHead().getPosition();
|
||||
|
@ -1890,17 +1852,6 @@ bool Application::isLookingAtMyAvatar(Avatar* avatar) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Application::renderLookatIndicator(glm::vec3 pointOfInterest) {
|
||||
|
||||
const float DISTANCE_FROM_HEAD_SPHERE = 0.1f * _lookatIndicatorScale;
|
||||
const float INDICATOR_RADIUS = 0.1f * _lookatIndicatorScale;
|
||||
const float YELLOW[] = { 1.0f, 1.0f, 0.0f };
|
||||
const int NUM_SEGMENTS = 30;
|
||||
glm::vec3 haloOrigin(pointOfInterest.x, pointOfInterest.y + DISTANCE_FROM_HEAD_SPHERE, pointOfInterest.z);
|
||||
glColor3f(YELLOW[0], YELLOW[1], YELLOW[2]);
|
||||
renderCircle(haloOrigin, INDICATOR_RADIUS, IDENTITY_UP, NUM_SEGMENTS);
|
||||
}
|
||||
|
||||
void Application::renderHighlightVoxel(VoxelDetail voxel) {
|
||||
glDisable(GL_LIGHTING);
|
||||
glPushMatrix();
|
||||
|
@ -1915,41 +1866,6 @@ void Application::renderHighlightVoxel(VoxelDetail voxel) {
|
|||
glPopMatrix();
|
||||
}
|
||||
|
||||
void Application::updateAvatars(float deltaTime) {
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
|
||||
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
QMutexLocker locker(&node->getMutex());
|
||||
if (node->getLinkedData()) {
|
||||
Avatar *avatar = (Avatar *)node->getLinkedData();
|
||||
if (!avatar->isInitialized()) {
|
||||
avatar->init();
|
||||
}
|
||||
avatar->simulate(deltaTime, NULL);
|
||||
avatar->setMouseRay(_mouseRayOrigin, _mouseRayDirection);
|
||||
}
|
||||
}
|
||||
|
||||
// simulate avatar fades
|
||||
for (vector<Avatar*>::iterator fade = _avatarFades.begin(); fade != _avatarFades.end(); fade++) {
|
||||
Avatar* avatar = *fade;
|
||||
const float SHRINK_RATE = 0.9f;
|
||||
|
||||
avatar->setTargetScale(avatar->getScale() * SHRINK_RATE);
|
||||
|
||||
const float MIN_FADE_SCALE = 0.001f;
|
||||
|
||||
if (avatar->getTargetScale() < MIN_FADE_SCALE) {
|
||||
delete avatar;
|
||||
_avatarFades.erase(fade--);
|
||||
|
||||
} else {
|
||||
avatar->simulate(deltaTime, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateMouseRay() {
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
|
@ -2002,7 +1918,6 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) {
|
|||
glm::vec3 rayOrigin, rayDirection;
|
||||
_viewFrustum.computePickRay(0.5f, 0.5f, rayOrigin, rayDirection);
|
||||
lookAtSpot = rayOrigin + rayDirection * FAR_AWAY_STARE;
|
||||
|
||||
} else {
|
||||
// just look in direction of the mouse ray
|
||||
lookAtSpot = _mouseRayOrigin + _mouseRayDirection * FAR_AWAY_STARE;
|
||||
|
@ -2359,7 +2274,7 @@ void Application::update(float deltaTime) {
|
|||
glm::vec3 lookAtSpot;
|
||||
|
||||
updateFaceshift();
|
||||
updateLookatTargetAvatar(lookAtSpot);
|
||||
_avatarManager.updateLookAtTargetAvatar(lookAtSpot);
|
||||
updateMyAvatarLookAtPosition(lookAtSpot);
|
||||
|
||||
// Find the voxel we are hovering over, and respond if clicked
|
||||
|
@ -2374,7 +2289,7 @@ void Application::update(float deltaTime) {
|
|||
updateSerialDevices(deltaTime); // Read serial port interface devices
|
||||
updateAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
|
||||
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
||||
updateAvatars(deltaTime); //loop through all the other avatars and simulate them...
|
||||
_avatarManager.updateAvatars(deltaTime); //loop through all the other avatars and simulate them...
|
||||
updateMyAvatarSimulation(deltaTime); // Simulate myself
|
||||
updateParticles(deltaTime); // Simulate particle cloud movements
|
||||
updateMetavoxels(deltaTime); // update metavoxels
|
||||
|
@ -2743,7 +2658,7 @@ void Application::updateShadowMap() {
|
|||
|
||||
glTranslatef(translation.x, translation.y, translation.z);
|
||||
|
||||
renderAvatars(true);
|
||||
_avatarManager.renderAvatars(true);
|
||||
_particles.render();
|
||||
|
||||
glPopMatrix();
|
||||
|
@ -2971,7 +2886,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
}
|
||||
}
|
||||
|
||||
renderAvatars(whichCamera.getMode() == CAMERA_MODE_MIRROR, selfAvatarOnly);
|
||||
_avatarManager.renderAvatars(whichCamera.getMode() == CAMERA_MODE_MIRROR, selfAvatarOnly);
|
||||
|
||||
if (!selfAvatarOnly) {
|
||||
// Render the world box
|
||||
|
@ -3125,11 +3040,8 @@ void Application::displayOverlay() {
|
|||
glPointSize(1.0f);
|
||||
char nodes[100];
|
||||
|
||||
int totalAvatars = 0, totalServers = 0;
|
||||
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
node->getType() == NODE_TYPE_AGENT ? totalAvatars++ : totalServers++;
|
||||
}
|
||||
int totalAvatars = _avatarManager.size();
|
||||
int totalServers = NodeList::getInstance()->size();
|
||||
|
||||
sprintf(nodes, "Servers: %d, Avatars: %d\n", totalServers, totalAvatars);
|
||||
drawtext(_glWidget->width() - 150, 20, 0.10f, 0, 1.0f, 0, nodes, 1, 0, 0);
|
||||
|
@ -3545,46 +3457,6 @@ void Application::renderCoverageMapsRecursively(CoverageMap* map) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
|
||||
return;
|
||||
}
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::renderAvatars()");
|
||||
|
||||
if (!selfAvatarOnly) {
|
||||
// Render avatars of other nodes
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
||||
QMutexLocker locker(&node->getMutex());
|
||||
|
||||
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
|
||||
Avatar *avatar = (Avatar *)node->getLinkedData();
|
||||
if (!avatar->isInitialized()) {
|
||||
avatar->init();
|
||||
}
|
||||
avatar->render(false);
|
||||
avatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors));
|
||||
}
|
||||
}
|
||||
|
||||
// render avatar fades
|
||||
Glower glower;
|
||||
for (vector<Avatar*>::iterator fade = _avatarFades.begin(); fade != _avatarFades.end(); fade++) {
|
||||
(*fade)->render(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Render my own Avatar
|
||||
_myAvatar.render(forceRenderHead);
|
||||
_myAvatar.setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors));
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::LookAtIndicator) && _lookatTargetAvatar) {
|
||||
renderLookatIndicator(_lookatOtherPosition);
|
||||
}
|
||||
}
|
||||
|
||||
// renderViewFrustum()
|
||||
//
|
||||
// Description: this will render the view frustum bounds for EITHER the head
|
||||
|
@ -3849,17 +3721,6 @@ void Application::setMenuShortcutsEnabled(bool enabled) {
|
|||
setShortcutsEnabled(_window->menuBar(), enabled);
|
||||
}
|
||||
|
||||
void Application::attachNewHeadToNode(Node* newNode) {
|
||||
if (newNode->getLinkedData() == NULL) {
|
||||
newNode->setLinkedData(new Avatar(newNode));
|
||||
|
||||
// new UUID requires mesh and skeleton request to data-server
|
||||
DataServerClient::getValuesForKeysAndUUID(QStringList() << DataServerKey::FaceMeshURL << DataServerKey::SkeletonURL,
|
||||
newNode->getUUID(), Application::getInstance()->getProfile());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateWindowTitle(){
|
||||
QString title = "";
|
||||
|
||||
|
@ -3891,6 +3752,9 @@ void Application::domainChanged(const QString& domainHostname) {
|
|||
_voxelServerJurisdictions.clear();
|
||||
_octreeServerSceneStats.clear();
|
||||
_particleServerJurisdictions.clear();
|
||||
|
||||
// reset the particle renderer
|
||||
_particles.clear();
|
||||
|
||||
// reset our persist thread
|
||||
qDebug() << "Domain changed to" << domainHostname << ". Swapping persist cache.";
|
||||
|
@ -3960,16 +3824,9 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
}
|
||||
_voxelSceneStatsLock.unlock();
|
||||
|
||||
} else if (node->getType() == NODE_TYPE_AGENT) {
|
||||
Avatar* avatar = static_cast<Avatar*>(node->getLinkedData());
|
||||
if (avatar == _lookatTargetAvatar) {
|
||||
_lookatTargetAvatar = NULL;
|
||||
}
|
||||
|
||||
// take over the avatar in order to fade it out
|
||||
node->setLinkedData(NULL);
|
||||
|
||||
_avatarFades.push_back(avatar);
|
||||
} else if (node->getType() == NODE_TYPE_AVATAR_MIXER) {
|
||||
// our avatar mixer has gone away - clear the hash of avatars
|
||||
_avatarManager.clearHash();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include "VoxelSystem.h"
|
||||
#include "VoxelImporter.h"
|
||||
#include "avatar/Avatar.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "avatar/Profile.h"
|
||||
#include "devices/Faceshift.h"
|
||||
|
@ -151,6 +152,7 @@ public:
|
|||
VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; }
|
||||
VoxelTree* getClipboard() { return &_clipboard; }
|
||||
Environment* getEnvironment() { return &_environment; }
|
||||
bool isMousePressed() const { return _mousePressed; }
|
||||
bool isMouseHidden() const { return _mouseHidden; }
|
||||
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
|
||||
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
|
||||
|
@ -169,8 +171,7 @@ public:
|
|||
TextureCache* getTextureCache() { return &_textureCache; }
|
||||
GlowEffect* getGlowEffect() { return &_glowEffect; }
|
||||
|
||||
Avatar* getLookatTargetAvatar() const { return _lookatTargetAvatar; }
|
||||
|
||||
AvatarManager& getAvatarManager() { return _avatarManager; }
|
||||
Profile* getProfile() { return &_profile; }
|
||||
void resetProfile(const QString& username);
|
||||
|
||||
|
@ -233,14 +234,12 @@ public slots:
|
|||
void initAvatarAndViewFrustum();
|
||||
|
||||
private slots:
|
||||
|
||||
void timer();
|
||||
void idle();
|
||||
|
||||
void setFullscreen(bool fullscreen);
|
||||
void setEnable3DTVMode(bool enable3DTVMode);
|
||||
|
||||
|
||||
|
||||
void renderThrustAtVoxel(const glm::vec3& thrust);
|
||||
|
||||
void renderCoverageMap();
|
||||
|
@ -279,7 +278,6 @@ private:
|
|||
void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot);
|
||||
void updateHoverVoxels(float deltaTime, float& distance, BoxFace& face);
|
||||
void updateMouseVoxels(float deltaTime, float& distance, BoxFace& face);
|
||||
void updateLookatTargetAvatar(glm::vec3& eyePosition);
|
||||
void updateHandAndTouch(float deltaTime);
|
||||
void updateLeap(float deltaTime);
|
||||
void updateSixense(float deltaTime);
|
||||
|
@ -301,7 +299,6 @@ private:
|
|||
void renderHighlightVoxel(VoxelDetail voxel);
|
||||
|
||||
void updateAvatar(float deltaTime);
|
||||
void updateAvatars(float deltaTime);
|
||||
void queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, NodeToJurisdictionMap& jurisdictions);
|
||||
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
|
||||
|
||||
|
@ -310,7 +307,6 @@ private:
|
|||
void updateShadowMap();
|
||||
void displayOverlay();
|
||||
void displayStats();
|
||||
void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false);
|
||||
void renderViewFrustum(ViewFrustum& viewFrustum);
|
||||
|
||||
void checkBandwidthMeterClick();
|
||||
|
@ -373,6 +369,7 @@ private:
|
|||
|
||||
VoxelQuery _voxelQuery; // NodeData derived class for querying voxels from voxel server
|
||||
|
||||
AvatarManager _avatarManager;
|
||||
MyAvatar _myAvatar; // The rendered avatar of oneself
|
||||
Profile _profile; // The data-server linked profile for this user
|
||||
|
||||
|
@ -442,10 +439,6 @@ private:
|
|||
bool _lookingAwayFromOrigin;
|
||||
glm::vec3 _nudgeGuidePosition;
|
||||
|
||||
Avatar* _lookatTargetAvatar;
|
||||
glm::vec3 _lookatOtherPosition;
|
||||
float _lookatIndicatorScale;
|
||||
|
||||
glm::vec3 _transmitterPickStart;
|
||||
glm::vec3 _transmitterPickEnd;
|
||||
|
||||
|
@ -494,7 +487,6 @@ private:
|
|||
QReadWriteLock _voxelSceneStatsLock;
|
||||
|
||||
std::vector<VoxelFade> _voxelFades;
|
||||
std::vector<Avatar*> _avatarFades;
|
||||
ControllerScriptingInterface _controllerScriptingInterface;
|
||||
QPointer<LogDialog> _logDialog;
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtCore/QWeakPointer>
|
||||
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
@ -39,7 +41,7 @@ void DatagramProcessor::processDatagrams() {
|
|||
_packetCount++;
|
||||
_byteCount += bytesReceived;
|
||||
|
||||
if (packetVersionMatch(incomingPacket)) {
|
||||
if (packetVersionMatch(incomingPacket, senderSockAddr)) {
|
||||
// only process this packet if we have a match on the packet version
|
||||
switch (incomingPacket[0]) {
|
||||
case PACKET_TYPE_TRANSMITTER_DATA_V2:
|
||||
|
@ -90,11 +92,30 @@ void DatagramProcessor::processDatagrams() {
|
|||
senderSockAddr);
|
||||
break;
|
||||
case PACKET_TYPE_BULK_AVATAR_DATA:
|
||||
NodeList::getInstance()->processBulkNodeData(senderSockAddr,
|
||||
incomingPacket,
|
||||
bytesReceived);
|
||||
case PACKET_TYPE_KILL_AVATAR: {
|
||||
// update having heard from the avatar-mixer and record the bytes received
|
||||
SharedNodePointer avatarMixer = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
|
||||
|
||||
if (avatarMixer) {
|
||||
avatarMixer->setLastHeardMicrostamp(usecTimestampNow());
|
||||
avatarMixer->recordBytesReceived(bytesReceived);
|
||||
|
||||
QByteArray datagram(reinterpret_cast<char*>(incomingPacket), bytesReceived);
|
||||
|
||||
if (incomingPacket[0] == PACKET_TYPE_BULK_AVATAR_DATA) {
|
||||
QMetaObject::invokeMethod(&application->getAvatarManager(), "processAvatarMixerDatagram",
|
||||
Q_ARG(const QByteArray&, datagram),
|
||||
Q_ARG(const QWeakPointer<Node>&, avatarMixer));
|
||||
} else {
|
||||
// this is an avatar kill, pass it to the application AvatarManager
|
||||
QMetaObject::invokeMethod(&application->getAvatarManager(), "processKillAvatar",
|
||||
Q_ARG(const QByteArray&, datagram));
|
||||
}
|
||||
}
|
||||
|
||||
application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived);
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_DATA_SERVER_GET:
|
||||
case PACKET_TYPE_DATA_SERVER_PUT:
|
||||
case PACKET_TYPE_DATA_SERVER_SEND:
|
||||
|
|
|
@ -334,7 +334,6 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollisionProxies);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtIndicator, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu,
|
||||
MenuOption::FaceshiftTCP,
|
||||
0,
|
||||
|
@ -902,7 +901,7 @@ void Menu::goToDomain() {
|
|||
}
|
||||
|
||||
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
||||
NodeList::getInstance()->sendKillNode(QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
|
||||
Application::getInstance()->getAvatar()->sendKillAvatar();
|
||||
|
||||
// give our nodeList the new domain-server hostname
|
||||
NodeList::getInstance()->setDomainHostname(domainDialog.textValue());
|
||||
|
@ -944,7 +943,7 @@ void Menu::goToLocation() {
|
|||
|
||||
if (newAvatarPos != avatarPos) {
|
||||
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
||||
NodeList::getInstance()->sendKillNode(QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
|
||||
MyAvatar::sendKillAvatar();
|
||||
|
||||
qDebug("Going To Location: %f, %f, %f...", x, y, z);
|
||||
myAvatar->setPosition(newAvatarPos);
|
||||
|
|
|
@ -221,7 +221,6 @@ namespace MenuOption {
|
|||
const QString LodTools = "LOD Tools";
|
||||
const QString Log = "Log";
|
||||
const QString Login = "Login";
|
||||
const QString LookAtIndicator = "Look-at Indicator";
|
||||
const QString LookAtVectors = "Look-at Vectors";
|
||||
const QString MetavoxelEditor = "Metavoxel Editor...";
|
||||
const QString Metavoxels = "Metavoxels";
|
||||
|
|
|
@ -45,7 +45,7 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
|
|||
if (messageLength > statsMessageLength) {
|
||||
packetData += statsMessageLength;
|
||||
messageLength -= statsMessageLength;
|
||||
if (!packetVersionMatch(packetData)) {
|
||||
if (!packetVersionMatch(packetData, senderSockAddr)) {
|
||||
return; // bail since piggyback data doesn't match our versioning
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -57,7 +57,7 @@ GLubyte identityIndicesFront[] = { 0, 2, 1, 0, 3, 2 };
|
|||
GLubyte identityIndicesBack[] = { 4, 5, 6, 4, 6, 7 };
|
||||
|
||||
VoxelSystem::VoxelSystem(float treeScale, int maxVoxels)
|
||||
: NodeData(NULL),
|
||||
: NodeData(),
|
||||
_treeScale(treeScale),
|
||||
_maxVoxels(maxVoxels),
|
||||
_initialized(false) {
|
||||
|
|
|
@ -58,8 +58,8 @@ const int NUM_BODY_CONE_SIDES = 9;
|
|||
const float CHAT_MESSAGE_SCALE = 0.0015f;
|
||||
const float CHAT_MESSAGE_HEIGHT = 0.1f;
|
||||
|
||||
Avatar::Avatar(Node* owningNode) :
|
||||
AvatarData(owningNode),
|
||||
Avatar::Avatar() :
|
||||
AvatarData(),
|
||||
_head(this),
|
||||
_hand(this),
|
||||
_skeletonModel(this),
|
||||
|
@ -75,6 +75,7 @@ Avatar::Avatar(Node* owningNode) :
|
|||
_mouseRayDirection(0.0f, 0.0f, 0.0f),
|
||||
_isCollisionsOn(true),
|
||||
_moving(false),
|
||||
_owningAvatarMixer(),
|
||||
_initialized(false)
|
||||
{
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
|
@ -85,17 +86,11 @@ Avatar::Avatar(Node* owningNode) :
|
|||
_handData = &_hand;
|
||||
}
|
||||
|
||||
|
||||
Avatar::~Avatar() {
|
||||
_headData = NULL;
|
||||
_handData = NULL;
|
||||
}
|
||||
|
||||
void Avatar::deleteOrDeleteLater() {
|
||||
this->deleteLater();
|
||||
}
|
||||
|
||||
|
||||
void Avatar::init() {
|
||||
_head.init();
|
||||
_hand.init();
|
||||
|
|
|
@ -69,10 +69,8 @@ class Avatar : public AvatarData {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Avatar(Node* owningNode = NULL);
|
||||
Avatar();
|
||||
~Avatar();
|
||||
void deleteOrDeleteLater();
|
||||
|
||||
void init();
|
||||
void simulate(float deltaTime, Transmitter* transmitter);
|
||||
|
@ -92,6 +90,9 @@ public:
|
|||
Head& getHead() { return _head; }
|
||||
Hand& getHand() { return _hand; }
|
||||
glm::quat getWorldAlignedOrientation() const;
|
||||
|
||||
Node* getOwningAvatarMixer() { return _owningAvatarMixer.data(); }
|
||||
void setOwningAvatarMixer(const QWeakPointer<Node>& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; }
|
||||
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
|
@ -110,8 +111,10 @@ public:
|
|||
/// \param collision[out] the details of the collision point
|
||||
/// \return whether or not the sphere collided
|
||||
virtual bool findSphereCollision(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision);
|
||||
|
||||
virtual bool isMyAvatar() { return false; }
|
||||
|
||||
virtual int parseData(unsigned char* sourceBuffer, int numBytes);
|
||||
int parseData(unsigned char* sourceBuffer, int numBytes);
|
||||
|
||||
static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);
|
||||
|
||||
|
@ -141,8 +144,8 @@ protected:
|
|||
glm::vec3 _mouseRayDirection;
|
||||
bool _isCollisionsOn;
|
||||
float _stringLength;
|
||||
|
||||
bool _moving; ///< set when position is changing
|
||||
QWeakPointer<Node> _owningAvatarMixer;
|
||||
|
||||
// protected methods...
|
||||
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
|
|
233
interface/src/avatar/AvatarManager.cpp
Normal file
233
interface/src/avatar/AvatarManager.cpp
Normal file
|
@ -0,0 +1,233 @@
|
|||
//
|
||||
// AvatarManager.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 1/23/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Avatar.h"
|
||||
#include "Menu.h"
|
||||
#include "MyAvatar.h"
|
||||
|
||||
#include "AvatarManager.h"
|
||||
|
||||
AvatarManager::AvatarManager(QObject* parent) :
|
||||
_lookAtTargetAvatar(),
|
||||
_lookAtOtherPosition(),
|
||||
_lookAtIndicatorScale(1.0f),
|
||||
_avatarHash(),
|
||||
_avatarFades()
|
||||
{
|
||||
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
|
||||
qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");
|
||||
}
|
||||
|
||||
void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) {
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()");
|
||||
|
||||
Application* applicationInstance = Application::getInstance();
|
||||
|
||||
if (!applicationInstance->isMousePressed()) {
|
||||
foreach (const AvatarSharedPointer& avatar, _avatarHash) {
|
||||
float distance;
|
||||
|
||||
if (avatar->findRayIntersection(applicationInstance->getMouseRayOrigin(),
|
||||
applicationInstance->getMouseRayDirection(), distance)) {
|
||||
// rescale to compensate for head embiggening
|
||||
eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) *
|
||||
(avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot();
|
||||
|
||||
_lookAtIndicatorScale = avatar->getHead().getScale();
|
||||
_lookAtOtherPosition = avatar->getHead().getPosition();
|
||||
|
||||
_lookAtTargetAvatar = avatar;
|
||||
|
||||
// found the look at target avatar, return
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_lookAtTargetAvatar.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarManager::updateAvatars(float deltaTime) {
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
|
||||
|
||||
// simulate avatars
|
||||
AvatarHash::iterator avatar = _avatarHash.begin();
|
||||
if (avatar != _avatarHash.end()) {
|
||||
if (avatar->data()->getOwningAvatarMixer()) {
|
||||
// this avatar's mixer is still around, go ahead and simulate it
|
||||
avatar->data()->simulate(deltaTime, NULL);
|
||||
|
||||
Application* applicationInstance = Application::getInstance();
|
||||
|
||||
avatar->data()->setMouseRay(applicationInstance->getMouseRayOrigin(),
|
||||
applicationInstance->getMouseRayDirection());
|
||||
} else {
|
||||
// the mixer that owned this avatar is gone, give it to the vector of fades and kill it
|
||||
avatar = removeAvatarAtHashIterator(avatar);
|
||||
}
|
||||
}
|
||||
|
||||
// simulate avatar fades
|
||||
simulateAvatarFades(deltaTime);
|
||||
}
|
||||
|
||||
void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
|
||||
return;
|
||||
}
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::renderAvatars()");
|
||||
|
||||
if (!selfAvatarOnly) {
|
||||
|
||||
// Render avatars of other nodes
|
||||
foreach (const AvatarSharedPointer& avatar, _avatarHash) {
|
||||
if (!avatar->isInitialized()) {
|
||||
avatar->init();
|
||||
}
|
||||
avatar->render(false);
|
||||
avatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors));
|
||||
}
|
||||
|
||||
renderAvatarFades();
|
||||
}
|
||||
|
||||
// Render my own Avatar
|
||||
Avatar* myAvatar = Application::getInstance()->getAvatar();
|
||||
myAvatar->render(forceRenderHead);
|
||||
myAvatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors));
|
||||
}
|
||||
|
||||
void AvatarManager::simulateAvatarFades(float deltaTime) {
|
||||
QVector<AvatarSharedPointer>::iterator fadingAvatar = _avatarFades.begin();
|
||||
|
||||
while (fadingAvatar != _avatarFades.end()) {
|
||||
const float SHRINK_RATE = 0.9f;
|
||||
|
||||
fadingAvatar->data()->setTargetScale(fadingAvatar->data()->getScale() * SHRINK_RATE);
|
||||
|
||||
const float MIN_FADE_SCALE = 0.001f;
|
||||
|
||||
if (fadingAvatar->data()->getTargetScale() < MIN_FADE_SCALE) {
|
||||
fadingAvatar = _avatarFades.erase(fadingAvatar);
|
||||
} else {
|
||||
fadingAvatar->data()->simulate(deltaTime, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarManager::renderAvatarFades() {
|
||||
// render avatar fades
|
||||
Glower glower;
|
||||
|
||||
foreach(const AvatarSharedPointer& fadingAvatar, _avatarFades) {
|
||||
fadingAvatar->render(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarManager::processDataServerResponse(const QString& userString, const QStringList& keyList,
|
||||
const QStringList &valueList) {
|
||||
for (int i = 0; i < keyList.size(); i++) {
|
||||
if (valueList[i] != " ") {
|
||||
if (keyList[i] == DataServerKey::FaceMeshURL || keyList[i] == DataServerKey::SkeletonURL) {
|
||||
// mesh URL for a UUID, find avatar in our list
|
||||
AvatarSharedPointer matchingAvatar = _avatarHash.value(QUuid(userString));
|
||||
if (matchingAvatar) {
|
||||
if (keyList[i] == DataServerKey::FaceMeshURL) {
|
||||
qDebug() << "Changing mesh to" << valueList[i] << "for avatar with UUID"
|
||||
<< uuidStringWithoutCurlyBraces(QUuid(userString));
|
||||
|
||||
QMetaObject::invokeMethod(&matchingAvatar->getHead().getFaceModel(),
|
||||
"setURL", Q_ARG(QUrl, QUrl(valueList[i])));
|
||||
} else if (keyList[i] == DataServerKey::SkeletonURL) {
|
||||
qDebug() << "Changing skeleton to" << valueList[i] << "for avatar with UUID"
|
||||
<< uuidStringWithoutCurlyBraces(QString(userString));
|
||||
|
||||
QMetaObject::invokeMethod(&matchingAvatar->getSkeletonModel(),
|
||||
"setURL", Q_ARG(QUrl, QUrl(valueList[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||
unsigned char packetData[MAX_PACKET_SIZE];
|
||||
memcpy(packetData, datagram.data(), datagram.size());
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
|
||||
int bytesRead = numBytesPacketHeader;
|
||||
|
||||
unsigned char avatarData[MAX_PACKET_SIZE];
|
||||
int numBytesDummyPacketHeader = populateTypeAndVersion(avatarData, PACKET_TYPE_HEAD_DATA);
|
||||
|
||||
// enumerate over all of the avatars in this packet
|
||||
// only add them if mixerWeakPointer points to something (meaning that mixer is still around)
|
||||
while (bytesRead < datagram.size() && mixerWeakPointer.data()) {
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(bytesRead, NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID);
|
||||
|
||||
if (!matchingAvatar) {
|
||||
// construct a new Avatar for this node
|
||||
matchingAvatar = AvatarSharedPointer(new Avatar());
|
||||
matchingAvatar->setOwningAvatarMixer(mixerWeakPointer);
|
||||
|
||||
// insert the new avatar into our hash
|
||||
_avatarHash.insert(nodeUUID, matchingAvatar);
|
||||
|
||||
// new UUID requires mesh and skeleton request to data-server
|
||||
DataServerClient::getValuesForKeysAndUUID(QStringList() << DataServerKey::FaceMeshURL << DataServerKey::SkeletonURL,
|
||||
nodeUUID, this);
|
||||
|
||||
qDebug() << "Adding avatar with UUID" << nodeUUID << "to AvatarManager hash.";
|
||||
}
|
||||
|
||||
// copy the rest of the packet to the avatarData holder so we can read the next Avatar from there
|
||||
memcpy(avatarData + numBytesDummyPacketHeader, packetData + bytesRead, datagram.size() - bytesRead);
|
||||
|
||||
// have the matching (or new) avatar parse the data from the packet
|
||||
bytesRead += matchingAvatar->parseData(avatarData, datagram.size() - bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarManager::processKillAvatar(const QByteArray& datagram) {
|
||||
// read the node id
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(numBytesForPacketHeader(reinterpret_cast<const unsigned char*>
|
||||
(datagram.data())),
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
// remove the avatar with that UUID from our hash, if it exists
|
||||
AvatarHash::iterator matchedAvatar = _avatarHash.find(nodeUUID);
|
||||
if (matchedAvatar != _avatarHash.end()) {
|
||||
removeAvatarAtHashIterator(matchedAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
AvatarHash::iterator AvatarManager::removeAvatarAtHashIterator(const AvatarHash::iterator& iterator) {
|
||||
qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarManager hash.";
|
||||
_avatarFades.push_back(iterator.value());
|
||||
return _avatarHash.erase(iterator);
|
||||
}
|
||||
|
||||
void AvatarManager::clearHash() {
|
||||
// clear the AvatarManager hash - typically happens on the removal of the avatar-mixer
|
||||
AvatarHash::iterator removeAvatar = _avatarHash.begin();
|
||||
|
||||
while (removeAvatar != _avatarHash.end()) {
|
||||
removeAvatar = removeAvatarAtHashIterator(removeAvatar);
|
||||
}
|
||||
}
|
60
interface/src/avatar/AvatarManager.h
Normal file
60
interface/src/avatar/AvatarManager.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// AvatarManager.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 1/23/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__AvatarManager__
|
||||
#define __hifi__AvatarManager__
|
||||
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
#include <DataServerClient.h>
|
||||
|
||||
#include "Avatar.h"
|
||||
|
||||
typedef QSharedPointer<Avatar> AvatarSharedPointer;
|
||||
typedef QHash<QUuid, AvatarSharedPointer> AvatarHash;
|
||||
|
||||
class AvatarManager : public QObject, public DataServerCallbackObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AvatarManager(QObject* parent = 0);
|
||||
|
||||
const AvatarHash& getAvatarHash() { return _avatarHash; }
|
||||
int size() const { return _avatarHash.size(); }
|
||||
|
||||
Avatar* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
|
||||
|
||||
void updateLookAtTargetAvatar(glm::vec3& eyePosition);
|
||||
|
||||
void updateAvatars(float deltaTime);
|
||||
void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false);
|
||||
|
||||
void clearHash();
|
||||
|
||||
public slots:
|
||||
void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList);
|
||||
|
||||
void processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer);
|
||||
void processKillAvatar(const QByteArray& datagram);
|
||||
|
||||
private:
|
||||
void simulateAvatarFades(float deltaTime);
|
||||
void renderAvatarFades();
|
||||
|
||||
AvatarHash::iterator removeAvatarAtHashIterator(const AvatarHash::iterator& iterator);
|
||||
|
||||
QWeakPointer<Avatar> _lookAtTargetAvatar;
|
||||
glm::vec3 _lookAtOtherPosition;
|
||||
float _lookAtIndicatorScale;
|
||||
|
||||
AvatarHash _avatarHash;
|
||||
QVector<AvatarSharedPointer> _avatarFades;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AvatarManager__) */
|
|
@ -43,7 +43,7 @@ Hand::Hand(Avatar* owningAvatar) :
|
|||
|
||||
void Hand::init() {
|
||||
// Different colors for my hand and others' hands
|
||||
if (_owningAvatar && _owningAvatar->getOwningNode() == NULL) {
|
||||
if (_owningAvatar && _owningAvatar->isMyAvatar()) {
|
||||
_ballColor = glm::vec3(0.0, 0.4, 0.0);
|
||||
}
|
||||
else {
|
||||
|
@ -54,8 +54,6 @@ void Hand::init() {
|
|||
void Hand::reset() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
glm::vec3 Hand::getAndResetGrabDelta() {
|
||||
const float HAND_GRAB_SCALE_DISTANCE = 2.f;
|
||||
glm::vec3 delta = _grabDelta * _owningAvatar->getScale() * HAND_GRAB_SCALE_DISTANCE;
|
||||
|
@ -185,49 +183,46 @@ void Hand::updateCollisions() {
|
|||
glm::vec3 totalPenetration;
|
||||
|
||||
// check other avatars
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
|
||||
Avatar* otherAvatar = (Avatar*)node->getLinkedData();
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
|
||||
// Check for palm collisions
|
||||
glm::vec3 myPalmPosition = palm.getPosition();
|
||||
float palmCollisionDistance = 0.1f;
|
||||
bool wasColliding = palm.getIsCollidingWithPalm();
|
||||
palm.setIsCollidingWithPalm(false);
|
||||
// If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound
|
||||
for (size_t j = 0; j < otherAvatar->getHand().getNumPalms(); j++) {
|
||||
PalmData& otherPalm = otherAvatar->getHand().getPalms()[j];
|
||||
if (!otherPalm.isActive()) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 otherPalmPosition = otherPalm.getPosition();
|
||||
if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) {
|
||||
palm.setIsCollidingWithPalm(true);
|
||||
if (!wasColliding) {
|
||||
const float PALM_COLLIDE_VOLUME = 1.f;
|
||||
const float PALM_COLLIDE_FREQUENCY = 1000.f;
|
||||
const float PALM_COLLIDE_DURATION_MAX = 0.75f;
|
||||
const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f;
|
||||
Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME,
|
||||
PALM_COLLIDE_FREQUENCY,
|
||||
PALM_COLLIDE_DURATION_MAX,
|
||||
PALM_COLLIDE_DECAY_PER_SAMPLE);
|
||||
// If the other person's palm is in motion, move mine downward to show I was hit
|
||||
const float MIN_VELOCITY_FOR_SLAP = 0.05f;
|
||||
if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) {
|
||||
// add slapback here
|
||||
}
|
||||
foreach (const AvatarSharedPointer& avatar, Application::getInstance()->getAvatarManager().getAvatarHash()) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
|
||||
// Check for palm collisions
|
||||
glm::vec3 myPalmPosition = palm.getPosition();
|
||||
float palmCollisionDistance = 0.1f;
|
||||
bool wasColliding = palm.getIsCollidingWithPalm();
|
||||
palm.setIsCollidingWithPalm(false);
|
||||
// If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound
|
||||
for (size_t j = 0; j < avatar->getHand().getNumPalms(); j++) {
|
||||
PalmData& otherPalm = avatar->getHand().getPalms()[j];
|
||||
if (!otherPalm.isActive()) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 otherPalmPosition = otherPalm.getPosition();
|
||||
if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) {
|
||||
palm.setIsCollidingWithPalm(true);
|
||||
if (!wasColliding) {
|
||||
const float PALM_COLLIDE_VOLUME = 1.f;
|
||||
const float PALM_COLLIDE_FREQUENCY = 1000.f;
|
||||
const float PALM_COLLIDE_DURATION_MAX = 0.75f;
|
||||
const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f;
|
||||
Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME,
|
||||
PALM_COLLIDE_FREQUENCY,
|
||||
PALM_COLLIDE_DURATION_MAX,
|
||||
PALM_COLLIDE_DECAY_PER_SAMPLE);
|
||||
// If the other person's palm is in motion, move mine downward to show I was hit
|
||||
const float MIN_VELOCITY_FOR_SLAP = 0.05f;
|
||||
if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) {
|
||||
// add slapback here
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
glm::vec3 avatarPenetration;
|
||||
if (otherAvatar->findSpherePenetration(palm.getPosition(), scaledPalmRadius, avatarPenetration)) {
|
||||
totalPenetration = addPenetrations(totalPenetration, avatarPenetration);
|
||||
// Check for collisions with the other avatar's leap palms
|
||||
}
|
||||
}
|
||||
glm::vec3 avatarPenetration;
|
||||
if (avatar->findSpherePenetration(palm.getPosition(), scaledPalmRadius, avatarPenetration)) {
|
||||
totalPenetration = addPenetrations(totalPenetration, avatarPenetration);
|
||||
// Check for collisions with the other avatar's leap palms
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,8 +37,8 @@ const bool USING_HEAD_LEAN = false;
|
|||
const float SKIN_COLOR[] = {1.0f, 0.84f, 0.66f};
|
||||
const float DARK_SKIN_COLOR[] = {0.9f, 0.78f, 0.63f};
|
||||
|
||||
MyAvatar::MyAvatar(Node* owningNode) :
|
||||
Avatar(owningNode),
|
||||
MyAvatar::MyAvatar() :
|
||||
Avatar(),
|
||||
_mousePressed(false),
|
||||
_bodyPitchDelta(0.0f),
|
||||
_bodyRollDelta(0.0f),
|
||||
|
@ -472,6 +472,21 @@ void MyAvatar::loadData(QSettings* settings) {
|
|||
settings->endGroup();
|
||||
}
|
||||
|
||||
void MyAvatar::sendKillAvatar() {
|
||||
unsigned char packet[MAX_PACKET_SIZE];
|
||||
unsigned char* packetPosition = packet;
|
||||
|
||||
packetPosition += populateTypeAndVersion(packetPosition, PACKET_TYPE_KILL_AVATAR);
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
QByteArray rfcUUID = nodeList->getOwnerUUID().toRfc4122();
|
||||
memcpy(packetPosition, rfcUUID.constData(), rfcUUID.size());
|
||||
packetPosition += rfcUUID.size();
|
||||
|
||||
nodeList->broadcastToNodes(packet, packetPosition - packet, QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
|
||||
}
|
||||
|
||||
void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) {
|
||||
// first orbit horizontally
|
||||
glm::quat orientation = getOrientation();
|
||||
|
@ -776,16 +791,16 @@ void MyAvatar::updateChatCircle(float deltaTime) {
|
|||
// find all circle-enabled members and sort by distance
|
||||
QVector<SortedAvatar> sortedAvatars;
|
||||
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
|
||||
SortedAvatar sortedAvatar;
|
||||
sortedAvatar.avatar = (Avatar*)node->getLinkedData();
|
||||
if (!sortedAvatar.avatar->isChatCirclingEnabled()) {
|
||||
continue;
|
||||
}
|
||||
sortedAvatar.distance = glm::distance(_position, sortedAvatar.avatar->getPosition());
|
||||
sortedAvatars.append(sortedAvatar);
|
||||
foreach (const AvatarSharedPointer& avatar, Application::getInstance()->getAvatarManager().getAvatarHash()) {
|
||||
SortedAvatar sortedAvatar;
|
||||
sortedAvatar.avatar = avatar.data();
|
||||
|
||||
if (!sortedAvatar.avatar->isChatCirclingEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sortedAvatar.distance = glm::distance(_position, sortedAvatar.avatar->getPosition());
|
||||
sortedAvatars.append(sortedAvatar);
|
||||
}
|
||||
|
||||
qSort(sortedAvatars.begin(), sortedAvatars.end());
|
||||
|
|
|
@ -24,8 +24,8 @@ enum AvatarHandState
|
|||
|
||||
class MyAvatar : public Avatar {
|
||||
public:
|
||||
MyAvatar(Node* owningNode = NULL);
|
||||
|
||||
MyAvatar();
|
||||
|
||||
void reset();
|
||||
void simulate(float deltaTime, Transmitter* transmitter);
|
||||
void updateFromGyros(bool turnWithHead);
|
||||
|
@ -62,6 +62,10 @@ public:
|
|||
void setDriveKeys(int key, float val) { _driveKeys[key] = val; };
|
||||
bool getDriveKeys(int key) { return _driveKeys[key]; };
|
||||
void jump() { _shouldJump = true; };
|
||||
|
||||
bool isMyAvatar() { return true; }
|
||||
|
||||
static void sendKillAvatar();
|
||||
|
||||
// Set/Get update the thrust that will move the avatar around
|
||||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
|
|
|
@ -158,35 +158,11 @@ void Profile::processDataServerResponse(const QString& userString, const QString
|
|||
if (userString == _username || userString == uuidStringWithoutCurlyBraces(_uuid)) {
|
||||
qDebug("Changing user's face model URL to %s", valueList[i].toLocal8Bit().constData());
|
||||
Application::getInstance()->getProfile()->setFaceModelURL(QUrl(valueList[i]));
|
||||
} else {
|
||||
// mesh URL for a UUID, find avatar in our list
|
||||
SharedNodePointer matchingNode = NodeList::getInstance()->nodeWithUUID(QUuid(userString));
|
||||
if (matchingNode && matchingNode->getType() == NODE_TYPE_AGENT) {
|
||||
qDebug() << "Changing mesh to" << valueList[i] << "for avatar with UUID"
|
||||
<< uuidStringWithoutCurlyBraces(matchingNode->getUUID());
|
||||
|
||||
Avatar* avatar = (Avatar *) matchingNode->getLinkedData();
|
||||
|
||||
QMetaObject::invokeMethod(&avatar->getHead().getFaceModel(),
|
||||
"setURL", Q_ARG(QUrl, QUrl(valueList[i])));
|
||||
}
|
||||
}
|
||||
} else if (keyList[i] == DataServerKey::SkeletonURL) {
|
||||
if (userString == _username || userString == uuidStringWithoutCurlyBraces(_uuid)) {
|
||||
qDebug("Changing user's skeleton URL to %s", valueList[i].toLocal8Bit().constData());
|
||||
Application::getInstance()->getProfile()->setSkeletonModelURL(QUrl(valueList[i]));
|
||||
} else {
|
||||
// skeleton URL for a UUID, find avatar in our list
|
||||
SharedNodePointer matchingNode = NodeList::getInstance()->nodeWithUUID(QUuid(userString));
|
||||
if (matchingNode && matchingNode->getType() == NODE_TYPE_AGENT) {
|
||||
qDebug() << "Changing skeleton to" << valueList[i] << "for avatar with UUID"
|
||||
<< uuidStringWithoutCurlyBraces(matchingNode->getUUID());
|
||||
|
||||
Avatar* avatar = (Avatar *) matchingNode->getLinkedData();
|
||||
|
||||
QMetaObject::invokeMethod(&avatar->getSkeletonModel(),
|
||||
"setURL", Q_ARG(QUrl, QUrl(valueList[i])));
|
||||
}
|
||||
}
|
||||
} else if (keyList[i] == DataServerKey::Domain && keyList[i + 1] == DataServerKey::Position &&
|
||||
keyList[i + 2] == DataServerKey::Orientation && valueList[i] != " " &&
|
||||
|
@ -198,7 +174,7 @@ void Profile::processDataServerResponse(const QString& userString, const QString
|
|||
if (coordinateItems.size() == 3 && orientationItems.size() == 3) {
|
||||
|
||||
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
||||
NodeList::getInstance()->sendKillNode(QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
|
||||
MyAvatar::sendKillAvatar();
|
||||
|
||||
qDebug() << "Changing domain to" << valueList[i].toLocal8Bit().constData() <<
|
||||
", position to" << valueList[i + 1].toLocal8Bit().constData() <<
|
||||
|
@ -211,8 +187,7 @@ void Profile::processDataServerResponse(const QString& userString, const QString
|
|||
orientationItems[1].toFloat(),
|
||||
orientationItems[2].toFloat()))) *
|
||||
glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f);
|
||||
Application::getInstance()->getAvatar()
|
||||
->setOrientation(newOrientation);
|
||||
Application::getInstance()->getAvatar()->setOrientation(newOrientation);
|
||||
|
||||
// move the user a couple units away
|
||||
const float DISTANCE_TO_USER = 2.0f;
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
// Copyright (c) 2013, 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Application.h" // HACK ATTACK WARNING: for windows build to work, we need this ahead of QtGui
|
||||
#include <QtGui>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ui_updateDialog.h"
|
||||
|
||||
#include "UpdateDialog.h"
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "AudioRingBuffer.h"
|
||||
|
||||
AudioRingBuffer::AudioRingBuffer(int numFrameSamples) :
|
||||
NodeData(NULL),
|
||||
NodeData(),
|
||||
_sampleCapacity(numFrameSamples * RING_BUFFER_LENGTH_FRAMES),
|
||||
_isStarved(true),
|
||||
_hasStarted(false)
|
||||
|
|
|
@ -22,8 +22,8 @@ using namespace std;
|
|||
|
||||
static const float fingerVectorRadix = 4; // bits of precision when converting from float<->fixed
|
||||
|
||||
AvatarData::AvatarData(Node* owningNode) :
|
||||
NodeData(owningNode),
|
||||
AvatarData::AvatarData() :
|
||||
NodeData(),
|
||||
_handPosition(0,0,0),
|
||||
_bodyYaw(-90.0),
|
||||
_bodyPitch(0.0),
|
||||
|
|
|
@ -70,7 +70,7 @@ class AvatarData : public NodeData {
|
|||
Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll)
|
||||
Q_PROPERTY(QString chatMessage READ getQStringChatMessage WRITE setChatMessage)
|
||||
public:
|
||||
AvatarData(Node* owningNode = NULL);
|
||||
AvatarData();
|
||||
~AvatarData();
|
||||
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
#include <cstdio>
|
||||
#include "OctreeSendThread.h"
|
||||
|
||||
OctreeQueryNode::OctreeQueryNode(Node* owningNode) :
|
||||
OctreeQuery(owningNode),
|
||||
OctreeQueryNode::OctreeQueryNode() :
|
||||
_viewSent(false),
|
||||
_octreePacketAvailableBytes(MAX_PACKET_SIZE),
|
||||
_maxSearchLevel(1),
|
||||
|
@ -38,9 +37,8 @@ OctreeQueryNode::OctreeQueryNode(Node* owningNode) :
|
|||
_sequenceNumber = 0;
|
||||
}
|
||||
|
||||
void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer) {
|
||||
void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, const QUuid& nodeUUID) {
|
||||
// Create octree sending thread...
|
||||
QUuid nodeUUID = getOwningNode()->getUUID();
|
||||
_octreeSendThread = new OctreeSendThread(nodeUUID, octreeServer);
|
||||
_octreeSendThread->initialize(true);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class OctreeServer;
|
|||
|
||||
class OctreeQueryNode : public OctreeQuery {
|
||||
public:
|
||||
OctreeQueryNode(Node* owningNode);
|
||||
OctreeQueryNode();
|
||||
virtual ~OctreeQueryNode();
|
||||
|
||||
virtual PACKET_TYPE getMyPacketType() const = 0;
|
||||
|
@ -80,7 +80,7 @@ public:
|
|||
|
||||
OctreeSceneStats stats;
|
||||
|
||||
void initializeOctreeSendThread(OctreeServer* octreeServer);
|
||||
void initializeOctreeSendThread(OctreeServer* octreeServer, const QUuid& nodeUUID);
|
||||
bool isOctreeSendThreadInitalized() { return _octreeSendThread; }
|
||||
|
||||
void dumpOutOfView();
|
||||
|
|
|
@ -22,23 +22,12 @@ OctreeServer* OctreeServer::_instance = NULL;
|
|||
|
||||
void OctreeServer::attachQueryNodeToNode(Node* newNode) {
|
||||
if (newNode->getLinkedData() == NULL) {
|
||||
OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode(newNode);
|
||||
OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode();
|
||||
newQueryNodeData->resetOctreePacket(true); // don't bump sequence
|
||||
newNode->setLinkedData(newQueryNodeData);
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeServer::nodeKilled(SharedNodePointer node) {
|
||||
// Use this to cleanup our node
|
||||
if (node->getType() == NODE_TYPE_AGENT) {
|
||||
OctreeQueryNode* nodeData = (OctreeQueryNode*)node->getLinkedData();
|
||||
if (nodeData) {
|
||||
node->setLinkedData(NULL);
|
||||
delete nodeData;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OctreeServer::OctreeServer(const unsigned char* dataBuffer, int numBytes) :
|
||||
ThreadedAssignment(dataBuffer, numBytes),
|
||||
_argc(0),
|
||||
|
@ -499,7 +488,7 @@ void OctreeServer::processDatagram(const QByteArray& dataByteArray, const HifiSo
|
|||
}
|
||||
OctreeQueryNode* nodeData = (OctreeQueryNode*) node->getLinkedData();
|
||||
if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
|
||||
nodeData->initializeOctreeSendThread(this);
|
||||
nodeData->initializeOctreeSendThread(this, nodeUUID);
|
||||
}
|
||||
}
|
||||
} else if (packetType == PACKET_TYPE_JURISDICTION_REQUEST) {
|
||||
|
@ -573,8 +562,6 @@ void OctreeServer::run() {
|
|||
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
// tell our NodeList about our desire to get notifications
|
||||
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), this, SLOT(nodeKilled(SharedNodePointer)));
|
||||
nodeList->linkedDataCreateCallback = &OctreeServer::attachQueryNodeToNode;
|
||||
|
||||
srand((unsigned)time(0));
|
||||
|
|
|
@ -48,7 +48,7 @@ public:
|
|||
uint64_t getLoadElapsedTime() const { return (_persistThread) ? _persistThread->getLoadElapsedTime() : 0; }
|
||||
|
||||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode) = 0;
|
||||
virtual OctreeQueryNode* createOctreeQueryNode() = 0;
|
||||
virtual Octree* createTree() = 0;
|
||||
virtual unsigned char getMyNodeType() const = 0;
|
||||
virtual PACKET_TYPE getMyQueryMessageType() const = 0;
|
||||
|
@ -69,8 +69,6 @@ public slots:
|
|||
void run();
|
||||
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
|
||||
|
||||
void nodeKilled(SharedNodePointer node);
|
||||
|
||||
protected:
|
||||
void parsePayload();
|
||||
void initHTTPManager(int port);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
/// within the domain. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets
|
||||
/// and adding them to the processing queue by calling queueReceivedPacket()
|
||||
class JurisdictionListener : public ReceivedPacketProcessor {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static const int DEFAULT_PACKETS_PER_SECOND = 1;
|
||||
static const int NO_SERVER_CHECK_RATE = 60; // if no servers yet detected, keep checking at 60fps
|
||||
|
|
|
@ -182,7 +182,7 @@ public:
|
|||
~Octree();
|
||||
|
||||
/// Your tree class must implement this to create the correct element type
|
||||
virtual OctreeElement* createNewElement(unsigned char * octalCode = NULL) const = 0;
|
||||
virtual OctreeElement* createNewElement(unsigned char * octalCode = NULL) = 0;
|
||||
|
||||
// These methods will allow the OctreeServer to send your tree inbound edit packets of your
|
||||
// own definition. Implement these to allow your octree based server to support editing
|
||||
|
|
|
@ -47,7 +47,7 @@ protected:
|
|||
// can only be constructed by derived implementation
|
||||
OctreeElement();
|
||||
|
||||
virtual OctreeElement* createNewElement(unsigned char * octalCode = NULL) const = 0;
|
||||
virtual OctreeElement* createNewElement(unsigned char * octalCode = NULL) = 0;
|
||||
|
||||
public:
|
||||
virtual void init(unsigned char * octalCode); /// Your subclass must call init on construction.
|
||||
|
|
|
@ -22,8 +22,8 @@ using namespace std;
|
|||
|
||||
static const float fingerVectorRadix = 4; // bits of precision when converting from float<->fixed
|
||||
|
||||
OctreeQuery::OctreeQuery(Node* owningNode) :
|
||||
NodeData(owningNode),
|
||||
OctreeQuery::OctreeQuery() :
|
||||
NodeData(),
|
||||
_uuid(),
|
||||
_cameraPosition(0,0,0),
|
||||
_cameraOrientation(),
|
||||
|
|
|
@ -51,7 +51,7 @@ class OctreeQuery : public NodeData {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OctreeQuery(Node* owningNode = NULL);
|
||||
OctreeQuery();
|
||||
virtual ~OctreeQuery();
|
||||
|
||||
int getBroadcastData(unsigned char* destinationBuffer);
|
||||
|
|
|
@ -132,3 +132,12 @@ void OctreeRenderer::render() {
|
|||
_tree->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeRenderer::clear() {
|
||||
if (_tree) {
|
||||
_tree->lockForWrite();
|
||||
_tree->eraseAllOctreeElements();
|
||||
_tree->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ public:
|
|||
|
||||
static bool renderOperation(OctreeElement* element, void* extraData);
|
||||
|
||||
/// clears the tree
|
||||
void clear();
|
||||
protected:
|
||||
Octree* _tree;
|
||||
QUuid _dataSourceUUID;
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
|
||||
class ParticleNodeData : public OctreeQueryNode {
|
||||
public:
|
||||
ParticleNodeData(Node* owningNode) :
|
||||
OctreeQueryNode(owningNode),
|
||||
ParticleNodeData() :
|
||||
OctreeQueryNode(),
|
||||
_lastDeletedParticlesSentAt(0) { };
|
||||
|
||||
virtual PACKET_TYPE getMyPacketType() const { return PACKET_TYPE_PARTICLE_DATA; }
|
||||
|
|
|
@ -26,8 +26,8 @@ ParticleServer::~ParticleServer() {
|
|||
tree->removeNewlyCreatedHook(this);
|
||||
}
|
||||
|
||||
OctreeQueryNode* ParticleServer::createOctreeQueryNode(Node* newNode) {
|
||||
return new ParticleNodeData(newNode);
|
||||
OctreeQueryNode* ParticleServer::createOctreeQueryNode() {
|
||||
return new ParticleNodeData();
|
||||
}
|
||||
|
||||
Octree* ParticleServer::createTree() {
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
~ParticleServer();
|
||||
|
||||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode);
|
||||
virtual OctreeQueryNode* createOctreeQueryNode();
|
||||
virtual Octree* createTree();
|
||||
virtual unsigned char getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
|
||||
virtual PACKET_TYPE getMyQueryMessageType() const { return PACKET_TYPE_PARTICLE_QUERY; }
|
||||
|
|
|
@ -292,10 +292,7 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
}
|
||||
|
||||
|
||||
Particle Particle::fromEditPacket(unsigned char* data, int length, int& processedBytes, ParticleTree* tree) {
|
||||
|
||||
//qDebug() << "Particle::fromEditPacket() length=" << length;
|
||||
|
||||
Particle Particle::fromEditPacket(unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid) {
|
||||
Particle newParticle; // id and _lastUpdated will get set here...
|
||||
unsigned char* dataAt = data;
|
||||
processedBytes = 0;
|
||||
|
@ -304,9 +301,6 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
|
|||
int octets = numberOfThreeBitSectionsInCode(data);
|
||||
int lengthOfOctcode = bytesRequiredForCodeLength(octets);
|
||||
|
||||
//qDebug() << "Particle::fromEditPacket() lengthOfOctcode=" << lengthOfOctcode;
|
||||
//printOctalCode(data);
|
||||
|
||||
// we don't actually do anything with this octcode...
|
||||
dataAt += lengthOfOctcode;
|
||||
processedBytes += lengthOfOctcode;
|
||||
|
@ -323,8 +317,6 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
|
|||
|
||||
// special case for handling "new" particles
|
||||
if (isNewParticle) {
|
||||
//qDebug() << "editID == NEW_PARTICLE";
|
||||
|
||||
// If this is a NEW_PARTICLE, then we assume that there's an additional uint32_t creatorToken, that
|
||||
// we want to send back to the creator as an map to the actual id
|
||||
uint32_t creatorTokenID;
|
||||
|
@ -332,11 +324,8 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
|
|||
dataAt += sizeof(creatorTokenID);
|
||||
processedBytes += sizeof(creatorTokenID);
|
||||
|
||||
//qDebug() << "creatorTokenID:" << creatorTokenID;
|
||||
|
||||
newParticle.setCreatorTokenID(creatorTokenID);
|
||||
newParticle._newlyCreated = true;
|
||||
|
||||
newParticle.setAge(0); // this guy is new!
|
||||
|
||||
} else {
|
||||
|
@ -346,15 +335,19 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
|
|||
// copy existing properties before over-writing with new properties
|
||||
if (existingParticle) {
|
||||
newParticle = *existingParticle;
|
||||
//qDebug() << "newParticle = *existingParticle... calling debugDump()...";
|
||||
//existingParticle->debugDump();
|
||||
} else {
|
||||
// the user attempted to edit a particle that doesn't exist
|
||||
qDebug() << "user attempted to edit a particle that doesn't exist...";
|
||||
valid = false;
|
||||
return newParticle;
|
||||
}
|
||||
|
||||
newParticle._id = editID;
|
||||
newParticle._newlyCreated = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// if we got this far, then our result will be valid
|
||||
valid = true;
|
||||
|
||||
|
||||
// lastEdited
|
||||
memcpy(&newParticle._lastEdited, dataAt, sizeof(newParticle._lastEdited));
|
||||
|
@ -840,6 +833,8 @@ ParticleProperties::ParticleProperties() :
|
|||
_inHand(false),
|
||||
_shouldDie(false),
|
||||
|
||||
_id(UNKNOWN_PARTICLE_ID),
|
||||
_idSet(false),
|
||||
_lastEdited(usecTimestampNow()),
|
||||
_positionChanged(false),
|
||||
_colorChanged(false),
|
||||
|
@ -925,6 +920,11 @@ QScriptValue ParticleProperties::copyToScriptValue(QScriptEngine* engine) const
|
|||
properties.setProperty("script", _script);
|
||||
properties.setProperty("inHand", _inHand);
|
||||
properties.setProperty("shouldDie", _shouldDie);
|
||||
|
||||
if (_idSet) {
|
||||
properties.setProperty("id", _id);
|
||||
properties.setProperty("isKnownID", (_id == UNKNOWN_PARTICLE_ID));
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
@ -1118,6 +1118,9 @@ void ParticleProperties::copyFromParticle(const Particle& particle) {
|
|||
_inHand = particle.getInHand();
|
||||
_shouldDie = particle.getShouldDie();
|
||||
|
||||
_id = particle.getID();
|
||||
_idSet = true;
|
||||
|
||||
_positionChanged = false;
|
||||
_colorChanged = false;
|
||||
_radiusChanged = false;
|
||||
|
|
|
@ -94,6 +94,9 @@ public:
|
|||
void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; _shouldDieChanged = true; }
|
||||
void setLifetime(float value) { _lifetime = value; _lifetimeChanged = true; }
|
||||
void setScript(QString updateScript) { _script = updateScript; _scriptChanged = true; }
|
||||
|
||||
/// used by ParticleScriptingInterface to return ParticleProperties for unknown particles
|
||||
void setIsUnknownID() { _id = UNKNOWN_PARTICLE_ID; _idSet = true; }
|
||||
|
||||
private:
|
||||
glm::vec3 _position;
|
||||
|
@ -107,6 +110,8 @@ private:
|
|||
bool _inHand;
|
||||
bool _shouldDie;
|
||||
|
||||
uint32_t _id;
|
||||
bool _idSet;
|
||||
uint64_t _lastEdited;
|
||||
bool _positionChanged;
|
||||
bool _colorChanged;
|
||||
|
@ -145,6 +150,7 @@ public:
|
|||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ParticleID);
|
||||
Q_DECLARE_METATYPE(QVector<ParticleID>);
|
||||
QScriptValue ParticleIDtoScriptValue(QScriptEngine* engine, const ParticleID& properties);
|
||||
void ParticleIDfromScriptValue(const QScriptValue &object, ParticleID& properties);
|
||||
|
||||
|
@ -162,7 +168,7 @@ public:
|
|||
bool inHand = NOT_IN_HAND, QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE);
|
||||
|
||||
/// creates an NEW particle from an PACKET_TYPE_PARTICLE_ADD_OR_EDIT edit data buffer
|
||||
static Particle fromEditPacket(unsigned char* data, int length, int& processedBytes, ParticleTree* tree);
|
||||
static Particle fromEditPacket(unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid);
|
||||
|
||||
virtual ~Particle();
|
||||
virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity,
|
||||
|
|
|
@ -191,37 +191,37 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
}
|
||||
|
||||
// loop through all the other avatars for potential interactions...
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
//qDebug() << "updateCollisionWithAvatars()... node:" << *node << "\n";
|
||||
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
|
||||
AvatarData* avatar = static_cast<AvatarData*>(node->getLinkedData());
|
||||
CollisionInfo collisionInfo;
|
||||
if (avatar->findSphereCollision(center, radius, collisionInfo)) {
|
||||
collisionInfo._addedVelocity /= (float)(TREE_SCALE);
|
||||
glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
|
||||
if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) {
|
||||
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar.
|
||||
// NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
|
||||
// TODO: make this less hacky when we have more per-collision details
|
||||
float elasticity = ELASTICITY;
|
||||
float attenuationFactor = glm::length(collisionInfo._addedVelocity) / HALTING_SPEED;
|
||||
float damping = DAMPING;
|
||||
if (attenuationFactor < 1.f) {
|
||||
collisionInfo._addedVelocity *= attenuationFactor;
|
||||
elasticity *= attenuationFactor;
|
||||
// NOTE: the math below keeps the damping piecewise continuous,
|
||||
// while ramping it up to 1.0 when attenuationFactor = 0
|
||||
damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING);
|
||||
}
|
||||
// HACK END
|
||||
|
||||
updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
|
||||
collisionInfo._penetration /= (float)(TREE_SCALE);
|
||||
applyHardCollision(particle, ELASTICITY, damping, collisionInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
// //qDebug() << "updateCollisionWithAvatars()... node:" << *node << "\n";
|
||||
// if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
|
||||
// AvatarData* avatar = static_cast<AvatarData*>(node->getLinkedData());
|
||||
// CollisionInfo collisionInfo;
|
||||
// if (avatar->findSphereCollision(center, radius, collisionInfo)) {
|
||||
// collisionInfo._addedVelocity /= (float)(TREE_SCALE);
|
||||
// glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
|
||||
// if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) {
|
||||
// // HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar.
|
||||
// // NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
|
||||
// // TODO: make this less hacky when we have more per-collision details
|
||||
// float elasticity = ELASTICITY;
|
||||
// float attenuationFactor = glm::length(collisionInfo._addedVelocity) / HALTING_SPEED;
|
||||
// float damping = DAMPING;
|
||||
// if (attenuationFactor < 1.f) {
|
||||
// collisionInfo._addedVelocity *= attenuationFactor;
|
||||
// elasticity *= attenuationFactor;
|
||||
// // NOTE: the math below keeps the damping piecewise continuous,
|
||||
// // while ramping it up to 1.0 when attenuationFactor = 0
|
||||
// damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING);
|
||||
// }
|
||||
// // HACK END
|
||||
//
|
||||
// updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
|
||||
// collisionInfo._penetration /= (float)(TREE_SCALE);
|
||||
// applyHardCollision(particle, ELASTICITY, damping, collisionInfo);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// TODO: convert applyHardCollision() to take a CollisionInfo& instead of penetration + addedVelocity
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
|
||||
ParticleTree::ParticleTree(bool shouldReaverage) : Octree(shouldReaverage) {
|
||||
ParticleTreeElement* rootNode = createNewElement();
|
||||
rootNode->setTree(this);
|
||||
_rootNode = rootNode;
|
||||
}
|
||||
|
||||
ParticleTreeElement* ParticleTree::createNewElement(unsigned char * octalCode) const {
|
||||
ParticleTreeElement* ParticleTree::createNewElement(unsigned char * octalCode) {
|
||||
ParticleTreeElement* newElement = new ParticleTreeElement(octalCode);
|
||||
newElement->setTree(this);
|
||||
return newElement;
|
||||
}
|
||||
|
||||
|
@ -160,6 +160,42 @@ const Particle* ParticleTree::findClosestParticle(glm::vec3 position, float targ
|
|||
return args.closestParticle;
|
||||
}
|
||||
|
||||
class FindAllNearPointArgs {
|
||||
public:
|
||||
glm::vec3 position;
|
||||
float targetRadius;
|
||||
QVector<const Particle*> particles;
|
||||
};
|
||||
|
||||
|
||||
bool ParticleTree::findInSphereOperation(OctreeElement* element, void* extraData) {
|
||||
FindAllNearPointArgs* args = static_cast<FindAllNearPointArgs*>(extraData);
|
||||
ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element);
|
||||
|
||||
glm::vec3 penetration;
|
||||
bool sphereIntersection = particleTreeElement->getAABox().findSpherePenetration(args->position,
|
||||
args->targetRadius, penetration);
|
||||
|
||||
// If this particleTreeElement contains the point, then search it...
|
||||
if (sphereIntersection) {
|
||||
QVector<const Particle*> moreParticles = particleTreeElement->getParticles(args->position, args->targetRadius);
|
||||
args->particles << moreParticles;
|
||||
return true; // keep searching in case children have closer particles
|
||||
}
|
||||
|
||||
// if this element doesn't contain the point, then none of it's children can contain the point, so stop searching
|
||||
return false;
|
||||
}
|
||||
|
||||
QVector<const Particle*> ParticleTree::findParticles(const glm::vec3& center, float radius) {
|
||||
QVector<Particle*> result;
|
||||
FindAllNearPointArgs args = { center, radius };
|
||||
lockForRead();
|
||||
recurseTreeWithOperation(findInSphereOperation, &args);
|
||||
unlock();
|
||||
return args.particles;
|
||||
}
|
||||
|
||||
class FindByIDArgs {
|
||||
public:
|
||||
uint32_t id;
|
||||
|
@ -214,13 +250,14 @@ int ParticleTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* p
|
|||
// we handle these types of "edit" packets
|
||||
switch (packetType) {
|
||||
case PACKET_TYPE_PARTICLE_ADD_OR_EDIT: {
|
||||
//qDebug() << " got PACKET_TYPE_PARTICLE_ADD_OR_EDIT... ";
|
||||
Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes, this);
|
||||
storeParticle(newParticle, senderNode);
|
||||
if (newParticle.isNewlyCreated()) {
|
||||
notifyNewlyCreatedParticle(newParticle, senderNode);
|
||||
bool isValid;
|
||||
Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes, this, isValid);
|
||||
if (isValid) {
|
||||
storeParticle(newParticle, senderNode);
|
||||
if (newParticle.isNewlyCreated()) {
|
||||
notifyNewlyCreatedParticle(newParticle, senderNode);
|
||||
}
|
||||
}
|
||||
//qDebug() << " DONE... PACKET_TYPE_PARTICLE_ADD_OR_EDIT... ";
|
||||
} break;
|
||||
|
||||
// TODO: wire in support here for server to get PACKET_TYPE_PARTICLE_ERASE messages
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
ParticleTree(bool shouldReaverage = false);
|
||||
|
||||
/// Implements our type specific root element factory
|
||||
virtual ParticleTreeElement* createNewElement(unsigned char * octalCode = NULL) const;
|
||||
virtual ParticleTreeElement* createNewElement(unsigned char * octalCode = NULL);
|
||||
|
||||
/// Type safe version of getRoot()
|
||||
ParticleTreeElement* getRoot() { return (ParticleTreeElement*)_rootNode; }
|
||||
|
@ -42,6 +42,7 @@ public:
|
|||
void storeParticle(const Particle& particle, Node* senderNode = NULL);
|
||||
const Particle* findClosestParticle(glm::vec3 position, float targetRadius);
|
||||
const Particle* findParticleByID(uint32_t id, bool alreadyLocked = false);
|
||||
QVector<const Particle*> findParticles(const glm::vec3& center, float radius);
|
||||
|
||||
void addNewlyCreatedHook(NewlyCreatedParticleHook* hook);
|
||||
void removeNewlyCreatedHook(NewlyCreatedParticleHook* hook);
|
||||
|
@ -58,6 +59,7 @@ private:
|
|||
static bool updateOperation(OctreeElement* element, void* extraData);
|
||||
static bool findAndUpdateOperation(OctreeElement* element, void* extraData);
|
||||
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
||||
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
||||
static bool pruneOperation(OctreeElement* element, void* extraData);
|
||||
static bool findByIDOperation(OctreeElement* element, void* extraData);
|
||||
static bool findAndDeleteOperation(OctreeElement* element, void* extraData);
|
||||
|
|
|
@ -25,7 +25,7 @@ ParticleTreeElement::~ParticleTreeElement() {
|
|||
// own type to our own tree. This means we should initialize that child with any tree and type
|
||||
// specific settings that our children must have. One example is out VoxelSystem, which
|
||||
// we know must match ours.
|
||||
OctreeElement* ParticleTreeElement::createNewElement(unsigned char* octalCode) const {
|
||||
OctreeElement* ParticleTreeElement::createNewElement(unsigned char* octalCode) {
|
||||
ParticleTreeElement* newChild = new ParticleTreeElement(octalCode);
|
||||
newChild->setTree(_myTree);
|
||||
return newChild;
|
||||
|
@ -185,6 +185,23 @@ const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) cons
|
|||
return closestParticle;
|
||||
}
|
||||
|
||||
QVector<const Particle*> ParticleTreeElement::getParticles(glm::vec3 searchPosition, float searchRadius) const {
|
||||
QVector<const Particle*> results;
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
const Particle* particle = &(*_particles)[i];
|
||||
glm::vec3 particlePosition = particle->getPosition();
|
||||
float particleRadius = particle->getRadius();
|
||||
glm::vec3 penetration;
|
||||
|
||||
// check to see that the particle (penetrator) penetrates the search area
|
||||
if (findSphereSpherePenetration(particlePosition, particleRadius, searchPosition, searchRadius, penetration)) {
|
||||
results << particle;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
const Particle* ParticleTreeElement::getParticleWithID(uint32_t id) const {
|
||||
// NOTE: this lookup is O(N) but maybe we don't care? (guaranteed that num particles per elemen is small?)
|
||||
const Particle* foundParticle = NULL;
|
||||
|
|
|
@ -31,7 +31,7 @@ class ParticleTreeElement : public OctreeElement {
|
|||
|
||||
ParticleTreeElement(unsigned char* octalCode = NULL);
|
||||
|
||||
virtual OctreeElement* createNewElement(unsigned char* octalCode = NULL) const;
|
||||
virtual OctreeElement* createNewElement(unsigned char* octalCode = NULL);
|
||||
|
||||
public:
|
||||
virtual ~ParticleTreeElement();
|
||||
|
@ -89,6 +89,7 @@ public:
|
|||
bool containsParticle(const Particle& particle) const;
|
||||
bool updateParticle(const Particle& particle);
|
||||
const Particle* getClosestParticle(glm::vec3 position) const;
|
||||
QVector<const Particle*> getParticles(glm::vec3 position, float radius) const;
|
||||
const Particle* getParticleWithID(uint32_t id) const;
|
||||
|
||||
bool removeParticleWithID(uint32_t id);
|
||||
|
|
|
@ -34,15 +34,44 @@ ParticleID ParticlesScriptingInterface::addParticle(const ParticleProperties& pr
|
|||
return id;
|
||||
}
|
||||
|
||||
void ParticlesScriptingInterface::editParticle(ParticleID particleID, const ParticleProperties& properties) {
|
||||
ParticleID ParticlesScriptingInterface::identifyParticle(ParticleID particleID) {
|
||||
uint32_t actualID = particleID.id;
|
||||
if (!particleID.isKnownID) {
|
||||
actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID);
|
||||
// hmmm... we kind of want to bail if someone attempts to edit an unknown
|
||||
if (actualID == UNKNOWN_PARTICLE_ID) {
|
||||
//qDebug() << "ParticlesScriptingInterface::editParticle()... BAILING!!! particleID.creatorTokenID="
|
||||
// << particleID.creatorTokenID;
|
||||
return; // bailing early
|
||||
return particleID; // bailing early
|
||||
}
|
||||
|
||||
// found it!
|
||||
particleID.id = actualID;
|
||||
particleID.isKnownID = true;
|
||||
}
|
||||
return particleID;
|
||||
}
|
||||
|
||||
ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID particleID) {
|
||||
ParticleProperties results;
|
||||
ParticleID identity = identifyParticle(particleID);
|
||||
if (!identity.isKnownID) {
|
||||
results.setIsUnknownID();
|
||||
return results;
|
||||
}
|
||||
if (_particleTree) {
|
||||
const Particle* particle = _particleTree->findParticleByID(identity.id);
|
||||
results.copyFromParticle(*particle);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParticleID ParticlesScriptingInterface::editParticle(ParticleID particleID, const ParticleProperties& properties) {
|
||||
uint32_t actualID = particleID.id;
|
||||
if (!particleID.isKnownID) {
|
||||
actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID);
|
||||
if (actualID == UNKNOWN_PARTICLE_ID) {
|
||||
return particleID; // bailing early
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,6 +100,7 @@ void ParticlesScriptingInterface::editParticle(ParticleID particleID, const Part
|
|||
}
|
||||
}
|
||||
queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties);
|
||||
return particleID;
|
||||
}
|
||||
|
||||
|
||||
|
@ -116,3 +146,17 @@ ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& cen
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
QVector<ParticleID> ParticlesScriptingInterface::findParticles(const glm::vec3& center, float radius) const {
|
||||
QVector<ParticleID> result;
|
||||
if (_particleTree) {
|
||||
QVector<const Particle*> particles = _particleTree->findParticles(center/(float)TREE_SCALE, radius/(float)TREE_SCALE);
|
||||
|
||||
foreach (const Particle* particle, particles) {
|
||||
ParticleID thisParticleID(particle->getID(), UNKNOWN_TOKEN, true);
|
||||
result << thisParticleID;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,11 +28,33 @@ public:
|
|||
ParticleTree* getParticleTree(ParticleTree*) { return _particleTree; }
|
||||
|
||||
public slots:
|
||||
/// adds a particle with the specific properties
|
||||
ParticleID addParticle(const ParticleProperties& properties);
|
||||
void editParticle(ParticleID particleID, const ParticleProperties& properties);
|
||||
|
||||
/// identify a recently created particle to determine its true ID
|
||||
ParticleID identifyParticle(ParticleID particleID);
|
||||
|
||||
/// gets the current particle properties for a specific particle
|
||||
/// this function will not find return results in script engine contexts which don't have access to particles
|
||||
ParticleProperties getParticleProperties(ParticleID particleID);
|
||||
|
||||
/// edits a particle updating only the included properties, will return the identified ParticleID in case of
|
||||
/// successful edit, if the input particleID is for an unknown particle this function will have no effect
|
||||
ParticleID editParticle(ParticleID particleID, const ParticleProperties& properties);
|
||||
|
||||
/// deletes a particle
|
||||
void deleteParticle(ParticleID particleID);
|
||||
|
||||
/// finds the closest particle to the center point, within the radius
|
||||
/// will return a ParticleID.isKnownID = false if no particles are in the radius
|
||||
/// this function will not find any particles in script engine contexts which don't have access to particles
|
||||
ParticleID findClosestParticle(const glm::vec3& center, float radius) const;
|
||||
|
||||
/// finds particles within the search sphere specified by the center point and radius
|
||||
/// this function will not find any particles in script engine contexts which don't have access to particles
|
||||
QVector<ParticleID> findParticles(const glm::vec3& center, float radius) const;
|
||||
|
||||
|
||||
private:
|
||||
void queueParticleMessage(PACKET_TYPE packetType, ParticleID particleID, const ParticleProperties& properties);
|
||||
|
||||
|
|
|
@ -118,6 +118,7 @@ void ScriptEngine::init() {
|
|||
registerMetaTypes(&_engine);
|
||||
qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue);
|
||||
qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue);
|
||||
qScriptRegisterSequenceMetaType<QVector<ParticleID> >(&_engine);
|
||||
|
||||
QScriptValue agentValue = _engine.newQObject(this);
|
||||
_engine.globalObject().setProperty("Agent", agentValue);
|
||||
|
|
|
@ -81,7 +81,7 @@ bool HifiSockAddr::operator==(const HifiSockAddr &rhsSockAddr) const {
|
|||
|
||||
QDebug operator<<(QDebug debug, const HifiSockAddr &hifiSockAddr) {
|
||||
debug.nospace() << hifiSockAddr._address.toString().toLocal8Bit().constData() << ":" << hifiSockAddr._port;
|
||||
return debug;
|
||||
return debug.space();
|
||||
}
|
||||
|
||||
quint32 getHostOrderLocalAddress() {
|
||||
|
|
|
@ -39,7 +39,7 @@ Node::Node(const QUuid& uuid, char type, const HifiSockAddr& publicSocket, const
|
|||
|
||||
Node::~Node() {
|
||||
if (_linkedData) {
|
||||
_linkedData->deleteOrDeleteLater();
|
||||
delete _linkedData;
|
||||
}
|
||||
|
||||
delete _bytesReceivedMovingAverage;
|
||||
|
|
|
@ -8,16 +8,6 @@
|
|||
|
||||
#include "NodeData.h"
|
||||
|
||||
NodeData::NodeData(Node* owningNode) :
|
||||
_owningNode(owningNode)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
NodeData::~NodeData() {
|
||||
|
||||
}
|
||||
|
||||
void NodeData::deleteOrDeleteLater() {
|
||||
delete this;
|
||||
|
||||
}
|
|
@ -16,16 +16,9 @@ class Node;
|
|||
class NodeData : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeData(Node* owningNode = NULL);
|
||||
|
||||
virtual ~NodeData() = 0;
|
||||
virtual int parseData(unsigned char* sourceBuffer, int numBytes) = 0;
|
||||
|
||||
virtual void deleteOrDeleteLater();
|
||||
|
||||
Node* getOwningNode() { return _owningNode; }
|
||||
protected:
|
||||
Node* _owningNode;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,7 +27,7 @@ const char SOLO_NODE_TYPES[2] = {
|
|||
NODE_TYPE_AUDIO_MIXER
|
||||
};
|
||||
|
||||
const QString DEFAULT_DOMAIN_HOSTNAME = "root.highfidelity.io";
|
||||
const QString DEFAULT_DOMAIN_HOSTNAME = "alpha.highfidelity.io";
|
||||
const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102;
|
||||
|
||||
NodeList* NodeList::_sharedInstance = NULL;
|
||||
|
@ -178,53 +178,6 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, unsigned char
|
|||
processSTUNResponse(packetData, dataBytes);
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_KILL_NODE: {
|
||||
processKillNode(packetData, dataBytes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::processBulkNodeData(const HifiSockAddr& senderAddress, unsigned char *packetData, int numTotalBytes) {
|
||||
SharedNodePointer bulkSendNode = nodeWithAddress(senderAddress);
|
||||
|
||||
// find the avatar mixer in our node list and update the lastRecvTime from it
|
||||
if (bulkSendNode) {
|
||||
|
||||
bulkSendNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
bulkSendNode->recordBytesReceived(numTotalBytes);
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
|
||||
unsigned char* startPosition = packetData;
|
||||
unsigned char* currentPosition = startPosition + numBytesPacketHeader;
|
||||
unsigned char* packetHolder = new unsigned char[numTotalBytes];
|
||||
|
||||
// we've already verified packet version for the bulk packet, so all head data in the packet is also up to date
|
||||
populateTypeAndVersion(packetHolder, PACKET_TYPE_HEAD_DATA);
|
||||
|
||||
while ((currentPosition - startPosition) < numTotalBytes) {
|
||||
|
||||
memcpy(packetHolder + numBytesPacketHeader,
|
||||
currentPosition,
|
||||
numTotalBytes - (currentPosition - startPosition));
|
||||
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)currentPosition, NUM_BYTES_RFC4122_UUID));
|
||||
SharedNodePointer matchingNode = nodeWithUUID(nodeUUID);
|
||||
|
||||
if (!matchingNode) {
|
||||
// we're missing this node, we need to add it to the list
|
||||
matchingNode = addOrUpdateNode(nodeUUID, NODE_TYPE_AGENT, HifiSockAddr(), HifiSockAddr());
|
||||
}
|
||||
|
||||
currentPosition += updateNodeWithData(matchingNode.data(),
|
||||
HifiSockAddr(),
|
||||
packetHolder,
|
||||
numTotalBytes - (currentPosition - startPosition));
|
||||
|
||||
}
|
||||
|
||||
delete[] packetHolder;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -460,30 +413,13 @@ NodeHash::iterator NodeList::killNodeAtHashIterator(NodeHash::iterator& nodeItem
|
|||
return _nodeHash.erase(nodeItemToKill);
|
||||
}
|
||||
|
||||
void NodeList::sendKillNode(const QSet<NODE_TYPE>& destinationNodeTypes) {
|
||||
unsigned char packet[MAX_PACKET_SIZE];
|
||||
unsigned char* packetPosition = packet;
|
||||
|
||||
packetPosition += populateTypeAndVersion(packetPosition, PACKET_TYPE_KILL_NODE);
|
||||
|
||||
QByteArray rfcUUID = _ownerUUID.toRfc4122();
|
||||
memcpy(packetPosition, rfcUUID.constData(), rfcUUID.size());
|
||||
packetPosition += rfcUUID.size();
|
||||
|
||||
broadcastToNodes(packet, packetPosition - packet, destinationNodeTypes);
|
||||
}
|
||||
|
||||
void NodeList::processKillNode(unsigned char* packetData, size_t dataBytes) {
|
||||
// skip the header
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
packetData += numBytesPacketHeader;
|
||||
dataBytes -= numBytesPacketHeader;
|
||||
|
||||
void NodeList::processKillNode(const QByteArray& dataByteArray) {
|
||||
// read the node id
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)packetData, NUM_BYTES_RFC4122_UUID));
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader(reinterpret_cast
|
||||
<const unsigned char*>(dataByteArray.data())),
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
packetData += NUM_BYTES_RFC4122_UUID;
|
||||
dataBytes -= NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
// kill the node with this UUID, if it exists
|
||||
killNodeWithUUID(nodeUUID);
|
||||
}
|
||||
|
|
|
@ -104,8 +104,6 @@ public:
|
|||
int fillPingPacket(unsigned char* buffer);
|
||||
int fillPingReplyPacket(unsigned char* pingBuffer, unsigned char* replyBuffer);
|
||||
void pingPublicAndLocalSocketsForInactiveNode(Node* node);
|
||||
|
||||
void sendKillNode(const QSet<NODE_TYPE>& destinationNodeTypes);
|
||||
|
||||
SharedNodePointer nodeWithAddress(const HifiSockAddr& senderSockAddr);
|
||||
SharedNodePointer nodeWithUUID(const QUuid& nodeUUID);
|
||||
|
@ -113,7 +111,8 @@ public:
|
|||
SharedNodePointer addOrUpdateNode(const QUuid& uuid, char nodeType, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket);
|
||||
|
||||
void processNodeData(const HifiSockAddr& senderSockAddr, unsigned char *packetData, size_t dataBytes);
|
||||
void processBulkNodeData(const HifiSockAddr& senderSockAddr, unsigned char *packetData, int numTotalBytes);
|
||||
|
||||
void processKillNode(const QByteArray& datagram);
|
||||
|
||||
int updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, unsigned char *packetData, int dataBytes);
|
||||
|
||||
|
@ -144,7 +143,6 @@ private:
|
|||
void sendSTUNRequest();
|
||||
void processSTUNResponse(unsigned char* packetData, size_t dataBytes);
|
||||
|
||||
void processKillNode(unsigned char* packetData, size_t dataBytes);
|
||||
NodeHash::iterator killNodeAtHashIterator(NodeHash::iterator& nodeItemToKill);
|
||||
|
||||
NodeHash _nodeHash;
|
||||
|
|
|
@ -20,7 +20,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
|||
return 2;
|
||||
|
||||
case PACKET_TYPE_HEAD_DATA:
|
||||
return 16;
|
||||
return 17;
|
||||
|
||||
case PACKET_TYPE_OCTREE_STATS:
|
||||
return 2;
|
||||
|
@ -64,13 +64,15 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
|||
}
|
||||
}
|
||||
|
||||
bool packetVersionMatch(unsigned char* packetHeader) {
|
||||
bool packetVersionMatch(unsigned char* packetHeader, const HifiSockAddr& senderSockAddr) {
|
||||
// currently this just checks if the version in the packet matches our return from versionForPacketType
|
||||
// may need to be expanded in the future for types and versions that take > than 1 byte
|
||||
if (packetHeader[1] == versionForPacketType(packetHeader[0]) || packetHeader[0] == PACKET_TYPE_STUN_RESPONSE) {
|
||||
return true;
|
||||
} else {
|
||||
qDebug("There is a packet version mismatch for packet with header %c", packetHeader[0]);
|
||||
qDebug() << "Packet mismatch on" << packetHeader[0] << "- Sender"
|
||||
<< senderSockAddr << "sent" << qPrintable(QString::number(packetHeader[1])) << "but"
|
||||
<< qPrintable(QString::number(versionForPacketType(packetHeader[0]))) << "expected.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,13 +12,15 @@
|
|||
#ifndef hifi_PacketHeaders_h
|
||||
#define hifi_PacketHeaders_h
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
typedef char PACKET_TYPE;
|
||||
const PACKET_TYPE PACKET_TYPE_UNKNOWN = 0;
|
||||
const PACKET_TYPE PACKET_TYPE_STUN_RESPONSE = 1;
|
||||
const PACKET_TYPE PACKET_TYPE_DOMAIN = 'D';
|
||||
const PACKET_TYPE PACKET_TYPE_PING = 'P';
|
||||
const PACKET_TYPE PACKET_TYPE_PING_REPLY = 'R';
|
||||
const PACKET_TYPE PACKET_TYPE_KILL_NODE = 'K';
|
||||
const PACKET_TYPE PACKET_TYPE_KILL_AVATAR = 'K';
|
||||
const PACKET_TYPE PACKET_TYPE_HEAD_DATA = 'H';
|
||||
const PACKET_TYPE PACKET_TYPE_INJECT_AUDIO = 'I';
|
||||
const PACKET_TYPE PACKET_TYPE_MIXED_AUDIO = 'A';
|
||||
|
@ -55,7 +57,7 @@ typedef char PACKET_VERSION;
|
|||
|
||||
PACKET_VERSION versionForPacketType(PACKET_TYPE type);
|
||||
|
||||
bool packetVersionMatch(unsigned char* packetHeader);
|
||||
bool packetVersionMatch(unsigned char* packetHeader, const HifiSockAddr& senderSockAddr);
|
||||
|
||||
int populateTypeAndVersion(unsigned char* destinationHeader, PACKET_TYPE type);
|
||||
int numBytesForPacketHeader(const unsigned char* packetHeader);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
class VoxelNodeData : public OctreeQueryNode {
|
||||
public:
|
||||
VoxelNodeData(Node* owningNode) : OctreeQueryNode(owningNode) { };
|
||||
VoxelNodeData() : OctreeQueryNode() { };
|
||||
virtual PACKET_TYPE getMyPacketType() const { return PACKET_TYPE_VOXEL_DATA; }
|
||||
};
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@ VoxelServer::~VoxelServer() {
|
|||
// nothing special to do here...
|
||||
}
|
||||
|
||||
OctreeQueryNode* VoxelServer::createOctreeQueryNode(Node* newNode) {
|
||||
return new VoxelNodeData(newNode);
|
||||
OctreeQueryNode* VoxelServer::createOctreeQueryNode() {
|
||||
return new VoxelNodeData();
|
||||
}
|
||||
|
||||
Octree* VoxelServer::createTree() {
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
int getEnvironmentDataCount() const { return sizeof(_environmentData)/sizeof(EnvironmentData); }
|
||||
|
||||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode);
|
||||
virtual OctreeQueryNode* createOctreeQueryNode();
|
||||
virtual Octree* createTree();
|
||||
virtual unsigned char getMyNodeType() const { return NODE_TYPE_VOXEL_SERVER; }
|
||||
virtual PACKET_TYPE getMyQueryMessageType() const { return PACKET_TYPE_VOXEL_QUERY; }
|
||||
|
|
|
@ -6,9 +6,4 @@
|
|||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "VoxelQuery.h"
|
||||
|
||||
VoxelQuery::VoxelQuery(Node* owningNode) : OctreeQuery(owningNode) {
|
||||
}
|
||||
|
||||
|
||||
#include "VoxelQuery.h"
|
|
@ -14,10 +14,8 @@
|
|||
class VoxelQuery : public OctreeQuery {
|
||||
|
||||
public:
|
||||
VoxelQuery(Node* owningNode = NULL);
|
||||
|
||||
// currently just an alias
|
||||
|
||||
VoxelQuery() : OctreeQuery() {};
|
||||
private:
|
||||
// privatize the copy constructor and assignment operator so they cannot be called
|
||||
VoxelQuery(const VoxelQuery&);
|
||||
|
|
|
@ -24,7 +24,7 @@ VoxelTree::VoxelTree(bool shouldReaverage) : Octree(shouldReaverage) {
|
|||
_rootNode = createNewElement();
|
||||
}
|
||||
|
||||
VoxelTreeElement* VoxelTree::createNewElement(unsigned char * octalCode) const {
|
||||
VoxelTreeElement* VoxelTree::createNewElement(unsigned char * octalCode) {
|
||||
VoxelSystem* voxelSystem = NULL;
|
||||
if (_rootNode) {
|
||||
voxelSystem = ((VoxelTreeElement*)_rootNode)->getVoxelSystem();
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
|
||||
VoxelTree(bool shouldReaverage = false);
|
||||
|
||||
virtual VoxelTreeElement* createNewElement(unsigned char * octalCode = NULL) const;
|
||||
virtual VoxelTreeElement* createNewElement(unsigned char * octalCode = NULL);
|
||||
VoxelTreeElement* getRoot() { return (VoxelTreeElement*)_rootNode; }
|
||||
|
||||
void deleteVoxelAt(float x, float y, float z, float s);
|
||||
|
|
|
@ -25,7 +25,7 @@ VoxelTreeElement::~VoxelTreeElement() {
|
|||
// own type to our own tree. This means we should initialize that child with any tree and type
|
||||
// specific settings that our children must have. One example is out VoxelSystem, which
|
||||
// we know must match ours.
|
||||
OctreeElement* VoxelTreeElement::createNewElement(unsigned char* octalCode) const {
|
||||
OctreeElement* VoxelTreeElement::createNewElement(unsigned char* octalCode) {
|
||||
VoxelTreeElement* newChild = new VoxelTreeElement(octalCode);
|
||||
newChild->setVoxelSystem(getVoxelSystem()); // our child is always part of our voxel system NULL ok
|
||||
return newChild;
|
||||
|
|
|
@ -31,7 +31,7 @@ class VoxelTreeElement : public OctreeElement {
|
|||
|
||||
VoxelTreeElement(unsigned char* octalCode = NULL);
|
||||
|
||||
virtual OctreeElement* createNewElement(unsigned char* octalCode = NULL) const;
|
||||
virtual OctreeElement* createNewElement(unsigned char* octalCode = NULL);
|
||||
|
||||
public:
|
||||
virtual ~VoxelTreeElement();
|
||||
|
|
Loading…
Reference in a new issue