mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 16:43:33 +02:00
Merge pull request #3053 from ey6es/metavoxels
Added basic tests for metavoxel streaming.
This commit is contained in:
commit
5b95eafa9b
10 changed files with 523 additions and 88 deletions
|
@ -211,6 +211,11 @@ void Attribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelSt
|
|||
root.writeSubdivision(state);
|
||||
}
|
||||
|
||||
bool Attribute::metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot,
|
||||
const glm::vec3& minimum, float size, const MetavoxelLOD& lod) {
|
||||
return firstRoot.deepEquals(this, secondRoot, minimum, size, lod);
|
||||
}
|
||||
|
||||
FloatAttribute::FloatAttribute(const QString& name, float defaultValue) :
|
||||
SimpleInlineAttribute<float>(name, defaultValue) {
|
||||
}
|
||||
|
@ -449,6 +454,12 @@ void SharedObjectAttribute::write(Bitstream& out, void* value, bool isLeaf) cons
|
|||
}
|
||||
}
|
||||
|
||||
bool SharedObjectAttribute::deepEqual(void* first, void* second) const {
|
||||
SharedObjectPointer firstObject = decodeInline<SharedObjectPointer>(first);
|
||||
SharedObjectPointer secondObject = decodeInline<SharedObjectPointer>(second);
|
||||
return firstObject ? firstObject->equals(secondObject) : !secondObject;
|
||||
}
|
||||
|
||||
bool SharedObjectAttribute::merge(void*& parent, void* children[], bool postRead) const {
|
||||
SharedObjectPointer firstChild = decodeInline<SharedObjectPointer>(children[0]);
|
||||
for (int i = 1; i < MERGE_COUNT; i++) {
|
||||
|
@ -489,6 +500,35 @@ MetavoxelNode* SharedObjectSetAttribute::createMetavoxelNode(
|
|||
return new MetavoxelNode(value, original);
|
||||
}
|
||||
|
||||
static bool setsEqual(const SharedObjectSet& firstSet, const SharedObjectSet& secondSet) {
|
||||
if (firstSet.size() != secondSet.size()) {
|
||||
return false;
|
||||
}
|
||||
// some hackiness here: we assume that the local ids of the first set correspond to the remote ids of the second,
|
||||
// so that this will work with the tests
|
||||
foreach (const SharedObjectPointer& firstObject, firstSet) {
|
||||
int id = firstObject->getID();
|
||||
bool found = false;
|
||||
foreach (const SharedObjectPointer& secondObject, secondSet) {
|
||||
if (secondObject->getRemoteID() == id) {
|
||||
if (!firstObject->equals(secondObject)) {
|
||||
return false;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedObjectSetAttribute::deepEqual(void* first, void* second) const {
|
||||
return setsEqual(decodeInline<SharedObjectSet>(first), decodeInline<SharedObjectSet>(second));
|
||||
}
|
||||
|
||||
bool SharedObjectSetAttribute::merge(void*& parent, void* children[], bool postRead) const {
|
||||
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||
if (!decodeInline<SharedObjectSet>(children[i]).isEmpty()) {
|
||||
|
@ -563,3 +603,12 @@ void SpannerSetAttribute::writeMetavoxelSubdivision(const MetavoxelNode& root, M
|
|||
state.stream << SharedObjectPointer();
|
||||
}
|
||||
|
||||
bool SpannerSetAttribute::metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot,
|
||||
const glm::vec3& minimum, float size, const MetavoxelLOD& lod) {
|
||||
|
||||
SharedObjectSet firstSet;
|
||||
firstRoot.getSpanners(this, minimum, size, lod, firstSet);
|
||||
SharedObjectSet secondSet;
|
||||
secondRoot.getSpanners(this, minimum, size, lod, secondSet);
|
||||
return setsEqual(firstSet, secondSet);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ class QScriptValue;
|
|||
|
||||
class Attribute;
|
||||
class MetavoxelData;
|
||||
class MetavoxelLOD;
|
||||
class MetavoxelNode;
|
||||
class MetavoxelStreamState;
|
||||
|
||||
|
@ -213,6 +214,11 @@ public:
|
|||
|
||||
virtual bool equal(void* first, void* second) const = 0;
|
||||
|
||||
virtual bool deepEqual(void* first, void* second) const { return equal(first, second); }
|
||||
|
||||
virtual bool metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot,
|
||||
const glm::vec3& minimum, float size, const MetavoxelLOD& lod);
|
||||
|
||||
/// Merges the value of a parent and its children.
|
||||
/// \param postRead whether or not the merge is happening after a read
|
||||
/// \return whether or not the children and parent values are all equal
|
||||
|
@ -406,6 +412,8 @@ public:
|
|||
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
|
||||
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
|
||||
|
||||
virtual bool deepEqual(void* first, void* second) const;
|
||||
|
||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
|
||||
virtual void* createFromVariant(const QVariant& value) const;
|
||||
|
@ -434,6 +442,8 @@ public:
|
|||
|
||||
virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const;
|
||||
|
||||
virtual bool deepEqual(void* first, void* second) const;
|
||||
|
||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
|
||||
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||
|
@ -462,6 +472,9 @@ public:
|
|||
|
||||
virtual void readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state);
|
||||
virtual void writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state);
|
||||
|
||||
virtual bool metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot,
|
||||
const glm::vec3& minimum, float size, const MetavoxelLOD& lod);
|
||||
};
|
||||
|
||||
#endif // hifi_AttributeRegistry_h
|
||||
|
|
|
@ -110,6 +110,10 @@ const TypeStreamer* Bitstream::getTypeStreamer(int type) {
|
|||
return getTypeStreamers().value(type);
|
||||
}
|
||||
|
||||
const ObjectStreamer* Bitstream::getObjectStreamer(const QMetaObject* metaObject) {
|
||||
return getObjectStreamers().value(metaObject);
|
||||
}
|
||||
|
||||
const QMetaObject* Bitstream::getMetaObject(const QByteArray& className) {
|
||||
return getMetaObjects().value(className);
|
||||
}
|
||||
|
@ -2316,6 +2320,15 @@ QObject* MappedObjectStreamer::putJSONData(JSONReader& reader, const QJsonObject
|
|||
return object;
|
||||
}
|
||||
|
||||
bool MappedObjectStreamer::equal(const QObject* first, const QObject* second) const {
|
||||
foreach (const StreamerPropertyPair& property, _properties) {
|
||||
if (!property.first->equal(property.second.read(first), property.second.read(second))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MappedObjectStreamer::write(Bitstream& out, const QObject* object) const {
|
||||
foreach (const StreamerPropertyPair& property, _properties) {
|
||||
property.first->write(out, property.second.read(object));
|
||||
|
@ -2433,6 +2446,17 @@ QObject* GenericObjectStreamer::putJSONData(JSONReader& reader, const QJsonObjec
|
|||
return object;
|
||||
}
|
||||
|
||||
bool GenericObjectStreamer::equal(const QObject* first, const QObject* second) const {
|
||||
const QVariantList& firstValues = static_cast<const GenericSharedObject*>(first)->getValues();
|
||||
const QVariantList& secondValues = static_cast<const GenericSharedObject*>(second)->getValues();
|
||||
for (int i = 0; i < _properties.size(); i++) {
|
||||
if (!_properties.at(i).first->equal(firstValues.at(i), secondValues.at(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GenericObjectStreamer::write(Bitstream& out, const QObject* object) const {
|
||||
const QVariantList& values = static_cast<const GenericSharedObject*>(object)->getValues();
|
||||
for (int i = 0; i < _properties.size(); i++) {
|
||||
|
|
|
@ -278,6 +278,9 @@ public:
|
|||
/// Returns the streamer registered for the supplied type, if any.
|
||||
static const TypeStreamer* getTypeStreamer(int type);
|
||||
|
||||
/// Returns the streamer registered for the supplied object, if any.
|
||||
static const ObjectStreamer* getObjectStreamer(const QMetaObject* metaObject);
|
||||
|
||||
/// Returns the meta-object registered under the supplied class name, if any.
|
||||
static const QMetaObject* getMetaObject(const QByteArray& className);
|
||||
|
||||
|
@ -1022,6 +1025,7 @@ public:
|
|||
virtual QJsonObject getJSONData(JSONWriter& writer, const QObject* object) const = 0;
|
||||
virtual QObject* putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const = 0;
|
||||
|
||||
virtual bool equal(const QObject* first, const QObject* second) const = 0;
|
||||
virtual void write(Bitstream& out, const QObject* object) const = 0;
|
||||
virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const = 0;
|
||||
virtual QObject* read(Bitstream& in, QObject* object = NULL) const = 0;
|
||||
|
@ -1047,6 +1051,7 @@ public:
|
|||
virtual QJsonObject getJSONMetadata(JSONWriter& writer) const;
|
||||
virtual QJsonObject getJSONData(JSONWriter& writer, const QObject* object) const;
|
||||
virtual QObject* putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const;
|
||||
virtual bool equal(const QObject* first, const QObject* second) const;
|
||||
virtual void write(Bitstream& out, const QObject* object) const;
|
||||
virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const;
|
||||
virtual QObject* read(Bitstream& in, QObject* object = NULL) const;
|
||||
|
@ -1070,6 +1075,7 @@ public:
|
|||
virtual QJsonObject getJSONMetadata(JSONWriter& writer) const;
|
||||
virtual QJsonObject getJSONData(JSONWriter& writer, const QObject* object) const;
|
||||
virtual QObject* putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const;
|
||||
virtual bool equal(const QObject* first, const QObject* second) const;
|
||||
virtual void write(Bitstream& out, const QObject* object) const;
|
||||
virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const;
|
||||
virtual QObject* read(Bitstream& in, QObject* object = NULL) const;
|
||||
|
@ -1104,7 +1110,7 @@ private:
|
|||
Q_DECLARE_METATYPE(const QMetaObject*)
|
||||
|
||||
/// Macro for registering streamable meta-objects. Typically, one would use this macro at the top level of the source file
|
||||
/// associated with the class.
|
||||
/// associated with the class. The class should have a no-argument constructor flagged with Q_INVOKABLE.
|
||||
#define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject);
|
||||
|
||||
/// Contains a value along with a pointer to its streamer. This is stored in QVariants when using fallback generics and
|
||||
|
@ -1563,8 +1569,8 @@ public:
|
|||
Bitstream::registerTypeStreamer(qMetaTypeId<X>(), new CollectionTypeStreamer<X>());
|
||||
|
||||
/// Declares the metatype and the streaming operators. Typically, one would use this immediately after the definition of a
|
||||
/// type flagged as STREAMABLE in its header file. The last lines ensure that the generated file will be included in the link
|
||||
/// phase.
|
||||
/// type flagged as STREAMABLE in its header file. The type should have a no-argument constructor. The last lines of this
|
||||
/// macro ensure that the generated file will be included in the link phase.
|
||||
#ifdef _WIN32
|
||||
#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \
|
||||
Bitstream& operator<<(Bitstream& out, const X& obj); \
|
||||
|
|
|
@ -610,6 +610,23 @@ MetavoxelNode* MetavoxelData::createRoot(const AttributePointer& attribute) {
|
|||
return root = new MetavoxelNode(attribute);
|
||||
}
|
||||
|
||||
bool MetavoxelData::deepEquals(const MetavoxelData& other, const MetavoxelLOD& lod) const {
|
||||
if (_size != other._size) {
|
||||
return false;
|
||||
}
|
||||
if (_roots.size() != other._roots.size()) {
|
||||
return false;
|
||||
}
|
||||
glm::vec3 minimum = getMinimum();
|
||||
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
|
||||
MetavoxelNode* otherNode = other._roots.value(it.key());
|
||||
if (!(otherNode && it.key()->metavoxelRootsEqual(*it.value(), *otherNode, minimum, _size, lod))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetavoxelData::operator==(const MetavoxelData& other) const {
|
||||
return _size == other._size && _roots == other._roots;
|
||||
}
|
||||
|
@ -1006,6 +1023,44 @@ void MetavoxelNode::clearChildren(const AttributePointer& attribute) {
|
|||
}
|
||||
}
|
||||
|
||||
bool MetavoxelNode::deepEquals(const AttributePointer& attribute, const MetavoxelNode& other,
|
||||
const glm::vec3& minimum, float size, const MetavoxelLOD& lod) const {
|
||||
if (!attribute->deepEqual(_attributeValue, other._attributeValue)) {
|
||||
return false;
|
||||
}
|
||||
if (!lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier())) {
|
||||
return true;
|
||||
}
|
||||
bool leaf = isLeaf(), otherLeaf = other.isLeaf();
|
||||
if (leaf && otherLeaf) {
|
||||
return true;
|
||||
}
|
||||
if (leaf || otherLeaf) {
|
||||
return false;
|
||||
}
|
||||
float nextSize = size * 0.5f;
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
glm::vec3 nextMinimum = getNextMinimum(minimum, nextSize, i);
|
||||
if (!_children[i]->deepEquals(attribute, *(other._children[i]), nextMinimum, nextSize, lod)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MetavoxelNode::getSpanners(const AttributePointer& attribute, const glm::vec3& minimum,
|
||||
float size, const MetavoxelLOD& lod, SharedObjectSet& results) const {
|
||||
results.unite(decodeInline<SharedObjectSet>(_attributeValue));
|
||||
if (isLeaf() || !lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier())) {
|
||||
return;
|
||||
}
|
||||
float nextSize = size * 0.5f;
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
glm::vec3 nextMinimum = getNextMinimum(minimum, nextSize, i);
|
||||
_children[i]->getSpanners(attribute, nextMinimum, nextSize, lod, results);
|
||||
}
|
||||
}
|
||||
|
||||
int MetavoxelVisitor::encodeOrder(int first, int second, int third, int fourth,
|
||||
int fifth, int sixth, int seventh, int eighth) {
|
||||
return first | (second << 3) | (third << 6) | (fourth << 9) |
|
||||
|
@ -1034,6 +1089,25 @@ int MetavoxelVisitor::encodeOrder(const glm::vec3& direction) {
|
|||
indexDistances.at(6).index, indexDistances.at(7).index);
|
||||
}
|
||||
|
||||
const int ORDER_ELEMENT_BITS = 3;
|
||||
const int ORDER_ELEMENT_MASK = (1 << ORDER_ELEMENT_BITS) - 1;
|
||||
|
||||
int MetavoxelVisitor::encodeRandomOrder() {
|
||||
// see http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_.22inside-out.22_algorithm
|
||||
int order;
|
||||
int randomValues = rand();
|
||||
for (int i = 0, iShift = 0; i < MetavoxelNode::CHILD_COUNT; i++, iShift += ORDER_ELEMENT_BITS) {
|
||||
int j = (randomValues >> iShift) % (i + 1);
|
||||
int jShift = j * ORDER_ELEMENT_BITS;
|
||||
if (j != i) {
|
||||
int jValue = (order >> jShift) & ORDER_ELEMENT_MASK;
|
||||
order = (order & ~(ORDER_ELEMENT_MASK << iShift)) | (jValue << iShift);
|
||||
}
|
||||
order = (order & ~(ORDER_ELEMENT_MASK << jShift)) | (i << jShift);
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
const int MetavoxelVisitor::DEFAULT_ORDER = encodeOrder(0, 1, 2, 3, 4, 5, 6, 7);
|
||||
const int MetavoxelVisitor::STOP_RECURSION = 0;
|
||||
const int MetavoxelVisitor::SHORT_CIRCUIT = -1;
|
||||
|
@ -1227,8 +1301,6 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
|||
QVector<OwnedAttributeValue>(visitation.outputNodes.size()) } };
|
||||
for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) {
|
||||
// the encoded order tells us the child indices for each iteration
|
||||
const int ORDER_ELEMENT_BITS = 3;
|
||||
const int ORDER_ELEMENT_MASK = (1 << ORDER_ELEMENT_BITS) - 1;
|
||||
int index = encodedOrder & ORDER_ELEMENT_MASK;
|
||||
encodedOrder >>= ORDER_ELEMENT_BITS;
|
||||
for (int j = 0; j < visitation.inputNodes.size(); j++) {
|
||||
|
@ -1269,7 +1341,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
|||
}
|
||||
}
|
||||
MetavoxelNode* node = visitation.outputNodes.at(j);
|
||||
MetavoxelNode* child = node->getChild(i);
|
||||
MetavoxelNode* child = node->getChild(index);
|
||||
if (child) {
|
||||
child->decrementReferenceCount(value.getAttribute());
|
||||
} else {
|
||||
|
|
|
@ -34,7 +34,8 @@ class NetworkValue;
|
|||
class Spanner;
|
||||
class SpannerRenderer;
|
||||
|
||||
/// Determines whether to subdivide each node when traversing.
|
||||
/// Determines whether to subdivide each node when traversing. Contains the position (presumed to be of the viewer) and a
|
||||
/// threshold value, where lower thresholds cause smaller/more distant voxels to be subdivided.
|
||||
class MetavoxelLOD {
|
||||
STREAMABLE
|
||||
|
||||
|
@ -46,6 +47,7 @@ public:
|
|||
|
||||
bool isValid() const { return threshold > 0.0f; }
|
||||
|
||||
/// Checks whether, according to this LOD, we should subdivide the described voxel.
|
||||
bool shouldSubdivide(const glm::vec3& minimum, float size, float multiplier = 1.0f) const;
|
||||
|
||||
/// Checks whether the node or any of the nodes underneath it have had subdivision enabled as compared to the reference.
|
||||
|
@ -54,7 +56,8 @@ public:
|
|||
|
||||
DECLARE_STREAMABLE_METATYPE(MetavoxelLOD)
|
||||
|
||||
/// The base metavoxel representation shared between server and client.
|
||||
/// The base metavoxel representation shared between server and client. Contains a size (for all dimensions) and a set of
|
||||
/// octrees for different attributes.
|
||||
class MetavoxelData {
|
||||
public:
|
||||
|
||||
|
@ -64,30 +67,38 @@ public:
|
|||
|
||||
MetavoxelData& operator=(const MetavoxelData& other);
|
||||
|
||||
/// Sets the size in all dimensions.
|
||||
void setSize(float size) { _size = size; }
|
||||
float getSize() const { return _size; }
|
||||
|
||||
/// Returns the minimum extent of the octrees (which are centered about the origin).
|
||||
glm::vec3 getMinimum() const { return glm::vec3(_size, _size, _size) * -0.5f; }
|
||||
|
||||
/// Returns the bounds of the octrees.
|
||||
Box getBounds() const;
|
||||
|
||||
/// Applies the specified visitor to the contained voxels.
|
||||
void guide(MetavoxelVisitor& visitor);
|
||||
|
||||
/// Inserts a spanner into the specified attribute layer.
|
||||
void insert(const AttributePointer& attribute, const SharedObjectPointer& object);
|
||||
void insert(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object);
|
||||
|
||||
/// Removes a spanner from the specified attribute layer.
|
||||
void remove(const AttributePointer& attribute, const SharedObjectPointer& object);
|
||||
void remove(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object);
|
||||
|
||||
/// Toggles the existence of a spanner in the specified attribute layer (removes if present, adds if not).
|
||||
void toggle(const AttributePointer& attribute, const SharedObjectPointer& object);
|
||||
void toggle(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object);
|
||||
|
||||
/// Replaces a spanner in the specified attribute layer.
|
||||
void replace(const AttributePointer& attribute, const SharedObjectPointer& oldObject,
|
||||
const SharedObjectPointer& newObject);
|
||||
void replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject,
|
||||
const SharedObjectPointer& newObject);
|
||||
|
||||
|
||||
/// Clears all data in the specified attribute layer.
|
||||
void clear(const AttributePointer& attribute);
|
||||
|
||||
/// Convenience function that finds the first spanner intersecting the provided ray.
|
||||
|
@ -97,7 +108,7 @@ public:
|
|||
/// Sets part of the data.
|
||||
void set(const glm::vec3& minimum, const MetavoxelData& data, bool blend = false);
|
||||
|
||||
/// Expands the tree, increasing its capacity in all dimensions.
|
||||
/// Expands the tree, doubling its size in all dimensions (that is, increasing its volume eightfold).
|
||||
void expand();
|
||||
|
||||
void read(Bitstream& in, const MetavoxelLOD& lod = MetavoxelLOD());
|
||||
|
@ -110,6 +121,10 @@ public:
|
|||
MetavoxelNode* getRoot(const AttributePointer& attribute) const { return _roots.value(attribute); }
|
||||
MetavoxelNode* createRoot(const AttributePointer& attribute);
|
||||
|
||||
/// Performs a deep comparison between this data and the specified other (as opposed to the == operator, which does a
|
||||
/// shallow comparison).
|
||||
bool deepEquals(const MetavoxelData& other, const MetavoxelLOD& lod = MetavoxelLOD()) const;
|
||||
|
||||
bool operator==(const MetavoxelData& other) const;
|
||||
bool operator!=(const MetavoxelData& other) const;
|
||||
|
||||
|
@ -198,6 +213,14 @@ public:
|
|||
|
||||
void clearChildren(const AttributePointer& attribute);
|
||||
|
||||
/// Performs a deep comparison between this and the specified other node.
|
||||
bool deepEquals(const AttributePointer& attribute, const MetavoxelNode& other,
|
||||
const glm::vec3& minimum, float size, const MetavoxelLOD& lod) const;
|
||||
|
||||
/// Retrieves all spanners satisfying the LOD constraint, placing them in the provided set.
|
||||
void getSpanners(const AttributePointer& attribute, const glm::vec3& minimum,
|
||||
float size, const MetavoxelLOD& lod, SharedObjectSet& results) const;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(MetavoxelNode)
|
||||
|
||||
|
@ -234,6 +257,9 @@ public:
|
|||
/// Encodes a visitation order sequence that visits each child as sorted along the specified direction.
|
||||
static int encodeOrder(const glm::vec3& direction);
|
||||
|
||||
/// Returns a random visitation order sequence.
|
||||
static int encodeRandomOrder();
|
||||
|
||||
/// The default visitation order.
|
||||
static const int DEFAULT_ORDER;
|
||||
|
||||
|
|
|
@ -84,11 +84,19 @@ bool SharedObject::equals(const SharedObject* other, bool sharedAncestry) const
|
|||
if (metaObject != other->metaObject() && !sharedAncestry) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty property = metaObject->property(i);
|
||||
if (property.isStored() && property.read(this) != property.read(other)) {
|
||||
// use the streamer, if we have one
|
||||
const ObjectStreamer* streamer = Bitstream::getObjectStreamer(metaObject);
|
||||
if (streamer) {
|
||||
if (!streamer->equal(this, other)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty property = metaObject->property(i);
|
||||
if (property.isStored() && property.read(this) != property.read(other)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
QList<QByteArray> dynamicPropertyNames = this->dynamicPropertyNames();
|
||||
if (dynamicPropertyNames.size() != other->dynamicPropertyNames().size()) {
|
||||
|
|
|
@ -68,7 +68,7 @@ float randFloat();
|
|||
int randIntInRange (int min, int max);
|
||||
float randFloatInRange (float min,float max);
|
||||
float randomSign(); /// \return -1.0 or 1.0
|
||||
unsigned char randomColorValue(int minimum);
|
||||
unsigned char randomColorValue(int minimum = 0);
|
||||
bool randomBoolean();
|
||||
|
||||
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
|
||||
|
|
|
@ -32,6 +32,8 @@ static int datagramsSent = 0;
|
|||
static int datagramsReceived = 0;
|
||||
static int bytesSent = 0;
|
||||
static int bytesReceived = 0;
|
||||
static int maxDatagramsPerPacket = 0;
|
||||
static int maxBytesPerPacket = 0;
|
||||
static int highPriorityMessagesSent = 0;
|
||||
static int highPriorityMessagesReceived = 0;
|
||||
static int unreliableMessagesSent = 0;
|
||||
|
@ -45,6 +47,8 @@ static int sharedObjectsDestroyed = 0;
|
|||
static int objectMutationsPerformed = 0;
|
||||
static int scriptObjectsCreated = 0;
|
||||
static int scriptMutationsPerformed = 0;
|
||||
static int metavoxelMutationsPerformed = 0;
|
||||
static int spannerMutationsPerformed = 0;
|
||||
|
||||
static QByteArray createRandomBytes(int minimumSize, int maximumSize) {
|
||||
QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0);
|
||||
|
@ -321,44 +325,80 @@ static bool testSerialization(Bitstream::MetadataType metadataType) {
|
|||
}
|
||||
|
||||
bool MetavoxelTests::run() {
|
||||
|
||||
qDebug() << "Running transmission tests...";
|
||||
qDebug();
|
||||
|
||||
// seed the random number generator so that our tests are reproducible
|
||||
srand(0xBAAAAABE);
|
||||
|
||||
// create two endpoints with the same header
|
||||
// check for an optional command line argument specifying a single test
|
||||
QStringList arguments = this->arguments();
|
||||
int test = (arguments.size() > 1) ? arguments.at(1).toInt() : 0;
|
||||
|
||||
QByteArray datagramHeader("testheader");
|
||||
Endpoint alice(datagramHeader), bob(datagramHeader);
|
||||
|
||||
alice.setOther(&bob);
|
||||
bob.setOther(&alice);
|
||||
|
||||
// perform a large number of simulation iterations
|
||||
const int SIMULATION_ITERATIONS = 10000;
|
||||
for (int i = 0; i < SIMULATION_ITERATIONS; i++) {
|
||||
if (alice.simulate(i) || bob.simulate(i)) {
|
||||
if (test == 0 || test == 1) {
|
||||
qDebug() << "Running transmission tests...";
|
||||
qDebug();
|
||||
|
||||
// create two endpoints with the same header
|
||||
Endpoint alice(datagramHeader), bob(datagramHeader);
|
||||
|
||||
alice.setOther(&bob);
|
||||
bob.setOther(&alice);
|
||||
|
||||
// perform a large number of simulation iterations
|
||||
for (int i = 0; i < SIMULATION_ITERATIONS; i++) {
|
||||
if (alice.simulate(i) || bob.simulate(i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Sent" << highPriorityMessagesSent << "high priority messages, received" << highPriorityMessagesReceived;
|
||||
qDebug() << "Sent" << unreliableMessagesSent << "unreliable messages, received" << unreliableMessagesReceived;
|
||||
qDebug() << "Sent" << reliableMessagesSent << "reliable messages, received" << reliableMessagesReceived;
|
||||
qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived;
|
||||
qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" <<
|
||||
datagramsReceived << "with" << bytesReceived << "bytes";
|
||||
qDebug() << "Max" << maxDatagramsPerPacket << "datagrams," << maxBytesPerPacket << "bytes per packet";
|
||||
qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed;
|
||||
qDebug() << "Performed" << objectMutationsPerformed << "object mutations";
|
||||
qDebug() << "Created" << scriptObjectsCreated << "script objects, mutated" << scriptMutationsPerformed;
|
||||
qDebug();
|
||||
}
|
||||
|
||||
if (test == 0 || test == 2) {
|
||||
qDebug() << "Running serialization tests...";
|
||||
qDebug();
|
||||
|
||||
if (testSerialization(Bitstream::HASH_METADATA) || testSerialization(Bitstream::FULL_METADATA)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Sent" << highPriorityMessagesSent << "high priority messages, received" << highPriorityMessagesReceived;
|
||||
qDebug() << "Sent" << unreliableMessagesSent << "unreliable messages, received" << unreliableMessagesReceived;
|
||||
qDebug() << "Sent" << reliableMessagesSent << "reliable messages, received" << reliableMessagesReceived;
|
||||
qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived;
|
||||
qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" <<
|
||||
datagramsReceived << "with" << bytesReceived << "bytes";
|
||||
qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed;
|
||||
qDebug() << "Performed" << objectMutationsPerformed << "object mutations";
|
||||
qDebug() << "Created" << scriptObjectsCreated << "script objects, mutated" << scriptMutationsPerformed;
|
||||
qDebug();
|
||||
if (test == 0 || test == 3) {
|
||||
qDebug() << "Running metavoxel data tests...";
|
||||
qDebug();
|
||||
|
||||
qDebug() << "Running serialization tests...";
|
||||
qDebug();
|
||||
// clear the stats
|
||||
datagramsSent = bytesSent = datagramsReceived = bytesReceived = maxDatagramsPerPacket = maxBytesPerPacket = 0;
|
||||
|
||||
if (testSerialization(Bitstream::HASH_METADATA) || testSerialization(Bitstream::FULL_METADATA)) {
|
||||
return true;
|
||||
// create client and server endpoints
|
||||
Endpoint client(datagramHeader, Endpoint::METAVOXEL_CLIENT_MODE);
|
||||
Endpoint server(datagramHeader, Endpoint::METAVOXEL_SERVER_MODE);
|
||||
|
||||
client.setOther(&server);
|
||||
server.setOther(&client);
|
||||
|
||||
// simulate
|
||||
for (int i = 0; i < SIMULATION_ITERATIONS; i++) {
|
||||
if (client.simulate(i) || server.simulate(i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" <<
|
||||
datagramsReceived << "with" << bytesReceived << "bytes";
|
||||
qDebug() << "Max" << maxDatagramsPerPacket << "datagrams," << maxBytesPerPacket << "bytes per packet";
|
||||
qDebug() << "Performed" << metavoxelMutationsPerformed << "metavoxel mutations," << spannerMutationsPerformed <<
|
||||
"spanner mutations";
|
||||
}
|
||||
|
||||
qDebug() << "All tests passed!";
|
||||
|
@ -375,7 +415,36 @@ static SharedObjectPointer createRandomSharedObject() {
|
|||
}
|
||||
}
|
||||
|
||||
Endpoint::Endpoint(const QByteArray& datagramHeader) :
|
||||
class RandomVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
int leafCount;
|
||||
|
||||
RandomVisitor();
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
};
|
||||
|
||||
RandomVisitor::RandomVisitor() :
|
||||
MetavoxelVisitor(QVector<AttributePointer>(),
|
||||
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getColorAttribute()),
|
||||
leafCount(0) {
|
||||
}
|
||||
|
||||
const float MAXIMUM_LEAF_SIZE = 0.5f;
|
||||
const float MINIMUM_LEAF_SIZE = 0.25f;
|
||||
|
||||
int RandomVisitor::visit(MetavoxelInfo& info) {
|
||||
if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) {
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline<QRgb>(qRgb(randomColorValue(),
|
||||
randomColorValue(), randomColorValue())));
|
||||
leafCount++;
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
Endpoint::Endpoint(const QByteArray& datagramHeader, Mode mode) :
|
||||
_mode(mode),
|
||||
_sequencer(new DatagramSequencer(datagramHeader, this)),
|
||||
_highPriorityMessagesToSend(0.0f),
|
||||
_reliableMessagesToSend(0.0f) {
|
||||
|
@ -396,6 +465,25 @@ Endpoint::Endpoint(const QByteArray& datagramHeader) :
|
|||
ReceiveRecord receiveRecord = { 0 };
|
||||
_receiveRecords.append(receiveRecord);
|
||||
|
||||
if (mode == METAVOXEL_CLIENT_MODE) {
|
||||
_lod = MetavoxelLOD(glm::vec3(), 0.01f);
|
||||
return;
|
||||
}
|
||||
if (mode == METAVOXEL_SERVER_MODE) {
|
||||
_data.expand();
|
||||
_data.expand();
|
||||
|
||||
RandomVisitor visitor;
|
||||
_data.guide(visitor);
|
||||
qDebug() << "Created" << visitor.leafCount << "base leaves";
|
||||
|
||||
_data.insert(AttributeRegistry::getInstance()->getSpannersAttribute(), new Sphere());
|
||||
|
||||
_sphere = new Sphere();
|
||||
static_cast<Transformable*>(_sphere.data())->setScale(0.01f);
|
||||
_data.insert(AttributeRegistry::getInstance()->getSpannersAttribute(), _sphere);
|
||||
return;
|
||||
}
|
||||
// create the object that represents out delta-encoded state
|
||||
_localState = new TestSharedObjectA();
|
||||
|
||||
|
@ -415,7 +503,7 @@ Endpoint::Endpoint(const QByteArray& datagramHeader) :
|
|||
QByteArray bytes = createRandomBytes(MIN_STREAM_BYTES, MAX_STREAM_BYTES);
|
||||
_dataStreamed.append(bytes);
|
||||
output->getBuffer().write(bytes);
|
||||
streamedBytesSent += bytes.size();
|
||||
streamedBytesSent += bytes.size();
|
||||
}
|
||||
|
||||
static QVariant createRandomMessage() {
|
||||
|
@ -512,6 +600,37 @@ static bool messagesEqual(const QVariant& firstMessage, const QVariant& secondMe
|
|||
}
|
||||
}
|
||||
|
||||
class MutateVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
MutateVisitor();
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
int _mutationsRemaining;
|
||||
};
|
||||
|
||||
MutateVisitor::MutateVisitor() :
|
||||
MetavoxelVisitor(QVector<AttributePointer>(),
|
||||
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getColorAttribute()),
|
||||
_mutationsRemaining(randIntInRange(2, 4)) {
|
||||
}
|
||||
|
||||
int MutateVisitor::visit(MetavoxelInfo& info) {
|
||||
if (_mutationsRemaining <= 0) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) {
|
||||
return encodeRandomOrder();
|
||||
}
|
||||
info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline<QRgb>(qRgb(randomColorValue(),
|
||||
randomColorValue(), randomColorValue())));
|
||||
_mutationsRemaining--;
|
||||
metavoxelMutationsPerformed++;
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
bool Endpoint::simulate(int iterationNumber) {
|
||||
// update/send our delayed datagrams
|
||||
for (QList<QPair<QByteArray, int> >::iterator it = _delayedDatagrams.begin(); it != _delayedDatagrams.end(); ) {
|
||||
|
@ -525,51 +644,101 @@ bool Endpoint::simulate(int iterationNumber) {
|
|||
}
|
||||
}
|
||||
|
||||
// enqueue some number of high priority messages
|
||||
const float MIN_HIGH_PRIORITY_MESSAGES = 0.0f;
|
||||
const float MAX_HIGH_PRIORITY_MESSAGES = 2.0f;
|
||||
_highPriorityMessagesToSend += randFloatInRange(MIN_HIGH_PRIORITY_MESSAGES, MAX_HIGH_PRIORITY_MESSAGES);
|
||||
while (_highPriorityMessagesToSend >= 1.0f) {
|
||||
QVariant message = createRandomMessage();
|
||||
_highPriorityMessagesSent.append(message);
|
||||
_sequencer->sendHighPriorityMessage(message);
|
||||
highPriorityMessagesSent++;
|
||||
_highPriorityMessagesToSend -= 1.0f;
|
||||
}
|
||||
|
||||
// and some number of reliable messages
|
||||
const float MIN_RELIABLE_MESSAGES = 0.0f;
|
||||
const float MAX_RELIABLE_MESSAGES = 4.0f;
|
||||
_reliableMessagesToSend += randFloatInRange(MIN_RELIABLE_MESSAGES, MAX_RELIABLE_MESSAGES);
|
||||
while (_reliableMessagesToSend >= 1.0f) {
|
||||
QVariant message = createRandomMessage();
|
||||
_reliableMessagesSent.append(message);
|
||||
_sequencer->getReliableOutputChannel()->sendMessage(message);
|
||||
reliableMessagesSent++;
|
||||
_reliableMessagesToSend -= 1.0f;
|
||||
}
|
||||
|
||||
// tweak the local state
|
||||
_localState = mutate(_localState);
|
||||
|
||||
// send a packet
|
||||
try {
|
||||
int oldDatagramsSent = datagramsSent;
|
||||
int oldBytesSent = bytesSent;
|
||||
if (_mode == METAVOXEL_CLIENT_MODE) {
|
||||
Bitstream& out = _sequencer->startPacket();
|
||||
SequencedTestMessage message = { iterationNumber, createRandomMessage(), _localState };
|
||||
_unreliableMessagesSent.append(message);
|
||||
unreliableMessagesSent++;
|
||||
out << message;
|
||||
|
||||
ClientStateMessage state = { _lod };
|
||||
out << QVariant::fromValue(state);
|
||||
_sequencer->endPacket();
|
||||
|
||||
// record the send
|
||||
SendRecord record = { _sequencer->getOutgoingPacketNumber(), SharedObjectPointer(), MetavoxelData(), _lod };
|
||||
_sendRecords.append(record);
|
||||
|
||||
} else if (_mode == METAVOXEL_SERVER_MODE) {
|
||||
// make a random change
|
||||
MutateVisitor visitor;
|
||||
_data.guide(visitor);
|
||||
|
||||
// perhaps mutate the spanner
|
||||
if (randomBoolean()) {
|
||||
SharedObjectPointer oldSphere = _sphere;
|
||||
_sphere = _sphere->clone(true);
|
||||
Sphere* newSphere = static_cast<Sphere*>(_sphere.data());
|
||||
if (randomBoolean()) {
|
||||
newSphere->setColor(QColor(randomColorValue(), randomColorValue(), randomColorValue()));
|
||||
} else {
|
||||
newSphere->setTranslation(newSphere->getTranslation() + glm::vec3(randFloatInRange(-0.01f, 0.01f),
|
||||
randFloatInRange(-0.01f, 0.01f), randFloatInRange(-0.01f, 0.01f)));
|
||||
}
|
||||
_data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), oldSphere, _sphere);
|
||||
spannerMutationsPerformed++;
|
||||
}
|
||||
|
||||
// wait until we have a valid lod before sending
|
||||
if (!_lod.isValid()) {
|
||||
return false;
|
||||
}
|
||||
Bitstream& out = _sequencer->startPacket();
|
||||
out << QVariant::fromValue(MetavoxelDeltaMessage());
|
||||
_data.writeDelta(_sendRecords.first().data, _sendRecords.first().lod, out, _lod);
|
||||
|
||||
// record the send
|
||||
SendRecord record = { _sequencer->getOutgoingPacketNumber() + 1, SharedObjectPointer(), _data, _lod };
|
||||
_sendRecords.append(record);
|
||||
|
||||
_sequencer->endPacket();
|
||||
|
||||
} else {
|
||||
// enqueue some number of high priority messages
|
||||
const float MIN_HIGH_PRIORITY_MESSAGES = 0.0f;
|
||||
const float MAX_HIGH_PRIORITY_MESSAGES = 2.0f;
|
||||
_highPriorityMessagesToSend += randFloatInRange(MIN_HIGH_PRIORITY_MESSAGES, MAX_HIGH_PRIORITY_MESSAGES);
|
||||
while (_highPriorityMessagesToSend >= 1.0f) {
|
||||
QVariant message = createRandomMessage();
|
||||
_highPriorityMessagesSent.append(message);
|
||||
_sequencer->sendHighPriorityMessage(message);
|
||||
highPriorityMessagesSent++;
|
||||
_highPriorityMessagesToSend -= 1.0f;
|
||||
}
|
||||
|
||||
// and some number of reliable messages
|
||||
const float MIN_RELIABLE_MESSAGES = 0.0f;
|
||||
const float MAX_RELIABLE_MESSAGES = 4.0f;
|
||||
_reliableMessagesToSend += randFloatInRange(MIN_RELIABLE_MESSAGES, MAX_RELIABLE_MESSAGES);
|
||||
while (_reliableMessagesToSend >= 1.0f) {
|
||||
QVariant message = createRandomMessage();
|
||||
_reliableMessagesSent.append(message);
|
||||
_sequencer->getReliableOutputChannel()->sendMessage(message);
|
||||
reliableMessagesSent++;
|
||||
_reliableMessagesToSend -= 1.0f;
|
||||
}
|
||||
|
||||
// tweak the local state
|
||||
_localState = mutate(_localState);
|
||||
|
||||
// send a packet
|
||||
try {
|
||||
Bitstream& out = _sequencer->startPacket();
|
||||
SequencedTestMessage message = { iterationNumber, createRandomMessage(), _localState };
|
||||
_unreliableMessagesSent.append(message);
|
||||
unreliableMessagesSent++;
|
||||
out << message;
|
||||
_sequencer->endPacket();
|
||||
|
||||
} catch (const QString& message) {
|
||||
qDebug() << message;
|
||||
return true;
|
||||
}
|
||||
|
||||
} catch (const QString& message) {
|
||||
qDebug() << message;
|
||||
return true;
|
||||
// record the send
|
||||
SendRecord record = { _sequencer->getOutgoingPacketNumber(), _localState };
|
||||
_sendRecords.append(record);
|
||||
}
|
||||
|
||||
// record the send
|
||||
SendRecord record = { _sequencer->getOutgoingPacketNumber(), _localState };
|
||||
_sendRecords.append(record);
|
||||
|
||||
maxDatagramsPerPacket = qMax(maxDatagramsPerPacket, datagramsSent - oldDatagramsSent);
|
||||
maxBytesPerPacket = qMax(maxBytesPerPacket, bytesSent - oldBytesSent);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -619,6 +788,39 @@ void Endpoint::handleHighPriorityMessage(const QVariant& message) {
|
|||
}
|
||||
|
||||
void Endpoint::readMessage(Bitstream& in) {
|
||||
if (_mode == METAVOXEL_CLIENT_MODE) {
|
||||
QVariant message;
|
||||
in >> message;
|
||||
handleMessage(message, in);
|
||||
|
||||
// deep-compare data to sent version
|
||||
int packetNumber = _sequencer->getIncomingPacketNumber();
|
||||
foreach (const SendRecord& sendRecord, _other->_sendRecords) {
|
||||
if (sendRecord.packetNumber == packetNumber) {
|
||||
if (!sendRecord.data.deepEquals(_data, _sendRecords.first().lod)) {
|
||||
qDebug() << "Sent/received metavoxel data mismatch.";
|
||||
exit(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// record the receipt
|
||||
ReceiveRecord record = { packetNumber, SharedObjectPointer(), _data, _sendRecords.first().lod };
|
||||
_receiveRecords.append(record);
|
||||
return;
|
||||
}
|
||||
if (_mode == METAVOXEL_SERVER_MODE) {
|
||||
QVariant message;
|
||||
in >> message;
|
||||
handleMessage(message, in);
|
||||
|
||||
// record the receipt
|
||||
ReceiveRecord record = { _sequencer->getIncomingPacketNumber() };
|
||||
_receiveRecords.append(record);
|
||||
return;
|
||||
}
|
||||
|
||||
SequencedTestMessage message;
|
||||
in >> message;
|
||||
|
||||
|
@ -632,17 +834,20 @@ void Endpoint::readMessage(Bitstream& in) {
|
|||
it != _other->_unreliableMessagesSent.end(); it++) {
|
||||
if (it->sequenceNumber == message.sequenceNumber) {
|
||||
if (!messagesEqual(it->submessage, message.submessage)) {
|
||||
throw QString("Sent/received unreliable message mismatch.");
|
||||
qDebug() << "Sent/received unreliable message mismatch.";
|
||||
exit(true);
|
||||
}
|
||||
if (!it->state->equals(message.state)) {
|
||||
throw QString("Delta-encoded object mismatch.");
|
||||
qDebug() << "Delta-encoded object mismatch.";
|
||||
exit(true);
|
||||
}
|
||||
_other->_unreliableMessagesSent.erase(_other->_unreliableMessagesSent.begin(), it + 1);
|
||||
unreliableMessagesReceived++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw QString("Received unsent/already sent unreliable message.");
|
||||
qDebug() << "Received unsent/already sent unreliable message.";
|
||||
exit(true);
|
||||
}
|
||||
|
||||
void Endpoint::handleReliableMessage(const QVariant& message) {
|
||||
|
@ -682,6 +887,22 @@ void Endpoint::clearReceiveRecordsBefore(int index) {
|
|||
_receiveRecords.erase(_receiveRecords.begin(), _receiveRecords.begin() + index + 1);
|
||||
}
|
||||
|
||||
void Endpoint::handleMessage(const QVariant& message, Bitstream& in) {
|
||||
int userType = message.userType();
|
||||
if (userType == ClientStateMessage::Type) {
|
||||
ClientStateMessage state = message.value<ClientStateMessage>();
|
||||
_lod = state.lod;
|
||||
|
||||
} else if (userType == MetavoxelDeltaMessage::Type) {
|
||||
_data.readDelta(_receiveRecords.first().data, _receiveRecords.first().lod, in, _sendRecords.first().lod);
|
||||
|
||||
} else if (userType == QMetaType::QVariantList) {
|
||||
foreach (const QVariant& element, message.toList()) {
|
||||
handleMessage(element, in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) :
|
||||
_foo(foo),
|
||||
_baz(baz),
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <QVariantList>
|
||||
|
||||
#include <DatagramSequencer.h>
|
||||
#include <MetavoxelData.h>
|
||||
#include <ScriptCache.h>
|
||||
|
||||
class SequencedTestMessage;
|
||||
|
@ -39,7 +40,9 @@ class Endpoint : public QObject {
|
|||
|
||||
public:
|
||||
|
||||
Endpoint(const QByteArray& datagramHeader);
|
||||
enum Mode { BASIC_PEER_MODE, METAVOXEL_SERVER_MODE, METAVOXEL_CLIENT_MODE };
|
||||
|
||||
Endpoint(const QByteArray& datagramHeader, Mode mode = BASIC_PEER_MODE);
|
||||
|
||||
void setOther(Endpoint* other) { _other = other; }
|
||||
|
||||
|
@ -60,18 +63,26 @@ private slots:
|
|||
|
||||
private:
|
||||
|
||||
void handleMessage(const QVariant& message, Bitstream& in);
|
||||
|
||||
class SendRecord {
|
||||
public:
|
||||
int packetNumber;
|
||||
SharedObjectPointer localState;
|
||||
MetavoxelData data;
|
||||
MetavoxelLOD lod;
|
||||
};
|
||||
|
||||
class ReceiveRecord {
|
||||
public:
|
||||
int packetNumber;
|
||||
SharedObjectPointer remoteState;
|
||||
MetavoxelData data;
|
||||
MetavoxelLOD lod;
|
||||
};
|
||||
|
||||
Mode _mode;
|
||||
|
||||
DatagramSequencer* _sequencer;
|
||||
QList<SendRecord> _sendRecords;
|
||||
QList<ReceiveRecord> _receiveRecords;
|
||||
|
@ -79,6 +90,11 @@ private:
|
|||
SharedObjectPointer _localState;
|
||||
SharedObjectPointer _remoteState;
|
||||
|
||||
MetavoxelData _data;
|
||||
MetavoxelLOD _lod;
|
||||
|
||||
SharedObjectPointer _sphere;
|
||||
|
||||
Endpoint* _other;
|
||||
QList<QPair<QByteArray, int> > _delayedDatagrams;
|
||||
float _highPriorityMessagesToSend;
|
||||
|
|
Loading…
Reference in a new issue