diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index 114e0e1726..065834cbc2 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -26,28 +26,28 @@ public: ~EntityServer(); // Subclasses must implement these methods - virtual OctreeQueryNode* createOctreeQueryNode(); - virtual char getMyNodeType() const { return NodeType::EntityServer; } - virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; } - virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; } - virtual const char* getMyLoggingServerTargetName() const { return MODEL_SERVER_LOGGING_TARGET_NAME; } - virtual const char* getMyDefaultPersistFilename() const { return LOCAL_MODELS_PERSIST_FILE; } - virtual PacketType getMyEditNackType() const { return PacketType::EntityEditNack; } - virtual QString getMyDomainSettingsKey() const { return QString("entity_server_settings"); } + virtual OctreeQueryNode* createOctreeQueryNode() override ; + virtual char getMyNodeType() const override { return NodeType::EntityServer; } + virtual PacketType getMyQueryMessageType() const override { return PacketType::EntityQuery; } + virtual const char* getMyServerName() const override { return MODEL_SERVER_NAME; } + virtual const char* getMyLoggingServerTargetName() const override { return MODEL_SERVER_LOGGING_TARGET_NAME; } + virtual const char* getMyDefaultPersistFilename() const override { return LOCAL_MODELS_PERSIST_FILE; } + virtual PacketType getMyEditNackType() const override { return PacketType::EntityEditNack; } + virtual QString getMyDomainSettingsKey() const override { return QString("entity_server_settings"); } // subclass may implement these method - virtual void beforeRun(); - virtual bool hasSpecialPacketsToSend(const SharedNodePointer& node); - virtual int sendSpecialPackets(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent); + virtual void beforeRun() override; + virtual bool hasSpecialPacketsToSend(const SharedNodePointer& node) override; + virtual int sendSpecialPackets(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) override; - virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode); + virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) override; virtual bool readAdditionalConfiguration(const QJsonObject& settingsSectionObject) override; public slots: void pruneDeletedEntities(); protected: - virtual OctreePointer createTree(); + virtual OctreePointer createTree() override; private slots: void handleEntityPacket(QSharedPointer packet, SharedNodePointer senderNode); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 614b0a1528..48bb9bed62 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -133,33 +133,14 @@ bool DomainServer::optionallyReadX509KeyAndCertificate() { QString keyPath = _settingsManager.getSettingsMap().value(X509_PRIVATE_KEY_OPTION).toString(); if (!certPath.isEmpty() && !keyPath.isEmpty()) { - // the user wants to use DTLS to encrypt communication with nodes + // the user wants to use the following cert and key for HTTPS + // this is used for Oauth callbacks when authorizing users against a data server // let's make sure we can load the key and certificate -// _x509Credentials = new gnutls_certificate_credentials_t; -// gnutls_certificate_allocate_credentials(_x509Credentials); QString keyPassphraseString = QProcessEnvironment::systemEnvironment().value(X509_KEY_PASSPHRASE_ENV); - qDebug() << "Reading certificate file at" << certPath << "for DTLS."; - qDebug() << "Reading key file at" << keyPath << "for DTLS."; - -// int gnutlsReturn = gnutls_certificate_set_x509_key_file2(*_x509Credentials, -// certPath.toLocal8Bit().constData(), -// keyPath.toLocal8Bit().constData(), -// GNUTLS_X509_FMT_PEM, -// keyPassphraseString.toLocal8Bit().constData(), -// 0); -// -// if (gnutlsReturn < 0) { -// qDebug() << "Unable to load certificate or key file." << "Error" << gnutlsReturn << "- domain-server will now quit."; -// QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); -// return false; -// } - -// qDebug() << "Successfully read certificate and private key."; - - // we need to also pass this certificate and private key to the HTTPS manager - // this is used for Oauth callbacks when authorizing users against a data server + qDebug() << "Reading certificate file at" << certPath << "for HTTPS."; + qDebug() << "Reading key file at" << keyPath << "for HTTPS."; QFile certFile(certPath); certFile.open(QIODevice::ReadOnly); diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 5705bd4498..cdcb15d9dc 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -79,23 +79,26 @@ var STATE_NEAR_GRABBING_NON_COLLIDING = 6; var STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING = 7; var STATE_RELEASE = 8; -var GRAB_USER_DATA_KEY = "grabKey"; -var GRABBABLE_DATA_KEY = "grabbableKey"; +var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js +var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js function getTag() { return "grab-" + MyAvatar.sessionUUID; } function entityIsGrabbedByOther(entityID) { + // by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*. var actionIDs = Entities.getActionIDs(entityID); for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { var actionID = actionIDs[actionIndex]; var actionArguments = Entities.getActionArguments(entityID, actionID); var tag = actionArguments["tag"]; if (tag == getTag()) { + // we see a grab-*uuid* shaped tag, but it's our tag, so that's okay. continue; } if (tag.slice(0, 5) == "grab-") { + // we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it. return true; } } @@ -164,6 +167,12 @@ function MyController(hand, triggerAction) { } }; + this.setState = function(newState) { + // print("STATE: " + this.state + " --> " + newState); + this.state = newState; + } + + this.lineOn = function(closePoint, farPoint, color) { // draw a line if (this.pointer === null) { @@ -217,14 +226,14 @@ function MyController(hand, triggerAction) { this.off = function() { if (this.triggerSmoothedSqueezed()) { - this.state = STATE_SEARCHING; + this.setState(STATE_SEARCHING); return; } } this.search = function() { if (this.triggerSmoothedReleased()) { - this.state = STATE_RELEASE; + this.setState(STATE_RELEASE); return; } @@ -235,6 +244,8 @@ function MyController(hand, triggerAction) { direction: Quat.getUp(this.getHandRotation()) }; + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + var defaultGrabbableData = { grabbable: true }; @@ -250,20 +261,20 @@ function MyController(hand, triggerAction) { var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, defaultGrabbableData); if (grabbableData.grabbable === false) { + this.grabbedEntity = null; return; } if (intersectionDistance < NEAR_PICK_MAX_DISTANCE) { // the hand is very close to the intersected object. go into close-grabbing mode. - this.state = STATE_NEAR_GRABBING; - + this.setState(STATE_NEAR_GRABBING); } else { + // don't allow two people to distance grab the same object if (entityIsGrabbedByOther(intersection.entityID)) { - // don't allow two people to distance grab the same object - return; + this.grabbedEntity = null; + } else { + // the hand is far from the intersected object. go into distance-holding mode + this.setState(STATE_DISTANCE_HOLDING); } - // the hand is far from the intersected object. go into distance-holding mode - this.state = STATE_DISTANCE_HOLDING; - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); } } else { // forward ray test failed, try sphere test. @@ -287,22 +298,22 @@ function MyController(hand, triggerAction) { } } if (this.grabbedEntity === null) { - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + // this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); } else if (props.locked === 0 && props.collisionsWillMove === 1) { - this.state = STATE_NEAR_GRABBING; + this.setState(STATE_NEAR_GRABBING); } else if (props.collisionsWillMove === 0) { // We have grabbed a non-physical object, so we want to trigger a non-colliding event as opposed to a grab event - this.state = STATE_NEAR_GRABBING_NON_COLLIDING; + this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); } } - }; this.distanceHolding = function() { var handControllerPosition = Controller.getSpatialControlPosition(this.palm); var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", + "gravity", "ignoreForCollisions"]); // add the action and initialize some variables this.currentObjectPosition = grabbedProperties.position; @@ -325,8 +336,8 @@ function MyController(hand, triggerAction) { } if (this.actionID !== null) { - this.state = STATE_CONTINUE_DISTANCE_HOLDING; - this.activateEntity(this.grabbedEntity); + this.setState(STATE_CONTINUE_DISTANCE_HOLDING); + this.activateEntity(this.grabbedEntity, grabbedProperties); if (this.hand === RIGHT_HAND) { Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); } else { @@ -342,7 +353,7 @@ function MyController(hand, triggerAction) { this.continueDistanceHolding = function() { if (this.triggerSmoothedReleased()) { - this.state = STATE_RELEASE; + this.setState(STATE_RELEASE); return; } @@ -400,7 +411,7 @@ function MyController(hand, triggerAction) { var now = Date.now(); var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds this.computeReleaseVelocity(deltaPosition, deltaTime, false); - + this.currentObjectPosition = newObjectPosition; this.currentObjectTime = now; @@ -423,15 +434,15 @@ function MyController(hand, triggerAction) { this.nearGrabbing = function() { if (this.triggerSmoothedReleased()) { - this.state = STATE_RELEASE; + this.setState(STATE_RELEASE); return; } this.lineOff(); - this.activateEntity(this.grabbedEntity); - - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, + ["position", "rotation", "gravity", "ignoreForCollisions"]); + this.activateEntity(this.grabbedEntity, grabbedProperties); var handRotation = this.getHandRotation(); var handPosition = this.getHandPosition(); @@ -444,7 +455,7 @@ function MyController(hand, triggerAction) { var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("hold", this.grabbedEntity, { + this.actionID = Entities.addAction("kinematic-hold", this.grabbedEntity, { hand: this.hand === RIGHT_HAND ? "right" : "left", timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: offsetPosition, @@ -454,7 +465,7 @@ function MyController(hand, triggerAction) { if (this.actionID === NULL_ACTION_ID) { this.actionID = null; } else { - this.state = STATE_CONTINUE_NEAR_GRABBING; + this.setState(STATE_CONTINUE_NEAR_GRABBING); if (this.hand === RIGHT_HAND) { Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); } else { @@ -471,7 +482,7 @@ function MyController(hand, triggerAction) { this.continueNearGrabbing = function() { if (this.triggerSmoothedReleased()) { - this.state = STATE_RELEASE; + this.setState(STATE_RELEASE); return; } @@ -498,7 +509,7 @@ function MyController(hand, triggerAction) { this.nearGrabbingNonColliding = function() { if (this.triggerSmoothedReleased()) { - this.state = STATE_RELEASE; + this.setState(STATE_RELEASE); return; } if (this.hand === RIGHT_HAND) { @@ -507,12 +518,12 @@ function MyController(hand, triggerAction) { Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); } Entities.callEntityMethod(this.grabbedEntity, "startNearGrabNonColliding"); - this.state = STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING; + this.setState(STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING); }; this.continueNearGrabbingNonColliding = function() { if (this.triggerSmoothedReleased()) { - this.state = STATE_RELEASE; + this.setState(STATE_RELEASE); return; } Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding"); @@ -623,27 +634,42 @@ function MyController(hand, triggerAction) { this.grabbedVelocity = ZERO_VEC; this.grabbedEntity = null; this.actionID = null; - this.state = STATE_OFF; + this.setState(STATE_OFF); }; this.cleanup = function() { this.release(); }; - this.activateEntity = function() { - var data = { - activated: true, - avatarId: MyAvatar.sessionUUID - }; - setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data); + this.activateEntity = function(entityID, grabbedProperties) { + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + data["activated"] = true; + data["avatarId"] = MyAvatar.sessionUUID; + data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1; + // zero gravity and set ignoreForCollisions to true, but in a way that lets us put them back, after all grabs are done + if (data["refCount"] == 1) { + data["gravity"] = grabbedProperties.gravity; + data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions; + Entities.editEntity(entityID, {gravity: {x:0, y:0, z:0}, ignoreForCollisions: true}); + } + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); }; - this.deactivateEntity = function() { - var data = { - activated: false, - avatarId: null - }; - setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data); + this.deactivateEntity = function(entityID) { + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + if (data && data["refCount"]) { + data["refCount"] = data["refCount"] - 1; + if (data["refCount"] < 1) { + Entities.editEntity(entityID, { + gravity: data["gravity"], + ignoreForCollisions: data["ignoreForCollisions"] + }); + data = null; + } + } else { + data = null; + } + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); }; } diff --git a/examples/grab.js b/examples/grab.js index 03a227931d..1c49775a49 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -26,7 +26,8 @@ var IDENTITY_QUAT = { z: 0, w: 0 }; -var GRABBABLE_DATA_KEY = "grabbableKey"; +var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with handControllerGrab.js +var GRAB_USER_DATA_KEY = "grabKey"; // shared with handControllerGrab.js var defaultGrabbableData = { grabbable: true @@ -45,26 +46,25 @@ var IDENTITY_QUAT = { z: 0, w: 0 }; -var ACTION_LIFETIME = 120; // 2 minutes +var ACTION_LIFETIME = 10; // seconds function getTag() { return "grab-" + MyAvatar.sessionUUID; } function entityIsGrabbedByOther(entityID) { + // by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*. var actionIDs = Entities.getActionIDs(entityID); - var actionIndex; - var actionID; - var actionArguments; - var tag; - for (actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { - actionID = actionIDs[actionIndex]; - actionArguments = Entities.getActionArguments(entityID, actionID); - tag = actionArguments.tag; - if (tag === getTag()) { + for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { + var actionID = actionIDs[actionIndex]; + var actionArguments = Entities.getActionArguments(entityID, actionID); + var tag = actionArguments["tag"]; + if (tag == getTag()) { + // we see a grab-*uuid* shaped tag, but it's our tag, so that's okay. continue; } - if (tag.slice(0, 5) === "grab-") { + if (tag.slice(0, 5) == "grab-") { + // we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it. return true; } } @@ -248,7 +248,6 @@ Grabber = function() { this.currentPosition = ZERO_VEC3; this.planeNormal = ZERO_VEC3; - this.originalGravity = ZERO_VEC3; // maxDistance is a function of the size of the object. this.maxDistance; @@ -347,14 +346,11 @@ Grabber.prototype.pressEvent = function(event) { return; } - Entities.editEntity(clickedEntity, { - gravity: ZERO_VEC3 - }); + this.activateEntity(clickedEntity, entityProperties); this.isGrabbing = true; this.entityID = clickedEntity; this.currentPosition = entityProperties.position; - this.originalGravity = entityProperties.gravity; this.targetPosition = { x: this.startPosition.x, y: this.startPosition.y, @@ -380,12 +376,7 @@ Grabber.prototype.pressEvent = function(event) { Grabber.prototype.releaseEvent = function() { if (this.isGrabbing) { - if (Vec3.length(this.originalGravity) != 0) { - Entities.editEntity(this.entityID, { - gravity: this.originalGravity - }); - } - + this.deactivateEntity(this.entityID); this.isGrabbing = false Entities.deleteAction(this.entityID, this.actionID); this.actionID = null; @@ -504,6 +495,39 @@ Grabber.prototype.keyPressEvent = function(event) { this.computeNewGrabPlane(); } +Grabber.prototype.activateEntity = function(entityID, grabbedProperties) { + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + data["activated"] = true; + data["avatarId"] = MyAvatar.sessionUUID; + data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1; + // zero gravity and set ignoreForCollisions to true, but in a way that lets us put them back, after all grabs are done + if (data["refCount"] == 1) { + data["gravity"] = grabbedProperties.gravity; + data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions; + Entities.editEntity(entityID, {gravity: {x:0, y:0, z:0}, ignoreForCollisions: true}); + } + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); +}; + +Grabber.prototype.deactivateEntity = function(entityID) { + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + if (data && data["refCount"]) { + data["refCount"] = data["refCount"] - 1; + if (data["refCount"] < 1) { + Entities.editEntity(entityID, { + gravity: data["gravity"], + ignoreForCollisions: data["ignoreForCollisions"] + }); + data = null; + } + } else { + data = null; + } + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); +}; + + + var grabber = new Grabber(); function pressEvent(event) { @@ -530,4 +554,4 @@ Controller.mousePressEvent.connect(pressEvent); Controller.mouseMoveEvent.connect(moveEvent); Controller.mouseReleaseEvent.connect(releaseEvent); Controller.keyPressEvent.connect(keyPressEvent); -Controller.keyReleaseEvent.connect(keyReleaseEvent); \ No newline at end of file +Controller.keyReleaseEvent.connect(keyReleaseEvent); diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index fa0f36cbb1..3583aad76a 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -79,13 +79,21 @@ getEntityUserData = function(id) { // Non-destructively modify the user data of an entity. setEntityCustomData = function(customKey, id, data) { var userData = getEntityUserData(id); - userData[customKey] = data; + if (data == null) { + delete userData[customKey]; + } else { + userData[customKey] = data; + } setEntityUserData(id, userData); } getEntityCustomData = function(customKey, id, defaultValue) { var userData = getEntityUserData(id); - return userData[customKey] ? userData[customKey] : defaultValue; + if (undefined != userData[customKey]) { + return userData[customKey]; + } else { + return defaultValue; + } } mergeObjects = function(proto, custom) { diff --git a/examples/toys/ping_pong_gun/createPingPongGun.js b/examples/toys/ping_pong_gun/createPingPongGun.js index 4b7ed27643..cf56d6f790 100644 --- a/examples/toys/ping_pong_gun/createPingPongGun.js +++ b/examples/toys/ping_pong_gun/createPingPongGun.js @@ -13,8 +13,8 @@ Script.include("../../utilities.js"); var scriptURL = Script.resolvePath('pingPongGun.js'); -var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx' -var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; +var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx?123' +var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj?123'; var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, @@ -25,14 +25,15 @@ var center = Vec3.sum(Vec3.sum(MyAvatar.position, { var pingPongGun = Entities.addEntity({ type: "Model", modelURL: MODEL_URL, - shapeType: 'compound', - compoundShapeURL: COLLISION_HULL_URL, + shapeType:'box', + // shapeType: 'compound', + // compoundShapeURL: COLLISION_HULL_URL, script: scriptURL, position: center, dimensions: { - x:0.67, - y: 0.14, - z: 0.09 + x: 0.08, + y: 0.21, + z: 0.47 }, collisionsWillMove: true, }); @@ -40,4 +41,4 @@ var pingPongGun = Entities.addEntity({ function cleanUp() { Entities.deleteEntity(pingPongGun); } -Script.scriptEnding.connect(cleanUp); +Script.scriptEnding.connect(cleanUp); \ No newline at end of file diff --git a/examples/toys/ping_pong_gun/pingPongGun.js b/examples/toys/ping_pong_gun/pingPongGun.js index a980fc1bd3..1b5973c8bb 100644 --- a/examples/toys/ping_pong_gun/pingPongGun.js +++ b/examples/toys/ping_pong_gun/pingPongGun.js @@ -21,14 +21,14 @@ //if the trigger value goes below this value, reload the gun. var RELOAD_THRESHOLD = 0.95; - var GUN_TIP_FWD_OFFSET = 0.45; + var GUN_TIP_FWD_OFFSET =-0.35; var GUN_TIP_UP_OFFSET = 0.040; - var GUN_FORCE = 15; + var GUN_FORCE = 9; var BALL_RESTITUTION = 0.6; var BALL_LINEAR_DAMPING = 0.4; var BALL_GRAVITY = { x: 0, - y: -9.8, + y: -4.8, z: 0 }; @@ -36,14 +36,14 @@ x: 0.04, y: 0.04, z: 0.04 - } + }; var BALL_COLOR = { red: 255, green: 255, blue: 255 - } + }; PingPongGun.prototype = { hand: null, @@ -68,7 +68,6 @@ }, continueNearGrab: function() { - if (this.whichHand === null) { //only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten this.setWhichHand(); @@ -81,10 +80,14 @@ }, releaseGrab: function() { - var _t = this; - this.canShootTimeout = Script.setTimeout(function() { - _t.canShoot = false; - }, 250) + var _this = this; + + if (this.whichHand === this.hand) { + this.whichHand = null; + this.canShootTimeout = Script.setTimeout(function() { + _this.canShoot = false; + }, 250); + } }, checkTriggerPressure: function(gunHand) { @@ -97,18 +100,20 @@ if (this.triggerValue < RELOAD_THRESHOLD) { // print('RELOAD'); this.canShoot = true; - } else if (this.triggerValue >= RELOAD_THRESHOLD && this.canShoot === true) { + } else if (this.triggerValue >= RELOAD_THRESHOLD && this.canShoot === true && this.hand === this.whichHand) { var gunProperties = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); this.shootBall(gunProperties); this.canShoot = false; } + return; }, shootBall: function(gunProperties) { - var forwardVec = Quat.getFront(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, -90, 0))); + var forwardVec = Quat.getFront(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, 180, 0))); forwardVec = Vec3.normalize(forwardVec); forwardVec = Vec3.multiply(forwardVec, GUN_FORCE); + var properties = { type: 'Sphere', color: BALL_COLOR, @@ -130,7 +135,7 @@ playSoundAtCurrentPosition: function(position) { var audioProperties = { - volume: 0.1, + volume: 0.2, position: position }; @@ -139,12 +144,14 @@ getGunTipPosition: function(properties) { //the tip of the gun is going to be in a different place than the center, so we move in space relative to the model to find that position - var frontVector = Quat.getRight(properties.rotation); + var frontVector = Quat.getFront(properties.rotation); var frontOffset = Vec3.multiply(frontVector, GUN_TIP_FWD_OFFSET); - var upVector = Quat.getRight(properties.rotation); + var upVector = Quat.getUp(properties.rotation); var upOffset = Vec3.multiply(upVector, GUN_TIP_UP_OFFSET); + var gunTipPosition = Vec3.sum(properties.position, frontOffset); gunTipPosition = Vec3.sum(gunTipPosition, upOffset); + return gunTipPosition; }, diff --git a/examples/toys/sprayPaintCan.js b/examples/toys/sprayPaintCan.js index b67224b976..1532ff19f3 100644 --- a/examples/toys/sprayPaintCan.js +++ b/examples/toys/sprayPaintCan.js @@ -9,7 +9,7 @@ // -(function() { +(function () { // Script.include("../libraries/utils.js"); //Need absolute path for now, for testing before PR merge and s3 cloning. Will change post-merge @@ -33,19 +33,19 @@ var MIN_POINT_DISTANCE = 0.01; var STROKE_WIDTH = 0.02; - this.setRightHand = function() { + this.setRightHand = function () { this.hand = 'RIGHT'; } - this.setLeftHand = function() { + this.setLeftHand = function () { this.hand = 'LEFT'; } - this.startNearGrab = function() { + this.startNearGrab = function () { this.whichHand = this.hand; } - this.toggleWithTriggerPressure = function() { + this.toggleWithTriggerPressure = function () { var handClickString = this.whichHand + "_HAND_CLICK"; var handClick = Controller.findAction(handClickString); @@ -60,7 +60,7 @@ } } - this.enableStream = function() { + this.enableStream = function () { var position = Entities.getEntityProperties(this.entityId, "position").position; var PI = 3.141593; var DEG_TO_RAD = PI / 180.0; @@ -78,6 +78,11 @@ particleRadius: 0.01, radiusSpread: 0.005, polarFinish: 0.05, + colorStart: { + red: 50, + green: 10, + blue: 150 + }, color: { red: 170, green: 20, @@ -98,11 +103,11 @@ } - this.releaseGrab = function() { + this.releaseGrab = function () { this.disableStream(); } - this.disableStream = function() { + this.disableStream = function () { Entities.deleteEntity(this.paintStream); this.paintStream = null; this.spraying = false; @@ -110,7 +115,7 @@ } - this.continueNearGrab = function() { + this.continueNearGrab = function () { this.toggleWithTriggerPressure(); @@ -136,7 +141,7 @@ }); } - this.preload = function(entityId) { + this.preload = function (entityId) { this.sprayVolume = 0.1; this.spraying = false; this.entityId = entityId; @@ -144,12 +149,9 @@ } - this.unload = function() { + this.unload = function () { if (this.paintStream) { Entities.deleteEntity(this.paintStream); } - this.strokes.forEach(function(stroke) { - Entities.deleteEntity(stroke); - }); } }); diff --git a/examples/utilities/tools/MonoHMD.js b/examples/utilities/tools/MonoHMD.js new file mode 100644 index 0000000000..4565ebd7bf --- /dev/null +++ b/examples/utilities/tools/MonoHMD.js @@ -0,0 +1,68 @@ +// +// MonoHMD.js +// +// Created by Chris Collins on 10/5/15 +// Copyright 2015 High Fidelity, Inc. +// +// This script allows you to switch between mono and stereo mode within the HMD. +// It will add adition menu to Tools called "IPD". +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + + + +function setupipdMenu() { + if (!Menu.menuExists("Tools > IPD")) { + Menu.addMenu("Tools > IPD"); + } + if (!Menu.menuItemExists("Tools > IPD", "Stereo")) { + Menu.addMenuItem({ + menuName: "Tools > IPD", + menuItemName: "Stereo", + isCheckable: true, + isChecked: true + }); + } + if (!Menu.menuItemExists("Tools > IPD", "Mono")) { + Menu.addMenuItem({ + menuName: "Tools > IPD", + menuItemName: "Mono", + isCheckable: true, + isChecked: false + }); + } + +} + + +function menuItemEvent(menuItem) { + if (menuItem == "Stereo") { + Menu.setIsOptionChecked("Mono", false); + HMD.ipdScale = 1.0; + + } + if (menuItem == "Mono") { + Menu.setIsOptionChecked("Stereo", false); + HMD.ipdScale = 0.0; + } + +} + + + +function scriptEnding() { + + Menu.removeMenuItem("Tools > IPD", "Stereo"); + Menu.removeMenuItem("Tools > IPD", "Mono"); + Menu.removeMenu("Tools > IPD"); + //reset the HMD to stereo mode + HMD.setIPDScale(1.0); + +} + + +setupipdMenu(); +Menu.menuItemEvent.connect(menuItemEvent); +Script.scriptEnding.connect(scriptEnding); \ No newline at end of file diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp index 2879c19eaa..e2cbdac425 100644 --- a/interface/src/InterfaceActionFactory.cpp +++ b/interface/src/InterfaceActionFactory.cpp @@ -12,6 +12,7 @@ #include +#include #include #include @@ -28,6 +29,8 @@ EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& i return (EntityActionPointer) new ObjectActionSpring(id, ownerEntity); case ACTION_TYPE_HOLD: return (EntityActionPointer) new AvatarActionHold(id, ownerEntity); + case ACTION_TYPE_KINEMATIC_HOLD: + return (EntityActionPointer) new AvatarActionKinematicHold(id, ownerEntity); } assert(false); @@ -65,9 +68,9 @@ EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEnt if (action) { action->deserialize(data); - } - if (action->lifetimeIsOver()) { - return nullptr; + if (action->lifetimeIsOver()) { + return nullptr; + } } return action; diff --git a/interface/src/PluginContainerProxy.cpp b/interface/src/PluginContainerProxy.cpp index 4ef755bd1b..469e7f7c77 100644 --- a/interface/src/PluginContainerProxy.cpp +++ b/interface/src/PluginContainerProxy.cpp @@ -139,6 +139,10 @@ void PluginContainerProxy::unsetFullscreen(const QScreen* avoid) { #endif } +void PluginContainerProxy::requestReset() { + // We could signal qApp to sequence this, but it turns out that requestReset is only used from within the main thread anyway. + qApp->resetSensors(); +} void PluginContainerProxy::showDisplayPluginsTools() { DependencyManager::get()->hmdTools(true); diff --git a/interface/src/PluginContainerProxy.h b/interface/src/PluginContainerProxy.h index e01cabc3b8..95865609c8 100644 --- a/interface/src/PluginContainerProxy.h +++ b/interface/src/PluginContainerProxy.h @@ -16,10 +16,11 @@ class PluginContainerProxy : public QObject, PluginContainer { virtual QAction* addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override; virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override; virtual bool isOptionChecked(const QString& name) override; - virtual void setIsOptionChecked(const QString& path, bool checked); + virtual void setIsOptionChecked(const QString& path, bool checked) override; virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = true) override; virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override; virtual void showDisplayPluginsTools() override; + virtual void requestReset() override; virtual QGLWidget* getPrimarySurface() override; virtual bool isForeground() override; QRect _savedGeometry{ 10, 120, 800, 600 }; diff --git a/interface/src/avatar/AvatarActionKinematicHold.cpp b/interface/src/avatar/AvatarActionKinematicHold.cpp new file mode 100644 index 0000000000..680363877f --- /dev/null +++ b/interface/src/avatar/AvatarActionKinematicHold.cpp @@ -0,0 +1,188 @@ +// +// AvatarActionKinematicHold.cpp +// interface/src/avatar/ +// +// Created by Seth Alves 2015-6-9 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "QVariantGLM.h" +#include "avatar/MyAvatar.h" +#include "avatar/AvatarManager.h" + +#include "AvatarActionKinematicHold.h" + +const uint16_t AvatarActionKinematicHold::holdVersion = 1; + +AvatarActionKinematicHold::AvatarActionKinematicHold(const QUuid& id, EntityItemPointer ownerEntity) : + ObjectActionSpring(id, ownerEntity), + _relativePosition(glm::vec3(0.0f)), + _relativeRotation(glm::quat()), + _hand("right"), + _mine(false), + _previousPositionalTarget(Vectors::ZERO), + _previousRotationalTarget(Quaternions::IDENTITY) +{ + _type = ACTION_TYPE_KINEMATIC_HOLD; + #if WANT_DEBUG + qDebug() << "AvatarActionKinematicHold::AvatarActionKinematicHold"; + #endif +} + +AvatarActionKinematicHold::~AvatarActionKinematicHold() { + #if WANT_DEBUG + qDebug() << "AvatarActionKinematicHold::~AvatarActionKinematicHold"; + #endif +} + +void AvatarActionKinematicHold::updateActionWorker(float deltaTimeStep) { + if (!_mine) { + // if a local script isn't updating this, then we are just getting spring-action data over the wire. + // let the super-class handle it. + ObjectActionSpring::updateActionWorker(deltaTimeStep); + return; + } + + assert(deltaTimeStep > 0.0f); + + glm::quat rotation; + glm::vec3 position; + glm::vec3 offset; + bool gotLock = withTryReadLock([&]{ + auto myAvatar = DependencyManager::get()->getMyAvatar(); + glm::vec3 palmPosition; + glm::quat palmRotation; + if (_hand == "right") { + palmPosition = myAvatar->getRightPalmPosition(); + palmRotation = myAvatar->getRightPalmRotation(); + } else { + palmPosition = myAvatar->getLeftPalmPosition(); + palmRotation = myAvatar->getLeftPalmRotation(); + } + + rotation = palmRotation * _relativeRotation; + offset = rotation * _relativePosition; + position = palmPosition + offset; + }); + + if (gotLock) { + gotLock = withTryWriteLock([&]{ + if (_positionalTarget != position || _rotationalTarget != rotation) { + _positionalTarget = position; + _rotationalTarget = rotation; + auto ownerEntity = _ownerEntity.lock(); + if (ownerEntity) { + ownerEntity->setActionDataDirty(true); + void* physicsInfo = ownerEntity->getPhysicsInfo(); + if (physicsInfo) { + ObjectMotionState* motionState = static_cast(physicsInfo); + btRigidBody* rigidBody = motionState ? motionState->getRigidBody() : nullptr; + if (!rigidBody) { + qDebug() << "ObjectActionSpring::updateActionWorker no rigidBody"; + return; + } + + if (_setVelocity) { + if (_previousSet) { + glm::vec3 positionalVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; + rigidBody->setLinearVelocity(glmToBullet(positionalVelocity)); + // back up along velocity a bit in order to smooth out a "vibrating" appearance + _positionalTarget -= positionalVelocity * deltaTimeStep / 2.0f; + } + } + + btTransform worldTrans = rigidBody->getWorldTransform(); + worldTrans.setOrigin(glmToBullet(_positionalTarget)); + worldTrans.setRotation(glmToBullet(_rotationalTarget)); + rigidBody->setWorldTransform(worldTrans); + + _previousPositionalTarget = _positionalTarget; + _previousRotationalTarget = _rotationalTarget; + _previousSet = true; + } + } + } + }); + } + + if (gotLock) { + ObjectActionSpring::updateActionWorker(deltaTimeStep); + } +} + + +bool AvatarActionKinematicHold::updateArguments(QVariantMap arguments) { + if (!ObjectAction::updateArguments(arguments)) { + return false; + } + bool ok = true; + glm::vec3 relativePosition = + EntityActionInterface::extractVec3Argument("kinematic-hold", arguments, "relativePosition", ok, false); + if (!ok) { + relativePosition = _relativePosition; + } + + ok = true; + glm::quat relativeRotation = + EntityActionInterface::extractQuatArgument("kinematic-hold", arguments, "relativeRotation", ok, false); + if (!ok) { + relativeRotation = _relativeRotation; + } + + ok = true; + QString hand = + EntityActionInterface::extractStringArgument("kinematic-hold", arguments, "hand", ok, false); + if (!ok || !(hand == "left" || hand == "right")) { + hand = _hand; + } + + ok = true; + int setVelocity = + EntityActionInterface::extractIntegerArgument("kinematic-hold", arguments, "setVelocity", ok, false); + if (!ok) { + setVelocity = false; + } + + if (relativePosition != _relativePosition + || relativeRotation != _relativeRotation + || hand != _hand) { + withWriteLock([&] { + _relativePosition = relativePosition; + _relativeRotation = relativeRotation; + _hand = hand; + _setVelocity = setVelocity; + + _mine = true; + _active = true; + activateBody(); + }); + } + return true; +} + + +QVariantMap AvatarActionKinematicHold::getArguments() { + QVariantMap arguments = ObjectAction::getArguments(); + withReadLock([&]{ + if (!_mine) { + arguments = ObjectActionSpring::getArguments(); + return; + } + + arguments["relativePosition"] = glmToQMap(_relativePosition); + arguments["relativeRotation"] = glmToQMap(_relativeRotation); + arguments["setVelocity"] = _setVelocity; + arguments["hand"] = _hand; + }); + return arguments; +} + + +void AvatarActionKinematicHold::deserialize(QByteArray serializedArguments) { + if (!_mine) { + ObjectActionSpring::deserialize(serializedArguments); + } +} diff --git a/interface/src/avatar/AvatarActionKinematicHold.h b/interface/src/avatar/AvatarActionKinematicHold.h new file mode 100644 index 0000000000..b4fceab1e7 --- /dev/null +++ b/interface/src/avatar/AvatarActionKinematicHold.h @@ -0,0 +1,47 @@ +// +// AvatarActionKinematicHold.h +// interface/src/avatar/ +// +// Created by Seth Alves 2015-10-2 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AvatarActionKinematicHold_h +#define hifi_AvatarActionKinematicHold_h + +#include + +#include +#include + +class AvatarActionKinematicHold : public ObjectActionSpring { +public: + AvatarActionKinematicHold(const QUuid& id, EntityItemPointer ownerEntity); + virtual ~AvatarActionKinematicHold(); + + virtual bool updateArguments(QVariantMap arguments); + virtual QVariantMap getArguments(); + + virtual void updateActionWorker(float deltaTimeStep); + + virtual void deserialize(QByteArray serializedArguments); + +private: + static const uint16_t holdVersion; + + glm::vec3 _relativePosition; + glm::quat _relativeRotation; + QString _hand; + bool _mine = false; + + bool _previousSet = false; + glm::vec3 _previousPositionalTarget; + glm::quat _previousRotationalTarget; + + bool _setVelocity = false; +}; + +#endif // hifi_AvatarActionKinematicHold_h diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 96c9f37c39..5d87737dd7 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -139,19 +139,19 @@ public: void updateLookAtTargetAvatar(); void clearLookAtTargetAvatar(); - virtual void setJointRotations(QVector jointRotations); - virtual void setJointTranslations(QVector jointTranslations); - virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation); - virtual void setJointRotation(int index, const glm::quat& rotation); - virtual void setJointTranslation(int index, const glm::vec3& translation); - virtual void clearJointData(int index); - virtual void clearJointsData(); + virtual void setJointRotations(QVector jointRotations) override; + virtual void setJointTranslations(QVector jointTranslations) override; + virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) override; + virtual void setJointRotation(int index, const glm::quat& rotation) override; + virtual void setJointTranslation(int index, const glm::vec3& translation) override; + virtual void clearJointData(int index) override; + virtual void clearJointsData() override; Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString()); Q_INVOKABLE const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; } Q_INVOKABLE const QString& getFullAvatarModelName() const { return _fullAvatarModelName; } - virtual void setAttachmentData(const QVector& attachmentData); + virtual void setAttachmentData(const QVector& attachmentData) override; DynamicCharacterController* getCharacterController() { return &_characterController; } @@ -218,7 +218,7 @@ public slots: void saveRecording(QString filename); void loadLastRecording(); - virtual void rebuildSkeletonBody(); + virtual void rebuildSkeletonBody() override; bool getEnableRigAnimations() const { return _rig->getEnableRig(); } void setEnableRigAnimations(bool isEnabled); @@ -243,7 +243,7 @@ private: glm::vec3 getWorldBodyPosition() const; glm::quat getWorldBodyOrientation() const; - QByteArray toByteArray(bool cullSmallChanges, bool sendAll); + QByteArray toByteArray(bool cullSmallChanges, bool sendAll) override; void simulate(float deltaTime); void updateFromTrackers(float deltaTime); virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override; @@ -252,9 +252,9 @@ private: void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; } bool getShouldRenderLocally() const { return _shouldRender; } bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; }; - bool isMyAvatar() const { return true; } - virtual int parseDataFromBuffer(const QByteArray& buffer); - virtual glm::vec3 getSkeletonPosition() const; + bool isMyAvatar() const override { return true; } + virtual int parseDataFromBuffer(const QByteArray& buffer) override; + virtual glm::vec3 getSkeletonPosition() const override; glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; } float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; } @@ -264,7 +264,7 @@ private: void setScriptedMotorFrame(QString frame); virtual void attach(const QString& modelURL, const QString& jointName = QString(), const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, - bool allowDuplicates = false, bool useSaved = true); + bool allowDuplicates = false, bool useSaved = true) override; void renderLaserPointers(gpu::Batch& batch); const RecorderPointer getRecorder() const { return _recorder; } @@ -273,8 +273,8 @@ private: bool cameraInsideHead() const; // These are made private for MyAvatar so that you will use the "use" methods instead - virtual void setFaceModelURL(const QUrl& faceModelURL); - virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); + virtual void setFaceModelURL(const QUrl& faceModelURL) override; + virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index e2c3ab8f83..d655d6e01f 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -27,10 +27,10 @@ public: SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr); ~SkeletonModel(); - virtual void initJointStates(QVector states); + virtual void initJointStates(QVector states) override; - virtual void simulate(float deltaTime, bool fullUpdate = true); - virtual void updateRig(float deltaTime, glm::mat4 parentTransform); + virtual void simulate(float deltaTime, bool fullUpdate = true) override; + virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; void updateAttitude(); void renderIKConstraints(gpu::Batch& batch); diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index c432b3b9ac..251cb0047a 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -159,7 +159,7 @@ void AnimClip::copyFromNetworkAnim() { // used to adjust translation offsets, so large translation animatons on the reference skeleton // will be adjusted when played on a skeleton with short limbs. - float limbLengthScale = fabs(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relBindPose.trans) / glm::length(fbxZeroTrans)); + float limbLengthScale = fabsf(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relBindPose.trans) / glm::length(fbxZeroTrans)); AnimPose& pose = _anim[frame][skeletonJoint]; const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame]; diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index 2eb0d9b08e..171bcc11d8 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -52,7 +52,7 @@ protected: void computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses); void solveWithCyclicCoordinateDescent(const std::vector& targets); - virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton); + virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override; // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override { return _relativePoses; } diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp index 187b25e75a..077f28350b 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp @@ -56,7 +56,7 @@ void RenderableBoxEntityItem::render(RenderArgs* args) { if (_procedural->ready()) { batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well - _procedural->prepare(batch, this->getDimensions()); + _procedural->prepare(batch, getPosition(), getDimensions()); auto color = _procedural->getColor(cubeColor); batch._glColor4f(color.r, color.g, color.b, color.a); DependencyManager::get()->renderCube(batch); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index 9581c43ca5..5d69d19026 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -25,8 +25,8 @@ public: void updateRenderItem(); - virtual bool addToScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges); - virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges); + virtual bool addToScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges) override; + virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges) override; protected: render::ItemID _renderItemId; diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp index 3cfc18046a..246cd2fea7 100644 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp @@ -62,7 +62,7 @@ void RenderableSphereEntityItem::render(RenderArgs* args) { modelTransform.postScale(SPHERE_ENTITY_SCALE); if (_procedural->ready()) { batch.setModelTransform(modelTransform); // use a transform with scale, rotation, registration point and translation - _procedural->prepare(batch, getDimensions()); + _procedural->prepare(batch, getPosition(), getDimensions()); auto color = _procedural->getColor(sphereColor); batch._glColor4f(color.r, color.g, color.b, color.a); DependencyManager::get()->renderSphere(batch); diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index ba7f3afea4..c34f072601 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -100,6 +100,9 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS if (normalizedActionTypeString == "hold") { return ACTION_TYPE_HOLD; } + if (normalizedActionTypeString == "kinematichold") { + return ACTION_TYPE_KINEMATIC_HOLD; + } qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString; return ACTION_TYPE_NONE; @@ -115,6 +118,8 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) { return "spring"; case ACTION_TYPE_HOLD: return "hold"; + case ACTION_TYPE_KINEMATIC_HOLD: + return "kinematic-hold"; } assert(false); return "none"; @@ -244,6 +249,28 @@ float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMa return v; } +int EntityActionInterface::extractIntegerArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required) { + if (!arguments.contains(argumentName)) { + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } + ok = false; + return 0.0f; + } + + QVariant vV = arguments[argumentName]; + bool vOk = true; + int v = vV.toInt(&vOk); + + if (!vOk || v != v) { + ok = false; + return 0; + } + + return v; +} + QString EntityActionInterface::extractStringArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index e61019c98c..57f605c3e3 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -23,7 +23,8 @@ enum EntityActionType { ACTION_TYPE_NONE = 0, ACTION_TYPE_OFFSET = 1000, ACTION_TYPE_SPRING = 2000, - ACTION_TYPE_HOLD = 3000 + ACTION_TYPE_HOLD = 3000, + ACTION_TYPE_KINEMATIC_HOLD = 4000 }; @@ -48,6 +49,8 @@ public: virtual bool lifetimeIsOver() { return false; } + bool locallyAddedButNotYetReceived = false; + protected: virtual glm::vec3 getPosition() = 0; virtual void setPosition(glm::vec3 position) = 0; @@ -67,6 +70,8 @@ protected: QString argumentName, bool& ok, bool required = true); static float extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok, bool required = true); + static int extractIntegerArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); static QString extractStringArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok, bool required = true); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index ce719ee976..c4f5ad0061 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -34,6 +34,7 @@ bool EntityItem::_sendPhysicsUpdates = true; int EntityItem::_maxActionsDataSize = 800; +quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND; EntityItem::EntityItem(const EntityItemID& entityItemID) : _type(EntityTypes::Unknown), @@ -1505,6 +1506,8 @@ bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer act result = addActionInternal(simulation, action); if (!result) { removeActionInternal(action->getID()); + } else { + action->locallyAddedButNotYetReceived = true; } }); @@ -1525,7 +1528,8 @@ bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPoi simulation->addAction(action); bool success; - QByteArray newDataCache = serializeActions(success); + QByteArray newDataCache; + serializeActions(success, newDataCache); if (success) { _allActionsDataCache = newDataCache; _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; @@ -1546,7 +1550,7 @@ bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionI success = action->updateArguments(arguments); if (success) { - _allActionsDataCache = serializeActions(success); + serializeActions(success, _allActionsDataCache); _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; } else { qDebug() << "EntityItem::updateAction failed"; @@ -1566,6 +1570,7 @@ bool EntityItem::removeAction(EntitySimulation* simulation, const QUuid& actionI bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* simulation) { assertWriteLocked(); + _previouslyDeletedActions.insert(actionID, usecTimestampNow()); if (_objectActions.contains(actionID)) { if (!simulation) { EntityTreePointer entityTree = _element ? _element->getTree() : nullptr; @@ -1581,7 +1586,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s } bool success = true; - _allActionsDataCache = serializeActions(success); + serializeActions(success, _allActionsDataCache); _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; return success; } @@ -1618,6 +1623,8 @@ void EntityItem::deserializeActions() { void EntityItem::deserializeActionsInternal() { assertWriteLocked(); + quint64 now = usecTimestampNow(); + if (!_element) { return; } @@ -1643,6 +1650,10 @@ void EntityItem::deserializeActionsInternal() { QUuid actionID; serializedActionStream >> actionType; serializedActionStream >> actionID; + if (_previouslyDeletedActions.contains(actionID)) { + continue; + } + updated << actionID; if (_objectActions.contains(actionID)) { @@ -1650,14 +1661,14 @@ void EntityItem::deserializeActionsInternal() { // TODO: make sure types match? there isn't currently a way to // change the type of an existing action. action->deserialize(serializedAction); + action->locallyAddedButNotYetReceived = false; } else { auto actionFactory = DependencyManager::get(); - - // EntityItemPointer entity = entityTree->findEntityByEntityItemID(_id, false); EntityItemPointer entity = shared_from_this(); EntityActionPointer action = actionFactory->factoryBA(entity, serializedAction); if (action) { entity->addActionInternal(simulation, action); + action->locallyAddedButNotYetReceived = false; } } } @@ -1667,11 +1678,27 @@ void EntityItem::deserializeActionsInternal() { while (i != _objectActions.end()) { QUuid id = i.key(); if (!updated.contains(id)) { - _actionsToRemove << id; + EntityActionPointer action = i.value(); + // if we've just added this action, don't remove it due to lack of mention in an incoming packet. + if (! action->locallyAddedButNotYetReceived) { + _actionsToRemove << id; + _previouslyDeletedActions.insert(id, now); + } } i++; } + // trim down _previouslyDeletedActions + QMutableHashIterator _previouslyDeletedIter(_previouslyDeletedActions); + while (_previouslyDeletedIter.hasNext()) { + _previouslyDeletedIter.next(); + if (now - _previouslyDeletedIter.value() > _rememberDeletedActionTime) { + _previouslyDeletedActions.remove(_previouslyDeletedIter.key()); + } + } + + _actionDataDirty = true; + return; } @@ -1697,13 +1724,13 @@ void EntityItem::setActionDataInternal(QByteArray actionData) { deserializeActionsInternal(); } -QByteArray EntityItem::serializeActions(bool& success) const { +void EntityItem::serializeActions(bool& success, QByteArray& result) const { assertLocked(); - QByteArray result; if (_objectActions.size() == 0) { success = true; - return QByteArray(); + result.clear(); + return; } QVector serializedActions; @@ -1721,21 +1748,20 @@ QByteArray EntityItem::serializeActions(bool& success) const { if (result.size() >= _maxActionsDataSize) { success = false; - return result; + return; } success = true; - return result; + return; } const QByteArray EntityItem::getActionDataInternal() const { if (_actionDataDirty) { bool success; - QByteArray newDataCache = serializeActions(success); + serializeActions(success, _allActionsDataCache); if (success) { - _allActionsDataCache = newDataCache; + _actionDataDirty = false; } - _actionDataDirty = false; } return _allActionsDataCache; } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index f070e2c1e4..d11ffadc75 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -508,17 +508,21 @@ protected: bool addActionInternal(EntitySimulation* simulation, EntityActionPointer action); bool removeActionInternal(const QUuid& actionID, EntitySimulation* simulation = nullptr); void deserializeActionsInternal(); - QByteArray serializeActions(bool& success) const; + void serializeActions(bool& success, QByteArray& result) const; QHash _objectActions; static int _maxActionsDataSize; mutable QByteArray _allActionsDataCache; + // when an entity-server starts up, EntityItem::setActionData is called before the entity-tree is // ready. This means we can't find our EntityItemPointer or add the action to the simulation. These // are used to keep track of and work around this situation. void checkWaitingToRemove(EntitySimulation* simulation = nullptr); mutable QSet _actionsToRemove; mutable bool _actionDataDirty = false; + // _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag + static quint64 _rememberDeletedActionTime; + mutable QHash _previouslyDeletedActions; }; #endif // hifi_EntityItem_h diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 2a35d04aac..e1b76e2e81 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -156,24 +156,24 @@ public: // Indirect buffer is used by the multiDrawXXXIndirect calls // The indirect buffer contains the command descriptions to execute multiple drawcalls in a single call void setIndirectBuffer(const BufferPointer& buffer, Offset offset = 0, Offset stride = 0); - - // multi command desctription for multiDrawIndexedIndirect - class DrawIndirectCommand { - public: - uint _count{ 0 }; - uint _instanceCount{ 0 }; - uint _firstIndex{ 0 }; - uint _baseInstance{ 0 }; + + // multi command desctription for multiDrawIndexedIndirect + class DrawIndirectCommand { + public: + uint _count{ 0 }; + uint _instanceCount{ 0 }; + uint _firstIndex{ 0 }; + uint _baseInstance{ 0 }; }; - - // multi command desctription for multiDrawIndexedIndirect - class DrawIndexedIndirectCommand { - public: - uint _count{ 0 }; - uint _instanceCount{ 0 }; - uint _firstIndex{ 0 }; - uint _baseVertex{ 0 }; - uint _baseInstance{ 0 }; + + // multi command desctription for multiDrawIndexedIndirect + class DrawIndexedIndirectCommand { + public: + uint _count{ 0 }; + uint _instanceCount{ 0 }; + uint _firstIndex{ 0 }; + uint _baseVertex{ 0 }; + uint _baseInstance{ 0 }; }; // Transform Stage @@ -246,6 +246,26 @@ public: void _glUniform4iv(int location, int count, const int* value); void _glUniformMatrix4fv(int location, int count, unsigned char transpose, const float* value); + void _glUniform(int location, int v0) { + _glUniform1i(location, v0); + } + + void _glUniform(int location, float v0) { + _glUniform1f(location, v0); + } + + void _glUniform(int location, const glm::vec2& v) { + _glUniform2f(location, v.x, v.y); + } + + void _glUniform(int location, const glm::vec3& v) { + _glUniform3f(location, v.x, v.y, v.z); + } + + void _glUniform(int location, const glm::vec4& v) { + _glUniform4f(location, v.x, v.y, v.z, v.w); + } + void _glColor4f(float red, float green, float blue, float alpha); enum Command { diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index d358b6431d..ae6f3b740d 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -343,9 +343,22 @@ bool Texture::assignStoredMipFace(uint16 level, const Element& format, Size size } uint16 Texture::autoGenerateMips(uint16 maxMip) { - _autoGenerateMips = true; - _maxMip = std::min((uint16) (evalNumMips() - 1), maxMip); - _stamp++; + bool changed = false; + if (!_autoGenerateMips) { + changed = true; + _autoGenerateMips = true; + } + + auto newMaxMip = std::min((uint16)(evalNumMips() - 1), maxMip); + if (newMaxMip != _maxMip) { + changed = true; + _maxMip = newMaxMip;; + } + + if (changed) { + _stamp++; + } + return _maxMip; } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 70e2ee5d34..6f703bc6f9 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -59,7 +59,7 @@ public: // Plugin functions virtual bool isSupported() const override { return true; } virtual bool isJointController() const override { return false; } - const QString& getName() const { return NAME; } + const QString& getName() const override { return NAME; } virtual void activate() override {}; virtual void deactivate() override {}; diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.h b/libraries/input-plugins/src/input-plugins/SDL2Manager.h index f017e2cc65..52d39597ef 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.h +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.h @@ -30,7 +30,7 @@ public: // Plugin functions virtual bool isSupported() const override; virtual bool isJointController() const override { return false; } - const QString& getName() const { return NAME; } + const QString& getName() const override { return NAME; } virtual void init() override; virtual void deinit() override; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index aa04c49adb..e900e8779e 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -285,6 +285,7 @@ void SixenseManager::updateCalibration(void* controllersX) { _avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis))); const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f; _avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR; + CONTAINER->requestReset(); qCDebug(inputplugins, "succeess: sixense calibration"); } break; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 22340cfc95..5e3815cd20 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -63,7 +63,7 @@ public: // Plugin functions virtual bool isSupported() const override; virtual bool isJointController() const override { return true; } - const QString& getName() const { return NAME; } + const QString& getName() const override { return NAME; } virtual void activate() override; virtual void deactivate() override; diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h index 98f32b9f35..5cae8daaf4 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h @@ -53,7 +53,7 @@ public: // Plugin functions virtual bool isSupported() const override; virtual bool isJointController() const override { return true; } - const QString& getName() const { return NAME; } + const QString& getName() const override { return NAME; } virtual void activate() override; virtual void deactivate() override; diff --git a/libraries/model-networking/CMakeLists.txt b/libraries/model-networking/CMakeLists.txt index 3613f76cff..f014885794 100644 --- a/libraries/model-networking/CMakeLists.txt +++ b/libraries/model-networking/CMakeLists.txt @@ -7,5 +7,5 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -link_hifi_libraries(shared networking gpu model) +link_hifi_libraries(shared networking gpu model fbx) diff --git a/libraries/octree/src/OctreeHeadlessViewer.h b/libraries/octree/src/OctreeHeadlessViewer.h index 5a17f48a19..605db15cd2 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.h +++ b/libraries/octree/src/OctreeHeadlessViewer.h @@ -30,9 +30,9 @@ class OctreeHeadlessViewer : public OctreeRenderer { public: OctreeHeadlessViewer(); virtual ~OctreeHeadlessViewer() {}; - virtual void renderElement(OctreeElementPointer element, RenderArgs* args) { /* swallow these */ } + virtual void renderElement(OctreeElementPointer element, RenderArgs* args) override { /* swallow these */ } - virtual void init(); + virtual void init() override ; virtual void render(RenderArgs* renderArgs) override { /* swallow these */ } void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; } @@ -58,7 +58,7 @@ public slots: // getters for LOD and PPS float getVoxelSizeScale() const { return _voxelSizeScale; } - int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } + int getBoundaryLevelAdjust() const override { return _boundaryLevelAdjust; } int getMaxPacketsPerSecond() const { return _maxPacketsPerSecond; } unsigned getOctreeElementsCount() const { return _tree->getOctreeElementsCount(); } diff --git a/libraries/plugins/src/plugins/PluginContainer.h b/libraries/plugins/src/plugins/PluginContainer.h index c2cba23392..f938758161 100644 --- a/libraries/plugins/src/plugins/PluginContainer.h +++ b/libraries/plugins/src/plugins/PluginContainer.h @@ -26,6 +26,7 @@ public: virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = false) = 0; virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) = 0; virtual void showDisplayPluginsTools() = 0; + virtual void requestReset() = 0; virtual QGLWidget* getPrimarySurface() = 0; virtual bool isForeground() = 0; }; diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index aa8946f62b..f5448bda5c 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -17,20 +17,30 @@ #include #include #include +#include #include "ProceduralShaders.h" -static const char* const UNIFORM_TIME_NAME= "iGlobalTime"; -static const char* const UNIFORM_SCALE_NAME = "iWorldScale"; +// Userdata parsing constants static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity"; - static const QString URL_KEY = "shaderUrl"; static const QString VERSION_KEY = "version"; static const QString UNIFORMS_KEY = "uniforms"; +static const QString CHANNELS_KEY = "channels"; + +// Shader replace strings static const std::string PROCEDURAL_BLOCK = "//PROCEDURAL_BLOCK"; static const std::string PROCEDURAL_COMMON_BLOCK = "//PROCEDURAL_COMMON_BLOCK"; static const std::string PROCEDURAL_VERSION = "//PROCEDURAL_VERSION"; +static const std::string STANDARD_UNIFORM_NAMES[Procedural::NUM_STANDARD_UNIFORMS] = { + "iDate", + "iGlobalTime", + "iFrameCount", + "iWorldScale", + "iWorldPosition", + "iChannelResolution" +}; // Example //{ @@ -100,7 +110,21 @@ void Procedural::parse(const QJsonObject& proceduralData) { { auto uniforms = proceduralData[UNIFORMS_KEY]; if (uniforms.isObject()) { - _uniforms = uniforms.toObject();; + _parsedUniforms = uniforms.toObject(); + } + } + + // Grab any textures + { + auto channels = proceduralData[CHANNELS_KEY]; + if (channels.isArray()) { + auto textureCache = DependencyManager::get(); + _parsedChannels = channels.toArray(); + size_t channelCount = std::min(MAX_PROCEDURAL_TEXTURE_CHANNELS, (size_t)_parsedChannels.size()); + for (size_t i = 0; i < channelCount; ++i) { + QString url = _parsedChannels.at(i).toString(); + _channels[i] = textureCache->getTexture(QUrl(url)); + } } } _enabled = true; @@ -111,20 +135,26 @@ bool Procedural::ready() { return false; } - if (!_shaderPath.isEmpty()) { - return true; + // Do we have a network or local shader + if (_shaderPath.isEmpty() && (!_networkShader || !_networkShader->isLoaded())) { + return false; } - if (_networkShader) { - return _networkShader->isLoaded(); + // Do we have textures, and if so, are they loaded? + for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) { + if (_channels[i] && !_channels[i]->isLoaded()) { + return false; + } } - return false; + return true; } -void Procedural::prepare(gpu::Batch& batch, const glm::vec3& size) { +void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size) { + _entityDimensions = size; + _entityPosition = position; if (_shaderUrl.isLocalFile()) { - auto lastModified = (quint64) QFileInfo(_shaderPath).lastModified().toMSecsSinceEpoch(); + auto lastModified = (quint64)QFileInfo(_shaderPath).lastModified().toMSecsSinceEpoch(); if (lastModified > _shaderModified) { QFile file(_shaderPath); file.open(QIODevice::ReadOnly); @@ -164,69 +194,183 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& size) { //qDebug() << "FragmentShader:\n" << fragmentShaderSource.c_str(); _fragmentShader = gpu::ShaderPointer(gpu::Shader::createPixel(fragmentShaderSource)); _shader = gpu::ShaderPointer(gpu::Shader::createProgram(_vertexShader, _fragmentShader)); - gpu::Shader::makeProgram(*_shader); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("iChannel0"), 0)); + slotBindings.insert(gpu::Shader::Binding(std::string("iChannel1"), 1)); + slotBindings.insert(gpu::Shader::Binding(std::string("iChannel2"), 2)); + slotBindings.insert(gpu::Shader::Binding(std::string("iChannel3"), 3)); + gpu::Shader::makeProgram(*_shader, slotBindings); + _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(_shader, _state)); - _timeSlot = _shader->getUniforms().findLocation(UNIFORM_TIME_NAME); - _scaleSlot = _shader->getUniforms().findLocation(UNIFORM_SCALE_NAME); + for (size_t i = 0; i < NUM_STANDARD_UNIFORMS; ++i) { + const std::string& name = STANDARD_UNIFORM_NAMES[i]; + _standardUniformSlots[i] = _shader->getUniforms().findLocation(name); + } _start = usecTimestampNow(); + _frameCount = 0; } batch.setPipeline(_pipeline); if (_pipelineDirty) { _pipelineDirty = false; - // Set any userdata specified uniforms - foreach(QString key, _uniforms.keys()) { - std::string uniformName = key.toLocal8Bit().data(); - int32_t slot = _shader->getUniforms().findLocation(uniformName); - if (gpu::Shader::INVALID_LOCATION == slot) { - continue; + setupUniforms(); + } + + + for (auto lambda : _uniforms) { + lambda(batch); + } + + static gpu::Sampler sampler; + static std::once_flag once; + std::call_once(once, [&] { + gpu::Sampler::Desc desc; + desc._filter = gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR; + }); + + for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) { + if (_channels[i] && _channels[i]->isLoaded()) { + auto gpuTexture = _channels[i]->getGPUTexture(); + if (gpuTexture) { + gpuTexture->setSampler(sampler); + gpuTexture->autoGenerateMips(-1); } - QJsonValue value = _uniforms[key]; - if (value.isDouble()) { - batch._glUniform1f(slot, value.toDouble()); - } else if (value.isArray()) { - auto valueArray = value.toArray(); - switch (valueArray.size()) { - case 0: - break; + batch.setResourceTexture(i, gpuTexture); + } + } +} - case 1: - batch._glUniform1f(slot, valueArray[0].toDouble()); - break; - case 2: - batch._glUniform2f(slot, - valueArray[0].toDouble(), - valueArray[1].toDouble()); - break; - case 3: - batch._glUniform3f(slot, - valueArray[0].toDouble(), - valueArray[1].toDouble(), - valueArray[2].toDouble()); - break; - case 4: - default: - batch._glUniform4f(slot, - valueArray[0].toDouble(), - valueArray[1].toDouble(), - valueArray[2].toDouble(), - valueArray[3].toDouble()); - break; +void Procedural::setupUniforms() { + _uniforms.clear(); + // Set any userdata specified uniforms + foreach(QString key, _parsedUniforms.keys()) { + std::string uniformName = key.toLocal8Bit().data(); + int32_t slot = _shader->getUniforms().findLocation(uniformName); + if (gpu::Shader::INVALID_LOCATION == slot) { + continue; + } + QJsonValue value = _parsedUniforms[key]; + if (value.isDouble()) { + float v = value.toDouble(); + _uniforms.push_back([=](gpu::Batch& batch) { + batch._glUniform1f(slot, v); + }); + } else if (value.isArray()) { + auto valueArray = value.toArray(); + switch (valueArray.size()) { + case 0: + break; + case 1: { + float v = valueArray[0].toDouble(); + _uniforms.push_back([=](gpu::Batch& batch) { + batch._glUniform1f(slot, v); + }); + break; + } + + case 2: { + glm::vec2 v{ valueArray[0].toDouble(), valueArray[1].toDouble() }; + _uniforms.push_back([=](gpu::Batch& batch) { + batch._glUniform2f(slot, v.x, v.y); + }); + break; + } + + case 3: { + glm::vec3 v{ + valueArray[0].toDouble(), + valueArray[1].toDouble(), + valueArray[2].toDouble(), + }; + _uniforms.push_back([=](gpu::Batch& batch) { + batch._glUniform3f(slot, v.x, v.y, v.z); + }); + break; + } + + default: + case 4: { + glm::vec4 v{ + valueArray[0].toDouble(), + valueArray[1].toDouble(), + valueArray[2].toDouble(), + valueArray[3].toDouble(), + }; + _uniforms.push_back([=](gpu::Batch& batch) { + batch._glUniform4f(slot, v.x, v.y, v.z, v.w); + }); + break; } - valueArray.size(); } } } - // Minimize floating point error by doing an integer division to milliseconds, before the floating point division to seconds - float time = (float)((usecTimestampNow() - _start) / USECS_PER_MSEC) / MSECS_PER_SECOND; - batch._glUniform1f(_timeSlot, time); - // FIXME move into the 'set once' section, since this doesn't change over time - batch._glUniform3f(_scaleSlot, size.x, size.y, size.z); -} + if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[TIME]) { + _uniforms.push_back([=](gpu::Batch& batch) { + // Minimize floating point error by doing an integer division to milliseconds, before the floating point division to seconds + float time = (float)((usecTimestampNow() - _start) / USECS_PER_MSEC) / MSECS_PER_SECOND; + batch._glUniform(_standardUniformSlots[TIME], time); + }); + } + if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[DATE]) { + _uniforms.push_back([=](gpu::Batch& batch) { + QDateTime now = QDateTime::currentDateTimeUtc(); + QDate date = now.date(); + QTime time = now.time(); + vec4 v; + v.x = date.year(); + // Shadertoy month is 0 based + v.y = date.month() - 1; + // But not the day... go figure + v.z = date.day(); + v.w = (time.hour() * 3600) + (time.minute() * 60) + time.second(); + batch._glUniform(_standardUniformSlots[DATE], v); + }); + } + + if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[FRAME_COUNT]) { + _uniforms.push_back([=](gpu::Batch& batch) { + batch._glUniform(_standardUniformSlots[FRAME_COUNT], ++_frameCount); + }); + } + + if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[SCALE]) { + // FIXME move into the 'set once' section, since this doesn't change over time + _uniforms.push_back([=](gpu::Batch& batch) { + batch._glUniform(_standardUniformSlots[SCALE], _entityDimensions); + }); + } + + if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[SCALE]) { + // FIXME move into the 'set once' section, since this doesn't change over time + _uniforms.push_back([=](gpu::Batch& batch) { + batch._glUniform(_standardUniformSlots[SCALE], _entityDimensions); + }); + } + + if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[POSITION]) { + // FIXME move into the 'set once' section, since this doesn't change over time + _uniforms.push_back([=](gpu::Batch& batch) { + batch._glUniform(_standardUniformSlots[POSITION], _entityPosition); + }); + } + + if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[CHANNEL_RESOLUTION]) { + _uniforms.push_back([=](gpu::Batch& batch) { + vec3 channelSizes[MAX_PROCEDURAL_TEXTURE_CHANNELS]; + for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) { + if (_channels[i]) { + channelSizes[i] = vec3(_channels[i]->getWidth(), _channels[i]->getHeight(), 1.0); + } + } + batch._glUniform3fv(_standardUniformSlots[CHANNEL_RESOLUTION], MAX_PROCEDURAL_TEXTURE_CHANNELS, &channelSizes[0].x); + }); + } +} glm::vec4 Procedural::getColor(const glm::vec4& entityColor) { if (_version == 1) { diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index 5b3f2b4742..1b02fbd435 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -14,11 +14,16 @@ #include #include #include +#include #include #include #include #include +#include + +using UniformLambdas = std::list>; +const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 }; // FIXME better encapsulation // FIXME better mechanism for extending to things rendered using shaders other than simple.slv @@ -29,7 +34,8 @@ struct Procedural { void parse(const QString& userDataJson); void parse(const QJsonObject&); bool ready(); - void prepare(gpu::Batch& batch, const glm::vec3& size); + void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size); + void setupUniforms(); glm::vec4 getColor(const glm::vec4& entityColor); bool _enabled{ false }; @@ -43,17 +49,34 @@ struct Procedural { QUrl _shaderUrl; quint64 _shaderModified{ 0 }; bool _pipelineDirty{ true }; - int32_t _timeSlot{ gpu::Shader::INVALID_LOCATION }; - int32_t _scaleSlot{ gpu::Shader::INVALID_LOCATION }; - uint64_t _start{ 0 }; - NetworkShaderPointer _networkShader; - QJsonObject _uniforms; + enum StandardUniforms { + DATE, + TIME, + FRAME_COUNT, + SCALE, + POSITION, + CHANNEL_RESOLUTION, + NUM_STANDARD_UNIFORMS + }; + + int32_t _standardUniformSlots[NUM_STANDARD_UNIFORMS]; + + uint64_t _start{ 0 }; + int32_t _frameCount{ 0 }; + NetworkShaderPointer _networkShader; + QJsonObject _parsedUniforms; + QJsonArray _parsedChannels; + + UniformLambdas _uniforms; + NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS]; gpu::PipelinePointer _pipeline; gpu::ShaderPointer _vertexShader; gpu::ShaderPointer _fragmentShader; gpu::ShaderPointer _shader; gpu::StatePointer _state; + glm::vec3 _entityDimensions; + glm::vec3 _entityPosition; }; #endif diff --git a/libraries/procedural/src/procedural/ProceduralShaders.h b/libraries/procedural/src/procedural/ProceduralShaders.h index 9943a322cc..eddf53cb09 100644 --- a/libraries/procedural/src/procedural/ProceduralShaders.h +++ b/libraries/procedural/src/procedural/ProceduralShaders.h @@ -262,15 +262,39 @@ float snoise(vec2 v) { return 130.0 * dot(m, g); } -// TODO add more uniforms -uniform float iGlobalTime; // shader playback time (in seconds) -uniform vec3 iWorldScale; // the dimensions of the object being rendered - -// TODO add support for textures -// TODO document available inputs other than the uniforms -// TODO provide world scale in addition to the untransformed position +// shader playback time (in seconds) +uniform float iGlobalTime; +// the dimensions of the object being rendered +uniform vec3 iWorldScale; #define PROCEDURAL 1 //PROCEDURAL_VERSION + +#ifdef PROCEDURAL_V1 + +#else + +// Unimplemented uniforms +// Resolution doesn't make sense in the VR context +const vec3 iResolution = vec3(1.0); +// Mouse functions not enabled currently +const vec4 iMouse = vec4(0.0); +// No support for audio input +const float iSampleRate = 1.0; +// No support for video input +const vec4 iChannelTime = vec4(0.0); + + +uniform vec4 iDate; +uniform int iFrameCount; +uniform vec3 iWorldPosition; +uniform vec3 iChannelResolution[4]; +uniform sampler2D iChannel0; +uniform sampler2D iChannel1; +uniform sampler2D iChannel2; +uniform sampler2D iChannel3; + +#endif + )SHADER"; diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index 1c7e7e457c..022bf7898a 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -1,20 +1,20 @@ -// -// ProceduralSkybox.cpp -// libraries/procedural/src/procedural -// -// Created by Sam Gateau on 9/21/2015. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#include "ProceduralSkybox.h" - - -#include -#include -#include - +// +// ProceduralSkybox.cpp +// libraries/procedural/src/procedural +// +// Created by Sam Gateau on 9/21/2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "ProceduralSkybox.h" + + +#include +#include +#include + #include "ProceduralSkybox_vert.h" #include "ProceduralSkybox_frag.h" @@ -74,7 +74,7 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, batch.setResourceTexture(0, skybox.getCubemap()); } - skybox._procedural->prepare(batch, glm::vec3(1)); + skybox._procedural->prepare(batch, glm::vec3(0), glm::vec3(1)); batch.draw(gpu::TRIANGLE_STRIP, 4); } } diff --git a/libraries/shared/src/LogHandler.cpp b/libraries/shared/src/LogHandler.cpp index d1f23531cb..22ea12c1b3 100644 --- a/libraries/shared/src/LogHandler.cpp +++ b/libraries/shared/src/LogHandler.cpp @@ -120,11 +120,11 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont } // log prefix is in the following format - // [DEBUG] [TIMESTAMP] [PID] [TARGET] logged string + // [TIMESTAMP] [DEBUG] [PID] [TARGET] logged string - QString prefixString = QString("[%1]").arg(stringForLogType(type)); + QString prefixString = QString("[%1]").arg(QDateTime::currentDateTime().toString(DATE_STRING_FORMAT)); - prefixString.append(QString(" [%1]").arg(QDateTime::currentDateTime().toString(DATE_STRING_FORMAT))); + prefixString.append(QString(" [%1]").arg(stringForLogType(type))); if (_shouldOutputPID) { prefixString.append(QString(" [%1]").arg(QCoreApplication::instance()->applicationPid())); diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index 6611e8f343..3879d0b029 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -260,7 +260,7 @@ protected: } - void keyPressEvent(QKeyEvent* event) { + void keyPressEvent(QKeyEvent* event) override { _altPressed = Qt::Key_Alt == event->key(); switch (event->key()) { case Qt::Key_B: @@ -292,13 +292,13 @@ protected: QWindow::keyPressEvent(event); } QQmlContext* menuContext{ nullptr }; - void keyReleaseEvent(QKeyEvent *event) { + void keyReleaseEvent(QKeyEvent *event) override { if (_altPressed && Qt::Key_Alt == event->key()) { VrMenu::toggle(); } } - void moveEvent(QMoveEvent* event) { + void moveEvent(QMoveEvent* event) override { static qreal oldPixelRatio = 0.0; if (devicePixelRatio() != oldPixelRatio) { oldPixelRatio = devicePixelRatio(); diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index f08d62e087..c2f1d8d393 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -71,13 +71,13 @@ function createAllToys() { createCombinedArmChair({ x: 549.29, - y: 495.05, + y: 494.9, z: 508.22 }); createPottedPlant({ x: 554.26, - y: 495.23, + y: 495.2, z: 504.53 }); @@ -98,7 +98,7 @@ function createAllToys() { function deleteAllToys() { var entities = Entities.findEntities(MyAvatar.position, 100); - entities.forEach(function (entity) { + entities.forEach(function(entity) { //params: customKey, id, defaultValue var shouldReset = getEntityCustomData(resetKey, entity, {}).resetMe; if (shouldReset === true) { @@ -447,8 +447,7 @@ function createPingPongBallGun() { var pingPongGun = Entities.addEntity({ type: "Model", modelURL: MODEL_URL, - shapeType: 'compound', - compoundShapeURL: COLLISION_HULL_URL, + shapeType: 'box', script: scriptURL, position: position, rotation: rotation, @@ -458,9 +457,9 @@ function createPingPongBallGun() { z: 0 }, dimensions: { - x: 0.67, - y: 0.14, - z: 0.09 + x: 0.08, + y: 0.21, + z: 0.47 }, collisionsWillMove: true, }); @@ -468,8 +467,6 @@ function createPingPongBallGun() { setEntityCustomData(resetKey, pingPongGun, { resetMe: true }); - - } function createBasketballHoop() {