first cut at particle renderer cleanup particle jurisdiction listener fix storage of particles

This commit is contained in:
ZappoMan 2013-12-09 11:45:32 -08:00
parent 4df2f2189d
commit 2ca628fdf1
17 changed files with 333 additions and 9 deletions

View file

@ -65,7 +65,10 @@ void Agent::run() {
NodeList* nodeList = NodeList::getInstance();
nodeList->setOwnerType(NODE_TYPE_AGENT);
const char AGENT_NODE_TYPES_OF_INTEREST[1] = { NODE_TYPE_VOXEL_SERVER };
// XXXBHG - this seems less than ideal. There might be classes (like jurisdiction listeners, that need access to
// other node types, but for them to get access to those node types, we have to add them here. It seems like
// NodeList should support adding types of interest
const NODE_TYPE AGENT_NODE_TYPES_OF_INTEREST[] = { NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER };
nodeList->setNodeTypesOfInterest(AGENT_NODE_TYPES_OF_INTEREST, sizeof(AGENT_NODE_TYPES_OF_INTEREST));

View file

@ -51,6 +51,9 @@ DomainServer::DomainServer(int argc, char* argv[]) :
const char VOXEL_CONFIG_OPTION[] = "--voxelServerConfig";
_voxelServerConfig = getCmdOption(argc, (const char**) argv, VOXEL_CONFIG_OPTION);
const char PARTICLE_CONFIG_OPTION[] = "--particleServerConfig";
_particleServerConfig = getCmdOption(argc, (const char**) argv, PARTICLE_CONFIG_OPTION);
// setup the mongoose web server
struct mg_callbacks callbacks = {};

View file

@ -84,6 +84,7 @@ include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})

View file

