From 9ea53f8070b11aa37a0866994af4fc4e9c5d2980 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 4 Jun 2014 11:38:46 -0700 Subject: [PATCH 01/38] some testing with editModels --- examples/editModels.js | 76 ++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 24ab7da1a1..c2072a46cd 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -152,6 +152,7 @@ function controller(wichSide) { this.jointsIntersectingFromStart.push(i); } } + this.showLaser(false); } } @@ -196,6 +197,7 @@ function controller(wichSide) { this.grabbing = false; this.modelID.isKnownID = false; this.jointsIntersectingFromStart = []; + this.showLaser(true); } this.checkTrigger = function () { @@ -258,41 +260,43 @@ function controller(wichSide) { Overlays.editOverlay(this.laser, { position: startPosition, - end: endPosition, - visible: true + end: endPosition }); Overlays.editOverlay(this.ball, { - position: endPosition, - visible: true + position: endPosition }); Overlays.editOverlay(this.leftRight, { position: Vec3.sum(endPosition, Vec3.multiply(this.right, 2 * this.guideScale)), - end: Vec3.sum(endPosition, Vec3.multiply(this.right, -2 * this.guideScale)), - visible: true + end: Vec3.sum(endPosition, Vec3.multiply(this.right, -2 * this.guideScale)) }); Overlays.editOverlay(this.topDown, {position: Vec3.sum(endPosition, Vec3.multiply(this.up, 2 * this.guideScale)), - end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale)), - visible: true + end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale)) }); + this.showLaser(!this.grabbing); } - this.hideLaser = function() { - Overlays.editOverlay(this.laser, { visible: false }); - Overlays.editOverlay(this.ball, { visible: false }); - Overlays.editOverlay(this.leftRight, { visible: false }); - Overlays.editOverlay(this.topDown, { visible: false }); + this.showLaser = function(show) { + Overlays.editOverlay(this.laser, { visible: show }); + Overlays.editOverlay(this.ball, { visible: show }); + Overlays.editOverlay(this.leftRight, { visible: show }); + Overlays.editOverlay(this.topDown, { visible: show }); } this.moveModel = function () { if (this.grabbing) { - var newPosition = Vec3.sum(this.palmPosition, - Vec3.multiply(this.front, this.x)); - newPosition = Vec3.sum(newPosition, - Vec3.multiply(this.up, this.y)); - newPosition = Vec3.sum(newPosition, - Vec3.multiply(this.right, this.z)); + var forward = Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 }); + var d = Vec3.dot(forward, MyAvatar.position); + + var factor1 = Vec3.dot(forward, this.palmPosition) - d; + var factor2 = Vec3.dot(forward, this.oldModelPosition) - d; + var vector = Vec3.subtract(this.palmPosition, this.oldPalmPosition); + + var newPosition = Vec3.sum(this.oldModelPosition, + Vec3.multiply(vector, + factor2 / factor1)); + var newRotation = Quat.multiply(this.rotation, Quat.inverse(this.oldRotation)); @@ -457,20 +461,34 @@ function moveModels() { var newPosition = Vec3.sum(middle, Vec3.multiply(Vec3.subtract(leftController.oldModelPosition, oldMiddle), ratio)); - //Vec3.print("Ratio : " + ratio + " New position: ", newPosition); - var rotation = Quat.multiply(leftController.rotation, - Quat.inverse(leftController.oldRotation)); + + + var u = Vec3.normalize(Vec3.subtract(rightController.oldPalmPosition, leftController.oldPalmPosition)); + var v = Vec3.normalize(Vec3.subtract(rightController.palmPosition, leftController.palmPosition)); + + var cos_theta = Vec3.dot(Vec3.normalize(u), Vec3.normalize(v)); + var angle = Math.acos(cos_theta); + var w = Vec3.normalize(Vec3.cross(u, v)); + + + var rotation = Quat.angleAxis(angle, w); + + rotation = Quat.multiply(rotation, leftController.oldModelRotation); Models.editModel(leftController.modelID, { - position: newPosition, - //modelRotation: rotation, - radius: leftController.oldModelRadius * ratio + //position: newPosition, + modelRotation: rotation, + //radius: leftController.oldModelRadius * ratio }); - leftController.oldModelPosition = newPosition; + //leftController.oldModelPosition = newPosition; leftController.oldModelRotation = rotation; - leftController.oldModelRadius *= ratio; + //leftController.oldModelRadius *= ratio; + + //rightController.oldModelPosition = newPosition; + rightController.oldModelRotation = rotation; + //rightController.oldModelRadius *= ratio; return; } @@ -498,8 +516,8 @@ function checkController(deltaTime) { if (hydraConnected) { hydraConnected = false; - leftController.hideLaser(); - rightController.hideLaser(); + leftController.showLaser(false); + rightController.showLaser(false); } } From 15863198d1f8a0792361e8af317080aa5cc0f75c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 4 Jun 2014 13:52:31 -0700 Subject: [PATCH 02/38] change editModels icon --- examples/editModels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/editModels.js b/examples/editModels.js index c2072a46cd..47da9b0c0a 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -528,7 +528,7 @@ function initToolBar() { toolBar = new ToolBar(0, 0, ToolBar.VERTICAL); // New Model newModel = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", + imageURL: toolIconUrl + "add-model-tool.svg", subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, width: toolWidth, height: toolHeight, visible: true, From 79092e3e81a7605c49d38050521c133c92595d88 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 4 Jun 2014 14:18:28 -0700 Subject: [PATCH 03/38] editModels now has 2 modes --- examples/editModels.js | 150 ++++++++++++++++++++++++++++------------- 1 file changed, 102 insertions(+), 48 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 47da9b0c0a..24360c526d 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -42,6 +42,8 @@ var toolBar; var jointList = MyAvatar.getJointNames(); +var mode = 0; + function isLocked(properties) { // special case to lock the ground plane model in hq. if (location.hostname == "hq.highfidelity.io" && @@ -57,6 +59,7 @@ function controller(wichSide) { this.palm = 2 * wichSide; this.tip = 2 * wichSide + 1; this.trigger = wichSide; + this.bumper = 6 * wichSide + 5; this.oldPalmPosition = Controller.getSpatialControlPosition(this.palm); this.palmPosition = Controller.getSpatialControlPosition(this.palm); @@ -77,6 +80,7 @@ function controller(wichSide) { this.rotation = this.oldRotation; this.triggerValue = Controller.getTriggerValue(this.trigger); + this.bumperValue = Controller.isButtonPressed(this.bumper); this.pressed = false; // is trigger pressed this.pressing = false; // is trigger being pressed (is pressed now but wasn't previously) @@ -274,7 +278,7 @@ function controller(wichSide) { Overlays.editOverlay(this.topDown, {position: Vec3.sum(endPosition, Vec3.multiply(this.up, 2 * this.guideScale)), end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale)) }); - this.showLaser(!this.grabbing); + this.showLaser(!this.grabbing || mode == 0); } this.showLaser = function(show) { @@ -286,20 +290,44 @@ function controller(wichSide) { this.moveModel = function () { if (this.grabbing) { - var forward = Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 }); - var d = Vec3.dot(forward, MyAvatar.position); + var newPosition; + var newRotation; - var factor1 = Vec3.dot(forward, this.palmPosition) - d; - var factor2 = Vec3.dot(forward, this.oldModelPosition) - d; - var vector = Vec3.subtract(this.palmPosition, this.oldPalmPosition); - - var newPosition = Vec3.sum(this.oldModelPosition, - Vec3.multiply(vector, - factor2 / factor1)); + switch (mode) { + case 0: + newPosition = Vec3.sum(this.palmPosition, + Vec3.multiply(this.front, this.x)); + newPosition = Vec3.sum(newPosition, + Vec3.multiply(this.up, this.y)); + newPosition = Vec3.sum(newPosition, + Vec3.multiply(this.right, this.z)); + break; + case 1: + var forward = Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 }); + var d = Vec3.dot(forward, MyAvatar.position); + + var factor1 = Vec3.dot(forward, this.palmPosition) - d; + var factor2 = Vec3.dot(forward, this.oldModelPosition) - d; + var vector = Vec3.subtract(this.palmPosition, this.oldPalmPosition); + + if (factor2 < 0) { + factor2 = 0; + } + if (factor1 <= 0) { + factor1 = 1; + factor2 = 1; + } + + newPosition = Vec3.sum(this.oldModelPosition, + Vec3.multiply(vector, + factor2 / factor1)); + break; + } - var newRotation = Quat.multiply(this.rotation, - Quat.inverse(this.oldRotation)); + + newRotation = Quat.multiply(this.rotation, + Quat.inverse(this.oldRotation)); newRotation = Quat.multiply(newRotation, this.oldModelRotation); @@ -345,6 +373,21 @@ function controller(wichSide) { this.triggerValue = Controller.getTriggerValue(this.trigger); + var bumperValue = Controller.isButtonPressed(this.bumper); + if (bumperValue && !this.bumperValue) { + if (mode == 0) { + mode = 1; + Overlays.editOverlay(leftController.laser, { color: { red: 0, green: 0, blue: 255 } }); + Overlays.editOverlay(rightController.laser, { color: { red: 0, green: 0, blue: 255 } }); + } else { + mode = 0; + Overlays.editOverlay(leftController.laser, { color: { red: 255, green: 0, blue: 0 } }); + Overlays.editOverlay(rightController.laser, { color: { red: 255, green: 0, blue: 0 } }); + } + } + this.bumperValue = bumperValue; + + this.checkTrigger(); this.moveLaser(); @@ -443,52 +486,63 @@ var rightController = new controller(RIGHT); function moveModels() { if (leftController.grabbing && rightController.grabbing && rightController.modelID.id == leftController.modelID.id) { - //print("Both controllers"); - var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x)); - var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x)); - - var oldMiddle = Vec3.multiply(Vec3.sum(oldLeftPoint, oldRightPoint), 0.5); - var oldLength = Vec3.length(Vec3.subtract(oldLeftPoint, oldRightPoint)); + var newPosition = this.oldModelPosition; + var rotation = this.oldModelRotation; + var ratio = 1; - var leftPoint = Vec3.sum(leftController.palmPosition, Vec3.multiply(leftController.front, leftController.x)); - var rightPoint = Vec3.sum(rightController.palmPosition, Vec3.multiply(rightController.front, rightController.x)); - - var middle = Vec3.multiply(Vec3.sum(leftPoint, rightPoint), 0.5); - var length = Vec3.length(Vec3.subtract(leftPoint, rightPoint)); - - var ratio = length / oldLength; - - var newPosition = Vec3.sum(middle, - Vec3.multiply(Vec3.subtract(leftController.oldModelPosition, oldMiddle), ratio)); - - - var u = Vec3.normalize(Vec3.subtract(rightController.oldPalmPosition, leftController.oldPalmPosition)); - var v = Vec3.normalize(Vec3.subtract(rightController.palmPosition, leftController.palmPosition)); - - var cos_theta = Vec3.dot(Vec3.normalize(u), Vec3.normalize(v)); - var angle = Math.acos(cos_theta); - var w = Vec3.normalize(Vec3.cross(u, v)); - - - var rotation = Quat.angleAxis(angle, w); - - - rotation = Quat.multiply(rotation, leftController.oldModelRotation); + switch (mode) { + case 0: + var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x)); + var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x)); + + var oldMiddle = Vec3.multiply(Vec3.sum(oldLeftPoint, oldRightPoint), 0.5); + var oldLength = Vec3.length(Vec3.subtract(oldLeftPoint, oldRightPoint)); + + + var leftPoint = Vec3.sum(leftController.palmPosition, Vec3.multiply(leftController.front, leftController.x)); + var rightPoint = Vec3.sum(rightController.palmPosition, Vec3.multiply(rightController.front, rightController.x)); + + var middle = Vec3.multiply(Vec3.sum(leftPoint, rightPoint), 0.5); + var length = Vec3.length(Vec3.subtract(leftPoint, rightPoint)); + + + ratio = length / oldLength; + newPosition = Vec3.sum(middle, + Vec3.multiply(Vec3.subtract(leftController.oldModelPosition, oldMiddle), ratio)); + break; + case 1: + var u = Vec3.normalize(Vec3.subtract(rightController.oldPalmPosition, leftController.oldPalmPosition)); + var v = Vec3.normalize(Vec3.subtract(rightController.palmPosition, leftController.palmPosition)); + + var cos_theta = Vec3.dot(u, v); + if (cos_theta > 1) { + cos_theta = 1; + } + var angle = Math.acos(cos_theta) / Math.PI * 180; + if (angle < 0.1) { + return; + + } + var w = Vec3.normalize(Vec3.cross(u, v)); + + rotation = Quat.multiply(Quat.angleAxis(angle, w), leftController.oldModelRotation); + break; + } Models.editModel(leftController.modelID, { - //position: newPosition, + position: newPosition, modelRotation: rotation, - //radius: leftController.oldModelRadius * ratio + radius: leftController.oldModelRadius * ratio }); - //leftController.oldModelPosition = newPosition; + leftController.oldModelPosition = newPosition; leftController.oldModelRotation = rotation; - //leftController.oldModelRadius *= ratio; + leftController.oldModelRadius *= ratio; - //rightController.oldModelPosition = newPosition; + rightController.oldModelPosition = newPosition; rightController.oldModelRotation = rotation; - //rightController.oldModelRadius *= ratio; + rightController.oldModelRadius *= ratio; return; } From c1d1a8de5c3bb0aaf23be67190e5cfabfe492836 Mon Sep 17 00:00:00 2001 From: Joseph Gilley Date: Thu, 5 Jun 2014 00:26:45 -0400 Subject: [PATCH 04/38] Fix for users with some special characters in their password not being able to log in using the login overlay in the Interface. --- libraries/networking/src/AccountManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 7d27332a57..b4aedbcb7c 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -313,7 +313,7 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas QByteArray postData; postData.append("grant_type=password&"); postData.append("username=" + login + "&"); - postData.append("password=" + password + "&"); + postData.append("password=" + QUrl::toPercentEncoding(password) + "&"); postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); request.setUrl(grantURL); From c44dba78cb3ad4458fe3abb75695967afb763c27 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 5 Jun 2014 15:32:55 -0700 Subject: [PATCH 05/38] Provide a reasonably painless way to stream enums that aren't object properties. --- libraries/metavoxels/src/Bitstream.h | 34 ++++++++++++++++++++++--- tests/metavoxels/src/MetavoxelTests.cpp | 7 +++-- tests/metavoxels/src/MetavoxelTests.h | 3 +++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 146713910f..9945284775 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -1037,12 +1037,12 @@ public: }; /// Macro for registering simple type streamers. -#define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \ - Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); +#define REGISTER_SIMPLE_TYPE_STREAMER(X) static int X##Streamer = \ + Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); /// Macro for registering collection type streamers. -#define REGISTER_COLLECTION_TYPE_STREAMER(x) static int x##Streamer = \ - Bitstream::registerTypeStreamer(qMetaTypeId(), new CollectionTypeStreamer()); +#define REGISTER_COLLECTION_TYPE_STREAMER(X) static int x##Streamer = \ + Bitstream::registerTypeStreamer(qMetaTypeId(), new CollectionTypeStreamer()); /// Declares the metatype and the streaming operators. The last lines /// ensure that the generated file will be included in the link phase. @@ -1077,6 +1077,25 @@ public: _Pragma(STRINGIFY(unused(_TypePtr##X))) #endif +#define DECLARE_ENUM_METATYPE(S, N) Q_DECLARE_METATYPE(S::N) \ + Bitstream& operator<<(Bitstream& out, const S::N& obj); \ + Bitstream& operator>>(Bitstream& in, S::N& obj); \ + template<> inline void Bitstream::writeRawDelta(const S::N& value, const S::N& reference) { *this << value; } \ + template<> inline void Bitstream::readRawDelta(S::N& value, const S::N& reference) { *this >> value; } + +#define IMPLEMENT_ENUM_METATYPE(S, N) \ + static int S##N##MetaTypeId = registerEnumMetaType(S::staticMetaObject.enumerator( \ + S::staticMetaObject.indexOfEnumerator(#N))); \ + Bitstream& operator<<(Bitstream& out, const S::N& obj) { \ + static int bits = static_cast(Bitstream::getTypeStreamer(qMetaTypeId()))->getBits(); \ + return out.write(&obj, bits); \ + } \ + Bitstream& operator>>(Bitstream& in, S::N& obj) { \ + static int bits = static_cast(Bitstream::getTypeStreamer(qMetaTypeId()))->getBits(); \ + obj = (S::N)0; \ + return in.read(&obj, bits); \ + } + /// Registers a simple type and its streamer. template int registerSimpleMetaType() { int type = qRegisterMetaType(); @@ -1084,6 +1103,13 @@ template int registerSimpleMetaType() { return type; } +/// Registers an enum type and its streamer. +template int registerEnumMetaType(const QMetaEnum& metaEnum) { + int type = qRegisterMetaType(); + Bitstream::registerTypeStreamer(type, new EnumTypeStreamer(metaEnum)); + return type; +} + /// Registers a streamable type and its streamer. template int registerStreamableMetaType() { int type = qRegisterMetaType(); diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 603f63b587..cb1b5c7900 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -20,6 +20,8 @@ REGISTER_META_OBJECT(TestSharedObjectA) REGISTER_META_OBJECT(TestSharedObjectB) +IMPLEMENT_ENUM_METATYPE(TestSharedObjectA, TestEnum) + MetavoxelTests::MetavoxelTests(int& argc, char** argv) : QCoreApplication(argc, argv) { } @@ -80,6 +82,7 @@ static TestMessageC createRandomMessageC() { message.bar = rand(); message.baz = randFloat(); message.bong.foo = createRandomBytes(); + message.bong.baz = getRandomTestEnum(); return message; } @@ -252,7 +255,7 @@ static QVariant createRandomMessage() { return QVariant::fromValue(message); } case 1: { - TestMessageB message = { createRandomBytes(), createRandomSharedObject() }; + TestMessageB message = { createRandomBytes(), createRandomSharedObject(), getRandomTestEnum() }; return QVariant::fromValue(message); } case 2: @@ -273,7 +276,7 @@ static bool messagesEqual(const QVariant& firstMessage, const QVariant& secondMe } else if (type == TestMessageB::Type) { TestMessageB first = firstMessage.value(); TestMessageB second = secondMessage.value(); - return first.foo == second.foo && equals(first.bar, second.bar); + return first.foo == second.foo && equals(first.bar, second.bar) && first.baz == second.baz; } else if (type == TestMessageC::Type) { return firstMessage.value() == secondMessage.value(); diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index 5e020b1e60..ffd1311f00 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -106,6 +106,8 @@ private: TestFlags _bong; }; +DECLARE_ENUM_METATYPE(TestSharedObjectA, TestEnum) + /// Another simple shared object. class TestSharedObjectB : public SharedObject { Q_OBJECT @@ -169,6 +171,7 @@ public: STREAM QByteArray foo; STREAM SharedObjectPointer bar; + STREAM TestSharedObjectA::TestEnum baz; }; DECLARE_STREAMABLE_METATYPE(TestMessageB) From 887561a4e134e97d30ad11131c83952fc986815c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 5 Jun 2014 15:39:43 -0700 Subject: [PATCH 06/38] Slight simplification. --- libraries/metavoxels/src/Bitstream.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 9945284775..caf0adf7fc 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -1084,19 +1084,18 @@ public: template<> inline void Bitstream::readRawDelta(S::N& value, const S::N& reference) { *this >> value; } #define IMPLEMENT_ENUM_METATYPE(S, N) \ - static int S##N##MetaTypeId = registerEnumMetaType(S::staticMetaObject.enumerator( \ + static int S##N##Bits = registerEnumMetaType(S::staticMetaObject.enumerator( \ S::staticMetaObject.indexOfEnumerator(#N))); \ Bitstream& operator<<(Bitstream& out, const S::N& obj) { \ - static int bits = static_cast(Bitstream::getTypeStreamer(qMetaTypeId()))->getBits(); \ - return out.write(&obj, bits); \ + return out.write(&obj, S##N##Bits); \ } \ Bitstream& operator>>(Bitstream& in, S::N& obj) { \ - static int bits = static_cast(Bitstream::getTypeStreamer(qMetaTypeId()))->getBits(); \ obj = (S::N)0; \ - return in.read(&obj, bits); \ + return in.read(&obj, S##N##Bits); \ } /// Registers a simple type and its streamer. +/// \return the metatype id template int registerSimpleMetaType() { int type = qRegisterMetaType(); Bitstream::registerTypeStreamer(type, new SimpleTypeStreamer()); @@ -1104,13 +1103,16 @@ template int registerSimpleMetaType() { } /// Registers an enum type and its streamer. +/// \return the number of bits required to stream the enum template int registerEnumMetaType(const QMetaEnum& metaEnum) { int type = qRegisterMetaType(); - Bitstream::registerTypeStreamer(type, new EnumTypeStreamer(metaEnum)); - return type; + EnumTypeStreamer* streamer = new EnumTypeStreamer(metaEnum); + Bitstream::registerTypeStreamer(type, streamer); + return streamer->getBits(); } /// Registers a streamable type and its streamer. +/// \return the metatype id template int registerStreamableMetaType() { int type = qRegisterMetaType(); Bitstream::registerTypeStreamer(type, new StreamableTypeStreamer()); @@ -1118,6 +1120,7 @@ template int registerStreamableMetaType() { } /// Registers a collection type and its streamer. +/// \return the metatype id template int registerCollectionMetaType() { int type = qRegisterMetaType(); Bitstream::registerTypeStreamer(type, new CollectionTypeStreamer()); From 145a159233604285fb2f180e9111fd677b601e1b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Jun 2014 15:45:25 -0700 Subject: [PATCH 07/38] Slight glow on laser intersection --- examples/editModels.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/examples/editModels.js b/examples/editModels.js index 24360c526d..8e2f6b4911 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -252,6 +252,7 @@ function controller(wichSide) { return { valid: false }; } + this.glowedIntersectingModel = { isKnownID: false }; this.moveLaser = function () { // the overlays here are anchored to the avatar, which means they are specified in the avatar's local frame @@ -279,6 +280,22 @@ function controller(wichSide) { end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale)) }); this.showLaser(!this.grabbing || mode == 0); + + + if (!this.grabbing) { + if (this.glowedIntersectingModel.isKnownID) { + Models.editModel(this.glowedIntersectingModel, { glowLevel: 0.0 }); + } + + var intersection = Models.findRayIntersection({ + origin: this.palmPosition, + direction: this.front + }); + if (intersection.accurate && intersection.modelID.isKnownID) { + this.glowedIntersectingModel = intersection.modelID; + Models.editModel(this.glowedIntersectingModel, { glowLevel: 0.25 }); + } + } } this.showLaser = function(show) { From 0bdd20abc9d06a8d986a44c0232cd8c75dbc6ea7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Jun 2014 15:46:07 -0700 Subject: [PATCH 08/38] Coding Standard --- libraries/models/src/ModelTree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/models/src/ModelTree.cpp b/libraries/models/src/ModelTree.cpp index e88a969061..466d4c5273 100644 --- a/libraries/models/src/ModelTree.cpp +++ b/libraries/models/src/ModelTree.cpp @@ -185,7 +185,7 @@ void ModelTree::addModel(const ModelItemID& modelID, const ModelItemProperties& glm::vec3 position = model.getPosition(); float size = std::max(MINIMUM_MODEL_ELEMENT_SIZE, model.getRadius()); - ModelTreeElement* element = (ModelTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size); + ModelTreeElement* element = static_cast(getOrCreateChildElementAt(position.x, position.y, position.z, size)); element->storeModel(model); _isDirty = true; From 150477eea44d0438137333499fcda498072c76ea Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Jun 2014 15:47:25 -0700 Subject: [PATCH 09/38] Models.addModel now returns a more accurate modelID --- libraries/models/src/ModelItem.cpp | 31 +++++-------------- libraries/models/src/ModelItem.h | 1 + .../models/src/ModelsScriptingInterface.cpp | 3 +- 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index b6f4fe6c1d..810129a81f 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -47,6 +47,12 @@ uint32_t ModelItem::getNextCreatorTokenID() { return creatorTokenID; } +uint32_t ModelItem::getNextModelItemID() { + uint32_t modelID = _nextID; + _nextID++; + return modelID; +} + void ModelItem::handleAddModelResponse(const QByteArray& packet) { const unsigned char* dataAt = reinterpret_cast(packet.data()); int numBytesPacketHeader = numBytesForPacketHeader(packet); @@ -70,31 +76,10 @@ ModelItem::ModelItem() { } ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties& properties) { - _id = modelItemID.id; _creatorTokenID = modelItemID.creatorTokenID; - - // init values with defaults before calling setProperties - uint64_t now = usecTimestampNow(); - _lastEdited = now; - _lastUpdated = now; - - _position = glm::vec3(0,0,0); - _radius = 0; - rgbColor noColor = { 0, 0, 0 }; - memcpy(_color, noColor, sizeof(_color)); - _shouldDie = false; - _modelURL = MODEL_DEFAULT_MODEL_URL; - _modelRotation = MODEL_DEFAULT_MODEL_ROTATION; - // animation related - _animationURL = MODEL_DEFAULT_ANIMATION_URL; - _animationIsPlaying = false; - _animationFrameIndex = 0.0f; - _animationFPS = MODEL_DEFAULT_ANIMATION_FPS; - _glowLevel = 0.0f; - - _jointMappingCompleted = false; - _lastAnimated = now; + rgbColor defaultColor = { 0, 0, 0 }; + init(glm::vec3(), 0.0f, defaultColor, modelItemID.id); setProperties(properties); } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index 9a558f2ef4..563d394419 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -276,6 +276,7 @@ public: // these methods allow you to create models, and later edit them. static uint32_t getIDfromCreatorTokenID(uint32_t creatorTokenID); static uint32_t getNextCreatorTokenID(); + static uint32_t getNextModelItemID(); static void handleAddModelResponse(const QByteArray& packet); void mapJoints(const QStringList& modelJointNames); diff --git a/libraries/models/src/ModelsScriptingInterface.cpp b/libraries/models/src/ModelsScriptingInterface.cpp index 7e08571fe5..f4cbf14086 100644 --- a/libraries/models/src/ModelsScriptingInterface.cpp +++ b/libraries/models/src/ModelsScriptingInterface.cpp @@ -28,8 +28,9 @@ ModelItemID ModelsScriptingInterface::addModel(const ModelItemProperties& proper // The application will keep track of creatorTokenID uint32_t creatorTokenID = ModelItem::getNextCreatorTokenID(); + uint32_t modelID = ModelItem::getNextModelItemID(); - ModelItemID id(NEW_MODEL, creatorTokenID, false ); + ModelItemID id(modelID, creatorTokenID, false ); // queue the packet queueModelMessage(PacketTypeModelAddOrEdit, id, properties); From c333fb904f88fa109a8776bd5a8a11b8ed17bbd0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 5 Jun 2014 16:45:13 -0700 Subject: [PATCH 10/38] Working on tests for delta streaming. --- tests/metavoxels/src/MetavoxelTests.cpp | 32 ++++++++++++++++++++++++- tests/metavoxels/src/MetavoxelTests.h | 21 ++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index cb1b5c7900..c4d973ad05 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -229,6 +229,20 @@ Endpoint::Endpoint(const QByteArray& datagramHeader) : connect(_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), SLOT(handleHighPriorityMessage(const QVariant&))); + connect(_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int))); + connect(_sequencer, SIGNAL(receiveAcknowledged(int)), SLOT(clearReceiveRecordsBefore(int))); + + // insert the baseline send record + SendRecord sendRecord = { 0 }; + _sendRecords.append(sendRecord); + + // insert the baseline receive record + ReceiveRecord receiveRecord = { 0 }; + _receiveRecords.append(receiveRecord); + + // create the object that represents out delta-encoded state + //_localState = new TestSharedObjectA(); + connect(_sequencer->getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&)), SLOT(handleReliableMessage(const QVariant&))); @@ -326,7 +340,7 @@ bool Endpoint::simulate(int iterationNumber) { // send a packet try { Bitstream& out = _sequencer->startPacket(); - SequencedTestMessage message = { iterationNumber, createRandomMessage() }; + SequencedTestMessage message = { iterationNumber, createRandomMessage(), _localState }; _unreliableMessagesSent.append(message); unreliableMessagesSent++; out << message; @@ -337,6 +351,10 @@ bool Endpoint::simulate(int iterationNumber) { return true; } + // record the send + SendRecord record = { _sequencer->getOutgoingPacketNumber(), _localState }; + _sendRecords.append(record); + return false; } @@ -387,6 +405,10 @@ void Endpoint::readMessage(Bitstream& in) { SequencedTestMessage message; in >> message; + // record the receipt + ReceiveRecord record = { _sequencer->getIncomingPacketNumber(), message.state }; + _receiveRecords.append(record); + for (QList::iterator it = _other->_unreliableMessagesSent.begin(); it != _other->_unreliableMessagesSent.end(); it++) { if (it->sequenceNumber == message.sequenceNumber) { @@ -430,6 +452,14 @@ void Endpoint::readReliableChannel() { streamedBytesReceived += bytes.size(); } +void Endpoint::clearSendRecordsBefore(int index) { + _sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index + 1); +} + +void Endpoint::clearReceiveRecordsBefore(int index) { + _receiveRecords.erase(_receiveRecords.begin(), _receiveRecords.begin() + index + 1); +} + TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) : _foo(foo), _baz(baz), diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index ffd1311f00..dc294692af 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -54,9 +54,29 @@ private slots: void handleReliableMessage(const QVariant& message); void readReliableChannel(); + void clearSendRecordsBefore(int index); + void clearReceiveRecordsBefore(int index); + private: + class SendRecord { + public: + int packetNumber; + SharedObjectPointer localState; + }; + + class ReceiveRecord { + public: + int packetNumber; + SharedObjectPointer remoteState; + }; + DatagramSequencer* _sequencer; + QList _sendRecords; + QList _receiveRecords; + + SharedObjectPointer _localState; + Endpoint* _other; QList > _delayedDatagrams; float _highPriorityMessagesToSend; @@ -195,6 +215,7 @@ public: STREAM int sequenceNumber; STREAM QVariant submessage; + STREAM SharedObjectPointer state; }; DECLARE_STREAMABLE_METATYPE(SequencedTestMessage) From 005a314695545fd1c9d24a6161f4e6bff77c0928 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 5 Jun 2014 17:30:19 -0700 Subject: [PATCH 11/38] Reenable enum streamers here. --- libraries/metavoxels/src/Bitstream.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 387b41a839..30d34580d7 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -92,14 +92,13 @@ int Bitstream::registerMetaObject(const char* className, const QMetaObject* meta } // register the streamers for all enumerators - // temporarily disabled: crashes on Windows - //for (int i = 0; i < metaObject->enumeratorCount(); i++) { - // QMetaEnum metaEnum = metaObject->enumerator(i); - // const TypeStreamer*& streamer = getEnumStreamers()[QPair(metaEnum.scope(), metaEnum.name())]; - // if (!streamer) { - // getEnumStreamersByName().insert(getEnumName(metaEnum), streamer = new EnumTypeStreamer(metaEnum)); - // } - //} + for (int i = 0; i < metaObject->enumeratorCount(); i++) { + QMetaEnum metaEnum = metaObject->enumerator(i); + const TypeStreamer*& streamer = getEnumStreamers()[QPair(metaEnum.scope(), metaEnum.name())]; + if (!streamer) { + getEnumStreamersByName().insert(getEnumName(metaEnum), streamer = new EnumTypeStreamer(metaEnum)); + } + } return 0; } From 9322e1f71b5d3e0f31127f3ef3b1762a24bdcd4f Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Jun 2014 17:36:56 -0700 Subject: [PATCH 12/38] More work on editModels --- examples/editModels.js | 205 ++++++++++++++++++++++++++++------------- 1 file changed, 139 insertions(+), 66 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 8e2f6b4911..67e2bdb198 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -18,7 +18,8 @@ var toolWidth = 50; var LASER_WIDTH = 4; var LASER_COLOR = { red: 255, green: 0, blue: 0 }; -var LASER_LENGTH_FACTOR = 5; +var LASER_LENGTH_FACTOR = 500 +; var LEFT = 0; var RIGHT = 1; @@ -92,6 +93,11 @@ function controller(wichSide) { this.oldModelPosition; this.oldModelRadius; + this.positionAtGrab; + this.rotationAtGrab; + this.modelPositionAtGrab; + this.modelRotationAtGrab; + this.jointsIntersectingFromStart = []; this.laser = Overlays.addOverlay("line3d", { @@ -149,6 +155,11 @@ function controller(wichSide) { this.oldModelRotation = properties.modelRotation; this.oldModelRadius = properties.radius; + this.positionAtGrab = this.palmPosition; + this.rotationAtGrab = this.rotation; + this.modelPositionAtGrab = properties.position; + this.modelRotationAtGrab = properties.modelRotation; + this.jointsIntersectingFromStart = []; for (var i = 0; i < jointList.length; i++) { var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition); @@ -174,12 +185,16 @@ function controller(wichSide) { } } - print("closestJoint: " + jointList[closestJointIndex]); - print("closestJointDistance (attach max distance): " + closestJointDistance + " (" + this.oldModelRadius + ")"); + if (closestJointIndex != -1) { + print("closestJoint: " + jointList[closestJointIndex]); + print("closestJointDistance (attach max distance): " + closestJointDistance + " (" + this.oldModelRadius + ")"); + } if (closestJointDistance < this.oldModelRadius) { - if (this.jointsIntersectingFromStart.indexOf(closestJointIndex) != -1) { + if (this.jointsIntersectingFromStart.indexOf(closestJointIndex) != -1 || + (leftController.grabbing && rightController.grabbing && + leftController.modelID.id == rightController.modelID.id)) { // Do nothing } else { print("Attaching to " + jointList[closestJointIndex]); @@ -193,6 +208,7 @@ function controller(wichSide) { MyAvatar.attach(this.modelURL, jointList[closestJointIndex], attachmentOffset, attachmentRotation, 2.0 * this.oldModelRadius, true, false); + Models.deleteModel(this.modelID); } } @@ -281,12 +297,11 @@ function controller(wichSide) { }); this.showLaser(!this.grabbing || mode == 0); - + if (this.glowedIntersectingModel.isKnownID) { + Models.editModel(this.glowedIntersectingModel, { glowLevel: 0.0 }); + this.glowedIntersectingModel.isKnownID = false; + } if (!this.grabbing) { - if (this.glowedIntersectingModel.isKnownID) { - Models.editModel(this.glowedIntersectingModel, { glowLevel: 0.0 }); - } - var intersection = Models.findRayIntersection({ origin: this.palmPosition, direction: this.front @@ -307,6 +322,14 @@ function controller(wichSide) { this.moveModel = function () { if (this.grabbing) { + if (!this.modelID.isKnownID) { + print("Unknown grabbed ID " + this.modelID.id + ", isKnown: " + this.modelID.isKnownID); + this.modelID = Models.findRayIntersection({ + origin: this.palmPosition, + direction: this.front + }).modelID; + print("Identified ID " + this.modelID.id + ", isKnown: " + this.modelID.isKnownID); + } var newPosition; var newRotation; @@ -318,15 +341,22 @@ function controller(wichSide) { Vec3.multiply(this.up, this.y)); newPosition = Vec3.sum(newPosition, Vec3.multiply(this.right, this.z)); + + + newRotation = Quat.multiply(this.rotation, + Quat.inverse(this.oldRotation)); + newRotation = Quat.multiply(newRotation, + this.oldModelRotation); break; case 1: var forward = Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 }); var d = Vec3.dot(forward, MyAvatar.position); - var factor1 = Vec3.dot(forward, this.palmPosition) - d; - var factor2 = Vec3.dot(forward, this.oldModelPosition) - d; - var vector = Vec3.subtract(this.palmPosition, this.oldPalmPosition); + var factor1 = Vec3.dot(forward, this.positionAtGrab) - d; + var factor2 = Vec3.dot(forward, this.modelPositionAtGrab) - d; + var vector = Vec3.subtract(this.palmPosition, this.positionAtGrab); + print("factor1: " + factor1 + ", factor2: " + factor2); if (factor2 < 0) { factor2 = 0; } @@ -335,19 +365,17 @@ function controller(wichSide) { factor2 = 1; } - newPosition = Vec3.sum(this.oldModelPosition, + newPosition = Vec3.sum(this.modelPositionAtGrab, Vec3.multiply(vector, factor2 / factor1)); + + newRotation = Quat.multiply(this.rotation, + Quat.inverse(this.rotationAtGrab)); + newRotation = Quat.multiply(newRotation, + this.modelRotationAtGrab); break; } - - - newRotation = Quat.multiply(this.rotation, - Quat.inverse(this.oldRotation)); - newRotation = Quat.multiply(newRotation, - this.oldModelRotation); - Models.editModel(this.modelID, { position: newPosition, modelRotation: newRotation @@ -367,8 +395,46 @@ function controller(wichSide) { for (var i = 0; i < indicesToRemove.length; ++i) { this.jointsIntersectingFromStart.splice(this.jointsIntersectingFromStart.indexOf(indicesToRemove[i], 1)); } + + + jointList = MyAvatar.getJointNames(); + + var closestJointIndex = -1; + var closestJointDistance = 999999; + for (var i = 0; i < jointList.length; i++) { + var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition); + if (distance < closestJointDistance) { + closestJointDistance = distance; + closestJointIndex = i; + } + } + + if (closestJointDistance < this.oldModelRadius) { + if (this.jointsIntersectingFromStart.indexOf(closestJointIndex) != -1 || + (leftController.grabbing && rightController.grabbing && + leftController.modelID.id == rightController.modelID.id)) { + // Do nothing + } else { + Vec3.print("Ball at: ", MyAvatar.getJointPosition(closestJointIndex)); + Overlays.editOverlay(this.ballGlowingJoint, { + position: MyAvatar.getJointPosition(closestJointIndex), + glowLevel: 0.25, + visible: true + }); + } + } else { + Overlays.editOverlay(this.ballGlowingJoint, { glowLevel: 0.0, visible: false }); + } } } + this.ballGlowingJoint = Overlays.addOverlay("sphere", { + position: { x: 0, y: 0, z: 0 }, + size: 1, + solid: true, + color: { red: 0, green: 255, blue: 0 }, + alpha: 1, + visible: false, + anchor: "MyAvatar"}); this.update = function () { this.oldPalmPosition = this.palmPosition; @@ -420,8 +486,12 @@ function controller(wichSide) { var attachmentIndex = -1; var attachmentX = LASER_LENGTH_FACTOR; + var newModel; + var newProperties; + for (var i = 0; i < attachments.length; ++i) { - var position = Vec3.sum(MyAvatar.getJointPosition(attachments[i].jointName), attachments[i].translation); + var position = Vec3.sum(MyAvatar.getJointPosition(attachments[i].jointName), + Vec3.multiplyQbyV(MyAvatar.getJointCombinedRotation(attachments[i].jointName), attachments[i].translation)); var scale = attachments[i].scale; var A = this.palmPosition; @@ -439,53 +509,56 @@ function controller(wichSide) { } if (attachmentIndex != -1) { + print("Detaching: " + attachments[attachmentIndex].modelURL); MyAvatar.detachOne(attachments[attachmentIndex].modelURL, attachments[attachmentIndex].jointName); - Models.addModel({ - position: Vec3.sum(MyAvatar.getJointPosition(attachments[attachmentIndex].jointName), - attachments[attachmentIndex].translation), - modelRotation: Quat.multiply(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName), - attachments[attachmentIndex].rotation), - radius: attachments[attachmentIndex].scale / 2.0, - modelURL: attachments[attachmentIndex].modelURL - }); - } - - // There is none so ... - // Checking model tree - Vec3.print("Looking at: ", this.palmPosition); - var pickRay = { origin: this.palmPosition, - direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) }; - var foundIntersection = Models.findRayIntersection(pickRay); - - if(!foundIntersection.accurate) { - return; - } - var foundModel = foundIntersection.modelID; - - if (!foundModel.isKnownID) { - var identify = Models.identifyModel(foundModel); - if (!identify.isKnownID) { - print("Unknown ID " + identify.id + " (update loop " + foundModel.id + ")"); - return; - } - foundModel = identify; - } - - var properties = Models.getModelProperties(foundModel); - print("foundModel.modelURL=" + properties.modelURL); - - if (isLocked(properties)) { - print("Model locked " + properties.id); + + newProperties = { + position: Vec3.sum(MyAvatar.getJointPosition(attachments[attachmentIndex].jointName), + Vec3.multiplyQbyV(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName), attachments[attachmentIndex].translation)), + modelRotation: Quat.multiply(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName), + attachments[attachmentIndex].rotation), + radius: attachments[attachmentIndex].scale / 2.0, + modelURL: attachments[attachmentIndex].modelURL + }; + newModel = Models.addModel(newProperties); } else { - print("Checking properties: " + properties.id + " " + properties.isKnownID); - var check = this.checkModel(properties); - if (check.valid) { - this.grab(foundModel, properties); - this.x = check.x; - this.y = check.y; - this.z = check.z; + // There is none so ... + // Checking model tree + Vec3.print("Looking at: ", this.palmPosition); + var pickRay = { origin: this.palmPosition, + direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) }; + var foundIntersection = Models.findRayIntersection(pickRay); + + if(!foundIntersection.accurate) { + print("No accurate intersection"); return; } + newModel = foundIntersection.modelID; + + if (!newModel.isKnownID) { + var identify = Models.identifyModel(newModel); + if (!identify.isKnownID) { + print("Unknown ID " + identify.id + " (update loop " + newModel.id + ")"); + return; + } + newModel = identify; + } + newProperties = Models.getModelProperties(newModel); + } + + + print("foundModel.modelURL=" + newProperties.modelURL); + + if (isLocked(newProperties)) { + print("Model locked " + newProperties.id); + } else { + this.grab(newModel, newProperties); + + var check = this.checkModel(newProperties); + this.x = check.x; + this.y = check.y; + this.z = check.z; + return; } } } @@ -503,8 +576,8 @@ var rightController = new controller(RIGHT); function moveModels() { if (leftController.grabbing && rightController.grabbing && rightController.modelID.id == leftController.modelID.id) { - var newPosition = this.oldModelPosition; - var rotation = this.oldModelRotation; + var newPosition = leftController.oldModelPosition; + var rotation = leftController.oldModelRotation; var ratio = 1; From e8724b02f307e8f265cd08dc79d6a2cd17813d43 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 6 Jun 2014 08:50:57 -0700 Subject: [PATCH 13/38] add CapsuleShape::setEndPoints() --- libraries/shared/src/CapsuleShape.cpp | 32 +++++++++++++++------------ libraries/shared/src/CapsuleShape.h | 1 + 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/libraries/shared/src/CapsuleShape.cpp b/libraries/shared/src/CapsuleShape.cpp index 11bd70f8d2..8e887107dc 100644 --- a/libraries/shared/src/CapsuleShape.cpp +++ b/libraries/shared/src/CapsuleShape.cpp @@ -33,20 +33,7 @@ CapsuleShape::CapsuleShape(float radius, float halfHeight, const glm::vec3& posi CapsuleShape::CapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint) : Shape(Shape::CAPSULE_SHAPE), _radius(radius), _halfHeight(0.0f) { - glm::vec3 axis = endPoint - startPoint; - _position = 0.5f * (endPoint + startPoint); - float height = glm::length(axis); - if (height > EPSILON) { - _halfHeight = 0.5f * height; - axis /= height; - glm::vec3 yAxis(0.0f, 1.0f, 0.0f); - float angle = glm::angle(axis, yAxis); - if (angle > EPSILON) { - axis = glm::normalize(glm::cross(yAxis, axis)); - _rotation = glm::angleAxis(angle, axis); - } - } - updateBoundingRadius(); + setEndPoints(startPoint, endPoint); } /// \param[out] startPoint is the center of start cap @@ -80,3 +67,20 @@ void CapsuleShape::setRadiusAndHalfHeight(float radius, float halfHeight) { updateBoundingRadius(); } +void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint) { + glm::vec3 axis = endPoint - startPoint; + _position = 0.5f * (endPoint + startPoint); + float height = glm::length(axis); + if (height > EPSILON) { + _halfHeight = 0.5f * height; + axis /= height; + glm::vec3 yAxis(0.0f, 1.0f, 0.0f); + float angle = glm::angle(axis, yAxis); + if (angle > EPSILON) { + axis = glm::normalize(glm::cross(yAxis, axis)); + _rotation = glm::angleAxis(angle, axis); + } + } + updateBoundingRadius(); +} + diff --git a/libraries/shared/src/CapsuleShape.h b/libraries/shared/src/CapsuleShape.h index 0889f6b2f3..756ae18911 100644 --- a/libraries/shared/src/CapsuleShape.h +++ b/libraries/shared/src/CapsuleShape.h @@ -37,6 +37,7 @@ public: void setRadius(float radius); void setHalfHeight(float height); void setRadiusAndHalfHeight(float radius, float height); + void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint); protected: void updateBoundingRadius() { _boundingRadius = _radius + _halfHeight; } From 551029c2f5e696f054f987d34b40fa6da56a8697 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 6 Jun 2014 08:51:22 -0700 Subject: [PATCH 14/38] slave SkeletonModel's collision shapes to RagDoll --- interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + interface/src/avatar/SkeletonModel.cpp | 20 +++++++++-- interface/src/avatar/SkeletonModel.h | 1 + interface/src/renderer/Model.h | 2 +- interface/src/renderer/RagDoll.cpp | 46 +++++++++++++++++++++++--- interface/src/renderer/RagDoll.h | 23 +++++++++++-- 7 files changed, 82 insertions(+), 12 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 5d9cd1f1c4..e429a81fa2 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -353,6 +353,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderSkeletonCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderHeadCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderBoundingCollisionShapes); + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollideAsRagDoll); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 6bc9adef05..c7d3a94741 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -311,6 +311,7 @@ namespace MenuOption { const QString CascadedShadows = "Cascaded"; const QString Chat = "Chat..."; const QString ChatCircling = "Chat Circling"; + const QString CollideAsRagDoll = "Collide As RagDoll"; const QString CollideWithAvatars = "Collide With Avatars"; const QString CollideWithEnvironment = "Collide With World Boundaries"; const QString CollideWithParticles = "Collide With Particles"; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index f5ca1ab218..ca40ba1d64 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -23,7 +23,10 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) : void SkeletonModel::setJointStates(QVector states) { Model::setJointStates(states); - _ragDoll.init(_jointStates); + + if (isActive() && _owningAvatar->isMyAvatar()) { + _ragDoll.init(_jointStates); + } } const float PALM_PRIORITY = 3.0f; @@ -88,7 +91,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } void SkeletonModel::simulateRagDoll(float deltaTime) { - _ragDoll.slaveToSkeleton(_jointStates, 0.5f); + _ragDoll.slaveToSkeleton(_jointStates, 0.1f); // fraction = 0.1f left intentionally low for demo purposes float MIN_CONSTRAINT_ERROR = 0.005f; // 5mm int MAX_ITERATIONS = 4; @@ -141,7 +144,9 @@ void SkeletonModel::getBodyShapes(QVector& shapes) const { void SkeletonModel::renderIKConstraints() { renderJointConstraints(getRightHandJointIndex()); renderJointConstraints(getLeftHandJointIndex()); - renderRagDoll(); + if (isActive() && _owningAvatar->isMyAvatar()) { + renderRagDoll(); + } } class IndexValue { @@ -254,6 +259,15 @@ void SkeletonModel::updateJointState(int index) { } } +void SkeletonModel::updateShapePositions() { + if (isActive() && _owningAvatar->isMyAvatar() && + Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagDoll)) { + _ragDoll.updateShapes(_jointShapes, _rotation, _translation); + } else { + Model::updateShapePositions(); + } +} + void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { if (!_owningAvatar->isMyAvatar() || Application::getInstance()->getPrioVR()->isActive()) { return; diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index d733d937ee..086973807d 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -29,6 +29,7 @@ public: void simulate(float deltaTime, bool fullUpdate = true); void simulateRagDoll(float deltaTime); + void updateShapePositions(); /// \param jointIndex index of hand joint /// \param shapes[out] list in which is stored pointers to hand shapes diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 3bc261ed44..11e6861775 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -140,7 +140,7 @@ public: void clearShapes(); void rebuildShapes(); void resetShapePositions(); - void updateShapePositions(); + virtual void updateShapePositions(); void renderJointCollisionShapes(float alpha); void renderBoundingCollisionShapes(float alpha); diff --git a/interface/src/renderer/RagDoll.cpp b/interface/src/renderer/RagDoll.cpp index 54fb776552..e2654d737f 100644 --- a/interface/src/renderer/RagDoll.cpp +++ b/interface/src/renderer/RagDoll.cpp @@ -15,13 +15,15 @@ #include #include +#include +#include #include "RagDoll.h" // ---------------------------------------------------------------------------- // FixedConstraint // ---------------------------------------------------------------------------- -FixedConstraint::FixedConstraint() : _point(NULL), _anchor(0.0f, 0.0f, 0.0f) { +FixedConstraint::FixedConstraint(glm::vec3* point, const glm::vec3& anchor ) : _point(point), _anchor(anchor) { } float FixedConstraint::enforce() { @@ -42,9 +44,9 @@ void FixedConstraint::setAnchor(const glm::vec3& anchor) { // ---------------------------------------------------------------------------- // DistanceConstraint // ---------------------------------------------------------------------------- -DistanceConstraint::DistanceConstraint(glm::vec3* pointA, glm::vec3* pointB) : _distance(-1.0f) { - _points[0] = pointA; - _points[1] = pointB; +DistanceConstraint::DistanceConstraint(glm::vec3* startPoint, glm::vec3* endPoint) : _distance(-1.0f) { + _points[0] = startPoint; + _points[1] = endPoint; _distance = glm::distance(*(_points[0]), *(_points[1])); } @@ -70,6 +72,28 @@ float DistanceConstraint::enforce() { return glm::abs(newDistance - _distance); } +void DistanceConstraint::updateProxyShape(Shape* shape, const glm::quat& rotation, const glm::vec3& translation) const { + if (!shape) { + return; + } + switch (shape->getType()) { + case Shape::SPHERE_SHAPE: { + // sphere collides at endPoint + SphereShape* sphere = static_cast(shape); + sphere->setPosition(translation + rotation * (*_points[1])); + } + break; + case Shape::CAPSULE_SHAPE: { + // capsule collides from startPoint to endPoint + CapsuleShape* capsule = static_cast(shape); + capsule->setEndPoints(translation + rotation * (*_points[0]), translation + rotation * (*_points[1])); + } + break; + default: + break; + } +} + // ---------------------------------------------------------------------------- // RagDoll // ---------------------------------------------------------------------------- @@ -90,12 +114,16 @@ void RagDoll::init(const QVector& states) { _points.push_back(state.getPosition()); int parentIndex = state.getFBXJoint().parentIndex; assert(parentIndex < i); - if (parentIndex != -1) { + if (parentIndex == -1) { + FixedConstraint* anchor = new FixedConstraint(&(_points[i]), glm::vec3(0.0f)); + _constraints.push_back(anchor); + } else { DistanceConstraint* stick = new DistanceConstraint(&(_points[i]), &(_points[parentIndex])); _constraints.push_back(stick); } } } + /// Delete all data. void RagDoll::clear() { int numConstraints = _constraints.size(); @@ -129,3 +157,11 @@ float RagDoll::enforceConstraints() { } return maxDistance; } + +void RagDoll::updateShapes(const QVector& shapes, const glm::quat& rotation, const glm::vec3& translation) const { + int numShapes = shapes.size(); + int numConstraints = _constraints.size(); + for (int i = 0; i < numShapes && i < numConstraints; ++i) { + _constraints[i]->updateProxyShape(shapes[i], rotation, translation); + } +} diff --git a/interface/src/renderer/RagDoll.h b/interface/src/renderer/RagDoll.h index 1d23973827..60e242d19b 100644 --- a/interface/src/renderer/RagDoll.h +++ b/interface/src/renderer/RagDoll.h @@ -14,6 +14,8 @@ #include "renderer/Model.h" +class Shape; + class Constraint { public: Constraint() {} @@ -22,11 +24,20 @@ public: /// Enforce contraint by moving relevant points. /// \return max distance of point movement virtual float enforce() = 0; + + /// \param shape pointer to shape that will be this Constraint's collision proxy + /// \param rotation rotation into shape's collision frame + /// \param translation translation into shape's collision frame + /// Moves the shape such that it will collide at this constraint's position + virtual void updateProxyShape(Shape* shape, const glm::quat& rotation, const glm::vec3& translation) const {} + +protected: + int _type; }; class FixedConstraint : public Constraint { public: - FixedConstraint(); + FixedConstraint(glm::vec3* point, const glm::vec3& anchor); float enforce(); void setPoint(glm::vec3* point); void setAnchor(const glm::vec3& anchor); @@ -37,10 +48,11 @@ private: class DistanceConstraint : public Constraint { public: - DistanceConstraint(glm::vec3* pointA, glm::vec3* pointB); + DistanceConstraint(glm::vec3* startPoint, glm::vec3* endPoint); DistanceConstraint(const DistanceConstraint& other); float enforce(); void setDistance(float distance); + void updateProxyShape(Shape* shape, const glm::quat& rotation, const glm::vec3& translation) const; private: float _distance; glm::vec3* _points[2]; @@ -69,7 +81,12 @@ public: float enforceConstraints(); const QVector& getPoints() const { return _points; } - + + /// \param shapes list of shapes to be updated with new positions + /// \param rotation rotation into shapes' collision frame + /// \param translation translation into shapes' collision frame + void updateShapes(const QVector& shapes, const glm::quat& rotation, const glm::vec3& translation) const; + private: QVector _constraints; QVector _points; From 4209c1102f56420a0024baa1ec997b008f3a329e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 6 Jun 2014 10:26:08 -0700 Subject: [PATCH 15/38] Revert "Models.addModel now returns a more accurate modelID" This reverts commit 150477eea44d0438137333499fcda498072c76ea. --- libraries/models/src/ModelItem.cpp | 31 ++++++++++++++----- libraries/models/src/ModelItem.h | 1 - .../models/src/ModelsScriptingInterface.cpp | 3 +- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 810129a81f..b6f4fe6c1d 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -47,12 +47,6 @@ uint32_t ModelItem::getNextCreatorTokenID() { return creatorTokenID; } -uint32_t ModelItem::getNextModelItemID() { - uint32_t modelID = _nextID; - _nextID++; - return modelID; -} - void ModelItem::handleAddModelResponse(const QByteArray& packet) { const unsigned char* dataAt = reinterpret_cast(packet.data()); int numBytesPacketHeader = numBytesForPacketHeader(packet); @@ -76,10 +70,31 @@ ModelItem::ModelItem() { } ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties& properties) { + _id = modelItemID.id; _creatorTokenID = modelItemID.creatorTokenID; + + // init values with defaults before calling setProperties + uint64_t now = usecTimestampNow(); + _lastEdited = now; + _lastUpdated = now; + + _position = glm::vec3(0,0,0); + _radius = 0; + rgbColor noColor = { 0, 0, 0 }; + memcpy(_color, noColor, sizeof(_color)); + _shouldDie = false; + _modelURL = MODEL_DEFAULT_MODEL_URL; + _modelRotation = MODEL_DEFAULT_MODEL_ROTATION; - rgbColor defaultColor = { 0, 0, 0 }; - init(glm::vec3(), 0.0f, defaultColor, modelItemID.id); + // animation related + _animationURL = MODEL_DEFAULT_ANIMATION_URL; + _animationIsPlaying = false; + _animationFrameIndex = 0.0f; + _animationFPS = MODEL_DEFAULT_ANIMATION_FPS; + _glowLevel = 0.0f; + + _jointMappingCompleted = false; + _lastAnimated = now; setProperties(properties); } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index 563d394419..9a558f2ef4 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -276,7 +276,6 @@ public: // these methods allow you to create models, and later edit them. static uint32_t getIDfromCreatorTokenID(uint32_t creatorTokenID); static uint32_t getNextCreatorTokenID(); - static uint32_t getNextModelItemID(); static void handleAddModelResponse(const QByteArray& packet); void mapJoints(const QStringList& modelJointNames); diff --git a/libraries/models/src/ModelsScriptingInterface.cpp b/libraries/models/src/ModelsScriptingInterface.cpp index f4cbf14086..7e08571fe5 100644 --- a/libraries/models/src/ModelsScriptingInterface.cpp +++ b/libraries/models/src/ModelsScriptingInterface.cpp @@ -28,9 +28,8 @@ ModelItemID ModelsScriptingInterface::addModel(const ModelItemProperties& proper // The application will keep track of creatorTokenID uint32_t creatorTokenID = ModelItem::getNextCreatorTokenID(); - uint32_t modelID = ModelItem::getNextModelItemID(); - ModelItemID id(modelID, creatorTokenID, false ); + ModelItemID id(NEW_MODEL, creatorTokenID, false ); // queue the packet queueModelMessage(PacketTypeModelAddOrEdit, id, properties); From 0fc6354b6cb14b5391761252183a167fb9999b32 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 6 Jun 2014 11:18:55 -0700 Subject: [PATCH 16/38] Fix for streaming the same object. --- libraries/metavoxels/src/Bitstream.cpp | 6 ++++-- tests/metavoxels/src/MetavoxelTests.cpp | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 30d34580d7..bc187d9f3d 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -224,7 +224,8 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) { } connect(it.key().data(), SIGNAL(destroyed(QObject*)), SLOT(clearSharedObject(QObject*))); QPointer& reference = _sharedObjectReferences[it.key()->getOriginID()]; - if (reference) { + if (reference && reference != it.key()) { + // the object has been replaced by a successor, so we can forget about the original _sharedObjectStreamer.removePersistentID(reference); reference->disconnect(this); } @@ -258,7 +259,8 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) { continue; } QPointer& reference = _sharedObjectReferences[it.value()->getRemoteOriginID()]; - if (reference) { + if (reference && reference != it.value()) { + // the object has been replaced by a successor, so we can forget about the original _sharedObjectStreamer.removePersistentValue(reference.data()); } reference = it.value(); diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index c4d973ad05..eec26ae411 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -183,7 +183,7 @@ bool MetavoxelTests::run() { bob.setOther(&alice); // perform a large number of simulation iterations - const int SIMULATION_ITERATIONS = 100000; + const int SIMULATION_ITERATIONS = 10000; for (int i = 0; i < SIMULATION_ITERATIONS; i++) { if (alice.simulate(i) || bob.simulate(i)) { return true; @@ -241,7 +241,7 @@ Endpoint::Endpoint(const QByteArray& datagramHeader) : _receiveRecords.append(receiveRecord); // create the object that represents out delta-encoded state - //_localState = new TestSharedObjectA(); + _localState = new TestSharedObjectA(); connect(_sequencer->getReliableInputChannel(), SIGNAL(receivedMessage(const QVariant&)), SLOT(handleReliableMessage(const QVariant&))); From 69adf8d0dd8edcb3314016ce8076d9b4c26e429a Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 6 Jun 2014 11:19:43 -0700 Subject: [PATCH 17/38] Removed extra debug, fix model jumping when releasing one of the two triggers. --- examples/editModels.js | 50 ++++++++++-------------------------------- 1 file changed, 11 insertions(+), 39 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 67e2bdb198..93a34b9a3a 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -356,7 +356,6 @@ function controller(wichSide) { var factor2 = Vec3.dot(forward, this.modelPositionAtGrab) - d; var vector = Vec3.subtract(this.palmPosition, this.positionAtGrab); - print("factor1: " + factor1 + ", factor2: " + factor2); if (factor2 < 0) { factor2 = 0; } @@ -395,46 +394,8 @@ function controller(wichSide) { for (var i = 0; i < indicesToRemove.length; ++i) { this.jointsIntersectingFromStart.splice(this.jointsIntersectingFromStart.indexOf(indicesToRemove[i], 1)); } - - - jointList = MyAvatar.getJointNames(); - - var closestJointIndex = -1; - var closestJointDistance = 999999; - for (var i = 0; i < jointList.length; i++) { - var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition); - if (distance < closestJointDistance) { - closestJointDistance = distance; - closestJointIndex = i; - } - } - - if (closestJointDistance < this.oldModelRadius) { - if (this.jointsIntersectingFromStart.indexOf(closestJointIndex) != -1 || - (leftController.grabbing && rightController.grabbing && - leftController.modelID.id == rightController.modelID.id)) { - // Do nothing - } else { - Vec3.print("Ball at: ", MyAvatar.getJointPosition(closestJointIndex)); - Overlays.editOverlay(this.ballGlowingJoint, { - position: MyAvatar.getJointPosition(closestJointIndex), - glowLevel: 0.25, - visible: true - }); - } - } else { - Overlays.editOverlay(this.ballGlowingJoint, { glowLevel: 0.0, visible: false }); - } } } - this.ballGlowingJoint = Overlays.addOverlay("sphere", { - position: { x: 0, y: 0, z: 0 }, - size: 1, - solid: true, - color: { red: 0, green: 255, blue: 0 }, - alpha: 1, - visible: false, - anchor: "MyAvatar"}); this.update = function () { this.oldPalmPosition = this.palmPosition; @@ -617,6 +578,17 @@ function moveModels() { var w = Vec3.normalize(Vec3.cross(u, v)); rotation = Quat.multiply(Quat.angleAxis(angle, w), leftController.oldModelRotation); + + + leftController.positionAtGrab = leftController.palmPosition; + leftController.rotationAtGrab = leftController.rotation; + leftController.modelPositionAtGrab = leftController.oldModelPosition; + leftController.modelRotationAtGrab = rotation; + + rightController.positionAtGrab = rightController.palmPosition; + rightController.rotationAtGrab = rightController.rotation; + rightController.modelPositionAtGrab = rightController.oldModelPosition; + rightController.modelRotationAtGrab = rotation; break; } From 56d9bc5215a60b72cc6696e9d21eb14f4caf312d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 6 Jun 2014 11:37:45 -0700 Subject: [PATCH 18/38] Working on delta-streaming tests. --- tests/metavoxels/src/MetavoxelTests.cpp | 33 ++++++++++++++++++++++++- tests/metavoxels/src/MetavoxelTests.h | 1 + 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index eec26ae411..b2dcc1dd45 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -38,6 +38,7 @@ static int streamedBytesSent = 0; static int streamedBytesReceived = 0; static int sharedObjectsCreated = 0; static int sharedObjectsDestroyed = 0; +static int objectMutationsPerformed = 0; static QByteArray createRandomBytes(int minimumSize, int maximumSize) { QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0); @@ -196,6 +197,7 @@ bool MetavoxelTests::run() { qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived; qDebug() << "Sent" << datagramsSent << "datagrams, received" << datagramsReceived; qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed; + qDebug() << "Performed" << objectMutationsPerformed << "object mutations"; qDebug(); qDebug() << "Running serialization tests..."; @@ -272,13 +274,37 @@ static QVariant createRandomMessage() { TestMessageB message = { createRandomBytes(), createRandomSharedObject(), getRandomTestEnum() }; return QVariant::fromValue(message); } - case 2: default: { return QVariant::fromValue(createRandomMessageC()); } } } +static SharedObjectPointer mutate(const SharedObjectPointer& state) { + switch(randIntInRange(0, 3)) { + case 0: { + SharedObjectPointer newState = state->clone(true); + static_cast(newState.data())->setFoo(randFloat()); + objectMutationsPerformed++; + return newState; + } + case 1: { + SharedObjectPointer newState = state->clone(true); + static_cast(newState.data())->setBaz(getRandomTestEnum()); + objectMutationsPerformed++; + return newState; + } + case 2: { + SharedObjectPointer newState = state->clone(true); + static_cast(newState.data())->setBong(getRandomTestFlags()); + objectMutationsPerformed++; + return newState; + } + default: + return state; + } +} + static bool messagesEqual(const QVariant& firstMessage, const QVariant& secondMessage) { int type = firstMessage.userType(); if (secondMessage.userType() != type) { @@ -337,6 +363,9 @@ bool Endpoint::simulate(int iterationNumber) { _reliableMessagesToSend -= 1.0f; } + // tweak the local state + _localState = mutate(_localState); + // send a packet try { Bitstream& out = _sequencer->startPacket(); @@ -405,6 +434,8 @@ void Endpoint::readMessage(Bitstream& in) { SequencedTestMessage message; in >> message; + _remoteState = message.state; + // record the receipt ReceiveRecord record = { _sequencer->getIncomingPacketNumber(), message.state }; _receiveRecords.append(record); diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index dc294692af..345ea624df 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -76,6 +76,7 @@ private: QList _receiveRecords; SharedObjectPointer _localState; + SharedObjectPointer _remoteState; Endpoint* _other; QList > _delayedDatagrams; From 361461516c8675897adb331edab325db298ebf1a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 6 Jun 2014 14:26:20 -0700 Subject: [PATCH 19/38] Check the delta-encoded objects, report bytes sent/received. --- tests/metavoxels/src/MetavoxelTests.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index b2dcc1dd45..81f1840342 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -28,6 +28,8 @@ MetavoxelTests::MetavoxelTests(int& argc, char** argv) : static int datagramsSent = 0; static int datagramsReceived = 0; +static int bytesSent = 0; +static int bytesReceived = 0; static int highPriorityMessagesSent = 0; static int highPriorityMessagesReceived = 0; static int unreliableMessagesSent = 0; @@ -195,7 +197,8 @@ bool MetavoxelTests::run() { 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, received" << datagramsReceived; + 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(); @@ -389,6 +392,7 @@ bool Endpoint::simulate(int iterationNumber) { void Endpoint::sendDatagram(const QByteArray& datagram) { datagramsSent++; + bytesSent += datagram.size(); // some datagrams are dropped const float DROP_PROBABILITY = 0.1f; @@ -414,6 +418,7 @@ void Endpoint::sendDatagram(const QByteArray& datagram) { _other->_sequencer->receivedDatagram(datagram); datagramsReceived++; + bytesReceived += datagram.size(); } void Endpoint::handleHighPriorityMessage(const QVariant& message) { @@ -446,6 +451,9 @@ void Endpoint::readMessage(Bitstream& in) { if (!messagesEqual(it->submessage, message.submessage)) { throw QString("Sent/received unreliable message mismatch."); } + if (!it->state->equals(message.state)) { + throw QString("Delta-encoded object mismatch."); + } _other->_unreliableMessagesSent.erase(_other->_unreliableMessagesSent.begin(), it + 1); unreliableMessagesReceived++; return; From 2fec0a9db603e09e7314a4221e761141a57b127d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 6 Jun 2014 15:50:28 -0700 Subject: [PATCH 20/38] Fixes for Windows. --- libraries/metavoxels/src/Bitstream.cpp | 87 ++++++++++++++++---------- libraries/metavoxels/src/Bitstream.h | 27 ++++---- 2 files changed, 71 insertions(+), 43 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index bc187d9f3d..6f4af8ed5f 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -79,10 +79,6 @@ IDStreamer& IDStreamer::operator>>(int& value) { return *this; } -static QByteArray getEnumName(const QMetaEnum& metaEnum) { - return QByteArray(metaEnum.scope()) + "::" + metaEnum.name(); -} - int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) { getMetaObjects().insert(className, metaObject); @@ -90,16 +86,6 @@ int Bitstream::registerMetaObject(const char* className, const QMetaObject* meta for (const QMetaObject* superClass = metaObject; superClass; superClass = superClass->superClass()) { getMetaObjectSubClasses().insert(superClass, metaObject); } - - // register the streamers for all enumerators - for (int i = 0; i < metaObject->enumeratorCount(); i++) { - QMetaEnum metaEnum = metaObject->enumerator(i); - const TypeStreamer*& streamer = getEnumStreamers()[QPair(metaEnum.scope(), metaEnum.name())]; - if (!streamer) { - getEnumStreamersByName().insert(getEnumName(metaEnum), streamer = new EnumTypeStreamer(metaEnum)); - } - } - return 0; } @@ -1030,12 +1016,34 @@ QHash& Bitstream::getTypeStreamers() { } QHash, const TypeStreamer*>& Bitstream::getEnumStreamers() { - static QHash, const TypeStreamer*> enumStreamers; + static QHash, const TypeStreamer*> enumStreamers = createEnumStreamers(); + return enumStreamers; +} + +QHash, const TypeStreamer*> Bitstream::createEnumStreamers() { + QHash, const TypeStreamer*> enumStreamers; + foreach (const QMetaObject* metaObject, getMetaObjects()) { + for (int i = 0; i < metaObject->enumeratorCount(); i++) { + QMetaEnum metaEnum = metaObject->enumerator(i); + const TypeStreamer*& streamer = enumStreamers[QPair(metaEnum.scope(), metaEnum.name())]; + if (!streamer) { + streamer = new EnumTypeStreamer(metaEnum); + } + } + } return enumStreamers; } QHash& Bitstream::getEnumStreamersByName() { - static QHash enumStreamersByName; + static QHash enumStreamersByName = createEnumStreamersByName(); + return enumStreamersByName; +} + +QHash Bitstream::createEnumStreamersByName() { + QHash enumStreamersByName; + foreach (const TypeStreamer* streamer, getEnumStreamers()) { + enumStreamersByName.insert(streamer->getName(), streamer); + } return enumStreamersByName; } @@ -1462,17 +1470,21 @@ QDebug& operator<<(QDebug& debug, const QMetaObject* metaObject) { return debug << (metaObject ? metaObject->className() : "null"); } +EnumTypeStreamer::EnumTypeStreamer(const QMetaObject* metaObject, const char* name) : + _metaObject(metaObject), + _enumName(name), + _name(QByteArray(metaObject->className()) + "::" + name), + _bits(-1) { + + setType(QMetaType::Int); +} + EnumTypeStreamer::EnumTypeStreamer(const QMetaEnum& metaEnum) : + _name(QByteArray(metaEnum.scope()) + "::" + metaEnum.name()), _metaEnum(metaEnum), - _name(getEnumName(metaEnum)) { - - setType(QMetaType::Int); - - int highestValue = 0; - for (int j = 0; j < metaEnum.keyCount(); j++) { - highestValue = qMax(highestValue, metaEnum.value(j)); - } - _bits = getBitsForHighestValue(highestValue); + _bits(-1) { + + setType(QMetaType::Int); } const char* EnumTypeStreamer::getName() const { @@ -1484,10 +1496,21 @@ TypeReader::Type EnumTypeStreamer::getReaderType() const { } int EnumTypeStreamer::getBits() const { + if (_bits == -1) { + int highestValue = 0; + QMetaEnum metaEnum = getMetaEnum(); + for (int j = 0; j < metaEnum.keyCount(); j++) { + highestValue = qMax(highestValue, metaEnum.value(j)); + } + const_cast(this)->_bits = getBitsForHighestValue(highestValue); + } return _bits; } QMetaEnum EnumTypeStreamer::getMetaEnum() const { + if (!_metaEnum.isValid()) { + const_cast(this)->_metaEnum = _metaObject->enumerator(_metaObject->indexOfEnumerator(_enumName)); + } return _metaEnum; } @@ -1497,12 +1520,12 @@ bool EnumTypeStreamer::equal(const QVariant& first, const QVariant& second) cons void EnumTypeStreamer::write(Bitstream& out, const QVariant& value) const { int intValue = value.toInt(); - out.write(&intValue, _bits); + out.write(&intValue, getBits()); } QVariant EnumTypeStreamer::read(Bitstream& in) const { int intValue = 0; - in.read(&intValue, _bits); + in.read(&intValue, getBits()); return intValue; } @@ -1512,7 +1535,7 @@ void EnumTypeStreamer::writeDelta(Bitstream& out, const QVariant& value, const Q out << false; } else { out << true; - out.write(&intValue, _bits); + out.write(&intValue, getBits()); } } @@ -1521,7 +1544,7 @@ void EnumTypeStreamer::readDelta(Bitstream& in, QVariant& value, const QVariant& in >> changed; if (changed) { int intValue = 0; - in.read(&intValue, _bits); + in.read(&intValue, getBits()); value = intValue; } else { value = reference; @@ -1530,17 +1553,17 @@ void EnumTypeStreamer::readDelta(Bitstream& in, QVariant& value, const QVariant& void EnumTypeStreamer::writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { int intValue = value.toInt(); - out.write(&intValue, _bits); + out.write(&intValue, getBits()); } void EnumTypeStreamer::readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { int intValue = 0; - in.read(&intValue, _bits); + in.read(&intValue, getBits()); value = intValue; } void EnumTypeStreamer::setEnumValue(QVariant& object, int value, const QHash& mappings) const { - if (_metaEnum.isFlag()) { + if (getMetaEnum().isFlag()) { int combined = 0; for (QHash::const_iterator it = mappings.constBegin(); it != mappings.constEnd(); it++) { if (value & it.key()) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index caf0adf7fc..b0c33754f9 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -427,7 +427,9 @@ private: static QMultiHash& getMetaObjectSubClasses(); static QHash& getTypeStreamers(); static QHash, const TypeStreamer*>& getEnumStreamers(); + static QHash, const TypeStreamer*> createEnumStreamers(); static QHash& getEnumStreamersByName(); + static QHash createEnumStreamersByName(); static QVector getPropertyReaders(const QMetaObject* metaObject); }; @@ -938,8 +940,9 @@ public: class EnumTypeStreamer : public TypeStreamer { public: + EnumTypeStreamer(const QMetaObject* metaObject, const char* name); EnumTypeStreamer(const QMetaEnum& metaEnum); - + virtual const char* getName() const; virtual TypeReader::Type getReaderType() const; virtual int getBits() const; @@ -955,8 +958,10 @@ public: private: - QMetaEnum _metaEnum; + const QMetaObject* _metaObject; + const char* _enumName; QByteArray _name; + QMetaEnum _metaEnum; int _bits; }; @@ -1084,14 +1089,15 @@ public: template<> inline void Bitstream::readRawDelta(S::N& value, const S::N& reference) { *this >> value; } #define IMPLEMENT_ENUM_METATYPE(S, N) \ - static int S##N##Bits = registerEnumMetaType(S::staticMetaObject.enumerator( \ - S::staticMetaObject.indexOfEnumerator(#N))); \ + static int S##N##MetaTypeId = registerEnumMetaType(&S::staticMetaObject, #N); \ Bitstream& operator<<(Bitstream& out, const S::N& obj) { \ - return out.write(&obj, S##N##Bits); \ + static int bits = Bitstream::getTypeStreamer(qMetaTypeId())->getBits(); \ + return out.write(&obj, bits); \ } \ Bitstream& operator>>(Bitstream& in, S::N& obj) { \ + static int bits = Bitstream::getTypeStreamer(qMetaTypeId())->getBits(); \ obj = (S::N)0; \ - return in.read(&obj, S##N##Bits); \ + return in.read(&obj, bits); \ } /// Registers a simple type and its streamer. @@ -1103,12 +1109,11 @@ template int registerSimpleMetaType() { } /// Registers an enum type and its streamer. -/// \return the number of bits required to stream the enum -template int registerEnumMetaType(const QMetaEnum& metaEnum) { +/// \return the metatype id +template int registerEnumMetaType(const QMetaObject* metaObject, const char* name) { int type = qRegisterMetaType(); - EnumTypeStreamer* streamer = new EnumTypeStreamer(metaEnum); - Bitstream::registerTypeStreamer(type, streamer); - return streamer->getBits(); + Bitstream::registerTypeStreamer(type, new EnumTypeStreamer(metaObject, name)); + return type; } /// Registers a streamable type and its streamer. From 384fe51937d169f4fd34d7edbf15350e9331e77b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 6 Jun 2014 16:38:52 -0700 Subject: [PATCH 21/38] If we're going to initialize one mapping lazily, we might as well initialize some more that way. --- libraries/metavoxels/src/Bitstream.cpp | 119 ++++++++++++++----------- libraries/metavoxels/src/Bitstream.h | 13 ++- 2 files changed, 71 insertions(+), 61 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 6f4af8ed5f..d9242ad9b7 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -298,7 +298,7 @@ void Bitstream::writeRawDelta(const QObject* value, const QObject* reference) { } const QMetaObject* metaObject = value->metaObject(); _metaObjectStreamer << metaObject; - foreach (const PropertyWriter& propertyWriter, getPropertyWriters(metaObject)) { + foreach (const PropertyWriter& propertyWriter, getPropertyWriters().value(metaObject)) { propertyWriter.writeDelta(*this, value, reference); } } @@ -476,7 +476,7 @@ Bitstream& Bitstream::operator<<(const QObject* object) { } const QMetaObject* metaObject = object->metaObject(); _metaObjectStreamer << metaObject; - foreach (const PropertyWriter& propertyWriter, getPropertyWriters(metaObject)) { + foreach (const PropertyWriter& propertyWriter, getPropertyWriters().value(metaObject)) { propertyWriter.write(*this, object); } return *this; @@ -561,7 +561,7 @@ Bitstream& Bitstream::operator<(const QMetaObject* metaObject) { if (_metadataType == NO_METADATA) { return *this; } - const QVector& propertyWriters = getPropertyWriters(metaObject); + const QVector& propertyWriters = getPropertyWriters().value(metaObject); *this << propertyWriters.size(); QCryptographicHash hash(QCryptographicHash::Md5); foreach (const PropertyWriter& propertyWriter, propertyWriters) { @@ -595,7 +595,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { qWarning() << "Unknown class name: " << className << "\n"; } if (_metadataType == NO_METADATA) { - objectReader = ObjectReader(className, metaObject, getPropertyReaders(metaObject)); + objectReader = ObjectReader(className, metaObject, getPropertyReaders().value(metaObject)); return *this; } int storedPropertyCount; @@ -619,7 +619,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { QCryptographicHash hash(QCryptographicHash::Md5); bool matches = true; if (metaObject) { - const QVector& propertyWriters = getPropertyWriters(metaObject); + const QVector& propertyWriters = getPropertyWriters().value(metaObject); if (propertyWriters.size() == properties.size()) { for (int i = 0; i < propertyWriters.size(); i++) { const PropertyWriter& propertyWriter = propertyWriters.at(i); @@ -638,7 +638,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { QByteArray remoteHashResult(localHashResult.size(), 0); read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); if (metaObject && matches && localHashResult == remoteHashResult) { - objectReader = ObjectReader(className, metaObject, getPropertyReaders(metaObject)); + objectReader = ObjectReader(className, metaObject, getPropertyReaders().value(metaObject)); return *this; } } @@ -975,31 +975,6 @@ void Bitstream::clearSharedObject(QObject* object) { } } -const QVector& Bitstream::getPropertyWriters(const QMetaObject* metaObject) { - QVector& propertyWriters = _propertyWriters[metaObject]; - if (propertyWriters.isEmpty()) { - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored()) { - continue; - } - const TypeStreamer* streamer; - if (property.isEnumType()) { - QMetaEnum metaEnum = property.enumerator(); - streamer = getEnumStreamers().value(QPair( - QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), - QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); - } else { - streamer = getTypeStreamers().value(property.userType()); - } - if (streamer) { - propertyWriters.append(PropertyWriter(property, streamer)); - } - } - } - return propertyWriters; -} - QHash& Bitstream::getMetaObjects() { static QHash metaObjects; return metaObjects; @@ -1015,7 +990,7 @@ QHash& Bitstream::getTypeStreamers() { return typeStreamers; } -QHash, const TypeStreamer*>& Bitstream::getEnumStreamers() { +const QHash, const TypeStreamer*>& Bitstream::getEnumStreamers() { static QHash, const TypeStreamer*> enumStreamers = createEnumStreamers(); return enumStreamers; } @@ -1034,7 +1009,7 @@ QHash, const TypeStreamer*> Bitstream::createEnumS return enumStreamers; } -QHash& Bitstream::getEnumStreamersByName() { +const QHash& Bitstream::getEnumStreamersByName() { static QHash enumStreamersByName = createEnumStreamersByName(); return enumStreamersByName; } @@ -1047,32 +1022,68 @@ QHash Bitstream::createEnumStreamersByName() { return enumStreamersByName; } -QVector Bitstream::getPropertyReaders(const QMetaObject* metaObject) { - QVector propertyReaders; - if (!metaObject) { - return propertyReaders; - } - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored()) { - continue; - } - const TypeStreamer* streamer; - if (property.isEnumType()) { - QMetaEnum metaEnum = property.enumerator(); - streamer = getEnumStreamers().value(QPair( - QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), - QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); - } else { - streamer = getTypeStreamers().value(property.userType()); - } - if (streamer) { - propertyReaders.append(PropertyReader(TypeReader(QByteArray(), streamer), property)); +const QHash >& Bitstream::getPropertyReaders() { + static QHash > propertyReaders = createPropertyReaders(); + return propertyReaders; +} + +QHash > Bitstream::createPropertyReaders() { + QHash > propertyReaders; + foreach (const QMetaObject* metaObject, getMetaObjects()) { + QVector& readers = propertyReaders[metaObject]; + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (!property.isStored()) { + continue; + } + const TypeStreamer* streamer; + if (property.isEnumType()) { + QMetaEnum metaEnum = property.enumerator(); + streamer = getEnumStreamers().value(QPair( + QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), + QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); + } else { + streamer = getTypeStreamers().value(property.userType()); + } + if (streamer) { + readers.append(PropertyReader(TypeReader(QByteArray(), streamer), property)); + } } } return propertyReaders; } +const QHash >& Bitstream::getPropertyWriters() { + static QHash > propertyWriters = createPropertyWriters(); + return propertyWriters; +} + +QHash > Bitstream::createPropertyWriters() { + QHash > propertyWriters; + foreach (const QMetaObject* metaObject, getMetaObjects()) { + QVector& writers = propertyWriters[metaObject]; + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (!property.isStored()) { + continue; + } + const TypeStreamer* streamer; + if (property.isEnumType()) { + QMetaEnum metaEnum = property.enumerator(); + streamer = getEnumStreamers().value(QPair( + QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), + QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); + } else { + streamer = getTypeStreamers().value(property.userType()); + } + if (streamer) { + writers.append(PropertyWriter(property, streamer)); + } + } + } + return propertyWriters; +} + TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer) : _typeName(typeName), _streamer(streamer), diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index b0c33754f9..80adfc4e8b 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -400,8 +400,6 @@ private slots: private: - const QVector& getPropertyWriters(const QMetaObject* metaObject); - QDataStream& _underlying; quint8 _byte; int _position; @@ -421,16 +419,17 @@ private: QHash _metaObjectSubstitutions; QHash _typeStreamerSubstitutions; - QHash > _propertyWriters; - static QHash& getMetaObjects(); static QMultiHash& getMetaObjectSubClasses(); static QHash& getTypeStreamers(); - static QHash, const TypeStreamer*>& getEnumStreamers(); + static const QHash, const TypeStreamer*>& getEnumStreamers(); static QHash, const TypeStreamer*> createEnumStreamers(); - static QHash& getEnumStreamersByName(); + static const QHash& getEnumStreamersByName(); static QHash createEnumStreamersByName(); - static QVector getPropertyReaders(const QMetaObject* metaObject); + static const QHash >& getPropertyReaders(); + static QHash > createPropertyReaders(); + static const QHash >& getPropertyWriters(); + static QHash > createPropertyWriters(); }; template inline void Bitstream::writeDelta(const T& value, const T& reference) { From e620518d2f938c14ae36717d8ec8ced13a436852 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 6 Jun 2014 17:59:30 -0700 Subject: [PATCH 22/38] Working on streaming script values. --- libraries/metavoxels/src/Bitstream.cpp | 92 ++++++++++++++++++++++++++ libraries/metavoxels/src/Bitstream.h | 4 ++ 2 files changed, 96 insertions(+) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index d9242ad9b7..63ef8b09bb 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -543,6 +543,98 @@ Bitstream& Bitstream::operator>>(QScriptString& string) { return *this; } +enum ScriptValueType { + INVALID_SCRIPT_VALUE, + UNDEFINED_SCRIPT_VALUE, + NULL_SCRIPT_VALUE, + BOOL_SCRIPT_VALUE, + NUMBER_SCRIPT_VALUE, + STRING_SCRIPT_VALUE, + QVARIANT_SCRIPT_VALUE, + QOBJECT_SCRIPT_VALUE, + DATE_SCRIPT_VALUE, + REGEXP_SCRIPT_VALUE, + ARRAY_SCRIPT_VALUE, + OBJECT_SCRIPT_VALUE +}; + +const int SCRIPT_VALUE_BITS = 4; + +void writeScriptValueType(Bitstream& out, ScriptValueType type) { + out.write(&type, SCRIPT_VALUE_BITS); +} + +ScriptValueType readScriptValueType(Bitstream& in) { + ScriptValueType type = (ScriptValueType)0; + in.read(&type, SCRIPT_VALUE_BITS); + return type; +} + +Bitstream& Bitstream::operator<<(const QScriptValue& value) { + if (value.isUndefined()) { + writeScriptValueType(*this, UNDEFINED_SCRIPT_VALUE); + + } else if (value.isNull()) { + writeScriptValueType(*this, NULL_SCRIPT_VALUE); + + } else if (value.isBool()) { + writeScriptValueType(*this, BOOL_SCRIPT_VALUE); + + } else if (value.isNumber()) { + writeScriptValueType(*this, NUMBER_SCRIPT_VALUE); + + } else if (value.isString()) { + writeScriptValueType(*this, STRING_SCRIPT_VALUE); + + } else if (value.isArray()) { + writeScriptValueType(*this, ARRAY_SCRIPT_VALUE); + + } else if (value.isObject()) { + writeScriptValueType(*this, OBJECT_SCRIPT_VALUE); + + } else { + writeScriptValueType(*this, INVALID_SCRIPT_VALUE); + } + return *this; +} + +Bitstream& Bitstream::operator>>(QScriptValue& value) { + switch (readScriptValueType(*this)) { + case UNDEFINED_SCRIPT_VALUE: + value = QScriptValue(QScriptValue::UndefinedValue); + break; + + case NULL_SCRIPT_VALUE: + value = QScriptValue(QScriptValue::NullValue); + break; + + case BOOL_SCRIPT_VALUE: + value = QScriptValue(); + break; + + case NUMBER_SCRIPT_VALUE: + value = QScriptValue(); + break; + + case STRING_SCRIPT_VALUE: + value = QScriptValue(); + break; + + case ARRAY_SCRIPT_VALUE: + value = QScriptValue(); + break; + + case OBJECT_SCRIPT_VALUE: + value = QScriptValue(); + break; + + default: + value = QScriptValue(); + break; + } + return *this; +} + Bitstream& Bitstream::operator<<(const SharedObjectPointer& object) { _sharedObjectStreamer << object; return *this; diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 80adfc4e8b..3b1459109c 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -29,6 +29,7 @@ class QByteArray; class QColor; class QDataStream; +class QScriptValue; class QUrl; class Attribute; @@ -372,6 +373,9 @@ public: Bitstream& operator<<(const QScriptString& string); Bitstream& operator>>(QScriptString& string); + Bitstream& operator<<(const QScriptValue& value); + Bitstream& operator>>(QScriptValue& value); + Bitstream& operator<<(const SharedObjectPointer& object); Bitstream& operator>>(SharedObjectPointer& object); From 4d423c679c2700646d7d54bd6a4f9dd3a65d84a7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 10:21:55 -0700 Subject: [PATCH 23/38] Added some typedefs to simplify the various mapping types. --- libraries/metavoxels/src/Bitstream.cpp | 42 +++++++++++++------------- libraries/metavoxels/src/Bitstream.h | 20 ++++++++---- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 63ef8b09bb..e621484986 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -653,7 +653,7 @@ Bitstream& Bitstream::operator<(const QMetaObject* metaObject) { if (_metadataType == NO_METADATA) { return *this; } - const QVector& propertyWriters = getPropertyWriters().value(metaObject); + const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject); *this << propertyWriters.size(); QCryptographicHash hash(QCryptographicHash::Md5); foreach (const PropertyWriter& propertyWriter, propertyWriters) { @@ -692,7 +692,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { } int storedPropertyCount; *this >> storedPropertyCount; - QVector properties(storedPropertyCount); + PropertyReaderVector properties(storedPropertyCount); for (int i = 0; i < storedPropertyCount; i++) { TypeReader typeReader; *this >> typeReader; @@ -711,7 +711,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { QCryptographicHash hash(QCryptographicHash::Md5); bool matches = true; if (metaObject) { - const QVector& propertyWriters = getPropertyWriters().value(metaObject); + const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject); if (propertyWriters.size() == properties.size()) { for (int i = 0; i < propertyWriters.size(); i++) { const PropertyWriter& propertyWriter = propertyWriters.at(i); @@ -1082,17 +1082,17 @@ QHash& Bitstream::getTypeStreamers() { return typeStreamers; } -const QHash, const TypeStreamer*>& Bitstream::getEnumStreamers() { - static QHash, const TypeStreamer*> enumStreamers = createEnumStreamers(); +const QHash& Bitstream::getEnumStreamers() { + static QHash enumStreamers = createEnumStreamers(); return enumStreamers; } -QHash, const TypeStreamer*> Bitstream::createEnumStreamers() { - QHash, const TypeStreamer*> enumStreamers; +QHash Bitstream::createEnumStreamers() { + QHash enumStreamers; foreach (const QMetaObject* metaObject, getMetaObjects()) { for (int i = 0; i < metaObject->enumeratorCount(); i++) { QMetaEnum metaEnum = metaObject->enumerator(i); - const TypeStreamer*& streamer = enumStreamers[QPair(metaEnum.scope(), metaEnum.name())]; + const TypeStreamer*& streamer = enumStreamers[ScopeNamePair(metaEnum.scope(), metaEnum.name())]; if (!streamer) { streamer = new EnumTypeStreamer(metaEnum); } @@ -1114,15 +1114,15 @@ QHash Bitstream::createEnumStreamersByName() { return enumStreamersByName; } -const QHash >& Bitstream::getPropertyReaders() { - static QHash > propertyReaders = createPropertyReaders(); +const QHash& Bitstream::getPropertyReaders() { + static QHash propertyReaders = createPropertyReaders(); return propertyReaders; } -QHash > Bitstream::createPropertyReaders() { - QHash > propertyReaders; +QHash Bitstream::createPropertyReaders() { + QHash propertyReaders; foreach (const QMetaObject* metaObject, getMetaObjects()) { - QVector& readers = propertyReaders[metaObject]; + PropertyReaderVector& readers = propertyReaders[metaObject]; for (int i = 0; i < metaObject->propertyCount(); i++) { QMetaProperty property = metaObject->property(i); if (!property.isStored()) { @@ -1131,7 +1131,7 @@ QHash > Bitstream::createPropertyRea const TypeStreamer* streamer; if (property.isEnumType()) { QMetaEnum metaEnum = property.enumerator(); - streamer = getEnumStreamers().value(QPair( + streamer = getEnumStreamers().value(ScopeNamePair( QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); } else { @@ -1145,15 +1145,15 @@ QHash > Bitstream::createPropertyRea return propertyReaders; } -const QHash >& Bitstream::getPropertyWriters() { - static QHash > propertyWriters = createPropertyWriters(); +const QHash& Bitstream::getPropertyWriters() { + static QHash propertyWriters = createPropertyWriters(); return propertyWriters; } -QHash > Bitstream::createPropertyWriters() { - QHash > propertyWriters; +QHash Bitstream::createPropertyWriters() { + QHash propertyWriters; foreach (const QMetaObject* metaObject, getMetaObjects()) { - QVector& writers = propertyWriters[metaObject]; + PropertyWriterVector& writers = propertyWriters[metaObject]; for (int i = 0; i < metaObject->propertyCount(); i++) { QMetaProperty property = metaObject->property(i); if (!property.isStored()) { @@ -1162,7 +1162,7 @@ QHash > Bitstream::createPropertyWri const TypeStreamer* streamer; if (property.isEnumType()) { QMetaEnum metaEnum = property.enumerator(); - streamer = getEnumStreamers().value(QPair( + streamer = getEnumStreamers().value(ScopeNamePair( QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); } else { @@ -1416,7 +1416,7 @@ void FieldReader::readDelta(Bitstream& in, const TypeStreamer* streamer, QVarian } ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject, - const QVector& properties) : + const PropertyReaderVector& properties) : _className(className), _metaObject(metaObject), _properties(properties) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 3b1459109c..4fe136f720 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -45,6 +45,10 @@ class TypeStreamer; typedef SharedObjectPointerTemplate AttributePointer; +typedef QPair ScopeNamePair; +typedef QVector PropertyReaderVector; +typedef QVector PropertyWriterVector; + /// Streams integer identifiers that conform to the following pattern: each ID encountered in the stream is either one that /// has been sent (received) before, or is one more than the highest previously encountered ID (starting at zero). This allows /// us to use the minimum number of bits to encode the IDs. @@ -426,14 +430,18 @@ private: static QHash& getMetaObjects(); static QMultiHash& getMetaObjectSubClasses(); static QHash& getTypeStreamers(); - static const QHash, const TypeStreamer*>& getEnumStreamers(); - static QHash, const TypeStreamer*> createEnumStreamers(); + + static const QHash& getEnumStreamers(); + static QHash createEnumStreamers(); + static const QHash& getEnumStreamersByName(); static QHash createEnumStreamersByName(); - static const QHash >& getPropertyReaders(); - static QHash > createPropertyReaders(); - static const QHash >& getPropertyWriters(); - static QHash > createPropertyWriters(); + + static const QHash& getPropertyReaders(); + static QHash createPropertyReaders(); + + static const QHash& getPropertyWriters(); + static QHash createPropertyWriters(); }; template inline void Bitstream::writeDelta(const T& value, const T& reference) { From ae22aa1a215d5fa540082d346b9aff24ece51946 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 10:53:44 -0700 Subject: [PATCH 24/38] Working on JS type streaming. --- libraries/metavoxels/src/Bitstream.cpp | 72 +++++++++++++++++++++++++- libraries/metavoxels/src/Bitstream.h | 12 +++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index e621484986..8f813adf90 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -350,6 +350,14 @@ Bitstream& Bitstream::operator>>(uint& value) { return *this; } +Bitstream& Bitstream::operator<<(qint64 value) { + return write(&value, 64); +} + +Bitstream& Bitstream::operator>>(qint64& value) { + return read(&value, 64); +} + Bitstream& Bitstream::operator<<(float value) { return write(&value, 32); } @@ -358,6 +366,14 @@ Bitstream& Bitstream::operator>>(float& value) { return read(&value, 32); } +Bitstream& Bitstream::operator<<(double value) { + return write(&value, 64); +} + +Bitstream& Bitstream::operator>>(double& value) { + return read(&value, 64); +} + Bitstream& Bitstream::operator<<(const glm::vec3& value) { return *this << value.x << value.y << value.z; } @@ -420,6 +436,36 @@ Bitstream& Bitstream::operator>>(QUrl& url) { return *this; } +Bitstream& Bitstream::operator<<(const QDateTime& dateTime) { + return *this << dateTime.toMSecsSinceEpoch(); +} + +Bitstream& Bitstream::operator>>(QDateTime& dateTime) { + qint64 msecsSinceEpoch; + *this >> msecsSinceEpoch; + dateTime = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch); + return *this; +} + +Bitstream& Bitstream::operator<<(const QRegExp& regExp) { + *this << regExp.pattern(); + + return *this; +} + +Bitstream& Bitstream::operator>>(QRegExp& regExp) { + QString pattern; + *this >> pattern; + Qt::CaseSensitivity caseSensitivity = (Qt::CaseSensitivity)0; + read(&caseSensitivity, 1); + QRegExp::PatternSyntax syntax = (QRegExp::PatternSyntax)0; + read(&syntax, 3); + + regExp = QRegExp(pattern, caseSensitivity, syntax); + + return *this; +} + Bitstream& Bitstream::operator<<(const QVariant& value) { if (!value.isValid()) { _typeStreamerStreamer << NULL; @@ -550,8 +596,9 @@ enum ScriptValueType { BOOL_SCRIPT_VALUE, NUMBER_SCRIPT_VALUE, STRING_SCRIPT_VALUE, - QVARIANT_SCRIPT_VALUE, + VARIANT_SCRIPT_VALUE, QOBJECT_SCRIPT_VALUE, + QMETAOBJECT_SCRIPT_VALUE, DATE_SCRIPT_VALUE, REGEXP_SCRIPT_VALUE, ARRAY_SCRIPT_VALUE, @@ -579,12 +626,35 @@ Bitstream& Bitstream::operator<<(const QScriptValue& value) { } else if (value.isBool()) { writeScriptValueType(*this, BOOL_SCRIPT_VALUE); + *this << value.toBool(); } else if (value.isNumber()) { writeScriptValueType(*this, NUMBER_SCRIPT_VALUE); + *this << value.toNumber(); } else if (value.isString()) { writeScriptValueType(*this, STRING_SCRIPT_VALUE); + *this << value.toString(); + + } else if (value.isVariant()) { + writeScriptValueType(*this, VARIANT_SCRIPT_VALUE); + *this << value.toVariant(); + + } else if (value.isQObject()) { + writeScriptValueType(*this, QOBJECT_SCRIPT_VALUE); + *this << value.toQObject(); + + } else if (value.isQMetaObject()) { + writeScriptValueType(*this, QMETAOBJECT_SCRIPT_VALUE); + *this << value.toQMetaObject(); + + } else if (value.isDate()) { + writeScriptValueType(*this, DATE_SCRIPT_VALUE); + *this << value.toDateTime(); + + } else if (value.isRegExp()) { + writeScriptValueType(*this, REGEXP_SCRIPT_VALUE); + *this << value.toRegExp(); } else if (value.isArray()) { writeScriptValueType(*this, ARRAY_SCRIPT_VALUE); diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 4fe136f720..83733674a4 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -321,9 +321,15 @@ public: Bitstream& operator<<(uint value); Bitstream& operator>>(uint& value); + Bitstream& operator<<(qint64 value); + Bitstream& operator>>(qint64& value); + Bitstream& operator<<(float value); Bitstream& operator>>(float& value); + Bitstream& operator<<(double value); + Bitstream& operator>>(double& value); + Bitstream& operator<<(const glm::vec3& value); Bitstream& operator>>(glm::vec3& value); @@ -342,6 +348,12 @@ public: Bitstream& operator<<(const QUrl& url); Bitstream& operator>>(QUrl& url); + Bitstream& operator<<(const QDateTime& dateTime); + Bitstream& operator>>(QDateTime& dateTime); + + Bitstream& operator<<(const QRegExp& regExp); + Bitstream& operator>>(QRegExp& regExp); + Bitstream& operator<<(const QVariant& value); Bitstream& operator>>(QVariant& value); From 6eac0ee2aeb262feee7a192b17c786c933e1d8db Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 10:58:56 -0700 Subject: [PATCH 25/38] Avoid crash on zero-length normals. --- libraries/fbx/src/FBXReader.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 60412cf0ce..d668349b96 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -894,14 +894,19 @@ FBXBlendshape extractBlendshape(const FBXNode& object) { } void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) { - glm::vec3 normal = glm::normalize(mesh.normals.at(firstIndex)); - glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex)); + const glm::vec3& normal = mesh.normals.at(firstIndex); + float normalLength = glm::length(normal); + if (normalLength < EPSILON) { + return; + } + glm::vec3 normalizedNormal = normal / normalLength; + glm::vec3 bitangent = glm::cross(normalizedNormal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex)); if (glm::length(bitangent) < EPSILON) { return; } glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex); - mesh.tangents[firstIndex] += glm::cross(glm::angleAxis( - - atan2f(-texCoordDelta.t, texCoordDelta.s), normal) * glm::normalize(bitangent), normal); + mesh.tangents[firstIndex] += glm::cross(glm::angleAxis(-atan2f(-texCoordDelta.t, texCoordDelta.s), normalizedNormal) * + glm::normalize(bitangent), normalizedNormal); } QVector getIndices(const QVector ids, QVector modelIDs) { From a1e208ae3d3173ca4490a9d96941e860adf9e0b0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 11:03:50 -0700 Subject: [PATCH 26/38] Simplification. If the normal is zero, then the cross product will also be zero, so we can just have the one check and postpone normalization until afterwards. --- libraries/fbx/src/FBXReader.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index d668349b96..9aeb81a2a3 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -895,16 +895,12 @@ FBXBlendshape extractBlendshape(const FBXNode& object) { void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) { const glm::vec3& normal = mesh.normals.at(firstIndex); - float normalLength = glm::length(normal); - if (normalLength < EPSILON) { - return; - } - glm::vec3 normalizedNormal = normal / normalLength; - glm::vec3 bitangent = glm::cross(normalizedNormal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex)); + glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex)); if (glm::length(bitangent) < EPSILON) { return; } glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex); + glm::vec3 normalizedNormal = glm::normalize(normal); mesh.tangents[firstIndex] += glm::cross(glm::angleAxis(-atan2f(-texCoordDelta.t, texCoordDelta.s), normalizedNormal) * glm::normalize(bitangent), normalizedNormal); } From 4f9da0cc3716dccf58ce25affc15cb1b9886040e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 9 Jun 2014 11:50:31 -0700 Subject: [PATCH 27/38] fix whitespace formatting --- interface/src/renderer/RagDoll.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/renderer/RagDoll.cpp b/interface/src/renderer/RagDoll.cpp index e2654d737f..305724d6e4 100644 --- a/interface/src/renderer/RagDoll.cpp +++ b/interface/src/renderer/RagDoll.cpp @@ -23,7 +23,7 @@ // ---------------------------------------------------------------------------- // FixedConstraint // ---------------------------------------------------------------------------- -FixedConstraint::FixedConstraint(glm::vec3* point, const glm::vec3& anchor ) : _point(point), _anchor(anchor) { +FixedConstraint::FixedConstraint(glm::vec3* point, const glm::vec3& anchor) : _point(point), _anchor(anchor) { } float FixedConstraint::enforce() { @@ -84,7 +84,7 @@ void DistanceConstraint::updateProxyShape(Shape* shape, const glm::quat& rotatio } break; case Shape::CAPSULE_SHAPE: { - // capsule collides from startPoint to endPoint + // capsule collides from startPoint to endPoint CapsuleShape* capsule = static_cast(shape); capsule->setEndPoints(translation + rotation * (*_points[0]), translation + rotation * (*_points[1])); } From 58bd1f7ab57c312933370a28242b1f8c8b68d97f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 11:54:43 -0700 Subject: [PATCH 28/38] More script streaming bits. --- libraries/metavoxels/src/Bitstream.cpp | 110 ++++++++++++++++++++----- 1 file changed, 89 insertions(+), 21 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 8f813adf90..c3d0348ee8 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -449,8 +450,11 @@ Bitstream& Bitstream::operator>>(QDateTime& dateTime) { Bitstream& Bitstream::operator<<(const QRegExp& regExp) { *this << regExp.pattern(); - - return *this; + Qt::CaseSensitivity caseSensitivity = regExp.caseSensitivity(); + write(&caseSensitivity, 1); + QRegExp::PatternSyntax syntax = regExp.patternSyntax(); + write(&syntax, 3); + return *this << regExp.isMinimal(); } Bitstream& Bitstream::operator>>(QRegExp& regExp) { @@ -460,9 +464,10 @@ Bitstream& Bitstream::operator>>(QRegExp& regExp) { read(&caseSensitivity, 1); QRegExp::PatternSyntax syntax = (QRegExp::PatternSyntax)0; read(&syntax, 3); - regExp = QRegExp(pattern, caseSensitivity, syntax); - + bool minimal; + *this >> minimal; + regExp.setMinimal(minimal); return *this; } @@ -658,10 +663,20 @@ Bitstream& Bitstream::operator<<(const QScriptValue& value) { } else if (value.isArray()) { writeScriptValueType(*this, ARRAY_SCRIPT_VALUE); - + int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + *this << length; + for (int i = 0; i < length; i++) { + *this << value.property(i); + } } else if (value.isObject()) { writeScriptValueType(*this, OBJECT_SCRIPT_VALUE); - + for (QScriptValueIterator it(value); it.hasNext(); ) { + it.next(); + *this << it.scriptName(); + *this << it.value(); + } + *this << QScriptString(); + } else { writeScriptValueType(*this, INVALID_SCRIPT_VALUE); } @@ -678,26 +693,79 @@ Bitstream& Bitstream::operator>>(QScriptValue& value) { value = QScriptValue(QScriptValue::NullValue); break; - case BOOL_SCRIPT_VALUE: - value = QScriptValue(); + case BOOL_SCRIPT_VALUE: { + bool boolValue; + *this >> boolValue; + value = QScriptValue(boolValue); break; - - case NUMBER_SCRIPT_VALUE: - value = QScriptValue(); + } + case NUMBER_SCRIPT_VALUE: { + qsreal numberValue; + *this >> numberValue; + value = QScriptValue(numberValue); break; - - case STRING_SCRIPT_VALUE: - value = QScriptValue(); + } + case STRING_SCRIPT_VALUE: { + QString stringValue; + *this >> stringValue; + value = QScriptValue(stringValue); break; - - case ARRAY_SCRIPT_VALUE: - value = QScriptValue(); + } + case VARIANT_SCRIPT_VALUE: { + QVariant variantValue; + *this >> variantValue; + value = ScriptCache::getInstance()->getEngine()->newVariant(variantValue); break; - - case OBJECT_SCRIPT_VALUE: - value = QScriptValue(); + } + case QOBJECT_SCRIPT_VALUE: { + QObject* object; + *this >> object; + ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); break; - + } + case QMETAOBJECT_SCRIPT_VALUE: { + const QMetaObject* metaObject; + *this >> metaObject; + ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject); + break; + } + case DATE_SCRIPT_VALUE: { + QDateTime dateTime; + *this >> dateTime; + value = ScriptCache::getInstance()->getEngine()->newDate(dateTime); + break; + } + case REGEXP_SCRIPT_VALUE: { + QRegExp regExp; + *this >> regExp; + value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp); + break; + } + case ARRAY_SCRIPT_VALUE: { + int length; + *this >> length; + value = ScriptCache::getInstance()->getEngine()->newArray(length); + for (int i = 0; i < length; i++) { + QScriptValue element; + *this >> element; + value.setProperty(i, element); + } + break; + } + case OBJECT_SCRIPT_VALUE: { + value = ScriptCache::getInstance()->getEngine()->newObject(); + forever { + QScriptString name; + *this >> name; + if (!name.isValid()) { + break; + } + QScriptValue scriptValue; + *this >> scriptValue; + value.setProperty(name, scriptValue); + } + break; + } default: value = QScriptValue(); break; From 936f04cececd2e29177d9189f25487e623ff7240 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 9 Jun 2014 11:55:29 -0700 Subject: [PATCH 29/38] Simple raypicking for scripts with oculus rift. --- interface/src/Camera.cpp | 8 +++++++- interface/src/ui/ApplicationOverlay.cpp | 21 +++++++++++++++++++-- interface/src/ui/ApplicationOverlay.h | 3 ++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index f9ee5bdd25..0e33e14f32 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -18,6 +18,7 @@ #include "Camera.h" #include "Menu.h" #include "Util.h" +#include "devices/OculusManager.h" const float CAMERA_FIRST_PERSON_MODE_UP_SHIFT = 0.0f; const float CAMERA_FIRST_PERSON_MODE_DISTANCE = 0.0f; @@ -264,7 +265,12 @@ PickRay CameraScriptableObject::computePickRay(float x, float y) { float screenWidth = Application::getInstance()->getGLWidget()->width(); float screenHeight = Application::getInstance()->getGLWidget()->height(); PickRay result; - _viewFrustum->computePickRay(x / screenWidth, y / screenHeight, result.origin, result.direction); + if (OculusManager::isConnected()) { + result.origin = _camera->getPosition(); + Application::getInstance()->getApplicationOverlay().computeOculusPickRay(x / screenWidth, y / screenHeight, result.direction); + } else { + _viewFrustum->computePickRay(x / screenWidth, y / screenHeight, result.origin, result.direction); + } return result; } diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 49ec8ecddb..f5de7459a8 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -297,6 +297,25 @@ void ApplicationOverlay::displayOverlayTexture(Camera& whichCamera) { glDisable(GL_TEXTURE_2D); } +const float textureFov = PI / 2.5f; + +void ApplicationOverlay::computeOculusPickRay(float x, float y, glm::vec3& direction) const { + glm::quat rot = Application::getInstance()->getAvatar()->getOrientation(); + + //invert y direction + y = 1.0 - y; + + //Get position on hemisphere UI + x = sin((x - 0.5f) * textureFov); + y = sin((y - 0.5f) * textureFov); + + float dist = sqrt(x * x + y * y); + float z = -sqrt(1.0f - dist * dist); + + //Rotate the UI pick ray by the avatar orientation + direction = glm::normalize(rot * glm::vec3(x, y, z)); +} + // Fast helper functions inline float max(float a, float b) { return (a > b) ? a : b; @@ -306,8 +325,6 @@ inline float min(float a, float b) { return (a < b) ? a : b; } -const float textureFov = PI / 2.5f; - // Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane. void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 8817549277..1bf0e18816 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -15,7 +15,7 @@ class Overlays; class QOpenGLFramebufferObject; -// Handles the drawing of the overlays to the scree +// Handles the drawing of the overlays to the screen class ApplicationOverlay { public: @@ -27,6 +27,7 @@ public: void renderOverlay(bool renderToTexture = false); void displayOverlayTexture(Camera& whichCamera); void displayOverlayTextureOculus(Camera& whichCamera); + void computeOculusPickRay(float x, float y, glm::vec3& direction) const; // Getters QOpenGLFramebufferObject* getFramebufferObject(); From ee71d34f021ba90327ac96e1b9094c858679604f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 9 Jun 2014 14:03:47 -0700 Subject: [PATCH 30/38] add a crash guard around negative silent samples --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 94a88897e3..bb47c6de9e 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -55,7 +55,9 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { readBytes += sizeof(int16_t); - addSilentFrame(numSilentSamples); + if (numSilentSamples > 0) { + addSilentFrame(numSilentSamples); + } } else { // there is audio data to read readBytes += writeData(packet.data() + readBytes, packet.size() - readBytes); From fd469c543c7b8cb7ef778ad280adda64bb3f6768 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 14:35:20 -0700 Subject: [PATCH 31/38] More script streaming bits. --- libraries/metavoxels/src/Bitstream.cpp | 276 ++++++++++++++++++++++- libraries/metavoxels/src/Bitstream.h | 4 + libraries/metavoxels/src/ScriptCache.cpp | 79 +++++++ libraries/metavoxels/src/ScriptCache.h | 5 + 4 files changed, 362 insertions(+), 2 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index c3d0348ee8..d27053aefb 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -31,6 +31,7 @@ REGISTER_SIMPLE_TYPE_STREAMER(uint) REGISTER_SIMPLE_TYPE_STREAMER(float) REGISTER_SIMPLE_TYPE_STREAMER(QByteArray) REGISTER_SIMPLE_TYPE_STREAMER(QColor) +REGISTER_SIMPLE_TYPE_STREAMER(QScriptValue) REGISTER_SIMPLE_TYPE_STREAMER(QString) REGISTER_SIMPLE_TYPE_STREAMER(QUrl) REGISTER_SIMPLE_TYPE_STREAMER(QVariantList) @@ -286,6 +287,12 @@ void Bitstream::writeDelta(const QVariant& value, const QVariant& reference) { streamer->writeRawDelta(*this, value, reference); } +void Bitstream::writeRawDelta(const QVariant& value, const QVariant& reference) { + const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); + _typeStreamerStreamer << streamer; + streamer->writeRawDelta(*this, value, reference); +} + void Bitstream::readRawDelta(QVariant& value, const QVariant& reference) { TypeReader typeReader; _typeStreamerStreamer >> typeReader; @@ -310,6 +317,271 @@ void Bitstream::readRawDelta(QObject*& value, const QObject* reference) { value = objectReader.readDelta(*this, reference); } +void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& reference) { + if (reference.isUndefined() || reference.isNull()) { + *this << value; + + } else if (reference.isBool()) { + if (value.isBool()) { + *this << false; + *this << value.toBool(); + + } else { + *this << true; + *this << value; + } + } else if (reference.isNumber()) { + if (value.isNumber()) { + *this << false; + *this << value.toNumber(); + + } else { + *this << true; + *this << value; + } + } else if (reference.isString()) { + if (value.isString()) { + *this << false; + *this << value.toString(); + + } else { + *this << true; + *this << value; + } + } else if (reference.isVariant()) { + if (value.isVariant()) { + *this << false; + writeRawDelta(value.toVariant(), reference.toVariant()); + + } else { + *this << true; + *this << value; + } + } else if (reference.isQObject()) { + if (value.isQObject()) { + *this << false; + writeRawDelta(value.toQObject(), reference.toQObject()); + + } else { + *this << true; + *this << value; + } + } else if (reference.isQMetaObject()) { + if (value.isQMetaObject()) { + *this << false; + *this << value.toQMetaObject(); + + } else { + *this << true; + *this << value; + } + } else if (reference.isDate()) { + if (value.isDate()) { + *this << false; + *this << value.toDateTime(); + + } else { + *this << true; + *this << value; + } + } else if (reference.isRegExp()) { + if (value.isRegExp()) { + *this << false; + *this << value.toRegExp(); + + } else { + *this << true; + *this << value; + } + } else if (reference.isArray()) { + if (value.isArray()) { + *this << false; + int length = value.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + *this << length; + int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + for (int i = 0; i < length; i++) { + if (i < referenceLength) { + writeDelta(value.property(i), reference.property(i)); + } else { + *this << value.property(i); + } + } + } else { + *this << true; + *this << value; + } + } else if (reference.isObject()) { + if (value.isObject()) { + *this << false; + for (QScriptValueIterator it(value); it.hasNext(); ) { + it.next(); + QScriptValue referenceValue = reference.property(it.scriptName()); + if (it.value() != referenceValue) { + *this << it.scriptName(); + writeRawDelta(it.value(), referenceValue); + } + } + for (QScriptValueIterator it(reference); it.hasNext(); ) { + it.next(); + if (!value.property(it.scriptName()).isValid()) { + *this << it.scriptName(); + writeRawDelta(QScriptValue(), it.value()); + } + } + *this << QScriptString(); + + } else { + *this << true; + *this << value; + } + } else { + *this << value; + } +} + +void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference) { + if (reference.isUndefined() || reference.isNull()) { + *this >> value; + + } else if (reference.isBool()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + bool boolValue; + *this >> boolValue; + value = QScriptValue(boolValue); + } + } else if (reference.isNumber()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + qsreal numberValue; + *this >> numberValue; + value = QScriptValue(numberValue); + } + } else if (reference.isString()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + QString stringValue; + *this >> stringValue; + value = QScriptValue(stringValue); + } + } else if (reference.isVariant()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + QVariant variant; + readRawDelta(variant, reference.toVariant()); + value = ScriptCache::getInstance()->getEngine()->newVariant(variant); + } + } else if (reference.isQObject()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + QObject* object; + readRawDelta(object, reference.toQObject()); + value = ScriptCache::getInstance()->getEngine()->newQObject(object, QScriptEngine::ScriptOwnership); + } + } else if (reference.isQMetaObject()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + const QMetaObject* metaObject; + *this >> metaObject; + value = ScriptCache::getInstance()->getEngine()->newQMetaObject(metaObject); + } + } else if (reference.isDate()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + QDateTime dateTime; + *this >> dateTime; + value = ScriptCache::getInstance()->getEngine()->newDate(dateTime); + } + } else if (reference.isRegExp()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + QRegExp regExp; + *this >> regExp; + value = ScriptCache::getInstance()->getEngine()->newRegExp(regExp); + } + } else if (reference.isArray()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + int length; + *this >> length; + value = ScriptCache::getInstance()->getEngine()->newArray(length); + int referenceLength = reference.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + for (int i = 0; i < length; i++) { + QScriptValue element; + if (i < referenceLength) { + readDelta(element, reference.property(i)); + } else { + *this >> element; + } + value.setProperty(i, element); + } + } + } else if (reference.isObject()) { + bool typeChanged; + *this >> typeChanged; + if (typeChanged) { + *this >> value; + + } else { + // start by shallow-copying the reference + value = ScriptCache::getInstance()->getEngine()->newObject(); + for (QScriptValueIterator it(reference); it.hasNext(); ) { + it.next(); + value.setProperty(it.scriptName(), it.value()); + } + // then apply the requested changes + forever { + QScriptString name; + *this >> name; + if (!name.isValid()) { + break; + } + QScriptValue scriptValue; + readRawDelta(scriptValue, reference.property(name)); + value.setProperty(name, scriptValue); + } + } + } else { + *this >> value; + } +} + Bitstream& Bitstream::operator<<(bool value) { if (value) { _byte |= (1 << _position); @@ -612,11 +884,11 @@ enum ScriptValueType { const int SCRIPT_VALUE_BITS = 4; -void writeScriptValueType(Bitstream& out, ScriptValueType type) { +static void writeScriptValueType(Bitstream& out, ScriptValueType type) { out.write(&type, SCRIPT_VALUE_BITS); } -ScriptValueType readScriptValueType(Bitstream& in) { +static ScriptValueType readScriptValueType(Bitstream& in) { ScriptValueType type = (ScriptValueType)0; in.read(&type, SCRIPT_VALUE_BITS); return type; diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 83733674a4..d05f6574c0 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -292,11 +292,15 @@ public: template void writeDelta(const T& value, const T& reference); template void readDelta(T& value, const T& reference); + void writeRawDelta(const QVariant& value, const QVariant& reference); void readRawDelta(QVariant& value, const QVariant& reference); void writeRawDelta(const QObject* value, const QObject* reference); void readRawDelta(QObject*& value, const QObject* reference); + void writeRawDelta(const QScriptValue& value, const QScriptValue& reference); + void readRawDelta(QScriptValue& value, const QScriptValue& reference); + template void writeRawDelta(const T& value, const T& reference); template void readRawDelta(T& value, const T& reference); diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index dd090613b7..9de624fb29 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -13,11 +13,90 @@ #include #include +#include #include #include "AttributeRegistry.h" #include "ScriptCache.h" +static int scriptValueMetaTypeId = qRegisterMetaType(); + +bool operator==(const QScriptValue& first, const QScriptValue& second) { + if (first.isUndefined()) { + return second.isUndefined(); + + } else if (first.isNull()) { + return second.isNull(); + + } else if (first.isBool()) { + return second.isBool() && first.toBool() == second.toBool(); + + } else if (first.isNumber()) { + return second.isNumber() && first.toNumber() == second.toNumber(); + + } else if (first.isString()) { + return second.isString() && first.toString() == second.toString(); + + } else if (first.isVariant()) { + return second.isVariant() && first.toVariant() == second.toVariant(); + + } else if (first.isQObject()) { + return second.isQObject() && first.toQObject() == second.toQObject(); + + } else if (first.isQMetaObject()) { + return second.isQMetaObject() && first.toQMetaObject() == second.toQMetaObject(); + + } else if (first.isDate()) { + return second.isDate() && first.toDateTime() == second.toDateTime(); + + } else if (first.isRegExp()) { + return second.isRegExp() && first.toRegExp() == second.toRegExp(); + + } else if (first.isArray()) { + if (!second.isArray()) { + return false; + } + int length = first.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + if (second.property(ScriptCache::getInstance()->getLengthString()).toInt32() != length) { + return false; + } + for (int i = 0; i < length; i++) { + if (first.property(i) != second.property(i)) { + return false; + } + } + return true; + + } else if (first.isObject()) { + if (!second.isObject()) { + return false; + } + int propertyCount = 0; + for (QScriptValueIterator it(first); it.hasNext(); ) { + it.next(); + if (second.property(it.scriptName()) != it.value()) { + return false; + } + propertyCount++; + } + // make sure the second has exactly as many properties as the first + for (QScriptValueIterator it(second); it.hasNext(); ) { + it.next(); + if (--propertyCount < 0) { + return false; + } + } + return true; + + } else { + return !second.isValid(); + } +} + +bool operator!=(const QScriptValue& first, const QScriptValue& second) { + return !(first == second); +} + ScriptCache* ScriptCache::getInstance() { static ScriptCache cache; return &cache; diff --git a/libraries/metavoxels/src/ScriptCache.h b/libraries/metavoxels/src/ScriptCache.h index f393d0e0a8..d88a967081 100644 --- a/libraries/metavoxels/src/ScriptCache.h +++ b/libraries/metavoxels/src/ScriptCache.h @@ -65,6 +65,11 @@ private: QScriptString _generatorString; }; +Q_DECLARE_METATYPE(QScriptValue) + +bool operator==(const QScriptValue& first, const QScriptValue& second); +bool operator!=(const QScriptValue& first, const QScriptValue& second); + /// A program loaded from the network. class NetworkProgram : public Resource { Q_OBJECT From e8b6338de9026938a34ac35191a4abb1d906ecc6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 9 Jun 2014 15:40:29 -0700 Subject: [PATCH 32/38] Fix IK for hydra hands. --- interface/src/avatar/SkeletonModel.cpp | 45 +++++++++----------------- interface/src/renderer/Model.cpp | 4 +-- 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index ca40ba1d64..d5a13b0381 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -144,9 +144,9 @@ void SkeletonModel::getBodyShapes(QVector& shapes) const { void SkeletonModel::renderIKConstraints() { renderJointConstraints(getRightHandJointIndex()); renderJointConstraints(getLeftHandJointIndex()); - if (isActive() && _owningAvatar->isMyAvatar()) { - renderRagDoll(); - } + //if (isActive() && _owningAvatar->isMyAvatar()) { + // renderRagDoll(); + //} } class IndexValue { @@ -193,45 +193,30 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { if (parentJointIndex == -1) { return; } - + // rotate palm to align with its normal (normal points out of hand's palm) - glm::quat palmRotation; - glm::quat r0, r1; - if (!Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK) && - Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) { - JointState parentState = _jointStates[parentJointIndex]; - palmRotation = parentState.getRotationFromBindToModelFrame(); - r0 = palmRotation; - } else { - JointState state = _jointStates[jointIndex]; - palmRotation = state.getRotationFromBindToModelFrame(); - } glm::quat inverseRotation = glm::inverse(_rotation); - glm::vec3 palmNormal = inverseRotation * palm.getNormal(); - palmRotation = rotationBetween(palmRotation * geometry.palmDirection, palmNormal) * palmRotation; - r1 = palmRotation; - - // rotate palm to align with finger direction - glm::vec3 direction = inverseRotation * palm.getFingerDirection(); - palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction) * palmRotation; - - // set hand position, rotation glm::vec3 palmPosition = inverseRotation * (palm.getPosition() - _translation); + glm::vec3 palmNormal = inverseRotation * palm.getNormal(); + glm::vec3 fingerDirection = inverseRotation * palm.getFingerDirection(); + + glm::quat palmRotation = rotationBetween(glm::vec3(0.0f, -1.0f, 0.0f), palmNormal); + palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), fingerDirection) * palmRotation; + if (Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK)) { setHandPosition(jointIndex, palmPosition, palmRotation); - } else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) { - glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f); - setJointPosition(parentJointIndex, palmPosition + forearmVector * - geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale), + float forearmLength = geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale); + glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f); + setJointPosition(parentJointIndex, palmPosition + forearm, glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); JointState& parentState = _jointStates[parentJointIndex]; parentState.setRotationFromBindFrame(palmRotation, PALM_PRIORITY); // lock hand to forearm by slamming its rotation (in parent-frame) to identity _jointStates[jointIndex]._rotationInParentFrame = glm::quat(); } else { - setJointPosition(jointIndex, palmPosition, palmRotation, - true, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); + setJointPosition(jointIndex, palmPosition, palmRotation, + true, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); } } diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 105301054b..aff023c2a0 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1275,8 +1275,8 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl if (useRotation) { JointState& state = _jointStates[jointIndex]; - state.setRotation(rotation, true, priority); - endRotation = state.getRotation(); + state.setRotationFromBindFrame(rotation, priority); + endRotation = state.getRotationFromBindToModelFrame(); } // then, we go from the joint upwards, rotating the end as close as possible to the target From 589b2e8fb758d8af3872733a9e9a3fdd81a6976e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 9 Jun 2014 16:12:54 -0700 Subject: [PATCH 33/38] recover some code for one less magic vec3 --- interface/src/avatar/SkeletonModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index d5a13b0381..1b1b2e032b 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -200,7 +200,7 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { glm::vec3 palmNormal = inverseRotation * palm.getNormal(); glm::vec3 fingerDirection = inverseRotation * palm.getFingerDirection(); - glm::quat palmRotation = rotationBetween(glm::vec3(0.0f, -1.0f, 0.0f), palmNormal); + glm::quat palmRotation = rotationBetween(geometry.palmDirection, palmNormal); palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), fingerDirection) * palmRotation; if (Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK)) { From 590f045ed1ce8806f964d252042a173c6f15ff12 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 16:51:41 -0700 Subject: [PATCH 34/38] Script value streaming tests, fixes. --- libraries/metavoxels/src/Bitstream.cpp | 9 ++- libraries/metavoxels/src/ScriptCache.cpp | 5 ++ libraries/metavoxels/src/ScriptCache.h | 1 + tests/metavoxels/src/MetavoxelTests.cpp | 88 +++++++++++++++++++++++- tests/metavoxels/src/MetavoxelTests.h | 9 ++- 5 files changed, 106 insertions(+), 6 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index d27053aefb..7595580c33 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -411,7 +411,7 @@ void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& ref *this << value; } } else if (reference.isObject()) { - if (value.isObject()) { + if (value.isObject()) { *this << false; for (QScriptValueIterator it(value); it.hasNext(); ) { it.next(); @@ -1401,14 +1401,17 @@ Bitstream& Bitstream::operator>(AttributePointer& attribute) { return *this; } +const QString INVALID_STRING("%INVALID%"); + Bitstream& Bitstream::operator<(const QScriptString& string) { - return *this << string.toString(); + return *this << (string.isValid() ? string.toString() : INVALID_STRING); } Bitstream& Bitstream::operator>(QScriptString& string) { QString rawString; *this >> rawString; - string = ScriptCache::getInstance()->getEngine()->toStringHandle(rawString); + string = (rawString == INVALID_STRING) ? QScriptString() : + ScriptCache::getInstance()->getEngine()->toStringHandle(rawString); return *this; } diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index 9de624fb29..9648a047cb 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -20,6 +20,7 @@ #include "ScriptCache.h" static int scriptValueMetaTypeId = qRegisterMetaType(); +static bool scriptValueComparators = QMetaType::registerComparators(); bool operator==(const QScriptValue& first, const QScriptValue& second) { if (first.isUndefined()) { @@ -97,6 +98,10 @@ bool operator!=(const QScriptValue& first, const QScriptValue& second) { return !(first == second); } +bool operator<(const QScriptValue& first, const QScriptValue& second) { + return first.lessThan(second); +} + ScriptCache* ScriptCache::getInstance() { static ScriptCache cache; return &cache; diff --git a/libraries/metavoxels/src/ScriptCache.h b/libraries/metavoxels/src/ScriptCache.h index d88a967081..5d29157b3d 100644 --- a/libraries/metavoxels/src/ScriptCache.h +++ b/libraries/metavoxels/src/ScriptCache.h @@ -69,6 +69,7 @@ Q_DECLARE_METATYPE(QScriptValue) bool operator==(const QScriptValue& first, const QScriptValue& second); bool operator!=(const QScriptValue& first, const QScriptValue& second); +bool operator<(const QScriptValue& first, const QScriptValue& second); /// A program loaded from the network. class NetworkProgram : public Resource { diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 81f1840342..4dc1cdddce 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -11,6 +11,8 @@ #include +#include + #include #include @@ -41,6 +43,8 @@ static int streamedBytesReceived = 0; static int sharedObjectsCreated = 0; static int sharedObjectsDestroyed = 0; static int objectMutationsPerformed = 0; +static int scriptObjectsCreated = 0; +static int scriptMutationsPerformed = 0; static QByteArray createRandomBytes(int minimumSize, int maximumSize) { QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0); @@ -79,6 +83,49 @@ static TestSharedObjectA::TestFlags getRandomTestFlags() { return flags; } +static QScriptValue createRandomScriptValue() { + scriptObjectsCreated++; + switch (randIntInRange(0, 3)) { + case 0: + return QScriptValue(QScriptValue::NullValue); + + case 1: + return QScriptValue(randomBoolean()); + + case 2: + return QScriptValue(randFloat()); + + case 3: + default: + return QScriptValue(QString(createRandomBytes())); + + case 4: { + int length = randIntInRange(2, 6); + QScriptValue value = ScriptCache::getInstance()->getEngine()->newArray(length); + for (int i = 0; i < length; i++) { + value.setProperty(i, createRandomScriptValue()); + } + return value; + } + case 5: { + QScriptValue value = ScriptCache::getInstance()->getEngine()->newObject(); + if (randomBoolean()) { + value.setProperty("foo", createRandomScriptValue()); + } + if (randomBoolean()) { + value.setProperty("bar", createRandomScriptValue()); + } + if (randomBoolean()) { + value.setProperty("baz", createRandomScriptValue()); + } + if (randomBoolean()) { + value.setProperty("bong", createRandomScriptValue()); + } + return value; + } + } +} + static TestMessageC createRandomMessageC() { TestMessageC message; message.foo = randomBoolean(); @@ -86,6 +133,7 @@ static TestMessageC createRandomMessageC() { message.baz = randFloat(); message.bong.foo = createRandomBytes(); message.bong.baz = getRandomTestEnum(); + message.bizzle = createRandomScriptValue(); return message; } @@ -201,6 +249,7 @@ bool MetavoxelTests::run() { datagramsReceived << "with" << bytesReceived << "bytes"; qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed; qDebug() << "Performed" << objectMutationsPerformed << "object mutations"; + qDebug() << "Created" << scriptObjectsCreated << "script objects, mutated" << scriptMutationsPerformed; qDebug(); qDebug() << "Running serialization tests..."; @@ -284,7 +333,7 @@ static QVariant createRandomMessage() { } static SharedObjectPointer mutate(const SharedObjectPointer& state) { - switch(randIntInRange(0, 3)) { + switch (randIntInRange(0, 4)) { case 0: { SharedObjectPointer newState = state->clone(true); static_cast(newState.data())->setFoo(randFloat()); @@ -303,6 +352,38 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) { objectMutationsPerformed++; return newState; } + case 3: { + SharedObjectPointer newState = state->clone(true); + QScriptValue oldValue = static_cast(newState.data())->getBizzle(); + QScriptValue newValue = ScriptCache::getInstance()->getEngine()->newObject(); + for (QScriptValueIterator it(oldValue); it.hasNext(); ) { + it.next(); + newValue.setProperty(it.scriptName(), it.value()); + } + switch (randIntInRange(0, 3)) { + case 0: { + QScriptValue oldArray = oldValue.property("foo"); + int oldLength = oldArray.property(ScriptCache::getInstance()->getLengthString()).toInt32(); + QScriptValue newArray = ScriptCache::getInstance()->getEngine()->newArray(oldLength); + for (int i = 0; i < oldLength; i++) { + newArray.setProperty(i, oldArray.property(i)); + } + newArray.setProperty(randIntInRange(0, oldLength - 1), createRandomScriptValue()); + break; + } + case 1: + newValue.setProperty("bar", QScriptValue(randFloat())); + break; + + default: + newValue.setProperty("baz", createRandomScriptValue()); + break; + } + static_cast(newState.data())->setBizzle(newValue); + scriptMutationsPerformed++; + objectMutationsPerformed++; + return newState; + } default: return state; } @@ -503,7 +584,10 @@ TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) : _foo(foo), _baz(baz), _bong(bong) { - sharedObjectsCreated++; + sharedObjectsCreated++; + + _bizzle = ScriptCache::getInstance()->getEngine()->newObject(); + _bizzle.setProperty("foo", ScriptCache::getInstance()->getEngine()->newArray(4)); } TestSharedObjectA::~TestSharedObjectA() { diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index 345ea624df..ac9eda2659 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -16,6 +16,7 @@ #include #include +#include class SequencedTestMessage; @@ -96,7 +97,8 @@ class TestSharedObjectA : public SharedObject { Q_PROPERTY(float foo READ getFoo WRITE setFoo NOTIFY fooChanged) Q_PROPERTY(TestEnum baz READ getBaz WRITE setBaz) Q_PROPERTY(TestFlags bong READ getBong WRITE setBong) - + Q_PROPERTY(QScriptValue bizzle READ getBizzle WRITE setBizzle) + public: enum TestEnum { FIRST_TEST_ENUM, SECOND_TEST_ENUM, THIRD_TEST_ENUM }; @@ -116,6 +118,9 @@ public: void setBong(TestFlags bong) { _bong = bong; } TestFlags getBong() const { return _bong; } + void setBizzle(const QScriptValue& bizzle) { _bizzle = bizzle; } + const QScriptValue& getBizzle() const { return _bizzle; } + signals: void fooChanged(float foo); @@ -125,6 +130,7 @@ private: float _foo; TestEnum _baz; TestFlags _bong; + QScriptValue _bizzle; }; DECLARE_ENUM_METATYPE(TestSharedObjectA, TestEnum) @@ -204,6 +210,7 @@ class TestMessageC : STREAM public TestMessageA { public: STREAM TestMessageB bong; + STREAM QScriptValue bizzle; }; DECLARE_STREAMABLE_METATYPE(TestMessageC) From 3293ab788b7340c98fbc923d509b216445362e09 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 17:19:36 -0700 Subject: [PATCH 35/38] Fix for script object deltas. --- libraries/metavoxels/src/Bitstream.cpp | 3 ++- tests/metavoxels/src/MetavoxelTests.cpp | 15 +++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 7595580c33..d7e15a9dd6 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -411,7 +411,8 @@ void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& ref *this << value; } } else if (reference.isObject()) { - if (value.isObject()) { + if (value.isObject() && !(value.isArray() || value.isRegExp() || value.isDate() || + value.isQMetaObject() || value.isQObject() || value.isVariant())) { *this << false; for (QScriptValueIterator it(value); it.hasNext(); ) { it.next(); diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 4dc1cdddce..c7e98ab0cf 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -83,9 +83,9 @@ static TestSharedObjectA::TestFlags getRandomTestFlags() { return flags; } -static QScriptValue createRandomScriptValue() { +static QScriptValue createRandomScriptValue(bool complex = false) { scriptObjectsCreated++; - switch (randIntInRange(0, 3)) { + switch (randIntInRange(0, complex ? 5 : 2)) { case 0: return QScriptValue(QScriptValue::NullValue); @@ -96,7 +96,6 @@ static QScriptValue createRandomScriptValue() { return QScriptValue(randFloat()); case 3: - default: return QScriptValue(QString(createRandomBytes())); case 4: { @@ -107,7 +106,7 @@ static QScriptValue createRandomScriptValue() { } return value; } - case 5: { + default: { QScriptValue value = ScriptCache::getInstance()->getEngine()->newObject(); if (randomBoolean()) { value.setProperty("foo", createRandomScriptValue()); @@ -133,7 +132,7 @@ static TestMessageC createRandomMessageC() { message.baz = randFloat(); message.bong.foo = createRandomBytes(); message.bong.baz = getRandomTestEnum(); - message.bizzle = createRandomScriptValue(); + message.bizzle = createRandomScriptValue(true); return message; } @@ -360,7 +359,7 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) { it.next(); newValue.setProperty(it.scriptName(), it.value()); } - switch (randIntInRange(0, 3)) { + switch (randIntInRange(0, 2)) { case 0: { QScriptValue oldArray = oldValue.property("foo"); int oldLength = oldArray.property(ScriptCache::getInstance()->getLengthString()).toInt32(); @@ -368,7 +367,7 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) { for (int i = 0; i < oldLength; i++) { newArray.setProperty(i, oldArray.property(i)); } - newArray.setProperty(randIntInRange(0, oldLength - 1), createRandomScriptValue()); + newArray.setProperty(randIntInRange(0, oldLength - 1), createRandomScriptValue(true)); break; } case 1: @@ -376,7 +375,7 @@ static SharedObjectPointer mutate(const SharedObjectPointer& state) { break; default: - newValue.setProperty("baz", createRandomScriptValue()); + newValue.setProperty("baz", createRandomScriptValue(true)); break; } static_cast(newState.data())->setBizzle(newValue); From d1549b00abffccc56dfad0951302054a15e71521 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 9 Jun 2014 17:22:44 -0700 Subject: [PATCH 36/38] Might as well include strings in the "simple" random script objects. --- tests/metavoxels/src/MetavoxelTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index c7e98ab0cf..6ec2331b14 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -85,7 +85,7 @@ static TestSharedObjectA::TestFlags getRandomTestFlags() { static QScriptValue createRandomScriptValue(bool complex = false) { scriptObjectsCreated++; - switch (randIntInRange(0, complex ? 5 : 2)) { + switch (randIntInRange(0, complex ? 5 : 3)) { case 0: return QScriptValue(QScriptValue::NullValue); From c9ba71aa015ef86d2a9fc89effbd77a18cbebb4a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 10 Jun 2014 09:38:38 -0700 Subject: [PATCH 37/38] fix for broken neck on some body models --- interface/src/avatar/FaceModel.cpp | 2 +- interface/src/avatar/SkeletonModel.cpp | 10 ++++++++-- interface/src/avatar/SkeletonModel.h | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 601dad5563..c3f31ff6e7 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -30,7 +30,7 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) { } setTranslation(neckPosition); glm::quat neckParentRotation; - if (!owningAvatar->getSkeletonModel().getNeckParentRotation(neckParentRotation)) { + if (!owningAvatar->getSkeletonModel().getNeckParentRotationFromDefaultOrientation(neckParentRotation)) { neckParentRotation = owningAvatar->getOrientation(); } setRotation(neckParentRotation); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 1b1b2e032b..576545f115 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -434,7 +434,7 @@ bool SkeletonModel::getNeckPosition(glm::vec3& neckPosition) const { return isActive() && getJointPositionInWorldFrame(_geometry->getFBXGeometry().neckJointIndex, neckPosition); } -bool SkeletonModel::getNeckParentRotation(glm::quat& neckParentRotation) const { +bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const { if (!isActive()) { return false; } @@ -442,7 +442,13 @@ bool SkeletonModel::getNeckParentRotation(glm::quat& neckParentRotation) const { if (geometry.neckJointIndex == -1) { return false; } - return getJointRotationInWorldFrame(geometry.joints.at(geometry.neckJointIndex).parentIndex, neckParentRotation); + int parentIndex = geometry.joints.at(geometry.neckJointIndex).parentIndex; + glm::quat worldFrameRotation; + if (getJointRotationInWorldFrame(parentIndex, worldFrameRotation)) { + neckParentRotation = worldFrameRotation * _jointStates[parentIndex].getFBXJoint().inverseDefaultRotation; + return true; + } + return false; } bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 086973807d..3b8e67df47 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -86,9 +86,9 @@ public: /// \return whether or not the neck was found bool getNeckPosition(glm::vec3& neckPosition) const; - /// Returns the rotation of the neck joint's parent. + /// Returns the rotation of the neck joint's parent from default orientation /// \return whether or not the neck was found - bool getNeckParentRotation(glm::quat& neckRotation) const; + bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const; /// Retrieve the positions of up to two eye meshes. /// \return whether or not both eye meshes were found From 0ae7411bf0ccb59bb4340989968c77700fbe0e9b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 10 Jun 2014 13:37:53 -0700 Subject: [PATCH 38/38] fix some bugs in models --- interface/src/models/ModelTreeRenderer.cpp | 193 +++++++++++++-------- libraries/models/src/ModelTreeElement.cpp | 5 + 2 files changed, 124 insertions(+), 74 deletions(-) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index 68bebd7b99..b20bdce8f5 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -57,21 +57,29 @@ void ModelTreeRenderer::render(RenderMode renderMode) { const FBXGeometry* ModelTreeRenderer::getGeometryForModel(const ModelItem& modelItem) { const FBXGeometry* result = NULL; + Model* model = getModel(modelItem); if (model) { result = &model->getGeometry()->getFBXGeometry(); - } return result; } Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) { Model* model = NULL; - + if (modelItem.isKnownID()) { if (_knownModelsItemModels.find(modelItem.getID()) != _knownModelsItemModels.end()) { model = _knownModelsItemModels[modelItem.getID()]; } else { + + // Make sure we only create new models on the thread that owns the ModelTreeRenderer + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(Model*, model), Q_ARG(const ModelItem&, modelItem)); + return model; + } + model = new Model(); model->init(); model->setURL(QUrl(modelItem.getModelURL())); @@ -81,6 +89,13 @@ Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) { if (_unknownModelsItemModels.find(modelItem.getCreatorTokenID()) != _unknownModelsItemModels.end()) { model = _unknownModelsItemModels[modelItem.getCreatorTokenID()]; } else { + // Make sure we only create new models on the thread that owns the ModelTreeRenderer + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(Model*, model), Q_ARG(const ModelItem&, modelItem)); + return model; + } + model = new Model(); model->init(); model->setURL(QUrl(modelItem.getModelURL())); @@ -187,92 +202,122 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) if (drawAsModel) { glPushMatrix(); + { const float alpha = 1.0f; Model* model = getModel(modelItem); - - model->setScaleToFit(true, radius * 2.0f); - model->setSnapModelToCenter(true); - - // set the rotation - glm::quat rotation = modelItem.getModelRotation(); - model->setRotation(rotation); - - // set the position - model->setTranslation(position); - // handle animations.. - if (modelItem.hasAnimation()) { - if (!modelItem.jointsMapped()) { - QStringList modelJointNames = model->getJointNames(); - modelItem.mapJoints(modelJointNames); + if (model) { + model->setScaleToFit(true, radius * 2.0f); + model->setSnapModelToCenter(true); + + // set the rotation + glm::quat rotation = modelItem.getModelRotation(); + model->setRotation(rotation); + + // set the position + model->setTranslation(position); + + // handle animations.. + if (modelItem.hasAnimation()) { + if (!modelItem.jointsMapped()) { + QStringList modelJointNames = model->getJointNames(); + modelItem.mapJoints(modelJointNames); + } + + QVector frameData = modelItem.getAnimationFrame(); + for (int i = 0; i < frameData.size(); i++) { + model->setJointState(i, true, frameData[i]); + } + } + + // make sure to simulate so everything gets set up correctly for rendering + model->simulate(0.0f); + + // TODO: should we allow modelItems to have alpha on their models? + Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE + ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; + + if (modelItem.getGlowLevel() > 0.0f) { + Glower glower(modelItem.getGlowLevel()); + + if (model->isActive()) { + model->render(alpha, modelRenderMode); + } else { + // if we couldn't get a model, then just draw a sphere + glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]); + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glutSolidSphere(radius, 15, 15); + glPopMatrix(); + } + } else { + if (model->isActive()) { + model->render(alpha, modelRenderMode); + } else { + // if we couldn't get a model, then just draw a sphere + glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]); + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glutSolidSphere(radius, 15, 15); + glPopMatrix(); + } } - QVector frameData = modelItem.getAnimationFrame(); - for (int i = 0; i < frameData.size(); i++) { - model->setJointState(i, true, frameData[i]); - } - } - - // make sure to simulate so everything gets set up correctly for rendering - model->simulate(0.0f); + if (!isShadowMode && displayModelBounds) { - // TODO: should we allow modelItems to have alpha on their models? - Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE - ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; - - if (modelItem.getGlowLevel() > 0.0f) { - Glower glower(modelItem.getGlowLevel()); - model->render(alpha, modelRenderMode); + glm::vec3 unRotatedMinimum = model->getUnscaledMeshExtents().minimum; + glm::vec3 unRotatedMaximum = model->getUnscaledMeshExtents().maximum; + glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum; + + float width = unRotatedExtents.x; + float height = unRotatedExtents.y; + float depth = unRotatedExtents.z; + + Extents rotatedExtents = model->getUnscaledMeshExtents(); + calculateRotatedExtents(rotatedExtents, rotation); + + glm::vec3 rotatedSize = rotatedExtents.maximum - rotatedExtents.minimum; + + const glm::vec3& modelScale = model->getScale(); + + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + + // draw the orignal bounding cube + glColor4f(1.0f, 1.0f, 0.0f, 1.0f); + glutWireCube(size); + + // draw the rotated bounding cube + glColor4f(0.0f, 0.0f, 1.0f, 1.0f); + glPushMatrix(); + glScalef(rotatedSize.x * modelScale.x, rotatedSize.y * modelScale.y, rotatedSize.z * modelScale.z); + glutWireCube(1.0); + glPopMatrix(); + + // draw the model relative bounding box + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glScalef(width * modelScale.x, height * modelScale.y, depth * modelScale.z); + glColor3f(0.0f, 1.0f, 0.0f); + glutWireCube(1.0); + + glPopMatrix(); + + } } else { - model->render(alpha, modelRenderMode); - } - - if (!isShadowMode && displayModelBounds) { - - glm::vec3 unRotatedMinimum = model->getUnscaledMeshExtents().minimum; - glm::vec3 unRotatedMaximum = model->getUnscaledMeshExtents().maximum; - glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum; - - float width = unRotatedExtents.x; - float height = unRotatedExtents.y; - float depth = unRotatedExtents.z; - - Extents rotatedExtents = model->getUnscaledMeshExtents(); - calculateRotatedExtents(rotatedExtents, rotation); - - glm::vec3 rotatedSize = rotatedExtents.maximum - rotatedExtents.minimum; - - const glm::vec3& modelScale = model->getScale(); - + // if we couldn't get a model, then just draw a sphere + glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]); glPushMatrix(); glTranslatef(position.x, position.y, position.z); - - // draw the orignal bounding cube - glColor4f(1.0f, 1.0f, 0.0f, 1.0f); - glutWireCube(size); - - // draw the rotated bounding cube - glColor4f(0.0f, 0.0f, 1.0f, 1.0f); - glPushMatrix(); - glScalef(rotatedSize.x * modelScale.x, rotatedSize.y * modelScale.y, rotatedSize.z * modelScale.z); - glutWireCube(1.0); - glPopMatrix(); - - // draw the model relative bounding box - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glScalef(width * modelScale.x, height * modelScale.y, depth * modelScale.z); - glColor3f(0.0f, 1.0f, 0.0f); - glutWireCube(1.0); - + glutSolidSphere(radius, 15, 15); glPopMatrix(); - } - + } glPopMatrix(); } else { - glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]); + //glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]); + glColor3f(1.0f, 0.0f, 0.0f); glPushMatrix(); glTranslatef(position.x, position.y, position.z); glutSolidSphere(radius, 15, 15); diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index b0c7e125b4..75b9670d0f 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -186,6 +186,11 @@ bool ModelTreeElement::findDetailedRayIntersection(const glm::vec3& origin, cons if (fbxGeometry && fbxGeometry->meshExtents.isValid()) { Extents extents = fbxGeometry->meshExtents; + // NOTE: If the model has a bad mesh, then extents will be 0,0,0 & 0,0,0 + if (extents.minimum == extents.maximum && extents.minimum == glm::vec3(0,0,0)) { + extents.maximum = glm::vec3(1.0f,1.0f,1.0f); // in this case we will simulate the unit cube + } + // NOTE: these extents are model space, so we need to scale and center them accordingly // size is our "target size in world space" // we need to set our model scale so that the extents of the mesh, fit in a cube that size...