diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index f52cdf3e49..5c4591cf61 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -311,7 +311,7 @@ MetavoxelPersister::MetavoxelPersister(MetavoxelServer* server) : const char* SAVE_FILE = "/resources/metavoxels.dat"; const int FILE_MAGIC = 0xDADAFACE; -const int FILE_VERSION = 3; +const int FILE_VERSION = 4; void MetavoxelPersister::load() { QString path = QCoreApplication::applicationDirPath() + SAVE_FILE; diff --git a/examples/billiards.js b/examples/billiards.js new file mode 100644 index 0000000000..9bc21ff611 --- /dev/null +++ b/examples/billiards.js @@ -0,0 +1,197 @@ +// Pool Table +var tableParts = []; +var balls = []; + +var LENGTH = 2.84; +var WIDTH = 1.42; +var HEIGHT = 0.80; +var SCALE = 2.0; +var BALL_SIZE = 0.05715; +var BUMPER_WIDTH = 0.15; +var BUMPER_HEIGHT = BALL_SIZE * 2.0; +var HOLE_SIZE = BALL_SIZE; +var DROP_HEIGHT = BALL_SIZE * 3.0; +var GRAVITY = -9.8; +var BALL_GAP = 0.001; + +var startStroke = 0; + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +var screenSize = Controller.getViewportDimensions(); +var reticle = Overlays.addOverlay("image", { + x: screenSize.x / 2 - 16, + y: screenSize.y / 2 - 16, + width: 32, + height: 32, + imageURL: HIFI_PUBLIC_BUCKET + "images/reticle.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); + +function makeTable(pos) { + // Top + tableParts.push(Entities.addEntity( + { type: "Box", + position: pos, + dimensions: { x: LENGTH * SCALE, y: HEIGHT, z: WIDTH * SCALE }, + color: { red: 0, green: 255, blue: 0 } })); + // Long Bumpers + tableParts.push(Entities.addEntity( + { type: "Box", + position: { x: pos.x - LENGTH / 2.0, + y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), + z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE }, + dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE }, + color: { red: 237, green: 201, blue: 175 } })); + tableParts.push(Entities.addEntity( + { type: "Box", + position: { x: pos.x + LENGTH / 2.0, + y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), + z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE }, + dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE }, + color: { red: 237, green: 201, blue: 175 } })); + + tableParts.push(Entities.addEntity( + { type: "Box", + position: { x: pos.x - LENGTH / 2.0, + y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), + z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE }, + dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE }, + color: { red: 237, green: 201, blue: 175 } })); + tableParts.push(Entities.addEntity( + { type: "Box", + position: { x: pos.x + LENGTH / 2.0, + y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), + z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE }, + dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE }, + color: { red: 237, green: 201, blue: 175 } })); + // End bumpers + tableParts.push(Entities.addEntity( + { type: "Box", + position: { x: pos.x + (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE, + y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), + z: pos.z }, + dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE }, + color: { red: 237, green: 201, blue: 175 } })); + + tableParts.push(Entities.addEntity( + { type: "Box", + position: { x: pos.x - (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE, + y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), + z: pos.z }, + dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE }, + color: { red: 237, green: 201, blue: 175 } })); + +} + +function makeBalls(pos) { + var colors = [{ red: 255, green: 255, blue: 0}, // Yellow + { red: 0, green: 0, blue: 255}, // Blue + { red: 255, green: 0, blue: 0}, // Red + { red: 128, green: 0, blue: 128}, // Purple + { red: 255, green: 165, blue: 0}, // Orange + { red: 0, green: 255, blue: 0}, // Green + { red: 128, green: 0, blue: 0}, // Maroon + { red: 0, green: 0, blue: 0}, // Black + { red: 255, green: 255, blue: 224}, // Light Yellow + { red: 173, green: 216, blue: 230}, // Light Blue + { red: 205, green: 92, blue: 92}, // Indian Red + { red: 218, green: 112, blue: 214}, // Orchid + { red: 218, green: 165, blue: 32}, // GoldenRod + { red: 255, green: 99, blue: 71}, // Tomato + { red: 128, green: 128, blue: 128}]; // Gray + + // Object balls + var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + DROP_HEIGHT, z: pos.z }; + for (var row = 1; row <= 5; row++) { + ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE); + for (var spot = 0; spot < row; spot++) { + balls.push(Entities.addEntity( + { type: "Sphere", + position: ballPosition, + dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE }, + color: colors[balls.length], + gravity: { x: 0, y: GRAVITY, z: 0 }, + ignoreCollisions: false, + damping: 0.40, + collisionsWillMove: true })); + ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE; + } + ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE; + } + // Cue Ball + ballPosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + DROP_HEIGHT, z: pos.z }; + balls.push(Entities.addEntity( + { type: "Sphere", + position: ballPosition, + dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE }, + color: { red: 255, green: 255, blue: 255 }, + gravity: { x: 0, y: GRAVITY, z: 0 }, + ignoreCollisions: false, + damping: 0.40, + collisionsWillMove: true })); +} + +function shootCue(velocity) { + var DISTANCE_FROM_CAMERA = BALL_SIZE * 5.0 * SCALE; + var camera = Camera.getPosition(); + var forwardVector = Quat.getFront(Camera.getOrientation()); + var cuePosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA)); + var velocity = Vec3.multiply(forwardVector, velocity); + var BULLET_LIFETIME = 3.0; + var BULLET_GRAVITY = 0.0; + bulletID = Entities.addEntity( + { type: "Sphere", + position: cuePosition, + dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE }, + color: { red: 255, green: 255, blue: 255 }, + velocity: velocity, + lifetime: BULLET_LIFETIME, + gravity: { x: 0, y: BULLET_GRAVITY, z: 0 }, + damping: 0.10, + density: 1000, + ignoreCollisions: false, + collisionsWillMove: true + }); +} + +function keyReleaseEvent(event) { + if ((startStroke > 0) && event.text == "SPACE") { + var endTime = new Date().getTime(); + var delta = endTime - startStroke; + shootCue(delta / 100.0); + startStroke = 0; + } +} + +function keyPressEvent(event) { + // Fire a cue ball + if ((startStroke == 0) && (event.text == "SPACE")) { + startStroke = new Date().getTime(); + } +} + +function cleanup() { + for (var i = 0; i < tableParts.length; i++) { + if (!tableParts[i].isKnownID) { + tableParts[i] = Entities.identifyEntity(tableParts[i]); + } + Entities.deleteEntity(tableParts[i]); + } + for (var i = 0; i < balls.length; i++) { + if (!balls[i].isKnownID) { + balls[i] = Entities.identifyEntity(balls[i]); + } + Entities.deleteEntity(balls[i]); + } + Overlays.deleteOverlay(reticle); +} + +var tableCenter = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation()))); + +makeTable(tableCenter); +makeBalls(tableCenter); + +Script.scriptEnding.connect(cleanup); +Controller.keyPressEvent.connect(keyPressEvent); +Controller.keyReleaseEvent.connect(keyReleaseEvent); diff --git a/interface/resources/shaders/metavoxel_heightfield_splat.frag b/interface/resources/shaders/metavoxel_heightfield_splat.frag index bb6b0d6536..0754987fc3 100644 --- a/interface/resources/shaders/metavoxel_heightfield_splat.frag +++ b/interface/resources/shaders/metavoxel_heightfield_splat.frag @@ -22,7 +22,8 @@ varying vec4 alphaValues; void main(void) { // blend the splat textures - gl_FragColor = gl_Color * (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x + + gl_FragColor = vec4(gl_Color.rgb, step(1.0, gl_Color.a + 1.0 / 512.0)) * + (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x + texture2D(diffuseMaps[1], gl_TexCoord[1].st) * alphaValues.y + texture2D(diffuseMaps[2], gl_TexCoord[2].st) * alphaValues.z + texture2D(diffuseMaps[3], gl_TexCoord[3].st) * alphaValues.w); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 9e953344d6..c07f249aa0 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -228,7 +228,12 @@ unsigned int Overlays::cloneOverlay(unsigned int id) { } else if (_overlaysWorld.contains(id)) { thisOverlay = _overlaysWorld[id]; } - return addOverlay(thisOverlay->createClone()); + + if (thisOverlay) { + return addOverlay(thisOverlay->createClone()); + } + + return 0; // Not found } bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index c3bd05d3c7..642562bfb5 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -2432,12 +2432,13 @@ void MappedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, } QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const { + bool reread = (object != NULL); if (!object && _metaObject) { object = _metaObject->newInstance(); } foreach (const StreamerPropertyPair& property, _properties) { QVariant value = property.first->read(in); - if (property.second.isValid() && object) { + if (property.second.isValid() && object && !reread) { property.second.write(object, value); } } @@ -2445,6 +2446,7 @@ QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const { } QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const { + bool reread = (object != NULL); if (!object && _metaObject) { object = _metaObject->newInstance(); } @@ -2452,7 +2454,7 @@ QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* refere QVariant value; property.first->readDelta(in, value, (property.second.isValid() && reference && reference->metaObject() == _metaObject) ? property.second.read(reference) : QVariant()); - if (property.second.isValid() && object) { + if (property.second.isValid() && object && !reread) { property.second.write(object, value); } } @@ -2475,13 +2477,13 @@ void SharedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, QObject* SharedObjectStreamer::read(Bitstream& in, QObject* object) const { QObject* result = MappedObjectStreamer::read(in, object); - static_cast(result)->readExtra(in); + static_cast(result)->readExtra(in, object != NULL); return result; } QObject* SharedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const { QObject* result = MappedObjectStreamer::readRawDelta(in, reference, object); - static_cast(result)->readExtraDelta(in, static_cast(reference)); + static_cast(result)->readExtraDelta(in, static_cast(reference), object != NULL); return result; } @@ -2592,6 +2594,7 @@ void GenericObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, } QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const { + bool reread = (object != NULL); if (!object) { object = new GenericSharedObject(_weakSelf); } @@ -2599,11 +2602,14 @@ QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const { foreach (const StreamerNamePair& property, _properties) { values.append(property.first->read(in)); } - static_cast(object)->setValues(values); + if (!reread) { + static_cast(object)->setValues(values); + } return object; } QObject* GenericObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const { + bool reread = (object != NULL); if (!object) { object = new GenericSharedObject(_weakSelf); } @@ -2615,7 +2621,9 @@ QObject* GenericObjectStreamer::readRawDelta(Bitstream& in, const QObject* refer static_cast(reference)->getValues().at(i) : QVariant()); values.append(value); } - static_cast(object)->setValues(values); + if (!reread) { + static_cast(object)->setValues(values); + } return object; } diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index dcfa9732b3..6369037e2a 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -135,7 +135,7 @@ void SharedObject::writeExtra(Bitstream& out) const { // nothing by default } -void SharedObject::readExtra(Bitstream& in) { +void SharedObject::readExtra(Bitstream& in, bool reread) { // nothing by default } @@ -143,7 +143,7 @@ void SharedObject::writeExtraDelta(Bitstream& out, const SharedObject* reference // nothing by default } -void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference) { +void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread) { // nothing by default } diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index ebea322bf1..cd46ae9658 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -84,13 +84,15 @@ public: virtual void writeExtra(Bitstream& out) const; /// Reads the non-property contents of this object from the specified stream. - virtual void readExtra(Bitstream& in); + /// \param reread if true, reread the contents from the stream but don't reapply them + virtual void readExtra(Bitstream& in, bool reread = false); /// Writes the delta-encoded non-property contents of this object to the specified stream. virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const; /// Reads the delta-encoded non-property contents of this object from the specified stream. - virtual void readExtraDelta(Bitstream& in, const SharedObject* reference); + /// \param reread if true, reread the contents from the stream but don't reapply them + virtual void readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread = false); /// Writes the subdivision of the contents of this object (preceeded by a /// reference to the object itself) to the specified stream if necessary. diff --git a/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index 15ab15ad1a..4c27a92edf 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -685,41 +685,17 @@ void HeightfieldHeightEditor::clear() { } static QByteArray encodeHeightfieldColor(int offsetX, int offsetY, int width, int height, const QByteArray& contents) { - QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE + contents.size(), 0); + QByteArray inflated(HEIGHTFIELD_DATA_HEADER_SIZE, 0); qint32* header = (qint32*)inflated.data(); *header++ = offsetX; *header++ = offsetY; *header++ = width; *header++ = height; if (!contents.isEmpty()) { - // encode with Paeth filter (see http://en.wikipedia.org/wiki/Portable_Network_Graphics#Filtering) - const uchar* src = (const uchar*)contents.constData(); - uchar* dest = (uchar*)inflated.data() + HEIGHTFIELD_DATA_HEADER_SIZE; - *dest++ = *src++; - *dest++ = *src++; - *dest++ = *src++; - int stride = width * DataBlock::COLOR_BYTES; - for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { - *dest = *src - src[-DataBlock::COLOR_BYTES]; - } - for (int y = 1; y < height; y++) { - *dest++ = *src - src[-stride]; - src++; - *dest++ = *src - src[-stride]; - src++; - *dest++ = *src - src[-stride]; - src++; - for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { - int a = src[-DataBlock::COLOR_BYTES]; - int b = src[-stride]; - int c = src[-stride - DataBlock::COLOR_BYTES]; - int p = a + b - c; - int ad = abs(a - p); - int bd = abs(b - p); - int cd = abs(c - p); - *dest = *src - (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); - } - } + QBuffer buffer(&inflated); + buffer.open(QIODevice::WriteOnly | QIODevice::Append); + QImage((const uchar*)contents.constData(), width, height, width * DataBlock::COLOR_BYTES, + QImage::Format_RGB888).save(&buffer, "JPG"); } return qCompress(inflated); } @@ -731,35 +707,19 @@ static QByteArray decodeHeightfieldColor(const QByteArray& encoded, int& offsetX offsetY = *header++; width = *header++; height = *header++; - QByteArray contents(inflated.size() - HEIGHTFIELD_DATA_HEADER_SIZE, 0); - if (!contents.isEmpty()) { - const uchar* src = (const uchar*)inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE; - uchar* dest = (uchar*)contents.data(); - *dest++ = *src++; - *dest++ = *src++; - *dest++ = *src++; - int stride = width * DataBlock::COLOR_BYTES; - for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { - *dest = *src + dest[-DataBlock::COLOR_BYTES]; - } - for (int y = 1; y < height; y++) { - *dest = (*src++) + dest[-stride]; - dest++; - *dest = (*src++) + dest[-stride]; - dest++; - *dest = (*src++) + dest[-stride]; - dest++; - for (uchar* end = dest + stride - DataBlock::COLOR_BYTES; dest != end; dest++, src++) { - int a = dest[-DataBlock::COLOR_BYTES]; - int b = dest[-stride]; - int c = dest[-stride - DataBlock::COLOR_BYTES]; - int p = a + b - c; - int ad = abs(a - p); - int bd = abs(b - p); - int cd = abs(c - p); - *dest = *src + (ad < bd ? (ad < cd ? a : c) : (bd < cd ? b : c)); - } - } + int payloadSize = inflated.size() - HEIGHTFIELD_DATA_HEADER_SIZE; + if (payloadSize == 0) { + return QByteArray(); + } + QImage image = QImage::fromData((const uchar*)inflated.constData() + HEIGHTFIELD_DATA_HEADER_SIZE, payloadSize, "JPG"); + if (image.format() != QImage::Format_RGB888) { + image = image.convertToFormat(QImage::Format_RGB888); + } + QByteArray contents(width * height * DataBlock::COLOR_BYTES, 0); + char* dest = contents.data(); + int stride = width * DataBlock::COLOR_BYTES; + for (int y = 0; y < height; y++, dest += stride) { + memcpy(dest, image.constScanLine(y), stride); } return contents; } @@ -3722,9 +3682,18 @@ void Heightfield::writeExtra(Bitstream& out) const { _root->write(state); } -void Heightfield::readExtra(Bitstream& in) { +void Heightfield::readExtra(Bitstream& in, bool reread) { if (getWillBeVoxelized()) { - in >> _height >> _color >> _material >> _stack; + if (reread) { + HeightfieldHeightPointer height; + HeightfieldColorPointer color; + HeightfieldMaterialPointer material; + HeightfieldStackPointer stack; + in >> height >> color >> material >> stack; + + } else { + in >> _height >> _color >> _material >> _stack; + } return; } MetavoxelLOD lod; @@ -3736,7 +3705,9 @@ void Heightfield::readExtra(Bitstream& in) { HeightfieldNodePointer root(new HeightfieldNode()); root->read(state); - setRoot(root); + if (!reread) { + setRoot(root); + } } void Heightfield::writeExtraDelta(Bitstream& out, const SharedObject* reference) const { @@ -3760,7 +3731,7 @@ void Heightfield::writeExtraDelta(Bitstream& out, const SharedObject* reference) } } -void Heightfield::readExtraDelta(Bitstream& in, const SharedObject* reference) { +void Heightfield::readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread) { MetavoxelLOD lod, referenceLOD; if (in.getContext()) { MetavoxelStreamBase* base = static_cast(in.getContext()); @@ -3770,16 +3741,21 @@ void Heightfield::readExtraDelta(Bitstream& in, const SharedObject* reference) { HeightfieldStreamBase base = { in, lod, referenceLOD }; HeightfieldStreamState state = { base, glm::vec2(), 1.0f }; - setRoot(static_cast(reference)->getRoot()); bool changed; in >> changed; if (changed) { HeightfieldNodePointer root(new HeightfieldNode()); root->readDelta(static_cast(reference)->getRoot(), state); - setRoot(root); - + if (!reread) { + setRoot(root); + } } else if (state.becameSubdividedOrCollapsed()) { - setRoot(HeightfieldNodePointer(_root->readSubdivision(state))); + HeightfieldNodePointer root(_root->readSubdivision(state)); + if (!reread) { + setRoot(root); + } + } else if (!reread) { + setRoot(static_cast(reference)->getRoot()); } } diff --git a/libraries/metavoxels/src/Spanner.h b/libraries/metavoxels/src/Spanner.h index 12d04edd14..653893c84d 100644 --- a/libraries/metavoxels/src/Spanner.h +++ b/libraries/metavoxels/src/Spanner.h @@ -820,9 +820,9 @@ public: virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal); virtual void writeExtra(Bitstream& out) const; - virtual void readExtra(Bitstream& in); + virtual void readExtra(Bitstream& in, bool reread); virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const; - virtual void readExtraDelta(Bitstream& in, const SharedObject* reference); + virtual void readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread); virtual void maybeWriteSubdivision(Bitstream& out); virtual SharedObject* readSubdivision(Bitstream& in); diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 02b45ca574..7e52e76f27 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -158,7 +158,8 @@ void AccountManager::setAuthURL(const QUrl& authURL) { void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation, const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray, - QHttpMultiPart* dataMultiPart) { + QHttpMultiPart* dataMultiPart, + const QVariantMap& propertyMap) { QMetaObject::invokeMethod(this, "invokedRequest", Q_ARG(const QString&, path), @@ -166,13 +167,15 @@ void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessMan Q_ARG(QNetworkAccessManager::Operation, operation), Q_ARG(const JSONCallbackParameters&, callbackParams), Q_ARG(const QByteArray&, dataByteArray), - Q_ARG(QHttpMultiPart*, dataMultiPart)); + Q_ARG(QHttpMultiPart*, dataMultiPart), + Q_ARG(QVariantMap, propertyMap)); } void AccountManager::unauthenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation, - const JSONCallbackParameters& callbackParams, - const QByteArray& dataByteArray, - QHttpMultiPart* dataMultiPart) { + const JSONCallbackParameters& callbackParams, + const QByteArray& dataByteArray, + QHttpMultiPart* dataMultiPart, + const QVariantMap& propertyMap) { QMetaObject::invokeMethod(this, "invokedRequest", Q_ARG(const QString&, path), @@ -180,14 +183,16 @@ void AccountManager::unauthenticatedRequest(const QString& path, QNetworkAccessM Q_ARG(QNetworkAccessManager::Operation, operation), Q_ARG(const JSONCallbackParameters&, callbackParams), Q_ARG(const QByteArray&, dataByteArray), - Q_ARG(QHttpMultiPart*, dataMultiPart)); + Q_ARG(QHttpMultiPart*, dataMultiPart), + Q_ARG(QVariantMap, propertyMap)); } void AccountManager::invokedRequest(const QString& path, bool requiresAuthentication, QNetworkAccessManager::Operation operation, const JSONCallbackParameters& callbackParams, - const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) { + const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart, + const QVariantMap& propertyMap) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); @@ -235,7 +240,9 @@ void AccountManager::invokedRequest(const QString& path, } else { networkReply = networkAccessManager.put(networkRequest, dataMultiPart); } - dataMultiPart->setParent(networkReply); + + // make sure dataMultiPart is destroyed when the reply is + connect(networkReply, &QNetworkReply::destroyed, dataMultiPart, &QHttpMultiPart::deleteLater); } else { networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); if (operation == QNetworkAccessManager::PostOperation) { @@ -255,6 +262,14 @@ void AccountManager::invokedRequest(const QString& path, } if (networkReply) { + if (!propertyMap.isEmpty()) { + // we have properties to set on the reply so the user can check them after + foreach(const QString& propertyKey, propertyMap.keys()) { + networkReply->setProperty(qPrintable(propertyKey), propertyMap.value(propertyKey)); + } + } + + if (!callbackParams.isEmpty()) { // if we have information for a callback, insert the callbackParams into our local map _pendingCallbackMap.insert(networkReply, callbackParams); diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 53512a8bb3..06fe366d69 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -48,13 +48,15 @@ public: QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation, const JSONCallbackParameters& callbackParams = JSONCallbackParameters(), const QByteArray& dataByteArray = QByteArray(), - QHttpMultiPart* dataMultiPart = NULL); + QHttpMultiPart* dataMultiPart = NULL, + const QVariantMap& propertyMap = QVariantMap()); void unauthenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation, const JSONCallbackParameters& callbackParams = JSONCallbackParameters(), const QByteArray& dataByteArray = QByteArray(), - QHttpMultiPart* dataMultiPart = NULL); + QHttpMultiPart* dataMultiPart = NULL, + const QVariantMap& propertyMap = QVariantMap()) ; const QUrl& getAuthURL() const { return _authURL; } void setAuthURL(const QUrl& authURL); @@ -109,7 +111,8 @@ private: QNetworkAccessManager::Operation operation, const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray, - QHttpMultiPart* dataMultiPart); + QHttpMultiPart* dataMultiPart, + const QVariantMap& propertyMap); QUrl _authURL; QMap _pendingCallbackMap; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 6c228054d4..4f5bd05537 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -121,14 +121,16 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { if (!handleUsername(lookupUrl.authority())) { // we're assuming this is either a network address or global place name // check if it is a network address first - if (!handleNetworkAddress(lookupUrl.host() + if (handleNetworkAddress(lookupUrl.host() + (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) { + // we may have a path that defines a relative viewpoint - if so we should jump to that now + handleRelativeViewpoint(lookupUrl.path()); + } else { // wasn't an address - lookup the place name - attemptPlaceNameLookup(lookupUrl.host()); + // we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after + attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path()); + } - - // we may have a path that defines a relative viewpoint - if so we should jump to that now - handleRelativeViewpoint(lookupUrl.path()); } return true; @@ -166,12 +168,14 @@ void AddressManager::handleAPIResponse(QNetworkReply& requestReply) { QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object(); QJsonObject dataObject = responseObject["data"].toObject(); - goToAddressFromObject(dataObject.toVariantMap()); + goToAddressFromObject(dataObject.toVariantMap(), requestReply); emit lookupResultsFinished(); } -void AddressManager::goToAddressFromObject(const QVariantMap& dataObject) { +const char OVERRIDE_PATH_KEY[] = "override_path"; + +void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QNetworkReply& reply) { const QString DATA_OBJECT_PLACE_KEY = "place"; const QString DATA_OBJECT_USER_LOCATION_KEY = "location"; @@ -205,6 +209,8 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject) { if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) { QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString(); + qDebug() << "Possible domain change required to connect to" << domainHostname + << "on" << DEFAULT_DOMAIN_SERVER_PORT; emit possibleDomainChangeRequired(domainHostname, DEFAULT_DOMAIN_SERVER_PORT); } else { QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString(); @@ -213,6 +219,9 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject) { QString domainIDString = domainObject[DOMAIN_ID_KEY].toString(); QUuid domainID(domainIDString); + qDebug() << "Possible domain change required to connect to domain with ID" << domainID + << "via ice-server at" << iceServerAddress; + emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID); } @@ -225,18 +234,29 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject) { QString newRootPlaceName = rootMap[PLACE_NAME_KEY].toString(); setRootPlaceName(newRootPlaceName); - // take the path that came back - const QString PLACE_PATH_KEY = "path"; - QString returnedPath = locationMap[PLACE_PATH_KEY].toString(); + // check if we had a path to override the path returned + QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString(); - bool shouldFaceViewpoint = locationMap.contains(LOCATION_API_ONLINE_KEY); - - if (!returnedPath.isEmpty()) { - // try to parse this returned path as a viewpoint, that's the only thing it could be for now - if (!handleRelativeViewpoint(returnedPath, shouldFaceViewpoint)) { - qDebug() << "Received a location path that was could not be handled as a viewpoint -" << returnedPath; + if (!overridePath.isEmpty()) { + if (!handleRelativeViewpoint(overridePath)){ + qDebug() << "User entered path could not be handled as a viewpoint - " << overridePath; + } + } else { + // take the path that came back + const QString PLACE_PATH_KEY = "path"; + QString returnedPath = locationMap[PLACE_PATH_KEY].toString(); + + bool shouldFaceViewpoint = locationMap.contains(LOCATION_API_ONLINE_KEY); + + if (!returnedPath.isEmpty()) { + // try to parse this returned path as a viewpoint, that's the only thing it could be for now + if (!handleRelativeViewpoint(returnedPath, shouldFaceViewpoint)) { + qDebug() << "Received a location path that was could not be handled as a viewpoint -" << returnedPath; + } } } + + } else { qDebug() << "Received an address manager API response with no domain key. Cannot parse."; qDebug() << locationMap; @@ -262,12 +282,21 @@ void AddressManager::handleAPIError(QNetworkReply& errorReply) { const QString GET_PLACE = "/api/v1/places/%1"; -void AddressManager::attemptPlaceNameLookup(const QString& lookupString) { +void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath) { // assume this is a place name and see if we can get any info on it QString placeName = QUrl::toPercentEncoding(lookupString); + + QVariantMap requestParams; + if (!overridePath.isEmpty()) { + requestParams.insert(OVERRIDE_PATH_KEY, overridePath); + } + AccountManager::getInstance().unauthenticatedRequest(GET_PLACE.arg(placeName), QNetworkAccessManager::GetOperation, - apiCallbackParameters()); + apiCallbackParameters(), + QByteArray(), + NULL, + requestParams); } bool AddressManager::handleNetworkAddress(const QString& lookupString) { diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 8d423e439e..0c24c12cc9 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -47,7 +47,7 @@ public: const QString& getRootPlaceName() const { return _rootPlaceName; } void setRootPlaceName(const QString& rootPlaceName); - void attemptPlaceNameLookup(const QString& lookupString); + void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath = QString()); void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } @@ -57,7 +57,7 @@ public: public slots: void handleLookupString(const QString& lookupString); void goToUser(const QString& username); - void goToAddressFromObject(const QVariantMap& addressMap); + void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply); void storeCurrentAddress(); diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 5b9811a152..110892a106 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -78,7 +78,7 @@ PacketVersion versionForPacketType(PacketType type) { case PacketTypeAudioStreamStats: return 1; case PacketTypeMetavoxelData: - return 11; + return 12; default: return 0; }