@ -3004,6 +3004,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
_voxels.render(Menu::getInstance()->isOptionChecked(MenuOption::VoxelTextures));
}
}
// render particles...
_particles.render();
// restore default, white specular
glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR);
@ -4217,6 +4220,12 @@ void* Application::networkReceive(void* args) {
QMetaObject::invokeMethod(&app->_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection,
Q_ARG(QByteArray, QByteArray((char*) app->_incomingPacket, bytesReceived)));
break;
case PACKET_TYPE_PARTICLE_DATA: {
app->_particles.processDatagram(QByteArray((char*) app->_incomingPacket, bytesReceived),
senderSockAddr);
break;
}
case PACKET_TYPE_VOXEL_DATA:
case PACKET_TYPE_VOXEL_ERASE:
case PACKET_TYPE_OCTREE_STATS:

View file

@ -63,6 +63,7 @@
#include "ui/VoxelStatsDialog.h"
#include "ui/RearMirrorTools.h"
#include "ui/LodToolsDialog.h"
#include "ParticleTreeRenderer.h"
class QAction;
class QActionGroup;
@ -341,6 +342,8 @@ private:
VoxelImporter _voxelImporter;
VoxelSystem _sharedVoxelSystem;
ViewFrustum _sharedVoxelSystemViewFrustum;
ParticleTreeRenderer _particles;
QByteArray _voxelsFilename;
bool _wantToKillLocalVoxels;

View file

@ -0,0 +1,37 @@
//
// ParticleTreeRenderer.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#include "InterfaceConfig.h"
#include "ParticleTreeRenderer.h"
ParticleTreeRenderer::ParticleTreeRenderer() :
OctreeRenderer() {
}
ParticleTreeRenderer::~ParticleTreeRenderer() {
}
void ParticleTreeRenderer::renderElement(OctreeElement* element) {
// actually render it here...
// we need to iterate the actual particles of the element
ParticleTreeElement* particleTreeElement = (ParticleTreeElement*)element;
const std::vector<Particle>& particles = particleTreeElement->getParticles();
uint16_t numberOfParticles = particles.size();
glBegin(GL_POINTS);
for (uint16_t i = 0; i < numberOfParticles; i++) {
const Particle& particle = particles[i];
// render particle aspoints
glVertex3f(particle.getPosition().x, particle.getPosition().y, particle.getPosition().z);
}
glEnd();
}

View file

@ -0,0 +1,39 @@
//
// ParticleTreeRenderer.h
// hifi
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __hifi__ParticleTreeRenderer__
#define __hifi__ParticleTreeRenderer__
#include <glm/glm.hpp>
#include <stdint.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <Octree.h>
#include <OctreePacketData.h>
#include <OctreeRenderer.h>
#include <ParticleTree.h>
#include <ViewFrustum.h>
// Generic client side Octree renderer class.
class ParticleTreeRenderer : public OctreeRenderer {
public:
ParticleTreeRenderer();
virtual ~ParticleTreeRenderer();
virtual Octree* createTree() { return new ParticleTree(true); }
virtual NODE_TYPE getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
virtual PACKET_TYPE getMyQueryMessageType() const { return PACKET_TYPE_PARTICLE_QUERY; }
virtual PACKET_TYPE getExpectedPacketType() const { return PACKET_TYPE_PARTICLE_DATA; }
virtual void renderElement(OctreeElement* element);
protected:
};
#endif /* defined(__hifi__ParticleTreeRenderer__) */

View file

@ -42,7 +42,7 @@ void OctreeInboundPacketProcessor::resetStats() {
void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr,
unsigned char* packetData, ssize_t packetLength) {
bool debugProcessPacket = _myServer->wantsVerboseDebug();
bool debugProcessPacket = true; //_myServer->wantsVerboseDebug();
if (debugProcessPacket) {
printf("OctreeInboundPacketProcessor::processPacket() packetData=%p packetLength=%ld\n", packetData, packetLength);
@ -66,8 +66,9 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA
uint64_t lockWaitTime = 0;
if (_myServer->wantsDebugReceiving()) {
printf("PROCESSING THREAD: got %c - %d command from client receivedBytes=%ld sequence=%d transitTime=%llu usecs\n",
packetType, _receivedPacketCount, packetLength, sequence, transitTime);
printf("PROCESSING THREAD: got '%c' packet - %d command from client "
"receivedBytes=%ld sequence=%d transitTime=%llu usecs\n",
packetType, _receivedPacketCount, packetLength, sequence, transitTime);
}
int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt);
unsigned char* editData = (unsigned char*)&packetData[atByte];

View file

@ -16,12 +16,16 @@
#include "JurisdictionListener.h"
JurisdictionListener::JurisdictionListener(PacketSenderNotify* notify) :
JurisdictionListener::JurisdictionListener(NODE_TYPE type, PacketSenderNotify* notify) :
PacketSender(notify, JurisdictionListener::DEFAULT_PACKETS_PER_SECOND)
{
_nodeType = type;
ReceivedPacketProcessor::_dontSleep = true; // we handle sleeping so this class doesn't need to
NodeList* nodeList = NodeList::getInstance();
nodeList->addHook(this);
qDebug("JurisdictionListener::JurisdictionListener(NODE_TYPE type=%c)\n", type);
}
JurisdictionListener::~JurisdictionListener() {
@ -40,6 +44,8 @@ void JurisdictionListener::nodeKilled(Node* node) {
}
bool JurisdictionListener::queueJurisdictionRequest() {
qDebug() << "JurisdictionListener::queueJurisdictionRequest()\n";
static unsigned char buffer[MAX_PACKET_SIZE];
unsigned char* bufferOut = &buffer[0];
ssize_t sizeOut = populateTypeAndVersion(bufferOut, PACKET_TYPE_JURISDICTION_REQUEST);
@ -47,7 +53,11 @@ bool JurisdictionListener::queueJurisdictionRequest() {
NodeList* nodeList = NodeList::getInstance();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (nodeList->getNodeActiveSocketOrPing(&(*node)) && node->getType() == NODE_TYPE_VOXEL_SERVER) {
qDebug() << "Hello..." << *node << "\n";
if (nodeList->getNodeActiveSocketOrPing(&(*node)) &&
node->getType() == getNodeType()) {
const HifiSockAddr* nodeAddress = node->getActiveSocket();
PacketSender::queuePacketForSending(*nodeAddress, bufferOut, sizeOut);
nodeCount++;

View file

@ -26,7 +26,7 @@ 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
JurisdictionListener(PacketSenderNotify* notify = NULL);
JurisdictionListener(NODE_TYPE type = NODE_TYPE_VOXEL_SERVER, PacketSenderNotify* notify = NULL);
~JurisdictionListener();
virtual bool process();
@ -38,6 +38,9 @@ public:
/// Called by NodeList to inform us that a node has been killed.
void nodeKilled(Node* node);
NODE_TYPE getNodeType() const { return _nodeType; }
void setNodeType(NODE_TYPE type) { _nodeType = type; }
protected:
/// Callback for processing of received packets. Will process any queued PACKET_TYPE_JURISDICTION and update the
/// jurisdiction map member variable
@ -49,6 +52,7 @@ protected:
private:
NodeToJurisdictionMap _jurisdictions;
NODE_TYPE _nodeType;
bool queueJurisdictionRequest();
};

View file

@ -0,0 +1,129 @@
//
// OctreeRenderer.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#include <glm/glm.hpp>
#include <stdint.h>
#include <SharedUtil.h>
#include <PerfStat.h>
#include "OctreeRenderer.h"
OctreeRenderer::OctreeRenderer() {
}
OctreeRenderer::~OctreeRenderer() {
}
void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
bool showTimingDetails = false; // Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
bool extraDebugging = false; // Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)
PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram()",showTimingDetails);
const unsigned char* packetData = (const unsigned char*)dataByteArray.constData();
int packetLength = dataByteArray.size();
unsigned char command = *packetData;
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
PACKET_TYPE expectedType = getExpectedPacketType();
if(command == expectedType) {
PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PACKET_TYPE",showTimingDetails);
const unsigned char* dataAt = packetData + numBytesPacketHeader;
OCTREE_PACKET_FLAGS flags = (*(OCTREE_PACKET_FLAGS*)(dataAt));
dataAt += sizeof(OCTREE_PACKET_FLAGS);
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
OCTREE_PACKET_SENT_TIME sentAt = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
bool packetIsColored = oneAtBit(flags, PACKET_IS_COLOR_BIT);
bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT);
OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
int flightTime = arrivedAt - sentAt;
OCTREE_PACKET_INTERNAL_SECTION_SIZE sectionLength = 0;
int dataBytes = packetLength - OCTREE_PACKET_HEADER_SIZE;
int subsection = 1;
while (dataBytes > 0) {
if (packetIsCompressed) {
if (dataBytes > sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE)) {
sectionLength = (*(OCTREE_PACKET_INTERNAL_SECTION_SIZE*)dataAt);
dataAt += sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
dataBytes -= sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
} else {
sectionLength = 0;
dataBytes = 0; // stop looping something is wrong
}
} else {
sectionLength = dataBytes;
}
if (sectionLength) {
// ask the VoxelTree to read the bitstream into the tree
ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL,
getDataSourceUUID());
_tree->lockForWrite();
OctreePacketData packetData(packetIsCompressed);
packetData.loadFinalizedContent(dataAt, sectionLength);
if (extraDebugging) {
qDebug("OctreeRenderer::processDatagram() ... Got Packet Section"
" color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d"
" subsection:%d sectionLength:%d uncompressed:%d\n",
debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed),
sequence, flightTime, packetLength, dataBytes, subsection, sectionLength, packetData.getUncompressedSize());
}
_tree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args);
_tree->unlock();
dataBytes -= sectionLength;
dataAt += sectionLength;
}
}
subsection++;
}
// setup rendering for the new items...
//setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY);
// handle bandwidth meter stuff??? seems like this would be better done in application...
//Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::VOXELS).updateValue(numBytes);
}
void OctreeRenderer::init() {
}
class RenderArgs {
public:
OctreeRenderer* _renderer;
ViewFrustum* _viewFrustum;
};
bool OctreeRenderer::renderOperation(OctreeElement* element, void* extraData) {
RenderArgs* args = static_cast<RenderArgs*>(extraData);
if (element->isInView(*args->_viewFrustum)) {
if (element->hasContent()) {
args->_renderer->renderElement(element);
}
return true;
}
// if not in view stop recursing
return false;
}
void OctreeRenderer::render() {
RenderArgs args = { this, _viewFrustum };
_tree->recurseTreeWithOperation(renderOperation, &args);
}

