first pass at adding actual particles to the ParticleTree

This commit is contained in:
ZappoMan 2013-12-05 12:07:52 -08:00
parent 50833cf04c
commit 86ffbc3d33
10 changed files with 434 additions and 30 deletions

View file

@ -178,8 +178,11 @@ public:
Octree(bool shouldReaverage = false);
~Octree();
/// Your tree class must implement this to create the correct element type
virtual OctreeElement* createNewElement(unsigned char * octalCode = NULL) const = 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
virtual bool handlesEditPacketType(PACKET_TYPE packetType) const { return false; }
virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
unsigned char* editData, int maxLength) { return 0; }

View file

@ -52,16 +52,52 @@ protected:
public:
virtual void init(unsigned char * octalCode); /// Your subclass must call init on construction.
virtual ~OctreeElement();
// methods you can and should override to implement your tree functionality
/// Adds a child to the current element. Override this if there is additional child initialization your class needs.
virtual OctreeElement* addChildAtIndex(int childIndex);
/// Override this to implement LOD averaging on changes to the tree.
virtual void calculateAverageFromChildren() { }
/// Override this to implement LOD collapsing and identical child pruning on changes to the tree.
virtual bool collapseChildren() { return false; }
/// Should this element be considered to have content in it. This will be used in collision and ray casting methods.
/// By default we assume that only leaves are actual content, but some octrees may have different semantics.
virtual bool hasContent() const { return isLeaf(); }
/// Override this to break up large octree elements when an edit operation is performed on a smaller octree element.
/// For example, if the octrees represent solid cubes and a delete of a smaller octree element is done then the
/// meaningful split would be to break the larger cube into smaller cubes of the same color/texture.
virtual void splitChildren() { }
/// Override to indicate that this element requires a split before editing lower elements in the octree
virtual bool requiresSplit() const { return false; }
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
virtual bool appendElementData(OctreePacketData* packetData) const { return true; }
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
/// from the network.
virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args)
{ return 0; }
/// Override to indicate that the item is currently rendered in the rendering engine. By default we assume that if
/// the element should be rendered, then your rendering engine is rendering. But some rendering engines my have cases
/// where an element is not actually rendering all should render elements. If the isRendered() state doesn't match the
/// shouldRender() state, the tree will remark elements as changed even in cases there the elements have not changed.
virtual bool isRendered() const { return getShouldRender(); }
// Base class methods you don't need to implement
const unsigned char* getOctalCode() const { return (_octcodePointer) ? _octalCode.pointer : &_octalCode.buffer[0]; }
OctreeElement* getChildAtIndex(int childIndex) const;
void deleteChildAtIndex(int childIndex);
OctreeElement* removeChildAtIndex(int childIndex);
virtual OctreeElement* addChildAtIndex(int childIndex);
void safeDeepDeleteChildAtIndex(int childIndex, int recursionCount = 0); // handles deletion of all descendents
virtual void calculateAverageFromChildren() { };
virtual bool collapseChildren() { return false; };
const AABox& getAABox() const { return _box; }
const glm::vec3& getCorner() const { return _box.getCorner(); }
@ -70,12 +106,6 @@ public:
float getEnclosingRadius() const;
virtual bool hasContent() const { return isLeaf(); }
virtual void splitChildren() { }
virtual bool requiresSplit() const { return false; }
virtual bool appendElementData(OctreePacketData* packetData) const { return true; }
virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args)
{ return 0; }
bool isInView(const ViewFrustum& viewFrustum) const;
ViewFrustum::location inFrustum(const ViewFrustum& viewFrustum) const;
@ -104,9 +134,6 @@ public:
void setShouldRender(bool shouldRender);
bool getShouldRender() const { return _shouldRender; }
/// we assume that if you should be rendered, then your subclass is rendering, but this allows subclasses to
/// implement alternate rendering strategies
virtual bool isRendered() const { return getShouldRender(); }
void setSourceUUID(const QUuid& sourceID);
QUuid getSourceUUID() const;

View file

