From f8f9c25d381620ff1e2ed0a6d2fcb274ab960eb4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 14:59:28 -0800 Subject: [PATCH 01/11] Back to JPG compression for heightfield colors. --- .../src/metavoxels/MetavoxelServer.cpp | 2 +- libraries/metavoxels/src/Spanner.cpp | 76 +++++-------------- libraries/networking/src/PacketHeaders.cpp | 2 +- 3 files changed, 20 insertions(+), 60 deletions(-) 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/libraries/metavoxels/src/Spanner.cpp b/libraries/metavoxels/src/Spanner.cpp index e890821ec2..b550207ec0 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -681,41 +681,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); } @@ -727,35 +703,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; } 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; } From e5f3d48ffbbdac5fb58923caf9ccc2edf6513f18 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 16:33:41 -0800 Subject: [PATCH 02/11] Fix for splatted textures against empty regions. --- interface/resources/shaders/metavoxel_heightfield_splat.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/shaders/metavoxel_heightfield_splat.frag b/interface/resources/shaders/metavoxel_heightfield_splat.frag index bb6b0d6536..180cb653ad 100644 --- a/interface/resources/shaders/metavoxel_heightfield_splat.frag +++ b/interface/resources/shaders/metavoxel_heightfield_splat.frag @@ -22,7 +22,7 @@ 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(0.5, gl_Color.a)) * (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); From dafa44570a448cffc50d04c828fa3e8bcf235584 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 17:15:58 -0800 Subject: [PATCH 03/11] That was not the shader change I wanted to check in. This was. --- interface/resources/shaders/metavoxel_heightfield_splat.frag | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/resources/shaders/metavoxel_heightfield_splat.frag b/interface/resources/shaders/metavoxel_heightfield_splat.frag index 180cb653ad..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 = vec4(gl_Color.rgb, step(0.5, gl_Color.a)) * (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); From a16b54a34a9ff0e9a15e8591a30c9dde29ce8735 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 17:23:31 -0800 Subject: [PATCH 04/11] Let's try not using the "augmented" data, since we don't need it. --- interface/src/MetavoxelSystem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index a566b18b7e..eb2933d113 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -154,7 +154,7 @@ void MetavoxelSystem::simulate(float deltaTime) { } SimulateVisitor simulateVisitor(deltaTime, getLOD()); - guideToAugmented(simulateVisitor); + guide(simulateVisitor); } class RenderVisitor : public MetavoxelVisitor { @@ -199,7 +199,7 @@ void MetavoxelSystem::render() { viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight()); RenderVisitor renderVisitor(getLOD()); - guideToAugmented(renderVisitor, true); + guide(renderVisitor); if (!_heightfieldBaseBatches.isEmpty() && Menu::getInstance()->isOptionChecked(MenuOption::RenderHeightfields)) { glEnableClientState(GL_VERTEX_ARRAY); From 16de6a130bfdc81346f942072b248687fc12df3e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 17:29:43 -0800 Subject: [PATCH 05/11] Scratch that last change; it wasn't the problem. --- interface/src/MetavoxelSystem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index eb2933d113..a566b18b7e 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -154,7 +154,7 @@ void MetavoxelSystem::simulate(float deltaTime) { } SimulateVisitor simulateVisitor(deltaTime, getLOD()); - guide(simulateVisitor); + guideToAugmented(simulateVisitor); } class RenderVisitor : public MetavoxelVisitor { @@ -199,7 +199,7 @@ void MetavoxelSystem::render() { viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight()); RenderVisitor renderVisitor(getLOD()); - guide(renderVisitor); + guideToAugmented(renderVisitor, true); if (!_heightfieldBaseBatches.isEmpty() && Menu::getInstance()->isOptionChecked(MenuOption::RenderHeightfields)) { glEnableClientState(GL_VERTEX_ARRAY); From 214a18c44649fd10b460150efcfb4acdfd5a0e4a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 19 Jan 2015 19:33:12 -0800 Subject: [PATCH 06/11] Guard against derefencing NULL --- interface/src/ui/overlays/Overlays.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 8f9f54c591..b08b429ad0 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -220,7 +220,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) { From 35bd1cda8987edc5bf209bfbdfecac7769428869 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 19 Jan 2015 21:52:56 -0800 Subject: [PATCH 07/11] I believe this should fix some problems with flickering/crashing on edits: don't reapply properties that we've already read (thus clearing cached data, stepping on other threads, etc.) --- libraries/metavoxels/src/Bitstream.cpp | 20 +++++++++----- libraries/metavoxels/src/SharedObject.cpp | 4 +-- libraries/metavoxels/src/SharedObject.h | 6 +++-- libraries/metavoxels/src/Spanner.cpp | 32 +++++++++++++++++------ libraries/metavoxels/src/Spanner.h | 4 +-- 5 files changed, 46 insertions(+), 20 deletions(-) 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 b550207ec0..c771b8fb4a 100644 --- a/libraries/metavoxels/src/Spanner.cpp +++ b/libraries/metavoxels/src/Spanner.cpp @@ -3678,9 +3678,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; @@ -3692,7 +3701,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 { @@ -3716,7 +3727,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()); @@ -3726,16 +3737,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); From 0f368f52fb78523f1f97a51040c938afe2a3807c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 20 Jan 2015 09:46:18 -0800 Subject: [PATCH 08/11] first cut of billiards.js, good enough to play with a bit --- examples/billiards.js | 197 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 examples/billiards.js 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); From 483d072709c086f20eec186984f4a45f7ad0cc36 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 Jan 2015 10:47:57 -0800 Subject: [PATCH 09/11] handle override path from address bar --- libraries/networking/src/AccountManager.cpp | 27 +++++++--- libraries/networking/src/AccountManager.h | 9 ++-- libraries/networking/src/AddressManager.cpp | 60 ++++++++++++++------- libraries/networking/src/AddressManager.h | 4 +- 4 files changed, 70 insertions(+), 30 deletions(-) diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 0c63ecc731..067bd110c4 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(); @@ -255,6 +260,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 fba3861f43..54f45304ad 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -119,14 +119,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; @@ -164,12 +166,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"; @@ -223,18 +227,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; @@ -260,12 +275,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 58e4e88330..3071d68ea2 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(); From a66888939c7c0ebef3520036db1b5449a3366537 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 Jan 2015 10:51:11 -0800 Subject: [PATCH 10/11] add some debugging for domain switch --- libraries/networking/src/AccountManager.cpp | 1 + libraries/networking/src/AddressManager.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 067bd110c4..f902adbc18 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -240,6 +240,7 @@ void AccountManager::invokedRequest(const QString& path, } else { networkReply = networkAccessManager.put(networkRequest, dataMultiPart); } + qDebug() << "IS THIS WHERE?"; dataMultiPart->setParent(networkReply); } else { networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 54f45304ad..ff2925beea 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -207,6 +207,8 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const 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(); @@ -215,6 +217,9 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const 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); } From cd23876afc6147526337c332ddf829b40fd5cb7b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 Jan 2015 11:00:08 -0800 Subject: [PATCH 11/11] fix QHttpMultiPart destroy from AccountManager --- libraries/networking/src/AccountManager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index f902adbc18..c4a4c4a458 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -240,8 +240,9 @@ void AccountManager::invokedRequest(const QString& path, } else { networkReply = networkAccessManager.put(networkRequest, dataMultiPart); } - qDebug() << "IS THIS WHERE?"; - 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) {