View file

@ -0,0 +1,57 @@
//
// OctreeRenderer.h
// hifi
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __hifi__OctreeRenderer__
#define __hifi__OctreeRenderer__
#include <glm/glm.hpp>
#include <stdint.h>
#include <NodeTypes.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include "Octree.h"
#include "OctreePacketData.h"
#include "ViewFrustum.h"
// Generic client side Octree renderer class.
class OctreeRenderer {
public:
OctreeRenderer();
virtual ~OctreeRenderer();
virtual Octree* createTree() = 0;
virtual NODE_TYPE getMyNodeType() const = 0;
virtual PACKET_TYPE getMyQueryMessageType() const = 0;
virtual PACKET_TYPE getExpectedPacketType() const = 0;
virtual void renderElement(OctreeElement* element) = 0;
/// process incoming data
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
/// initialize and GPU/rendering related resources
void init();
/// render the content of the octree
virtual void render();
void setDataSourceUUID(const QUuid& dataSourceUUID) { _dataSourceUUID = dataSourceUUID; }
const QUuid& getDataSourceUUID() const { return _dataSourceUUID; }
ViewFrustum* getViewFrustum() const { return _viewFrustum; }
void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; }
static bool renderOperation(OctreeElement* element, void* extraData);
protected:
Octree* _tree;
QUuid _dataSourceUUID;
ViewFrustum* _viewFrustum;
};
#endif /* defined(__hifi__OctreeRenderer__) */