@ -13,6 +13,9 @@ bool OctreePacketData::_debug = false;
uint64_t OctreePacketData::_totalBytesOfOctalCodes = 0;
uint64_t OctreePacketData::_totalBytesOfBitMasks = 0;
uint64_t OctreePacketData::_totalBytesOfColor = 0;
uint64_t OctreePacketData::_totalBytesOfValues = 0;
uint64_t OctreePacketData::_totalBytesOfPositions = 0;
uint64_t OctreePacketData::_totalBytesOfRawData = 0;
@ -210,14 +213,22 @@ bool OctreePacketData::appendBitMask(unsigned char bitmask) {
}
bool OctreePacketData::appendColor(const nodeColor& color) {
return appendColor(color[RED_INDEX], color[GREEN_INDEX], color[BLUE_INDEX]);
}
bool OctreePacketData::appendColor(const rgbColor& color) {
return appendColor(color[RED_INDEX], color[GREEN_INDEX], color[BLUE_INDEX]);
}
bool OctreePacketData::appendColor(colorPart red, colorPart green, colorPart blue) {
// eventually we can make this use a dictionary...
bool success = false;
const int BYTES_PER_COLOR = 3;
if (_bytesAvailable > BYTES_PER_COLOR) {
// handles checking compression...
if (append(color[RED_INDEX])) {
if (append(color[GREEN_INDEX])) {
if (append(color[BLUE_INDEX])) {
if (append(red)) {
if (append(green)) {
if (append(blue)) {
success = true;
}
}
@ -230,6 +241,90 @@ bool OctreePacketData::appendColor(const nodeColor& color) {
return success;
}
bool OctreePacketData::appendValue(uint8_t value) {
bool success = append(value); // used unsigned char version
if (success) {
_bytesOfValues++;
_totalBytesOfValues++;
}
return success;
}
bool OctreePacketData::appendValue(uint16_t value) {
const unsigned char* data = (const unsigned char*)&value;
int length = sizeof(value);
bool success = append(data, length);
if (success) {
_bytesOfValues += length;
_totalBytesOfValues += length;
}
return success;
}
bool OctreePacketData::appendValue(uint32_t value) {
const unsigned char* data = (const unsigned char*)&value;
int length = sizeof(value);
bool success = append(data, length);
if (success) {
_bytesOfValues += length;
_totalBytesOfValues += length;
}
return success;
}
bool OctreePacketData::appendValue(uint64_t value) {
const unsigned char* data = (const unsigned char*)&value;
int length = sizeof(value);
bool success = append(data, length);
if (success) {
_bytesOfValues += length;
_totalBytesOfValues += length;
}
return success;
}
bool OctreePacketData::appendValue(float value) {
const unsigned char* data = (const unsigned char*)&value;
int length = sizeof(value);
bool success = append(data, length);
if (success) {
_bytesOfValues += length;
_totalBytesOfValues += length;
}
return success;
}
bool OctreePacketData::appendValue(const glm::vec3& value) {
const unsigned char* data = (const unsigned char*)&value;
int length = sizeof(value);
bool success = append(data, length);
if (success) {
_bytesOfValues += length;
_totalBytesOfValues += length;
}
return success;
}
bool OctreePacketData::appendPosition(const glm::vec3& value) {
const unsigned char* data = (const unsigned char*)&value;
int length = sizeof(value);
bool success = append(data, length);
if (success) {
_bytesOfPositions += length;
_totalBytesOfPositions += length;
}
return success;
}
bool OctreePacketData::appendRawData(const unsigned char* data, int length) {
bool success = append(data, length);
if (success) {
_bytesOfRawData += length;
_totalBytesOfRawData += length;
}
return success;
}
uint64_t OctreePacketData::_compressContentTime = 0;
uint64_t OctreePacketData::_compressContentCalls = 0;

View file

@ -106,6 +106,36 @@ public:
/// appends a color to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendColor(const nodeColor& color);
/// appends a color to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendColor(const rgbColor& color);
/// appends a color to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendColor(colorPart red, colorPart green, colorPart blue);
/// appends a unsigned 8 bit int to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendValue(uint8_t value);
/// appends a unsigned 16 bit int to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendValue(uint16_t value);
/// appends a unsigned 32 bit int to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendValue(uint32_t value);
/// appends a unsigned 64 bit int to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendValue(uint64_t value);
/// appends a float value to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendValue(float value);
/// appends a non-position vector to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendValue(const glm::vec3& value);
/// appends a position to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendPosition(const glm::vec3& value);
/// appends raw bytes, might fail if byte would cause packet to be too large
bool appendRawData(const unsigned char* data, int length);
/// returns a byte offset from beginning of the uncompressed stream based on offset from end.
/// Positive offsetFromEnd returns that many bytes before the end of uncompressed stream
int getUncompressedByteOffset(int offsetFromEnd = 0) const { return _bytesInUse - offsetFromEnd; }
@ -167,6 +197,10 @@ private:
int _bytesOfOctalCodes;
int _bytesOfBitMasks;
int _bytesOfColor;
int _bytesOfValues;
int _bytesOfPositions;
int _bytesOfRawData;
int _bytesOfOctalCodesCurrentSubTree;
static bool _debug;
@ -177,6 +211,9 @@ private:
static uint64_t _totalBytesOfOctalCodes;
static uint64_t _totalBytesOfBitMasks;
static uint64_t _totalBytesOfColor;
static uint64_t _totalBytesOfValues;
static uint64_t _totalBytesOfPositions;
static uint64_t _totalBytesOfRawData;
};
#endif /* defined(__hifi__OctreePacketData__) */

View file

@ -0,0 +1,101 @@
//
// Particle.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#include <SharedUtil.h> // usecTimestampNow()
#include "Particle.h"
uint32_t Particle::_nextID = 0;
Particle::Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity) {
init(position, radius, color, velocity);
}
Particle::Particle() {
rgbColor noColor = { 0, 0, 0 };
init(glm::vec3(0,0,0), 0, noColor, glm::vec3(0,0,0));
}
Particle::~Particle() {
}
void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity) {
_id = _nextID;
_nextID++;
_lastUpdated = usecTimestampNow();
_position = position;
_radius = radius;
memcpy(_color, color, sizeof(_color));
_velocity = velocity;
}
bool Particle::appendParticleData(OctreePacketData* packetData) const {
bool success = packetData->appendValue(getID());
if (success) {
success = packetData->appendValue(getLastUpdated());
}
if (success) {
success = packetData->appendValue(getRadius());
}
if (success) {
success = packetData->appendPosition(getPosition());
}
if (success) {
success = packetData->appendColor(getColor());
}
if (success) {
success = packetData->appendValue(getVelocity());
}
return success;
}
int Particle::expectedBytes() {
int expectedBytes = sizeof(uint32_t) + sizeof(uint64_t) + sizeof(float) +
sizeof(glm::vec3) + sizeof(rgbColor) + sizeof(glm::vec3);
return expectedBytes;
}
int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
int bytesRead = 0;
if (bytesLeftToRead >= expectedBytes()) {
const unsigned char* dataAt = data;
// id
memcpy(&_id, dataAt, sizeof(_id));
dataAt += sizeof(_id);
// lastupdated
memcpy(&_lastUpdated, dataAt, sizeof(_lastUpdated));
dataAt += sizeof(_lastUpdated);
// radius
memcpy(&_radius, dataAt, sizeof(_radius));
dataAt += sizeof(_radius);
// position
memcpy(&_position, dataAt, sizeof(_position));
dataAt += sizeof(_position);
// color
memcpy(_color, dataAt, sizeof(_color));
dataAt += sizeof(_color);
// velocity
memcpy(&_velocity, dataAt, sizeof(_velocity));
dataAt += sizeof(_velocity);
bytesRead = expectedBytes();
}
return bytesRead;
}

View file

@ -0,0 +1,46 @@
//
// Particle.h
// hifi
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __hifi__Particle__
#define __hifi__Particle__
#include <glm/glm.hpp>
#include <stdint.h>
#include <SharedUtil.h>
#include <OctreePacketData.h>
class Particle {
public:
Particle();
Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity);
virtual ~Particle();
virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity);
const glm::vec3& getPosition() const { return _position; }
const rgbColor& getColor() const { return _color; }
float getRadius() const { return _radius; }
const glm::vec3& getVelocity() const { return _velocity; }
uint64_t getLastUpdated() const { return _lastUpdated; }
uint32_t getID() const { return _id; }
bool appendParticleData(OctreePacketData* packetData) const;
int readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
static int expectedBytes();
protected:
glm::vec3 _position;
rgbColor _color;
float _radius;
glm::vec3 _velocity;
uint64_t _lastUpdated;
uint32_t _id;
static uint32_t _nextID;
};
#endif /* defined(__hifi__Particle__) */

View file

@ -17,3 +17,12 @@ ParticleTreeElement* ParticleTree::createNewElement(unsigned char * octalCode) c
return newElement;
}
bool ParticleTree::handlesEditPacketType(PACKET_TYPE packetType) const {
return false; // not yet.
}
int ParticleTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
unsigned char* editData, int maxLength) {
return 0; // not yet... soon...
}

View file

@ -16,8 +16,20 @@ class ParticleTree : public Octree {
Q_OBJECT
public:
ParticleTree(bool shouldReaverage = false);
/// Implements our type specific root element factory
virtual ParticleTreeElement* createNewElement(unsigned char * octalCode = NULL) const;
/// Type safe version of getRoot()
ParticleTreeElement* getRoot() { return (ParticleTreeElement*)_rootNode; }
// 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
virtual bool handlesEditPacketType(PACKET_TYPE packetType) const;
virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
unsigned char* editData, int maxLength);
private:
};

View file

@ -33,17 +33,60 @@ void ParticleTreeElement::init(unsigned char* octalCode) {
_voxelMemoryUsage += sizeof(ParticleTreeElement);
}
ParticleTreeElement* ParticleTreeElement::addChildAtIndex(int index) {
return (ParticleTreeElement*)OctreeElement::addChildAtIndex(index);
}
bool ParticleTreeElement::appendElementData(OctreePacketData* packetData) const {
// will write to the encoded stream all of the contents of this element
return true;
bool success = true; // assume the best...
// write our particles out...
uint16_t numberOfParticles = _particles.size();
success = packetData->appendValue(numberOfParticles);
if (success) {
for (uint16_t i = 0; i < numberOfParticles; i++) {
const Particle& particle = _particles[i];
success = particle.appendParticleData(packetData);
if (!success) {
break;
}
}
}
return success;
}
int ParticleTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args) {
// will read from the encoded stream all of the contents of this element
return 0;
const unsigned char* dataAt = data;
int bytesRead = 0;
uint16_t numberOfParticles = 0;
int expectedBytesPerParticle = Particle::expectedBytes();
_particles.clear();
if (bytesLeftToRead >= sizeof(numberOfParticles)) {
// read our particles in....
numberOfParticles = *(uint16_t*)dataAt;
dataAt += sizeof(numberOfParticles);
bytesLeftToRead -= sizeof(numberOfParticles);
bytesRead += sizeof(numberOfParticles);
if (bytesLeftToRead >= (numberOfParticles * expectedBytesPerParticle)) {
for (uint16_t i = 0; i < numberOfParticles; i++) {
int bytesForThisParticle = _particles[i].readParticleDataFromBuffer(dataAt, bytesLeftToRead, args);
dataAt += bytesForThisParticle;
bytesLeftToRead -= bytesForThisParticle;
bytesRead += bytesForThisParticle;
}
}
}
return bytesRead;
}
// will average a "common reduced LOD view" from the the child elements...

View file

@ -10,8 +10,12 @@
#ifndef __hifi__ParticleTreeElement__
#define __hifi__ParticleTreeElement__
#include <vector>
#include <OctreeElement.h>
#include "Particle.h"
class ParticleTree;
class ParticleTreeElement;
@ -26,20 +30,47 @@ public:
virtual ~ParticleTreeElement();
virtual void init(unsigned char * octalCode);
virtual bool hasContent() const { return isLeaf(); }
virtual void splitChildren() {}
virtual bool requiresSplit() const { return false; }
virtual bool appendElementData(OctreePacketData* packetData) const;
virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
virtual void calculateAverageFromChildren();
virtual bool collapseChildren();
virtual bool isRendered() const { return getShouldRender(); }
// type safe versions of OctreeElement methods
ParticleTreeElement* getChildAtIndex(int index) { return (ParticleTreeElement*)OctreeElement::getChildAtIndex(index); }
ParticleTreeElement* addChildAtIndex(int index) { return (ParticleTreeElement*)OctreeElement::addChildAtIndex(index); }
// methods you can and should override to implement your tree functionality
/// Adds a child to the current element. Override this if there is additional child initialization your class needs.
virtual ParticleTreeElement* addChildAtIndex(int index);
/// Override this to implement LOD averaging on changes to the tree.
virtual void calculateAverageFromChildren();
/// Override this to implement LOD collapsing and identical child pruning on changes to the tree.
virtual bool collapseChildren();
/// Should this element be considered to have content in it. This will be used in collision and ray casting methods.
/// By default we assume that only leaves are actual content, but some octrees may have different semantics.
virtual bool hasContent() const { return isLeaf(); }
/// Override this to break up large octree elements when an edit operation is performed on a smaller octree element.
/// For example, if the octrees represent solid cubes and a delete of a smaller octree element is done then the
/// meaningful split would be to break the larger cube into smaller cubes of the same color/texture.
virtual void splitChildren() { }
/// Override to indicate that this element requires a split before editing lower elements in the octree
virtual bool requiresSplit() const { return false; }
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
virtual bool appendElementData(OctreePacketData* packetData) const;
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
/// from the network.
virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
/// Override to indicate that the item is currently rendered in the rendering engine. By default we assume that if
/// the element should be rendered, then your rendering engine is rendering. But some rendering engines my have cases
/// where an element is not actually rendering all should render elements. If the isRendered() state doesn't match the
/// shouldRender() state, the tree will remark elements as changed even in cases there the elements have not changed.
virtual bool isRendered() const { return getShouldRender(); }
protected:
std::vector<Particle> _particles;
};
#endif /* defined(__hifi__ParticleTreeElement__) */