View file

@ -8,7 +8,9 @@
#include "ParticleScriptingInterface.h"
ParticleScriptingInterface::ParticleScriptingInterface() {
ParticleScriptingInterface::ParticleScriptingInterface() :
_jurisdictionListener(NODE_TYPE_PARTICLE_SERVER)
{
_jurisdictionListener.initialize(true);
_particlePacketSender.setServerJurisdictions(_jurisdictionListener.getJurisdictions());
}

View file

@ -31,7 +31,15 @@ void ParticleTree::storeParticle(const Particle& particle) {
glm::vec3 position = particle.getPosition();
float size = particle.getRadius();
ParticleTreeElement* element = (ParticleTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size);
printf("ParticleTree::storeParticle() element=%p particle.getPosition()=%f,%f,%f\n",
element,
particle.getPosition().x, particle.getPosition().y, particle.getPosition().z);
element->storeParticle(particle);
// what else do we need to do here to get reaveraging to work
_isDirty = true;
}
int ParticleTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
@ -41,7 +49,12 @@ int ParticleTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* p
// we handle these types of "edit" packets
switch (packetType) {
case PACKET_TYPE_PARTICLE_ADD: {
printf("got PACKET_TYPE_PARTICLE_ADD....\n");
Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes);
printf("newParticle...getPosition()=%f,%f,%f\n", newParticle.getPosition().x, newParticle.getPosition().y, newParticle.getPosition().z);
storeParticle(newParticle);
// It seems like we need some way to send the ID back to the creator??

View file

@ -78,7 +78,9 @@ int ParticleTreeElement::readElementDataFromBuffer(const unsigned char* data, in
if (bytesLeftToRead >= (numberOfParticles * expectedBytesPerParticle)) {
for (uint16_t i = 0; i < numberOfParticles; i++) {
int bytesForThisParticle = _particles[i].readParticleDataFromBuffer(dataAt, bytesLeftToRead, args);
Particle tempParticle;
int bytesForThisParticle = tempParticle.readParticleDataFromBuffer(dataAt, bytesLeftToRead, args);
_particles.push_back(tempParticle);
dataAt += bytesForThisParticle;
bytesLeftToRead -= bytesForThisParticle;
bytesRead += bytesForThisParticle;
@ -105,5 +107,11 @@ bool ParticleTreeElement::collapseChildren() {
void ParticleTreeElement::storeParticle(const Particle& particle) {
_particles.push_back(particle);
markWithChangedTime();
printf("ParticleTreeElement::storeParticle() element=%p _particles.size()=%ld particle.getPosition()=%f,%f,%f\n",
this, _particles.size(),
particle.getPosition().x, particle.getPosition().y, particle.getPosition().z);
}

View file

@ -69,6 +69,8 @@ public:
/// shouldRender() state, the tree will remark elements as changed even in cases there the elements have not changed.
virtual bool isRendered() const { return getShouldRender(); }
const std::vector<Particle>& getParticles() const { return _particles; }
protected:
void storeParticle(const Particle& particle);

View file

@ -50,6 +50,7 @@ Node::~Node() {
// Names of Node Types
const char* NODE_TYPE_NAME_DOMAIN = "Domain";
const char* NODE_TYPE_NAME_VOXEL_SERVER = "Voxel Server";
const char* NODE_TYPE_NAME_PARTICLE_SERVER = "Particle Server";
const char* NODE_TYPE_NAME_AGENT = "Agent";
const char* NODE_TYPE_NAME_AUDIO_MIXER = "Audio Mixer";
const char* NODE_TYPE_NAME_AVATAR_MIXER = "Avatar Mixer";
@ -64,6 +65,8 @@ const char* Node::getTypeName() const {
return NODE_TYPE_NAME_DOMAIN;
case NODE_TYPE_VOXEL_SERVER:
return NODE_TYPE_NAME_VOXEL_SERVER;
case NODE_TYPE_PARTICLE_SERVER:
return NODE_TYPE_NAME_PARTICLE_SERVER;
case NODE_TYPE_AGENT:
return NODE_TYPE_NAME_AGENT;
case NODE_TYPE_AUDIO_MIXER: