diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index f38d4a1008..20b0003cc7 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -10,72 +10,71 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - Script.include("../libraries/utils.js"); -var RADIUS_FACTOR = 4; -var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK"); -var rightTriggerAction = RIGHT_HAND_CLICK; +///////////////////////////////////////////////////////////////// +// +// these tune time-averaging and "on" value for analog trigger +// + +var TRIGGER_SMOOTH_RATIO = 0.0; // 0.0 disables smoothing of trigger value +var TRIGGER_ON_VALUE = 0.2; + +///////////////////////////////////////////////////////////////// +// +// distant manipulation +// + +var DISTANCE_HOLDING_RADIUS_FACTOR = 5; // multiplied by distance between hand and object +var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position +var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did +var NO_INTERSECT_COLOR = {red: 10, green: 10, blue: 255}; // line color when pick misses +var INTERSECT_COLOR = {red: 250, green: 10, blue: 10}; // line color when pick hits +var LINE_ENTITY_DIMENSIONS = {x: 1000, y: 1000, z: 1000}; +var LINE_LENGTH = 500; + + +///////////////////////////////////////////////////////////////// +// +// near grabbing +// + +var GRAB_RADIUS = 0.3; // if the ray misses but an object is this close, it will still be selected +var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position +var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. +var NEAR_PICK_MAX_DISTANCE = 0.6; // max length of pick-ray for close grabbing to be selected +var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things + +///////////////////////////////////////////////////////////////// +// +// other constants +// + +var RIGHT_HAND = 1; +var LEFT_HAND = 0; + +var ZERO_VEC = {x: 0, y: 0, z: 0}; +var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; +var MSEC_PER_SEC = 1000.0; + +// these control how long an abandoned pointer line will hang around +var startTime = Date.now(); +var LIFETIME = 10; + +// states for the state machine +var STATE_SEARCHING = 0; +var STATE_DISTANCE_HOLDING = 1; +var STATE_CONTINUE_DISTANCE_HOLDING = 2; +var STATE_NEAR_GRABBING = 3; +var STATE_CONTINUE_NEAR_GRABBING = 4; +var STATE_RELEASE = 5; var GRAB_USER_DATA_KEY = "grabKey"; -var LEFT_HAND_CLICK = Controller.findAction("LEFT_HAND_CLICK"); -var leftTriggerAction = LEFT_HAND_CLICK; - -var LIFETIME = 10; -var EXTRA_TIME = 5; -var POINTER_CHECK_TIME = 5000; - -var ZERO_VEC = { - x: 0, - y: 0, - z: 0 -} -var LINE_LENGTH = 500; -var THICK_LINE_WIDTH = 7; -var THIN_LINE_WIDTH = 2; - -var NO_INTERSECT_COLOR = { - red: 10, - green: 10, - blue: 255 -}; -var INTERSECT_COLOR = { - red: 250, - green: 10, - blue: 10 -}; - -var GRAB_RADIUS = 0.3; - -var GRAB_COLOR = { - red: 250, - green: 10, - blue: 250 -}; -var SHOW_LINE_THRESHOLD = 0.2; -var DISTANCE_HOLD_THRESHOLD = 0.8; - -var right4Action = 18; -var left4Action = 17; - -var RIGHT = 1; -var LEFT = 0; -var rightController = new controller(RIGHT, rightTriggerAction, right4Action, "right"); -var leftController = new controller(LEFT, leftTriggerAction, left4Action, "left"); -var startTime = Date.now(); - - -//Need to wait before calling these methods for some reason... -Script.setTimeout(function() { - rightController.checkPointer(); - leftController.checkPointer(); -}, 100) - -function controller(side, triggerAction, pullAction, hand) { +function controller(hand, triggerAction) { this.hand = hand; - if (hand === "right") { + if (this.hand === RIGHT_HAND) { this.getHandPosition = MyAvatar.getRightPalmPosition; this.getHandRotation = MyAvatar.getRightPalmRotation; } else { @@ -83,309 +82,345 @@ function controller(side, triggerAction, pullAction, hand) { this.getHandRotation = MyAvatar.getLeftPalmRotation; } this.triggerAction = triggerAction; - this.pullAction = pullAction; - this.actionID = null; - this.distanceHolding = false; - this.closeGrabbing = false; - this.triggerValue = 0; - this.prevTriggerValue = 0; - this.palm = 2 * side; - this.tip = 2 * side + 1; - this.pointer = null; -} + this.palm = 2 * hand; + // this.tip = 2 * hand + 1; // unused, but I'm leaving this here for fear it will be needed + this.actionID = null; // action this script created... + this.grabbedEntity = null; // on this entity. + this.grabbedVelocity = ZERO_VEC; // rolling average of held object's velocity + this.state = 0; + this.pointer = null; // entity-id of line object + this.triggerValue = 0; // rolling average of trigger value -controller.prototype.updateLine = function() { - if (this.pointer != null) { - if (Entities.getEntityProperties(this.pointer).id != this.pointer) { - this.pointer = null; + this.update = function() { + switch(this.state) { + case STATE_SEARCHING: + this.search(); + break; + case STATE_DISTANCE_HOLDING: + this.distanceHolding(); + break; + case STATE_CONTINUE_DISTANCE_HOLDING: + this.continueDistanceHolding(); + break; + case STATE_NEAR_GRABBING: + this.nearGrabbing(); + break; + case STATE_CONTINUE_NEAR_GRABBING: + this.continueNearGrabbing(); + break; + case STATE_RELEASE: + this.release(); + break; } } - if (this.pointer == null) { - this.lineCreationTime = Date.now(); - this.pointer = Entities.addEntity({ - type: "Line", - name: "pointer", - color: NO_INTERSECT_COLOR, - dimensions: { - x: 1000, - y: 1000, - z: 1000 - }, - visible: true, - lifetime: LIFETIME - }); - } - var handPosition = this.getHandPosition(); - var direction = Quat.getUp(this.getHandRotation()); - - //only check if we havent already grabbed an object - if (this.distanceHolding) { - Entities.editEntity(this.pointer, { - position: handPosition, - linePoints: [ ZERO_VEC, Vec3.subtract(Entities.getEntityProperties(this.grabbedEntity).position, handPosition) ], - lifetime: (Date.now() - startTime) / 1000.0 + LIFETIME - }); - - return; - } - - Entities.editEntity(this.pointer, { - position: handPosition, - linePoints: [ ZERO_VEC, Vec3.multiply(direction, LINE_LENGTH) ], - lifetime: (Date.now() - startTime) / 1000.0 + LIFETIME - }); - - if (this.checkForIntersections(handPosition, direction)) { - Entities.editEntity(this.pointer, { - color: INTERSECT_COLOR, - }); - } else { - Entities.editEntity(this.pointer, { - color: NO_INTERSECT_COLOR, - }); - } -} - - -controller.prototype.checkPointer = function() { - var self = this; - Script.setTimeout(function() { - var props = Entities.getEntityProperties(self.pointer); - Entities.editEntity(self.pointer, { - lifetime: (Date.now() - startTime) / 1000.0 + LIFETIME - }); - self.checkPointer(); - }, POINTER_CHECK_TIME); -} - -controller.prototype.checkForIntersections = function(origin, direction) { - var pickRay = { - origin: origin, - direction: direction - }; - - var intersection = Entities.findRayIntersection(pickRay, true); - if (intersection.intersects && intersection.properties.collisionsWillMove === 1) { - var handPosition = Controller.getSpatialControlPosition(this.palm); - this.distanceToEntity = Vec3.distance(handPosition, intersection.properties.position); - var intersectionDistance = Vec3.distance(handPosition, intersection.intersection); - - if (intersectionDistance < 0.6) { - //We are grabbing an entity, so let it know we've grabbed it - this.grabbedEntity = intersection.entityID; - this.activateEntity(this.grabbedEntity); - this.hidePointer(); - this.shouldDisplayLine = false; - this.grabEntity(); - return true; + this.lineOn = function(closePoint, farPoint, color) { + // draw a line + if (this.pointer == null) { + this.pointer = Entities.addEntity({ + type: "Line", + name: "pointer", + dimensions: LINE_ENTITY_DIMENSIONS, + visible: true, + position: closePoint, + linePoints: [ ZERO_VEC, farPoint ], + color: color, + lifetime: LIFETIME + }); } else { Entities.editEntity(this.pointer, { - linePoints: [ - ZERO_VEC, - Vec3.multiply(direction, this.distanceToEntity) - ] + position: closePoint, + linePoints: [ ZERO_VEC, farPoint ], + color: color, + lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME }); + } + } + + + this.lineOff = function() { + if (this.pointer != null) { + Entities.deleteEntity(this.pointer); + } + this.pointer = null; + } + + + this.triggerSmoothedSqueezed = function() { + var triggerValue = Controller.getActionValue(this.triggerAction); + // smooth out trigger value + this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + + (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); + return this.triggerValue > TRIGGER_ON_VALUE; + } + + + this.triggerSqueezed = function() { + var triggerValue = Controller.getActionValue(this.triggerAction); + return triggerValue > TRIGGER_ON_VALUE; + } + + + this.search = function() { + if (!this.triggerSmoothedSqueezed()) { + this.state = STATE_RELEASE; + return; + } + + // the trigger is being pressed, do a ray test + var handPosition = this.getHandPosition(); + var pickRay = {origin: handPosition, direction: Quat.getUp(this.getHandRotation())}; + var intersection = Entities.findRayIntersection(pickRay, true); + if (intersection.intersects && + intersection.properties.collisionsWillMove === 1 && + intersection.properties.locked === 0) { + // the ray is intersecting something we can move. + var handControllerPosition = Controller.getSpatialControlPosition(this.palm); + var intersectionDistance = Vec3.distance(handControllerPosition, intersection.intersection); this.grabbedEntity = intersection.entityID; - return true; + 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; + } else { + // 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. + var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); + var minDistance = GRAB_RADIUS; + var grabbedEntity = null; + for (var i = 0; i < nearbyEntities.length; i++) { + var props = Entities.getEntityProperties(nearbyEntities[i]); + var distance = Vec3.distance(props.position, handPosition); + if (distance < minDistance && props.name !== "pointer" && + props.collisionsWillMove === 1 && + props.locked === 0) { + this.grabbedEntity = nearbyEntities[i]; + minDistance = distance; + } + } + if (this.grabbedEntity === null) { + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + } else { + this.state = STATE_NEAR_GRABBING; + } } } - return false; -} -controller.prototype.attemptMove = function() { - if (this.grabbedEntity || this.distanceHolding) { - var handPosition = Controller.getSpatialControlPosition(this.palm); + 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"]); - this.distanceHolding = true; - if (this.actionID === null) { - this.currentObjectPosition = Entities.getEntityProperties(this.grabbedEntity).position; - this.currentObjectRotation = Entities.getEntityProperties(this.grabbedEntity).rotation; + // add the action and initialize some variables + this.currentObjectPosition = grabbedProperties.position; + this.currentObjectRotation = grabbedProperties.rotation; + this.handPreviousPosition = handControllerPosition; + this.handPreviousRotation = handRotation; - this.handPreviousPosition = handPosition; - this.handPreviousRotation = handRotation; + this.actionID = Entities.addAction("spring", this.grabbedEntity, { + targetPosition: this.currentObjectPosition, + linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + targetRotation: this.currentObjectRotation, + angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME + }); + if (this.actionID == NULL_ACTION_ID) { + this.actionID = null; + } - this.actionID = Entities.addAction("spring", this.grabbedEntity, { - targetPosition: this.currentObjectPosition, - linearTimeScale: .1, - targetRotation: this.currentObjectRotation, - angularTimeScale: .1 - }); - } else { - var radius = Math.max(Vec3.distance(this.currentObjectPosition, handPosition) * RADIUS_FACTOR, 1.0); + if (this.actionID != null) { + this.state = STATE_CONTINUE_DISTANCE_HOLDING; + this.activateEntity(this.grabbedEntity); + Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab"); - var handMoved = Vec3.subtract(handPosition, this.handPreviousPosition); - this.handPreviousPosition = handPosition; - var superHandMoved = Vec3.multiply(handMoved, radius); - this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); - - // ---------------- this tracks hand rotation - // var handChange = Quat.multiply(handRotation, Quat.inverse(this.handPreviousRotation)); - // this.handPreviousRotation = handRotation; - // this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); - // ---------------- - - // ---------------- this doubles hand rotation - var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, 2.0), - Quat.inverse(this.handPreviousRotation)); - this.handPreviousRotation = handRotation; - this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); - // ---------------- - - - Entities.updateAction(this.grabbedEntity, this.actionID, { - targetPosition: this.currentObjectPosition, linearTimeScale: .1, - targetRotation: this.currentObjectRotation, angularTimeScale: .1 - }); + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } } } -} - -controller.prototype.showPointer = function() { - Entities.editEntity(this.pointer, { - visible: true - }); - -} - -controller.prototype.hidePointer = function() { - Entities.editEntity(this.pointer, { - visible: false - }); -} -controller.prototype.letGo = function() { - if (this.grabbedEntity && this.actionID) { - this.deactivateEntity(this.grabbedEntity); - Entities.deleteAction(this.grabbedEntity, this.actionID); - } - this.grabbedEntity = null; - this.actionID = null; - this.distanceHolding = false; - this.closeGrabbing = false; -} - -controller.prototype.update = function() { - this.triggerValue = Controller.getActionValue(this.triggerAction); - if (this.triggerValue > SHOW_LINE_THRESHOLD && this.prevTriggerValue < SHOW_LINE_THRESHOLD) { - //First check if an object is within close range and then run the close grabbing logic - if (this.checkForInRangeObject()) { - this.grabEntity(); - } else { - this.showPointer(); - this.shouldDisplayLine = true; + this.continueDistanceHolding = function() { + if (!this.triggerSmoothedSqueezed()) { + this.state = STATE_RELEASE; + return; } - } else if (this.triggerValue < SHOW_LINE_THRESHOLD && this.prevTriggerValue > SHOW_LINE_THRESHOLD) { - this.hidePointer(); - this.letGo(); - this.shouldDisplayLine = false; + + var handPosition = this.getHandPosition(); + var handControllerPosition = Controller.getSpatialControlPosition(this.palm); + var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position","rotation"]); + + this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); + + // the action was set up on a previous call. update the targets. + var radius = Math.max(Vec3.distance(this.currentObjectPosition, + handControllerPosition) * DISTANCE_HOLDING_RADIUS_FACTOR, + DISTANCE_HOLDING_RADIUS_FACTOR); + + var handMoved = Vec3.subtract(handControllerPosition, this.handPreviousPosition); + this.handPreviousPosition = handControllerPosition; + var superHandMoved = Vec3.multiply(handMoved, radius); + this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); + + // this doubles hand rotation + var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, + DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), + Quat.inverse(this.handPreviousRotation)); + this.handPreviousRotation = handRotation; + this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); + + Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); + + Entities.updateAction(this.grabbedEntity, this.actionID, { + targetPosition: this.currentObjectPosition, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + targetRotation: this.currentObjectRotation, angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME + }); } - if (this.shouldDisplayLine) { - this.updateLine(); - } - if (this.triggerValue > DISTANCE_HOLD_THRESHOLD && !this.closeGrabbing) { - this.attemptMove(); - } - this.prevTriggerValue = this.triggerValue; -} - -controller.prototype.grabEntity = function() { - var handRotation = this.getHandRotation(); - var handPosition = this.getHandPosition(); - this.closeGrabbing = true; - //check if our entity has instructions on how to be grabbed, otherwise, just use default relative position and rotation - var userData = getEntityUserData(this.grabbedEntity); - - var objectRotation = Entities.getEntityProperties(this.grabbedEntity).rotation; - var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - - var objectPosition = Entities.getEntityProperties(this.grabbedEntity).position; - var offset = Vec3.subtract(objectPosition, handPosition); - var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); - - var relativePosition = offsetPosition; - var relativeRotation = offsetRotation; - if (userData.grabFrame) { - if (userData.grabFrame.relativePosition) { - relativePosition = userData.grabFrame.relativePosition; + this.nearGrabbing = function() { + if (!this.triggerSmoothedSqueezed()) { + this.state = STATE_RELEASE; + return; } - if (userData.grabFrame.relativeRotation) { - relativeRotation = userData.grabFrame.relativeRotation; - } - } - this.actionID = Entities.addAction("hold", this.grabbedEntity, { - hand: this.hand, - timeScale: 0.05, - relativePosition: relativePosition, - relativeRotation: relativeRotation - }); -} + this.lineOff(); -controller.prototype.checkForInRangeObject = function() { - var handPosition = Controller.getSpatialControlPosition(this.palm); - var entities = Entities.findEntities(handPosition, GRAB_RADIUS); - var minDistance = GRAB_RADIUS; - var grabbedEntity = null; - //Get nearby entities and assign nearest - for (var i = 0; i < entities.length; i++) { - var props = Entities.getEntityProperties(entities[i]); - var distance = Vec3.distance(props.position, handPosition); - if (distance < minDistance && props.name !== "pointer" && props.collisionsWillMove === 1) { - grabbedEntity = entities[i]; - minDistance = distance; - } - } - if (grabbedEntity === null) { - return false; - } else { - //We are grabbing an entity, so let it know we've grabbed it - this.grabbedEntity = grabbedEntity; this.activateEntity(this.grabbedEntity); - return true; + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, "position"); + + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + + var objectRotation = grabbedProperties.rotation; + var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + + currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); + var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); + + this.actionID = Entities.addAction("hold", this.grabbedEntity, { + hand: this.hand == RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: offsetPosition, + relativeRotation: offsetRotation + }); + if (this.actionID == NULL_ACTION_ID) { + this.actionID = null; + } else { + this.state = STATE_CONTINUE_NEAR_GRABBING; + Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } + } + + this.currentHandControllerPosition = Controller.getSpatialControlPosition(this.palm); + this.currentObjectTime = Date.now(); + } + + + this.continueNearGrabbing = function() { + if (!this.triggerSmoothedSqueezed()) { + this.state = STATE_RELEASE; + return; + } + + // keep track of the measured velocity of the held object + var handControllerPosition = Controller.getSpatialControlPosition(this.palm); + var now = Date.now(); + + var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerPosition); // meters + var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds + + if (deltaTime > 0.0 && !vec3equal(this.currentHandControllerPosition, handControllerPosition)) { + var grabbedVelocity = Vec3.multiply(deltaPosition, 1.0 / deltaTime); + // don't update grabbedVelocity if the trigger is off. the smoothing of the trigger + // value would otherwise give the held object time to slow down. + if (this.triggerSqueezed()) { + this.grabbedVelocity = + Vec3.sum(Vec3.multiply(this.grabbedVelocity, + (1.0 - NEAR_GRABBING_VELOCITY_SMOOTH_RATIO)), + Vec3.multiply(grabbedVelocity, NEAR_GRABBING_VELOCITY_SMOOTH_RATIO)); + } + } + + this.currentHandControllerPosition = handControllerPosition; + this.currentObjectTime = now; + Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); + } + + + this.release = function() { + this.lineOff(); + + if (this.grabbedEntity != null && this.actionID != null) { + Entities.deleteAction(this.grabbedEntity, this.actionID); + } + + // the action will tend to quickly bring an object's velocity to zero. now that + // the action is gone, set the objects velocity to something the holder might expect. + Entities.editEntity(this.grabbedEntity, + {velocity: Vec3.multiply(this.grabbedVelocity, RELEASE_VELOCITY_MULTIPLIER)} + ); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + this.deactivateEntity(this.grabbedEntity); + + this.grabbedVelocity = ZERO_VEC; + this.grabbedEntity = null; + this.actionID = null; + this.state = STATE_SEARCHING; + } + + + this.cleanup = function() { + release(); + } + + this.activateEntity = function(entity) { + var data = { + activated: true, + avatarId: MyAvatar.sessionUUID + }; + setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data); + } + + this.deactivateEntity = function(entity) { + var data = { + activated: false, + avatarId: null + }; + setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data); } } -controller.prototype.activateEntity = function(entity) { - var data = { - activated: true, - avatarId: MyAvatar.sessionUUID - }; - setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data); -} -controller.prototype.deactivateEntity = function(entity) { - var data = { - activated: false, - avatarId: null - }; - setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data); -} +var rightController = new controller(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK")); +var leftController = new controller(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK")); -controller.prototype.cleanup = function() { - Entities.deleteEntity(this.pointer); - if (this.grabbedEntity) { - Entities.deleteAction(this.grabbedEntity, this.actionID); - } -} function update() { rightController.update(); leftController.update(); } + function cleanup() { rightController.cleanup(); leftController.cleanup(); } + Script.scriptEnding.connect(cleanup); Script.update.connect(update) diff --git a/examples/entityScripts/detectGrabExample.js b/examples/entityScripts/detectGrabExample.js index cdc79e119d..7e97572159 100644 --- a/examples/entityScripts/detectGrabExample.js +++ b/examples/entityScripts/detectGrabExample.js @@ -12,7 +12,6 @@ // (function() { - Script.include("../libraries/utils.js"); var _this; @@ -24,39 +23,29 @@ DetectGrabbed.prototype = { - // update() will be called regulary, because we've hooked the update signal in our preload() function - // we will check out userData for the grabData. In the case of the hydraGrab script, it will tell us - // if we're currently being grabbed and if the person grabbing us is the current interfaces avatar. - // we will watch this for state changes and print out if we're being grabbed or released when it changes. - update: function() { - var GRAB_USER_DATA_KEY = "grabKey"; + setRightHand: function () { + print("I am being held in a right hand... entity:" + this.entityID); + }, + setLeftHand: function () { + print("I am being held in a left hand... entity:" + this.entityID); + }, - // because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID - var entityID = _this.entityID; + startDistantGrab: function () { + print("I am being distance held... entity:" + this.entityID); + }, + continueDistantGrab: function () { + print("I continue to be distance held... entity:" + this.entityID); + }, - // we want to assume that if there is no grab data, then we are not being grabbed - var defaultGrabData = { activated: false, avatarId: null }; + startNearGrab: function () { + print("I was just grabbed... entity:" + this.entityID); + }, + continueNearGrab: function () { + print("I am still being grabbed... entity:" + this.entityID); + }, - // this handy function getEntityCustomData() is available in utils.js and it will return just the specific section - // of user data we asked for. If it's not available it returns our default data. - var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, defaultGrabData); - - // if the grabData says we're being grabbed, and the owner ID is our session, then we are being grabbed by this interface - if (grabData.activated && grabData.avatarId == MyAvatar.sessionUUID) { - - // remember we're being grabbed so we can detect being released - _this.beingGrabbed = true; - - // print out that we're being grabbed - print("I'm being grabbed..."); - - } else if (_this.beingGrabbed) { - - // if we are not being grabbed, and we previously were, then we were just released, remember that - // and print out a message - _this.beingGrabbed = false; - print("I'm was released..."); - } + release: function () { + print("I was released... entity:" + this.entityID); }, // preload() will be called when the entity has become visible (or known) to the interface @@ -65,14 +54,6 @@ // * connecting to the update signal so we can check our grabbed state preload: function(entityID) { this.entityID = entityID; - Script.update.connect(this.update); - }, - - // unload() will be called when our entity is no longer available. It may be because we were deleted, - // or because we've left the domain or quit the application. In all cases we want to unhook our connection - // to the update signal - unload: function(entityID) { - Script.update.disconnect(this.update); }, }; diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index 1275975fd8..f6f635c73a 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -6,11 +6,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -vec3toStr = function (v, digits) { +vec3toStr = function(v, digits) { if (!digits) { digits = 3; } return "{ " + v.x.toFixed(digits) + ", " + v.y.toFixed(digits) + ", " + v.z.toFixed(digits)+ " }"; } +vec3equal = function(v0, v1) { + return (v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z); +} colorMix = function(colorA, colorB, mix) { var result = {}; @@ -60,7 +63,7 @@ setEntityUserData = function(id, data) { // FIXME do non-destructive modification of the existing user data getEntityUserData = function(id) { var results = null; - var properties = Entities.getEntityProperties(id); + var properties = Entities.getEntityProperties(id, "userData"); if (properties.userData) { try { results = JSON.parse(properties.userData); @@ -175,4 +178,4 @@ pointInExtents = function(point, minPoint, maxPoint) { return (point.x >= minPoint.x && point.x <= maxPoint.x) && (point.y >= minPoint.y && point.y <= maxPoint.y) && (point.z >= minPoint.z && point.z <= maxPoint.z); -} \ No newline at end of file +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a313308023..1b6be53e83 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1063,7 +1063,7 @@ void Application::paintGL() { auto lodManager = DependencyManager::get(); - RenderArgs renderArgs(_gpuContext, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(), + RenderArgs renderArgs(_gpuContext, getEntities(), getViewFrustum(), lodManager->getOctreeSizeScale(), lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); @@ -3562,7 +3562,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se (RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP); } renderArgs->_debugFlags = renderDebugFlags; - _entities.render(renderArgs); //ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, pendingChanges); } } @@ -3648,14 +3647,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se sceneInterface->setEngineDrawnOverlay3DItems(engineRC->_numDrawnOverlay3DItems); } - if (!selfAvatarOnly) { - // give external parties a change to hook in - { - PerformanceTimer perfTimer("inWorldInterface"); - emit renderingInWorldInterface(); - } - } - activeRenderingThread = nullptr; } diff --git a/interface/src/Application.h b/interface/src/Application.h index 6213dae4fa..0a591bf500 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -347,9 +347,6 @@ signals: /// Fired when we're simulating; allows external parties to hook in. void simulating(float deltaTime); - /// Fired when we're rendering in-world interface elements; allows external parties to hook in. - void renderingInWorldInterface(); - /// Fired when the import window is closed void importDone(); diff --git a/interface/src/scripting/GlobalServicesScriptingInterface.cpp b/interface/src/scripting/GlobalServicesScriptingInterface.cpp index 26bee34d75..668bd92664 100644 --- a/interface/src/scripting/GlobalServicesScriptingInterface.cpp +++ b/interface/src/scripting/GlobalServicesScriptingInterface.cpp @@ -22,8 +22,10 @@ GlobalServicesScriptingInterface::GlobalServicesScriptingInterface() { connect(&accountManager, &AccountManager::logoutComplete, this, &GlobalServicesScriptingInterface::loggedOut); _downloading = false; - connect(Application::getInstance(), &Application::renderingInWorldInterface, - this, &GlobalServicesScriptingInterface::checkDownloadInfo); + QTimer* checkDownloadTimer = new QTimer(this); + connect(checkDownloadTimer, &QTimer::timeout, this, &GlobalServicesScriptingInterface::checkDownloadInfo); + const int CHECK_DOWNLOAD_INTERVAL = MSECS_PER_SECOND / 2; + checkDownloadTimer->start(CHECK_DOWNLOAD_INTERVAL); auto discoverabilityManager = DependencyManager::get(); connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged, diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 36b23c313e..6af58e89a1 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -153,6 +153,11 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar ++constraintItr; } } else { + // clear the accumulators before we start the IK solver + for (auto& accumulatorPair: _accumulators) { + accumulatorPair.second.clear(); + } + // compute absolute poses that correspond to relative target poses AnimPoseVec absolutePoses; computeAbsolutePoses(absolutePoses); @@ -165,8 +170,8 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar quint64 expiry = usecTimestampNow() + MAX_IK_TIME; do { largestError = 0.0f; + int lowestMovedIndex = _relativePoses.size(); for (auto& target: targets) { - int lowestMovedIndex = _relativePoses.size() - 1; int tipIndex = target.index; AnimPose targetPose = target.pose; int rootIndex = target.rootIndex; @@ -226,7 +231,8 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar glm::inverse(absolutePoses[pivotIndex].rot); } } - _relativePoses[pivotIndex].rot = newRot; + // store the rotation change in the accumulator + _accumulators[pivotIndex].add(newRot); } // this joint has been changed so we check to see if it has the lowest index if (pivotIndex < lowestMovedIndex) { @@ -243,36 +249,47 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar if (largestError < error) { largestError = error; } - - if (lowestMovedIndex <= _maxTargetIndex && lowestMovedIndex < tipIndex) { - // only update the absolutePoses that matter: those between lowestMovedIndex and _maxTargetIndex - for (int i = lowestMovedIndex; i <= _maxTargetIndex; ++i) { - int parentIndex = _skeleton->getParentIndex(i); - if (parentIndex != -1) { - absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i]; - } - } - } - - // finally set the relative rotation of the tip to agree with absolute target rotation - int parentIndex = _skeleton->getParentIndex(tipIndex); - if (parentIndex != -1) { - // compute tip's new parent-relative rotation - // Q = Qp * q --> q' = Qp^ * Q - glm::quat newRelativeRotation = glm::inverse(absolutePoses[parentIndex].rot) * targetPose.rot; - RotationConstraint* constraint = getConstraint(tipIndex); - if (constraint) { - constraint->apply(newRelativeRotation); - // TODO: ATM the final rotation target just fails but we need to provide - // feedback to the IK system so that it can adjust the bones up the skeleton - // to help this rotation target get met. - } - _relativePoses[tipIndex].rot = newRelativeRotation; - absolutePoses[tipIndex].rot = targetPose.rot; - } } ++numLoops; + + // harvest accumulated rotations and apply the average + for (auto& accumulatorPair: _accumulators) { + RotationAccumulator& accumulator = accumulatorPair.second; + if (accumulator.size() > 0) { + _relativePoses[accumulatorPair.first].rot = accumulator.getAverage(); + accumulator.clear(); + } + } + + // only update the absolutePoses that need it: those between lowestMovedIndex and _maxTargetIndex + for (int i = lowestMovedIndex; i <= _maxTargetIndex; ++i) { + int parentIndex = _skeleton->getParentIndex(i); + if (parentIndex != -1) { + absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i]; + } + } } while (largestError > ACCEPTABLE_RELATIVE_ERROR && numLoops < MAX_IK_LOOPS && usecTimestampNow() < expiry); + + // finally set the relative rotation of each tip to agree with absolute target rotation + for (auto& target: targets) { + int tipIndex = target.index; + int parentIndex = _skeleton->getParentIndex(tipIndex); + if (parentIndex != -1) { + AnimPose targetPose = target.pose; + // compute tip's new parent-relative rotation + // Q = Qp * q --> q' = Qp^ * Q + glm::quat newRelativeRotation = glm::inverse(absolutePoses[parentIndex].rot) * targetPose.rot; + RotationConstraint* constraint = getConstraint(tipIndex); + if (constraint) { + constraint->apply(newRelativeRotation); + // TODO: ATM the final rotation target just fails but we need to provide + // feedback to the IK system so that it can adjust the bones up the skeleton + // to help this rotation target get met. + } + _relativePoses[tipIndex].rot = newRelativeRotation; + absolutePoses[tipIndex].rot = targetPose.rot; + } + } } return _relativePoses; } @@ -628,6 +645,7 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele _maxTargetIndex = 0; + _accumulators.clear(); if (skeleton) { initConstraints(); } else { diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index b59fb4d5fc..c4bda1be89 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -12,8 +12,13 @@ #include +#include +#include + #include "AnimNode.h" +#include "RotationAccumulator.h" + class RotationConstraint; class AnimInverseKinematics : public AnimNode { @@ -24,7 +29,6 @@ public: void loadDefaultPoses(const AnimPoseVec& poses); void loadPoses(const AnimPoseVec& poses); - const AnimPoseVec& getRelativePoses() const { return _relativePoses; } void computeAbsolutePoses(AnimPoseVec& absolutePoses) const; void setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar); @@ -60,6 +64,7 @@ protected: }; std::map _constraints; + std::map _accumulators; std::vector _targetVarVec; AnimPoseVec _defaultRelativePoses; // poses of the relaxed state AnimPoseVec _relativePoses; // current relative poses diff --git a/libraries/animation/src/RotationAccumulator.cpp b/libraries/animation/src/RotationAccumulator.cpp new file mode 100644 index 0000000000..58ce4b9f36 --- /dev/null +++ b/libraries/animation/src/RotationAccumulator.cpp @@ -0,0 +1,27 @@ +// +// RotationAccumulator.h +// +// 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 "RotationAccumulator.h" + +#include + +void RotationAccumulator::add(glm::quat rotation) { + // make sure both quaternions are on the same hyper-hemisphere before we add them linearly (lerp) + _rotationSum += copysignf(1.0f, glm::dot(_rotationSum, rotation)) * rotation; + ++_numRotations; +} + +glm::quat RotationAccumulator::getAverage() { + return (_numRotations > 0) ? glm::normalize(_rotationSum) : glm::quat(); +} + +void RotationAccumulator::clear() { + _rotationSum *= 0.0f; + _numRotations = 0; +} diff --git a/libraries/animation/src/RotationAccumulator.h b/libraries/animation/src/RotationAccumulator.h new file mode 100644 index 0000000000..634a3d0eac --- /dev/null +++ b/libraries/animation/src/RotationAccumulator.h @@ -0,0 +1,32 @@ +// +// RotationAccumulator.h +// +// 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_RotationAccumulator_h +#define hifi_RotationAccumulator_h + +#include + +class RotationAccumulator { +public: + RotationAccumulator() : _rotationSum(0.0f, 0.0f, 0.0f, 0.0f), _numRotations(0) { } + + int size() const { return _numRotations; } + + void add(glm::quat rotation); + + glm::quat getAverage(); + + void clear(); + +private: + glm::quat _rotationSum; + int _numRotations; +}; + +#endif // hifi_RotationAccumulator_h diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index c55eaaeff9..c026f7352c 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -52,9 +52,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf _lastMouseEventValid(false), _viewState(viewState), _scriptingServices(scriptingServices), - _displayElementChildProxies(false), _displayModelBounds(false), - _displayModelElementProxy(false), _dontDoPrecisionPicking(false) { REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory) @@ -112,6 +110,7 @@ void EntityTreeRenderer::init() { _scriptingServices->getControllerScriptingInterface()); _scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine); _entitiesScriptEngine->runInThread(); + DependencyManager::get()->setEntitiesScriptEngine(_entitiesScriptEngine); } // make sure our "last avatar position" is something other than our current position, so that on our @@ -151,6 +150,7 @@ void EntityTreeRenderer::update() { } } + deleteReleasedModels(); } void EntityTreeRenderer::checkEnterLeaveEntities() { @@ -167,12 +167,41 @@ void EntityTreeRenderer::checkEnterLeaveEntities() { _tree->withReadLock([&] { std::static_pointer_cast(_tree)->findEntities(avatarPosition, radius, foundEntities); + // Whenever you're in an intersection between zones, we will always choose the smallest zone. + _bestZone = NULL; // NOTE: Is this what we want? + _bestZoneVolume = std::numeric_limits::max(); + // create a list of entities that actually contain the avatar's position foreach(EntityItemPointer entity, foundEntities) { if (entity->contains(avatarPosition)) { entitiesContainingAvatar << entity->getEntityItemID(); + + // if this entity is a zone, use this time to determine the bestZone + if (entity->getType() == EntityTypes::Zone) { + float entityVolumeEstimate = entity->getVolumeEstimate(); + if (entityVolumeEstimate < _bestZoneVolume) { + _bestZoneVolume = entityVolumeEstimate; + _bestZone = std::dynamic_pointer_cast(entity); + } else if (entityVolumeEstimate == _bestZoneVolume) { + if (!_bestZone) { + _bestZoneVolume = entityVolumeEstimate; + _bestZone = std::dynamic_pointer_cast(entity); + } else { + // in the case of the volume being equal, we will use the + // EntityItemID to deterministically pick one entity over the other + if (entity->getEntityItemID() < _bestZone->getEntityItemID()) { + _bestZoneVolume = entityVolumeEstimate; + _bestZone = std::dynamic_pointer_cast(entity); + } + } + } + + } } } + + applyZonePropertiesToScene(_bestZone); + }); // Note: at this point we don't need to worry about the tree being locked, because we only deal with @@ -307,27 +336,6 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr_renderer = this; - _tree->withReadLock([&] { - // Whenever you're in an intersection between zones, we will always choose the smallest zone. - _bestZone = NULL; // NOTE: Is this what we want? - _bestZoneVolume = std::numeric_limits::max(); - - // FIX ME: right now the renderOperation does the following: - // 1) determining the best zone (not really rendering) - // 2) render the debug cell details - // we should clean this up - _tree->recurseTreeWithOperation(renderOperation, renderArgs); - - applyZonePropertiesToScene(_bestZone); - }); - } - deleteReleasedModels(); // seems like as good as any other place to do some memory cleanup -} - const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer entityItem) { const FBXGeometry* result = NULL; @@ -372,121 +380,6 @@ const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(EntityItemP return result; } -void EntityTreeRenderer::renderElementProxy(EntityTreeElementPointer entityTreeElement, RenderArgs* args) { - auto deferredLighting = DependencyManager::get(); - Q_ASSERT(args->_batch); - gpu::Batch& batch = *args->_batch; - Transform transform; - - glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter(); - float elementSize = entityTreeElement->getScale(); - - auto drawWireCube = [&](glm::vec3 offset, float size, glm::vec4 color) { - transform.setTranslation(elementCenter + offset); - batch.setModelTransform(transform); - deferredLighting->renderWireCube(batch, size, color); - }; - - drawWireCube(glm::vec3(), elementSize, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); - - if (_displayElementChildProxies) { - // draw the children - float halfSize = elementSize / 2.0f; - float quarterSize = elementSize / 4.0f; - - drawWireCube(glm::vec3(-quarterSize, -quarterSize, -quarterSize), halfSize, glm::vec4(1.0f, 1.0f, 0.0f, 1.0f)); - drawWireCube(glm::vec3(quarterSize, -quarterSize, -quarterSize), halfSize, glm::vec4(1.0f, 0.0f, 1.0f, 1.0f)); - drawWireCube(glm::vec3(-quarterSize, quarterSize, -quarterSize), halfSize, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); - drawWireCube(glm::vec3(-quarterSize, -quarterSize, quarterSize), halfSize, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); - drawWireCube(glm::vec3(quarterSize, quarterSize, quarterSize), halfSize, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); - drawWireCube(glm::vec3(-quarterSize, quarterSize, quarterSize), halfSize, glm::vec4(0.0f, 0.5f, 0.5f, 1.0f)); - drawWireCube(glm::vec3(quarterSize, -quarterSize, quarterSize), halfSize, glm::vec4(0.5f, 0.0f, 0.0f, 1.0f)); - drawWireCube(glm::vec3(quarterSize, quarterSize, -quarterSize), halfSize, glm::vec4(0.0f, 0.5f, 0.0f, 1.0f)); - } -} - -void EntityTreeRenderer::renderProxies(EntityItemPointer entity, RenderArgs* args) { - bool isShadowMode = args->_renderMode == RenderArgs::SHADOW_RENDER_MODE; - if (!isShadowMode && _displayModelBounds) { - PerformanceTimer perfTimer("renderProxies"); - - AACube maxCube = entity->getMaximumAACube(); - AACube minCube = entity->getMinimumAACube(); - AABox entityBox = entity->getAABox(); - - glm::vec3 maxCenter = maxCube.calcCenter(); - glm::vec3 minCenter = minCube.calcCenter(); - glm::vec3 entityBoxCenter = entityBox.calcCenter(); - glm::vec3 entityBoxScale = entityBox.getScale(); - - auto deferredLighting = DependencyManager::get(); - Q_ASSERT(args->_batch); - gpu::Batch& batch = *args->_batch; - Transform transform; - - // draw the max bounding cube - transform.setTranslation(maxCenter); - batch.setModelTransform(transform); - deferredLighting->renderWireCube(batch, maxCube.getScale(), glm::vec4(1.0f, 1.0f, 0.0f, 1.0f)); - - // draw the min bounding cube - transform.setTranslation(minCenter); - batch.setModelTransform(transform); - deferredLighting->renderWireCube(batch, minCube.getScale(), glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); - - // draw the entityBox bounding box - transform.setTranslation(entityBoxCenter); - transform.setScale(entityBoxScale); - batch.setModelTransform(transform); - deferredLighting->renderWireCube(batch, 1.0f, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); - - // Rotated bounding box - batch.setModelTransform(entity->getTransformToCenter()); - deferredLighting->renderWireCube(batch, 1.0f, glm::vec4(1.0f, 0.0f, 1.0f, 1.0f)); - } -} - -void EntityTreeRenderer::renderElement(OctreeElementPointer element, RenderArgs* args) { - // actually render it here... - // we need to iterate the actual entityItems of the element - EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - - - bool isShadowMode = args->_renderMode == RenderArgs::SHADOW_RENDER_MODE; - - if (!isShadowMode && _displayModelElementProxy && entityTreeElement->size() > 0) { - renderElementProxy(entityTreeElement, args); - } - - entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) { - if (entityItem->isVisible()) { - // NOTE: Zone Entities are a special case we handle here... - if (entityItem->getType() == EntityTypes::Zone) { - if (entityItem->contains(_viewState->getAvatarPosition())) { - float entityVolumeEstimate = entityItem->getVolumeEstimate(); - if (entityVolumeEstimate < _bestZoneVolume) { - _bestZoneVolume = entityVolumeEstimate; - _bestZone = std::dynamic_pointer_cast(entityItem); - } else if (entityVolumeEstimate == _bestZoneVolume) { - if (!_bestZone) { - _bestZoneVolume = entityVolumeEstimate; - _bestZone = std::dynamic_pointer_cast(entityItem); - } else { - // in the case of the volume being equal, we will use the - // EntityItemID to deterministically pick one entity over the other - if (entityItem->getEntityItemID() < _bestZone->getEntityItemID()) { - _bestZoneVolume = entityVolumeEstimate; - _bestZone = std::dynamic_pointer_cast(entityItem); - } - } - } - } - } - } - }); - -} - float EntityTreeRenderer::getSizeScale() const { return _viewState->getSizeScale(); } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 18874957fd..d100597969 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -40,7 +40,6 @@ public: virtual char getMyNodeType() const { return NodeType::EntityServer; } virtual PacketType getMyQueryMessageType() const { return PacketType::EntityQuery; } virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; } - virtual void renderElement(OctreeElementPointer element, RenderArgs* args); virtual float getSizeScale() const; virtual int getBoundaryLevelAdjust() const; virtual void setTree(OctreePointer newTree); @@ -53,7 +52,6 @@ public: void processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode); virtual void init(); - virtual void render(RenderArgs* renderArgs) override; virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem); virtual const Model* getModelForEntityItem(EntityItemPointer entityItem); @@ -114,9 +112,7 @@ public slots: void updateEntityRenderStatus(bool shouldRenderEntities); // optional slots that can be wired to menu items - void setDisplayElementChildProxies(bool value) { _displayElementChildProxies = value; } void setDisplayModelBounds(bool value) { _displayModelBounds = value; } - void setDisplayModelElementProxy(bool value) { _displayModelElementProxy = value; } void setDontDoPrecisionPicking(bool value) { _dontDoPrecisionPicking = value; } protected: @@ -130,11 +126,9 @@ private: void addEntityToScene(EntityItemPointer entity); void applyZonePropertiesToScene(std::shared_ptr zone); - void renderElementProxy(EntityTreeElementPointer entityTreeElement, RenderArgs* args); void checkAndCallPreload(const EntityItemID& entityID, const bool reload = false); QList _releasedModels; - void renderProxies(EntityItemPointer entity, RenderArgs* args); RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, bool precisionPicking); @@ -157,9 +151,7 @@ private: MouseEvent _lastMouseEvent; AbstractViewStateInterface* _viewState; AbstractScriptingServicesInterface* _scriptingServices; - bool _displayElementChildProxies; bool _displayModelBounds; - bool _displayModelElementProxy; bool _dontDoPrecisionPicking; bool _shuttingDown = false; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index e0cc5f644f..74317b7eff 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -358,8 +358,8 @@ bool RenderableModelEntityItem::needsToCallUpdate() const { return _needsInitialSimulation || ModelEntityItem::needsToCallUpdate(); } -EntityItemProperties RenderableModelEntityItem::getProperties() const { - EntityItemProperties properties = ModelEntityItem::getProperties(); // get the properties from our base class +EntityItemProperties RenderableModelEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = ModelEntityItem::getProperties(desiredProperties); // get the properties from our base class if (_originalTexturesRead) { properties.setTextureNames(_originalTextures); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index d2c9370553..5b61046816 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -35,7 +35,7 @@ public: virtual ~RenderableModelEntityItem(); - virtual EntityItemProperties getProperties() const; + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; virtual bool setProperties(const EntityItemProperties& properties); virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, diff --git a/libraries/entities/src/AtmospherePropertyGroup.cpp b/libraries/entities/src/AtmospherePropertyGroup.cpp index 364612d4db..f8117dbb62 100644 --- a/libraries/entities/src/AtmospherePropertyGroup.cpp +++ b/libraries/entities/src/AtmospherePropertyGroup.cpp @@ -32,14 +32,14 @@ AtmospherePropertyGroup::AtmospherePropertyGroup() { _hasStars = true; } -void AtmospherePropertyGroup::copyToScriptValue(QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Atmosphere, atmosphere, Center, center); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Atmosphere, atmosphere, InnerRadius, innerRadius); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Atmosphere, atmosphere, OuterRadius, outerRadius); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Atmosphere, atmosphere, MieScattering, mieScattering); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Atmosphere, atmosphere, RayleighScattering, rayleighScattering); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Atmosphere, atmosphere, ScatteringWavelengths, scatteringWavelengths); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Atmosphere, atmosphere, HasStars, hasStars); +void AtmospherePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ATMOSPHERE_CENTER, Atmosphere, atmosphere, Center, center); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ATMOSPHERE_INNER_RADIUS, Atmosphere, atmosphere, InnerRadius, innerRadius); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ATMOSPHERE_OUTER_RADIUS, Atmosphere, atmosphere, OuterRadius, outerRadius); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ATMOSPHERE_MIE_SCATTERING, Atmosphere, atmosphere, MieScattering, mieScattering); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ATMOSPHERE_RAYLEIGH_SCATTERING, Atmosphere, atmosphere, RayleighScattering, rayleighScattering); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ATMOSPHERE_SCATTERING_WAVELENGTHS, Atmosphere, atmosphere, ScatteringWavelengths, scatteringWavelengths); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ATMOSPHERE_HAS_STARS, Atmosphere, atmosphere, HasStars, hasStars); } void AtmospherePropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { diff --git a/libraries/entities/src/AtmospherePropertyGroup.h b/libraries/entities/src/AtmospherePropertyGroup.h index e081033cd6..c4b50822fa 100644 --- a/libraries/entities/src/AtmospherePropertyGroup.h +++ b/libraries/entities/src/AtmospherePropertyGroup.h @@ -53,7 +53,7 @@ public: virtual ~AtmospherePropertyGroup() {} // EntityItemProperty related helpers - virtual void copyToScriptValue(QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); virtual void debugDump() const; diff --git a/libraries/entities/src/BoxEntityItem.cpp b/libraries/entities/src/BoxEntityItem.cpp index ce05bd93f0..4f30060207 100644 --- a/libraries/entities/src/BoxEntityItem.cpp +++ b/libraries/entities/src/BoxEntityItem.cpp @@ -32,8 +32,8 @@ BoxEntityItem::BoxEntityItem(const EntityItemID& entityItemID, const EntityItemP setProperties(properties); } -EntityItemProperties BoxEntityItem::getProperties() const { - EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class +EntityItemProperties BoxEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class properties._color = getXColor(); properties._colorChanged = false; diff --git a/libraries/entities/src/BoxEntityItem.h b/libraries/entities/src/BoxEntityItem.h index 49ce67f361..fdc66dd3e5 100644 --- a/libraries/entities/src/BoxEntityItem.h +++ b/libraries/entities/src/BoxEntityItem.h @@ -23,7 +23,7 @@ public: ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties() const; + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; virtual bool setProperties(const EntityItemProperties& properties); // TODO: eventually only include properties changed since the params.lastViewFrustumSent time diff --git a/libraries/entities/src/EntitiesScriptEngineProvider.h b/libraries/entities/src/EntitiesScriptEngineProvider.h new file mode 100644 index 0000000000..d112a6c0f9 --- /dev/null +++ b/libraries/entities/src/EntitiesScriptEngineProvider.h @@ -0,0 +1,25 @@ +// +// EntitiesScriptEngineProvider.h +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on Sept. 18, 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 +// +// TODO: How will we handle collision callbacks with Entities +// + +#ifndef hifi_EntitiesScriptEngineProvider_h +#define hifi_EntitiesScriptEngineProvider_h + +#include +#include "EntityItemID.h" + +class EntitiesScriptEngineProvider { +public: + virtual void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName) = 0; +}; + +#endif // hifi_EntitiesScriptEngineProvider_h \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 60a3004635..ce719ee976 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1019,8 +1019,10 @@ quint64 EntityItem::getExpiry() const { return _created + (quint64)(_lifetime * (float)USECS_PER_SECOND); } -EntityItemProperties EntityItem::getProperties() const { - EntityItemProperties properties; +EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EncodeBitstreamParams params; // unknown + EntityPropertyFlags propertyFlags = desiredProperties.isEmpty() ? getEntityProperties(params) : desiredProperties; + EntityItemProperties properties(propertyFlags); properties._id = getID(); properties._idSet = true; properties._created = _created; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 14f06f9f18..deebfc400d 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -131,7 +131,7 @@ public: EntityItemID getEntityItemID() const { return EntityItemID(_id); } // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties() const; + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; /// returns true if something changed virtual bool setProperties(const EntityItemProperties& properties); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 4b50ec0d02..ffa44194a9 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -36,7 +36,7 @@ StagePropertyGroup EntityItemProperties::_staticStage; EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1); -EntityItemProperties::EntityItemProperties() : +EntityItemProperties::EntityItemProperties(EntityPropertyFlags desiredProperties) : CONSTRUCT_PROPERTY(visible, ENTITY_ITEM_DEFAULT_VISIBLE), CONSTRUCT_PROPERTY(position, 0.0f), @@ -147,7 +147,8 @@ _localRenderAlphaChanged(false), _defaultSettings(true), _naturalDimensions(1.0f, 1.0f, 1.0f), -_naturalPosition(0.0f, 0.0f, 0.0f) +_naturalPosition(0.0f, 0.0f, 0.0f), +_desiredProperties(desiredProperties) { } @@ -437,112 +438,164 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool EntityItemProperties defaultEntityProperties; if (_idSet) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(id, _id.toString()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(id, _id.toString()); } - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(type, EntityTypes::getEntityTypeName(_type)); - COPY_PROPERTY_TO_QSCRIPTVALUE(position); - COPY_PROPERTY_TO_QSCRIPTVALUE(dimensions); - if (!skipDefaults) { - COPY_PROPERTY_TO_QSCRIPTVALUE(naturalDimensions); // gettable, but not settable - COPY_PROPERTY_TO_QSCRIPTVALUE(naturalPosition); - } - COPY_PROPERTY_TO_QSCRIPTVALUE(rotation); - COPY_PROPERTY_TO_QSCRIPTVALUE(velocity); - COPY_PROPERTY_TO_QSCRIPTVALUE(gravity); - COPY_PROPERTY_TO_QSCRIPTVALUE(acceleration); - COPY_PROPERTY_TO_QSCRIPTVALUE(damping); - COPY_PROPERTY_TO_QSCRIPTVALUE(restitution); - COPY_PROPERTY_TO_QSCRIPTVALUE(friction); - COPY_PROPERTY_TO_QSCRIPTVALUE(density); - COPY_PROPERTY_TO_QSCRIPTVALUE(lifetime); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(type, EntityTypes::getEntityTypeName(_type)); + auto created = QDateTime::fromMSecsSinceEpoch(getCreated() / 1000.0f, Qt::UTC); // usec per msec + created.setTimeSpec(Qt::OffsetFromUTC); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(created, created.toString(Qt::ISODate)); if (!skipDefaults || _lifetime != defaultEntityProperties._lifetime) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(age, getAge()); // gettable, but not settable COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable } - auto created = QDateTime::fromMSecsSinceEpoch(getCreated() / 1000.0f, Qt::UTC); // usec per msec - created.setTimeSpec(Qt::OffsetFromUTC); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(created, created.toString(Qt::ISODate)); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POSITION, position); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DIMENSIONS, dimensions); + if (!skipDefaults) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DIMENSIONS, naturalDimensions); // gettable, but not settable + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POSITION, naturalPosition); + } + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ROTATION, rotation); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VELOCITY, velocity); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAVITY, gravity); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ACCELERATION, acceleration); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DAMPING, damping); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RESTITUTION, restitution); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FRICTION, friction); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DENSITY, density); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LIFETIME, lifetime); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SCRIPT, script); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SCRIPT_TIMESTAMP, scriptTimestamp); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_REGISTRATION_POINT, registrationPoint); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_VELOCITY, angularVelocity); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_DAMPING, angularDamping); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VISIBLE, visible); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IGNORE_FOR_COLLISIONS, ignoreForCollisions); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISIONS_WILL_MOVE, collisionsWillMove); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_HREF, href); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DESCRIPTION, description); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FACE_CAMERA, faceCamera); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ACTION_DATA, actionData); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCKED, locked); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_DATA, userData); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MARKETPLACE_ID, marketplaceID); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_SOUND_URL, collisionSoundURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(script); - COPY_PROPERTY_TO_QSCRIPTVALUE(scriptTimestamp); - COPY_PROPERTY_TO_QSCRIPTVALUE(registrationPoint); - COPY_PROPERTY_TO_QSCRIPTVALUE(angularVelocity); - COPY_PROPERTY_TO_QSCRIPTVALUE(angularDamping); - COPY_PROPERTY_TO_QSCRIPTVALUE(visible); - COPY_PROPERTY_TO_QSCRIPTVALUE(color); - COPY_PROPERTY_TO_QSCRIPTVALUE(colorSpread); - COPY_PROPERTY_TO_QSCRIPTVALUE(colorStart); - COPY_PROPERTY_TO_QSCRIPTVALUE(colorFinish); - COPY_PROPERTY_TO_QSCRIPTVALUE(alpha); - COPY_PROPERTY_TO_QSCRIPTVALUE(alphaSpread); - COPY_PROPERTY_TO_QSCRIPTVALUE(alphaStart); - COPY_PROPERTY_TO_QSCRIPTVALUE(alphaFinish); - COPY_PROPERTY_TO_QSCRIPTVALUE(modelURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(compoundShapeURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(animationURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(animationIsPlaying); - COPY_PROPERTY_TO_QSCRIPTVALUE(animationFPS); - COPY_PROPERTY_TO_QSCRIPTVALUE(animationFrameIndex); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(animationSettings, getAnimationSettings()); - COPY_PROPERTY_TO_QSCRIPTVALUE(glowLevel); - COPY_PROPERTY_TO_QSCRIPTVALUE(localRenderAlpha); - COPY_PROPERTY_TO_QSCRIPTVALUE(ignoreForCollisions); - COPY_PROPERTY_TO_QSCRIPTVALUE(collisionsWillMove); - COPY_PROPERTY_TO_QSCRIPTVALUE(isSpotlight); - COPY_PROPERTY_TO_QSCRIPTVALUE(intensity); - COPY_PROPERTY_TO_QSCRIPTVALUE(exponent); - COPY_PROPERTY_TO_QSCRIPTVALUE(cutoff); - COPY_PROPERTY_TO_QSCRIPTVALUE(locked); - COPY_PROPERTY_TO_QSCRIPTVALUE(textures); - COPY_PROPERTY_TO_QSCRIPTVALUE(userData); - //COPY_PROPERTY_TO_QSCRIPTVALUE(simulationOwner); // TODO: expose this for JSON saves? - COPY_PROPERTY_TO_QSCRIPTVALUE(text); - COPY_PROPERTY_TO_QSCRIPTVALUE(lineHeight); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(textColor, getTextColor()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(backgroundColor, getBackgroundColor()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(shapeType, getShapeTypeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE(maxParticles); - COPY_PROPERTY_TO_QSCRIPTVALUE(lifespan); - COPY_PROPERTY_TO_QSCRIPTVALUE(emitRate); - COPY_PROPERTY_TO_QSCRIPTVALUE(emitSpeed); - COPY_PROPERTY_TO_QSCRIPTVALUE(speedSpread); - COPY_PROPERTY_TO_QSCRIPTVALUE(emitOrientation); - COPY_PROPERTY_TO_QSCRIPTVALUE(emitDimensions); - COPY_PROPERTY_TO_QSCRIPTVALUE(emitRadiusStart); - COPY_PROPERTY_TO_QSCRIPTVALUE(polarStart); - COPY_PROPERTY_TO_QSCRIPTVALUE(polarFinish); - COPY_PROPERTY_TO_QSCRIPTVALUE(azimuthStart); - COPY_PROPERTY_TO_QSCRIPTVALUE(azimuthFinish); - COPY_PROPERTY_TO_QSCRIPTVALUE(emitAcceleration); - COPY_PROPERTY_TO_QSCRIPTVALUE(accelerationSpread); - COPY_PROPERTY_TO_QSCRIPTVALUE(particleRadius); - COPY_PROPERTY_TO_QSCRIPTVALUE(radiusSpread); - COPY_PROPERTY_TO_QSCRIPTVALUE(radiusStart); - COPY_PROPERTY_TO_QSCRIPTVALUE(radiusFinish); - COPY_PROPERTY_TO_QSCRIPTVALUE(marketplaceID); - COPY_PROPERTY_TO_QSCRIPTVALUE(name); - COPY_PROPERTY_TO_QSCRIPTVALUE(collisionSoundURL); + // Boxes, Spheres, Light, Line, Model(??), Particle, PolyLine + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLOR, color); - COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightColor); - COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightIntensity); - COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightAmbientIntensity); - COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightDirection); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(backgroundMode, getBackgroundModeAsString()); - COPY_PROPERTY_TO_QSCRIPTVALUE(sourceUrl); - COPY_PROPERTY_TO_QSCRIPTVALUE(voxelVolumeSize); - COPY_PROPERTY_TO_QSCRIPTVALUE(voxelData); - COPY_PROPERTY_TO_QSCRIPTVALUE(voxelSurfaceStyle); - COPY_PROPERTY_TO_QSCRIPTVALUE(lineWidth); - COPY_PROPERTY_TO_QSCRIPTVALUE(linePoints); - COPY_PROPERTY_TO_QSCRIPTVALUE(href); - COPY_PROPERTY_TO_QSCRIPTVALUE(description); - COPY_PROPERTY_TO_QSCRIPTVALUE(faceCamera); - COPY_PROPERTY_TO_QSCRIPTVALUE(actionData); - COPY_PROPERTY_TO_QSCRIPTVALUE(normals); - COPY_PROPERTY_TO_QSCRIPTVALUE(strokeWidths); + // Particles only + if (_type == EntityTypes::ParticleEffect) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_PARTICLES, maxParticles); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LIFESPAN, lifespan); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_RATE, emitRate); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_SPEED, emitSpeed); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPEED_SPREAD, speedSpread); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_ORIENTATION, emitOrientation); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_DIMENSIONS, emitDimensions); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_RADIUS_START, emitRadiusStart); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POLAR_START, polarStart); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POLAR_FINISH, polarFinish); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_AZIMUTH_START, azimuthStart); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_AZIMUTH_FINISH, azimuthFinish); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_ACCELERATION, emitAcceleration); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ACCELERATION_SPREAD, accelerationSpread); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARTICLE_RADIUS, particleRadius); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_SPREAD, radiusSpread); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_START, radiusStart); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_FINISH, radiusFinish); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLOR_SPREAD, colorSpread); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLOR_START, colorStart); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLOR_FINISH, colorFinish); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_SPREAD, alphaSpread); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish); + } + + // Models only + if (_type == EntityTypes::Model) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MODEL_URL, modelURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, animationURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); + } + + + if (_type == EntityTypes::Model || _type == EntityTypes::Zone || _type == EntityTypes::ParticleEffect) { + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); + } + + // Models & Particles + if (_type == EntityTypes::Model || _type == EntityTypes::ParticleEffect) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_PLAYING, animationIsPlaying); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FPS, animationFPS); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FRAME_INDEX, animationFrameIndex); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_SETTINGS, animationSettings, getAnimationSettings()); + } + + // Lights only + if (_type == EntityTypes::Light) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_SPOTLIGHT, isSpotlight); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_INTENSITY, intensity); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EXPONENT, exponent); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CUTOFF, cutoff); + } + + // Text only + if (_type == EntityTypes::Text) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT, text); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_HEIGHT, lineHeight); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TEXT_COLOR, textColor, getTextColor()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BACKGROUND_COLOR, backgroundColor, getBackgroundColor()); + } + + // Zones only + if (_type == EntityTypes::Zone) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_COLOR, keyLightColor); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_INTENSITY, keyLightIntensity); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_AMBIENT_INTENSITY, keyLightAmbientIntensity); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_DIRECTION, keyLightDirection); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BACKGROUND_MODE, backgroundMode, getBackgroundModeAsString()); + + _stage.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _atmosphere.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _skybox.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + } + + // Web only + if (_type == EntityTypes::Web) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOURCE_URL, sourceUrl); + } + + // PolyVoxel only + if (_type == EntityTypes::PolyVox) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VOXEL_DATA, voxelData); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VOXEL_SURFACE_STYLE, voxelSurfaceStyle); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_X_TEXTURE_URL, xTextureURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Y_TEXTURE_URL, yTextureURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Z_TEXTURE_URL, zTextureURL); + + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_X_N_NEIGHBOR_ID, xNNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Y_N_NEIGHBOR_ID, yNNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Z_N_NEIGHBOR_ID, zNNeighborID); + + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_X_P_NEIGHBOR_ID, xPNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Y_P_NEIGHBOR_ID, yPNeighborID); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Z_P_NEIGHBOR_ID, zPNeighborID); + } + + // Lines & PolyLines + if (_type == EntityTypes::Line || _type == EntityTypes::PolyLine) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_WIDTH, lineWidth); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_POINTS, linePoints); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NORMALS, normals); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_WIDTHS, strokeWidths); + } // Sitting properties support if (!skipDefaults) { @@ -555,7 +608,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool sittingPoints.setProperty(i, sittingPoint); } sittingPoints.setProperty("length", _sittingPoints.size()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(sittingPoints, sittingPoints); // gettable, but not settable + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(sittingPoints, sittingPoints); // gettable, but not settable } if (!skipDefaults) { @@ -577,21 +630,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(originalTextures, textureNamesList); // gettable, but not settable } - _stage.copyToScriptValue(properties, engine, skipDefaults, defaultEntityProperties); - _atmosphere.copyToScriptValue(properties, engine, skipDefaults, defaultEntityProperties); - _skybox.copyToScriptValue(properties, engine, skipDefaults, defaultEntityProperties); - - COPY_PROPERTY_TO_QSCRIPTVALUE(xTextureURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(yTextureURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(zTextureURL); - - COPY_PROPERTY_TO_QSCRIPTVALUE(xNNeighborID); - COPY_PROPERTY_TO_QSCRIPTVALUE(yNNeighborID); - COPY_PROPERTY_TO_QSCRIPTVALUE(zNNeighborID); - - COPY_PROPERTY_TO_QSCRIPTVALUE(xPNeighborID); - COPY_PROPERTY_TO_QSCRIPTVALUE(yPNeighborID); - COPY_PROPERTY_TO_QSCRIPTVALUE(zPNeighborID); + // FIXME - I don't think these properties are supported any more + //COPY_PROPERTY_TO_QSCRIPTVALUE(glowLevel); + //COPY_PROPERTY_TO_QSCRIPTVALUE(localRenderAlpha); return properties; } @@ -737,6 +778,162 @@ void EntityItemPropertiesFromScriptValueHonorReadOnly(const QScriptValue &object } +QScriptValue EntityPropertyFlagsToScriptValue(QScriptEngine* engine, const EntityPropertyFlags& flags) { + return EntityItemProperties::entityPropertyFlagsToScriptValue(engine, flags); + QScriptValue result = engine->newObject(); + return result; +} + +void EntityPropertyFlagsFromScriptValue(const QScriptValue& object, EntityPropertyFlags& flags) { + EntityItemProperties::entityPropertyFlagsFromScriptValue(object, flags); +} + + +QScriptValue EntityItemProperties::entityPropertyFlagsToScriptValue(QScriptEngine* engine, const EntityPropertyFlags& flags) { + QScriptValue result = engine->newObject(); + return result; +} + +static QHash _propertyStringsToEnums; + +void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue& object, EntityPropertyFlags& flags) { + static std::once_flag initMap; + + std::call_once(initMap, [](){ + ADD_PROPERTY_TO_MAP(PROP_VISIBLE, Visible, visible, bool); + ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_ROTATION, Rotation, rotation, glm::quat); + ADD_PROPERTY_TO_MAP(PROP_DENSITY, Density, density, float); + ADD_PROPERTY_TO_MAP(PROP_VELOCITY, Velocity, velocity, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_GRAVITY, Gravity, gravity, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_ACCELERATION, Acceleration, acceleration, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_DAMPING, Damping, damping, float); + ADD_PROPERTY_TO_MAP(PROP_RESTITUTION, Restitution, restitution, float); + ADD_PROPERTY_TO_MAP(PROP_FRICTION, Friction, friction, float); + ADD_PROPERTY_TO_MAP(PROP_LIFETIME, Lifetime, lifetime, float); + ADD_PROPERTY_TO_MAP(PROP_SCRIPT, Script, script, QString); + ADD_PROPERTY_TO_MAP(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64); + ADD_PROPERTY_TO_MAP(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString); + ADD_PROPERTY_TO_MAP(PROP_COLOR, Color, color, xColor); + ADD_PROPERTY_TO_MAP(PROP_COLOR_SPREAD, ColorSpread, colorSpread, xColor); + ADD_PROPERTY_TO_MAP(PROP_COLOR_START, ColorStart, colorStart, xColor); + ADD_PROPERTY_TO_MAP(PROP_COLOR_FINISH, ColorFinish, colorFinish, xColor); + ADD_PROPERTY_TO_MAP(PROP_ALPHA, Alpha, alpha, float); + ADD_PROPERTY_TO_MAP(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float); + ADD_PROPERTY_TO_MAP(PROP_ALPHA_START, AlphaStart, alphaStart, float); + ADD_PROPERTY_TO_MAP(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float); + ADD_PROPERTY_TO_MAP(PROP_MODEL_URL, ModelURL, modelURL, QString); + ADD_PROPERTY_TO_MAP(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); + ADD_PROPERTY_TO_MAP(PROP_ANIMATION_URL, AnimationURL, animationURL, QString); + ADD_PROPERTY_TO_MAP(PROP_ANIMATION_FPS, AnimationFPS, animationFPS, float); + ADD_PROPERTY_TO_MAP(PROP_ANIMATION_FRAME_INDEX, AnimationFrameIndex, animationFrameIndex, float); + ADD_PROPERTY_TO_MAP(PROP_ANIMATION_PLAYING, AnimationIsPlaying, animationIsPlaying, bool); + ADD_PROPERTY_TO_MAP(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float); + ADD_PROPERTY_TO_MAP(PROP_IGNORE_FOR_COLLISIONS, IgnoreForCollisions, ignoreForCollisions, bool); + ADD_PROPERTY_TO_MAP(PROP_COLLISIONS_WILL_MOVE, CollisionsWillMove, collisionsWillMove, bool); + ADD_PROPERTY_TO_MAP(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool); + ADD_PROPERTY_TO_MAP(PROP_INTENSITY, Intensity, intensity, float); + ADD_PROPERTY_TO_MAP(PROP_EXPONENT, Exponent, exponent, float); + ADD_PROPERTY_TO_MAP(PROP_CUTOFF, Cutoff, cutoff, float); + ADD_PROPERTY_TO_MAP(PROP_LOCKED, Locked, locked, bool); + ADD_PROPERTY_TO_MAP(PROP_TEXTURES, Textures, textures, QString); + ADD_PROPERTY_TO_MAP(PROP_ANIMATION_SETTINGS, AnimationSettings, animationSettings, QString); + ADD_PROPERTY_TO_MAP(PROP_USER_DATA, UserData, userData, QString); + ADD_PROPERTY_TO_MAP(PROP_SIMULATION_OWNER, SimulationOwner, simulationOwner, SimulationOwner); + ADD_PROPERTY_TO_MAP(PROP_TEXT, Text, text, QString); + ADD_PROPERTY_TO_MAP(PROP_LINE_HEIGHT, LineHeight, lineHeight, float); + ADD_PROPERTY_TO_MAP(PROP_TEXT_COLOR, TextColor, textColor, xColor); + ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, xColor); + ADD_PROPERTY_TO_MAP(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType); + ADD_PROPERTY_TO_MAP(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32); + ADD_PROPERTY_TO_MAP(PROP_LIFESPAN, Lifespan, lifespan, float); + ADD_PROPERTY_TO_MAP(PROP_EMIT_RATE, EmitRate, emitRate, float); + ADD_PROPERTY_TO_MAP(PROP_EMIT_SPEED, EmitSpeed, emitSpeed, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_SPEED_SPREAD, SpeedSpread, speedSpread, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_EMIT_ORIENTATION, EmitOrientation, emitOrientation, glm::quat); + ADD_PROPERTY_TO_MAP(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_EMIT_RADIUS_START, EmitRadiusStart, emitRadiusStart, float); + ADD_PROPERTY_TO_MAP(PROP_POLAR_START, EmitPolarStart, polarStart, float); + ADD_PROPERTY_TO_MAP(PROP_POLAR_FINISH, EmitPolarFinish, polarFinish, float); + ADD_PROPERTY_TO_MAP(PROP_AZIMUTH_START, EmitAzimuthStart, azimuthStart, float); + ADD_PROPERTY_TO_MAP(PROP_AZIMUTH_FINISH, EmitAzimuthFinish, azimuthFinish, float); + ADD_PROPERTY_TO_MAP(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float); + ADD_PROPERTY_TO_MAP(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float); + ADD_PROPERTY_TO_MAP(PROP_RADIUS_START, RadiusStart, radiusStart, float); + ADD_PROPERTY_TO_MAP(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float); + ADD_PROPERTY_TO_MAP(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString); + ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor); + ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float); + ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLightAmbientIntensity, keyLightAmbientIntensity, float); + ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray); + ADD_PROPERTY_TO_MAP(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t); + ADD_PROPERTY_TO_MAP(PROP_NAME, Name, name, QString); + ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_MODE, BackgroundMode, backgroundMode, BackgroundMode); + ADD_PROPERTY_TO_MAP(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString); + ADD_PROPERTY_TO_MAP(PROP_LINE_WIDTH, LineWidth, lineWidth, float); + ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); + ADD_PROPERTY_TO_MAP(PROP_HREF, Href, href, QString); + ADD_PROPERTY_TO_MAP(PROP_DESCRIPTION, Description, description, QString); + ADD_PROPERTY_TO_MAP(PROP_FACE_CAMERA, FaceCamera, faceCamera, bool); + ADD_PROPERTY_TO_MAP(PROP_ACTION_DATA, ActionData, actionData, QByteArray); + ADD_PROPERTY_TO_MAP(PROP_NORMALS, Normals, normals, QVector); + ADD_PROPERTY_TO_MAP(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector); + ADD_PROPERTY_TO_MAP(PROP_X_TEXTURE_URL, XTextureURL, xTextureURL, QString); + ADD_PROPERTY_TO_MAP(PROP_Y_TEXTURE_URL, YTextureURL, yTextureURL, QString); + ADD_PROPERTY_TO_MAP(PROP_Z_TEXTURE_URL, ZTextureURL, zTextureURL, QString); + ADD_PROPERTY_TO_MAP(PROP_X_N_NEIGHBOR_ID, XNNeighborID, xNNeighborID, EntityItemID); + ADD_PROPERTY_TO_MAP(PROP_Y_N_NEIGHBOR_ID, YNNeighborID, yNNeighborID, EntityItemID); + ADD_PROPERTY_TO_MAP(PROP_Z_N_NEIGHBOR_ID, ZNNeighborID, zNNeighborID, EntityItemID); + ADD_PROPERTY_TO_MAP(PROP_X_P_NEIGHBOR_ID, XPNeighborID, xPNeighborID, EntityItemID); + ADD_PROPERTY_TO_MAP(PROP_Y_P_NEIGHBOR_ID, YPNeighborID, yPNeighborID, EntityItemID); + ADD_PROPERTY_TO_MAP(PROP_Z_P_NEIGHBOR_ID, ZPNeighborID, zPNeighborID, EntityItemID); + + ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color); + ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_URL, Skybox, skybox, URL, url); + + ADD_GROUP_PROPERTY_TO_MAP(PROP_ATMOSPHERE_CENTER, Atmosphere, atmosphere, Center, center); + ADD_GROUP_PROPERTY_TO_MAP(PROP_ATMOSPHERE_INNER_RADIUS, Atmosphere, atmosphere, InnerRadius, innerRadius); + ADD_GROUP_PROPERTY_TO_MAP(PROP_ATMOSPHERE_OUTER_RADIUS, Atmosphere, atmosphere, OuterRadius, outerRadius); + ADD_GROUP_PROPERTY_TO_MAP(PROP_ATMOSPHERE_MIE_SCATTERING, Atmosphere, atmosphere, MieScattering, mieScattering); + ADD_GROUP_PROPERTY_TO_MAP(PROP_ATMOSPHERE_RAYLEIGH_SCATTERING, Atmosphere, atmosphere, RayleighScattering, rayleighScattering); + ADD_GROUP_PROPERTY_TO_MAP(PROP_ATMOSPHERE_SCATTERING_WAVELENGTHS, Atmosphere, atmosphere, ScatteringWavelengths, scatteringWavelengths); + ADD_GROUP_PROPERTY_TO_MAP(PROP_ATMOSPHERE_HAS_STARS, Atmosphere, atmosphere, HasStars, hasStars); + + ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_SUN_MODEL_ENABLED, Stage, stage, SunModelEnabled, sunModelEnabled); + ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_LATITUDE, Stage, stage, Latitude, latitude); + ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_LONGITUDE, Stage, stage, Longitude, longitude); + ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_ALTITUDE, Stage, stage, Altitude, altitude); + ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_DAY, Stage, stage, Day, day); + ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_HOUR, Stage, stage, Hour, hour); + ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_AUTOMATIC_HOURDAY, Stage, stage, AutomaticHourDay, automaticHourDay); + + // FIXME - these are not yet handled + //ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); + + }); + + if (object.isString()) { + if (_propertyStringsToEnums.contains(object.toString())) { + flags << _propertyStringsToEnums[object.toString()]; + } + } else if (object.isArray()) { + quint32 length = object.property("length").toInt32(); + for (quint32 i = 0; i < length; i++) { + QString propertyName = object.property(i).toString(); + if (_propertyStringsToEnums.contains(propertyName)) { + flags << _propertyStringsToEnums[propertyName]; + } + } + } +} + // TODO: Implement support for edit packets that can span an MTU sized buffer. We need to implement a mechanism for the // encodeEntityEditPacket() method to communicate the the caller which properties couldn't fit in the buffer. Similar // to how we handle this in the Octree streaming case. diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 30d63b407c..55be145d85 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -58,7 +58,7 @@ class EntityItemProperties { friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods friend class PolyLineEntityItem; // TODO: consider removing this friend relationship and use public methods public: - EntityItemProperties(); + EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); virtual ~EntityItemProperties(); EntityTypes::EntityType getType() const { return _type; } @@ -67,6 +67,9 @@ public: virtual QScriptValue copyToScriptValue(QScriptEngine* engine, bool skipDefaults) const; virtual void copyFromScriptValue(const QScriptValue& object, bool honorReadOnly); + static QScriptValue entityPropertyFlagsToScriptValue(QScriptEngine* engine, const EntityPropertyFlags& flags); + static void entityPropertyFlagsFromScriptValue(const QScriptValue& object, EntityPropertyFlags& flags); + // editing related features supported by all entities quint64 getLastEdited() const { return _lastEdited; } float getEditedAgo() const /// Elapsed seconds since this entity was last edited @@ -266,13 +269,19 @@ private: QStringList _textureNames; glm::vec3 _naturalDimensions; glm::vec3 _naturalPosition; + + EntityPropertyFlags _desiredProperties; // if set will narrow scopes of copy/to/from to just these properties }; Q_DECLARE_METATYPE(EntityItemProperties); QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); QScriptValue EntityItemNonDefaultPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); -void EntityItemPropertiesFromScriptValueIgnoreReadOnly(const QScriptValue &object, EntityItemProperties& properties); -void EntityItemPropertiesFromScriptValueHonorReadOnly(const QScriptValue &object, EntityItemProperties& properties); +void EntityItemPropertiesFromScriptValueIgnoreReadOnly(const QScriptValue& object, EntityItemProperties& properties); +void EntityItemPropertiesFromScriptValueHonorReadOnly(const QScriptValue& object, EntityItemProperties& properties); + +Q_DECLARE_METATYPE(EntityPropertyFlags); +QScriptValue EntityPropertyFlagsToScriptValue(QScriptEngine* engine, const EntityPropertyFlags& flags); +void EntityPropertyFlagsFromScriptValue(const QScriptValue& object, EntityPropertyFlags& flags); // define these inline here so the macros work diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index e7ff53026e..731e49b388 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -119,8 +119,9 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const QByteArray& v) { inline QScriptValue convertScriptValue(QScriptEngine* e, const EntityItemID& v) { return QScriptValue(QUuid(v).toString()); } -#define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(G,g,P,p) \ - if (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P()) { \ +#define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(X,G,g,P,p) \ + if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ + (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ QScriptValue groupProperties = properties.property(#g); \ if (!groupProperties.isValid()) { \ groupProperties = engine->newObject(); \ @@ -130,8 +131,9 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const EntityItemID& v) properties.setProperty(#g, groupProperties); \ } -#define COPY_PROPERTY_TO_QSCRIPTVALUE(P) \ - if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ +#define COPY_PROPERTY_TO_QSCRIPTVALUE(p,P) \ + if ((_desiredProperties.isEmpty() || _desiredProperties.getHasProperty(p)) && \ + (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ QScriptValue V = convertScriptValue(engine, _##P); \ properties.setProperty(#P, V); \ } @@ -139,12 +141,19 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const EntityItemID& v) #define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(P, G) \ properties.setProperty(#P, G); -#define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(P, G) \ - if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ +#define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(p, P, G) \ + if ((_desiredProperties.isEmpty() || _desiredProperties.getHasProperty(p)) && \ + (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ QScriptValue V = convertScriptValue(engine, G); \ properties.setProperty(#P, V); \ } +#define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(P, G) \ + if (!skipDefaults || defaultEntityProperties._##P != _##P) { \ + QScriptValue V = convertScriptValue(engine, G); \ + properties.setProperty(#P, V); \ + } + typedef glm::vec3 glmVec3; typedef glm::quat glmQuat; typedef QVector qVectorVec3; @@ -304,6 +313,12 @@ inline xColor xColor_convertFromScriptValue(const QScriptValue& v, bool& isValid T _##n; \ static T _static##N; +#define ADD_PROPERTY_TO_MAP(P, N, n, T) \ + _propertyStringsToEnums[#n] = P; + +#define ADD_GROUP_PROPERTY_TO_MAP(P, G, g, N, n) \ + _propertyStringsToEnums[#g "." #n] = P; + #define DEFINE_PROPERTY(P, N, n, T) \ public: \ T get##N() const { return _##n; } \ diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 2298e41bde..71215a5596 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -12,33 +12,8 @@ #ifndef hifi_EntityPropertyFlags_h #define hifi_EntityPropertyFlags_h -/* -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include // for SittingPoint -*/ - #include -/* -#include -#include - -#include "AtmospherePropertyGroup.h" -#include "EntityItemID.h" -#include "EntityItemPropertiesMacros.h" -#include "EntityTypes.h" -*/ - enum EntityPropertyList { PROP_PAGED_PROPERTY, PROP_CUSTOM_PROPERTIES_INCLUDED, @@ -197,6 +172,7 @@ enum EntityPropertyList { PROP_STAGE_ALTITUDE = PROP_SPECULAR_COLOR_UNUSED, PROP_STAGE_DAY = PROP_LINEAR_ATTENUATION_UNUSED, PROP_STAGE_HOUR = PROP_QUADRATIC_ATTENUATION_UNUSED, + PROP_STAGE_AUTOMATIC_HOURDAY = PROP_ANIMATION_FRAME_INDEX, PROP_ATMOSPHERE_CENTER = PROP_MAX_PARTICLES, PROP_ATMOSPHERE_INNER_RADIUS = PROP_LIFESPAN, PROP_ATMOSPHERE_OUTER_RADIUS = PROP_EMIT_RATE, @@ -207,7 +183,6 @@ enum EntityPropertyList { PROP_BACKGROUND_MODE = PROP_MODEL_URL, PROP_SKYBOX_COLOR = PROP_ANIMATION_URL, PROP_SKYBOX_URL = PROP_ANIMATION_FPS, - PROP_STAGE_AUTOMATIC_HOURDAY = PROP_ANIMATION_FRAME_INDEX, // Aliases/Piggyback properties for Web. These properties intentionally reuse the enum values for // other properties which will never overlap with each other. diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 4b8b4e2903..91edfe9a42 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -11,6 +11,7 @@ #include "EntityScriptingInterface.h" +#include "EntityItemID.h" #include #include "EntitiesLogging.h" @@ -100,12 +101,17 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties } EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity) { + EntityPropertyFlags noSpecificProperties; + return getEntityProperties(identity, noSpecificProperties); +} + +EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties) { EntityItemProperties results; if (_entityTree) { _entityTree->withReadLock([&] { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity)); if (entity) { - results = entity->getProperties(); + results = entity->getProperties(desiredProperties); // TODO: improve sitting points and naturalDimensions in the future, // for now we've included the old sitting points model behavior for entity types that are models @@ -211,6 +217,14 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { } } +void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method) { + if (_entitiesScriptEngine) { + EntityItemID entityID{ id }; + _entitiesScriptEngine->callEntityScriptMethod(entityID, method); + } +} + + QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const { EntityItemID result; if (_entityTree) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 8d2b0b6892..877edd16e3 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -20,18 +20,19 @@ #include #include #include + #include "PolyVoxEntityItem.h" #include "LineEntityItem.h" #include "PolyLineEntityItem.h" #include "EntityTree.h" #include "EntityEditPacketSender.h" +#include "EntitiesScriptEngineProvider.h" class EntityTree; class MouseEvent; - class RayToEntityIntersectionResult { public: RayToEntityIntersectionResult(); @@ -63,6 +64,7 @@ public: void setEntityTree(EntityTreePointer modelTree); EntityTreePointer getEntityTree() { return _entityTree; } + void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine) { _entitiesScriptEngine = engine; } public slots: @@ -78,6 +80,7 @@ public slots: /// gets the current model properties for a specific model /// this function will not find return results in script engine contexts which don't have access to models Q_INVOKABLE EntityItemProperties getEntityProperties(QUuid entityID); + Q_INVOKABLE EntityItemProperties getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties); /// edits a model updating only the included properties, will return the identified EntityItemID in case of /// successful edit, if the input entityID is for an unknown model this function will have no effect @@ -86,6 +89,11 @@ public slots: /// deletes a model Q_INVOKABLE void deleteEntity(QUuid entityID); + /// Allows a script to call a method on an entity's script. The method will execute in the entity script + /// engine. If the entity does not have an entity script or the method does not exist, this call will have + /// no effect. + Q_INVOKABLE void callEntityMethod(QUuid entityID, const QString& method); + /// finds the closest model to the center point, within the radius /// will return a EntityItemID.isKnownID = false if no models are in the radius /// this function will not find any models in script engine contexts which don't have access to models @@ -180,6 +188,7 @@ private: bool precisionPicking); EntityTreePointer _entityTree; + EntitiesScriptEngineProvider* _entitiesScriptEngine = nullptr; }; #endif // hifi_EntityScriptingInterface_h diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp index d67d09e4b1..1d2e358799 100644 --- a/libraries/entities/src/LightEntityItem.cpp +++ b/libraries/entities/src/LightEntityItem.cpp @@ -56,8 +56,8 @@ void LightEntityItem::setDimensions(const glm::vec3& value) { } -EntityItemProperties LightEntityItem::getProperties() const { - EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class +EntityItemProperties LightEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(isSpotlight, getIsSpotlight); COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); diff --git a/libraries/entities/src/LightEntityItem.h b/libraries/entities/src/LightEntityItem.h index 3ed28a252a..0590955700 100644 --- a/libraries/entities/src/LightEntityItem.h +++ b/libraries/entities/src/LightEntityItem.h @@ -26,7 +26,7 @@ public: virtual void setDimensions(const glm::vec3& value); // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties() const; + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; virtual bool setProperties(const EntityItemProperties& properties); virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; diff --git a/libraries/entities/src/LineEntityItem.cpp b/libraries/entities/src/LineEntityItem.cpp index 222aee1a8f..856d443a44 100644 --- a/libraries/entities/src/LineEntityItem.cpp +++ b/libraries/entities/src/LineEntityItem.cpp @@ -43,9 +43,9 @@ LineEntityItem::LineEntityItem(const EntityItemID& entityItemID, const EntityIte } -EntityItemProperties LineEntityItem::getProperties() const { +EntityItemProperties LineEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { - EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class properties._color = getXColor(); diff --git a/libraries/entities/src/LineEntityItem.h b/libraries/entities/src/LineEntityItem.h index 3b7590a460..7e0f49c984 100644 --- a/libraries/entities/src/LineEntityItem.h +++ b/libraries/entities/src/LineEntityItem.h @@ -22,8 +22,8 @@ class LineEntityItem : public EntityItem { ALLOW_INSTANTIATION // This class can be instantiated - // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties() const; + // methods for getting/setting all properties of an entity + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; virtual bool setProperties(const EntityItemProperties& properties); // TODO: eventually only include properties changed since the params.lastViewFrustumSent time diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 4c03f4a7da..70747937d8 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -43,8 +43,8 @@ ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID, const EntityI _color[0] = _color[1] = _color[2] = 0; } -EntityItemProperties ModelEntityItem::getProperties() const { - EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class +EntityItemProperties ModelEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(modelURL, getModelURL); diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 950a95bae2..bf6d7a9785 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -25,7 +25,7 @@ public: ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties() const; + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; virtual bool setProperties(const EntityItemProperties& properties); // TODO: eventually only include properties changed since the params.lastViewFrustumSent time diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index eb769dff3b..b5ec879f61 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -163,8 +163,8 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() { } -EntityItemProperties ParticleEffectEntityItem::getProperties() const { - EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class +EntityItemProperties ParticleEffectEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 9d39c4cd10..74d39aa4af 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -25,7 +25,7 @@ public: ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of this entity - virtual EntityItemProperties getProperties() const; + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; virtual bool setProperties(const EntityItemProperties& properties); virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index 196fde47fb..29a44547ff 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -45,9 +45,9 @@ _strokeWidths(QVector(0.0f)) setProperties(properties); } -EntityItemProperties PolyLineEntityItem::getProperties() const { +EntityItemProperties PolyLineEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { QWriteLocker lock(&_quadReadWriteLock); - EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class properties._color = getXColor(); diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index e5fdcf9b78..27a116e1b1 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -23,7 +23,7 @@ class PolyLineEntityItem : public EntityItem { ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties() const; + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; virtual bool setProperties(const EntityItemProperties& properties); // TODO: eventually only include properties changed since the params.lastViewFrustumSent time diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 6c53dbfa16..70ae25c65c 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -104,8 +104,8 @@ const glm::vec3& PolyVoxEntityItem::getVoxelVolumeSize() const { } -EntityItemProperties PolyVoxEntityItem::getProperties() const { - EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class +EntityItemProperties PolyVoxEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelVolumeSize, getVoxelVolumeSize); COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelData, getVoxelData); COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelSurfaceStyle, getVoxelSurfaceStyle); diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 31906a9cc0..4cacf22457 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -23,7 +23,7 @@ class PolyVoxEntityItem : public EntityItem { ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties() const; + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; virtual bool setProperties(const EntityItemProperties& properties); // TODO: eventually only include properties changed since the params.lastViewFrustumSent time diff --git a/libraries/entities/src/PropertyGroup.h b/libraries/entities/src/PropertyGroup.h index 7ac4b54a8e..f780907896 100644 --- a/libraries/entities/src/PropertyGroup.h +++ b/libraries/entities/src/PropertyGroup.h @@ -55,7 +55,7 @@ public: virtual ~PropertyGroup() {} // EntityItemProperty related helpers - virtual void copyToScriptValue(QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const = 0; + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const = 0; virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) = 0; virtual void debugDump() const { } diff --git a/libraries/entities/src/SkyboxPropertyGroup.cpp b/libraries/entities/src/SkyboxPropertyGroup.cpp index 5be7c1eb49..624a382d55 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.cpp +++ b/libraries/entities/src/SkyboxPropertyGroup.cpp @@ -20,9 +20,9 @@ SkyboxPropertyGroup::SkyboxPropertyGroup() { _url = QString(); } -void SkyboxPropertyGroup::copyToScriptValue(QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Skybox, skybox, Color, color); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Skybox, skybox, URL, url); +void SkyboxPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_SKYBOX_URL, Skybox, skybox, URL, url); } void SkyboxPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { diff --git a/libraries/entities/src/SkyboxPropertyGroup.h b/libraries/entities/src/SkyboxPropertyGroup.h index a92ec5abbb..25d982cbe7 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.h +++ b/libraries/entities/src/SkyboxPropertyGroup.h @@ -33,7 +33,7 @@ public: virtual ~SkyboxPropertyGroup() {} // EntityItemProperty related helpers - virtual void copyToScriptValue(QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); virtual void debugDump() const; diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp index 48eb5e01f7..b00544e979 100644 --- a/libraries/entities/src/SphereEntityItem.cpp +++ b/libraries/entities/src/SphereEntityItem.cpp @@ -37,8 +37,8 @@ SphereEntityItem::SphereEntityItem(const EntityItemID& entityItemID, const Entit _volumeMultiplier *= PI / 6.0f; } -EntityItemProperties SphereEntityItem::getProperties() const { - EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class +EntityItemProperties SphereEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class properties.setColor(getXColor()); return properties; } diff --git a/libraries/entities/src/SphereEntityItem.h b/libraries/entities/src/SphereEntityItem.h index 3b29a3a1f5..46798d6b10 100644 --- a/libraries/entities/src/SphereEntityItem.h +++ b/libraries/entities/src/SphereEntityItem.h @@ -23,7 +23,7 @@ public: ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties() const; + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; virtual bool setProperties(const EntityItemProperties& properties); virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; diff --git a/libraries/entities/src/StagePropertyGroup.cpp b/libraries/entities/src/StagePropertyGroup.cpp index 937dc2412e..fb27f9a464 100644 --- a/libraries/entities/src/StagePropertyGroup.cpp +++ b/libraries/entities/src/StagePropertyGroup.cpp @@ -36,14 +36,14 @@ StagePropertyGroup::StagePropertyGroup() { _automaticHourDay = false; } -void StagePropertyGroup::copyToScriptValue(QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, SunModelEnabled, sunModelEnabled); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, Latitude, latitude); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, Longitude, longitude); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, Altitude, altitude); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, Day, day); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, Hour, hour); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Stage, stage, AutomaticHourDay, automaticHourDay); +void StagePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_SUN_MODEL_ENABLED, Stage, stage, SunModelEnabled, sunModelEnabled); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_LATITUDE, Stage, stage, Latitude, latitude); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_LONGITUDE, Stage, stage, Longitude, longitude); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_ALTITUDE, Stage, stage, Altitude, altitude); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_DAY, Stage, stage, Day, day); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_HOUR, Stage, stage, Hour, hour); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_AUTOMATIC_HOURDAY, Stage, stage, AutomaticHourDay, automaticHourDay); } void StagePropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { diff --git a/libraries/entities/src/StagePropertyGroup.h b/libraries/entities/src/StagePropertyGroup.h index c5df4fe0bd..32eb3462a3 100644 --- a/libraries/entities/src/StagePropertyGroup.h +++ b/libraries/entities/src/StagePropertyGroup.h @@ -33,7 +33,7 @@ public: virtual ~StagePropertyGroup() {} // EntityItemProperty related helpers - virtual void copyToScriptValue(QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); virtual void debugDump() const; diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index 61730b208c..a4ce27f8b2 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -47,8 +47,8 @@ void TextEntityItem::setDimensions(const glm::vec3& value) { EntityItem::setDimensions(glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH)); } -EntityItemProperties TextEntityItem::getProperties() const { - EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class +EntityItemProperties TextEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(text, getText); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight); diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index a659f3c39b..101cef50b5 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -27,7 +27,7 @@ public: virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; } // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties() const; + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; virtual bool setProperties(const EntityItemProperties& properties); // TODO: eventually only include properties changed since the params.lastViewFrustumSent time diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index 041022a916..a4f60d5150 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -40,8 +40,8 @@ void WebEntityItem::setDimensions(const glm::vec3& value) { EntityItem::setDimensions(glm::vec3(value.x, value.y, WEB_ENTITY_ITEM_FIXED_DEPTH)); } -EntityItemProperties WebEntityItem::getProperties() const { - EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class +EntityItemProperties WebEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(sourceUrl, getSourceUrl); return properties; } diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index 24e19e1cb1..a2ca955916 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -26,7 +26,7 @@ public: virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; } // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties() const; + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; virtual bool setProperties(const EntityItemProperties& properties); // TODO: eventually only include properties changed since the params.lastViewFrustumSent time diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index fed85bf6a5..8d843bd0b9 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -73,8 +73,8 @@ EnvironmentData ZoneEntityItem::getEnvironmentData() const { return result; } -EntityItemProperties ZoneEntityItem::getProperties() const { - EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class +EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightColor, getKeyLightColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightIntensity, getKeyLightIntensity); diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 41af2f14d8..dbc3cede63 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -27,7 +27,7 @@ public: ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties() const; + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; virtual bool setProperties(const EntityItemProperties& properties); // TODO: eventually only include properties changed since the params.lastViewFrustumSent time diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 5410db11a4..4af3dd97d0 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -42,7 +42,7 @@ const unsigned int GRIP_BUTTON = 1U << 2; const unsigned int TRACKPAD_BUTTON = 1U << 3; const unsigned int TRIGGER_BUTTON = 1U << 4; -const float CONTROLLER_LENGTH_OFFSET = 0.175f; +const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b"; const QString ViveControllerManager::NAME = "OpenVR"; diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index 5fda4c9f4a..e2d97f0484 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -38,7 +38,7 @@ public: virtual char getMyNodeType() const = 0; virtual PacketType getMyQueryMessageType() const = 0; virtual PacketType getExpectedPacketType() const = 0; - virtual void renderElement(OctreeElementPointer element, RenderArgs* args) = 0; + virtual void renderElement(OctreeElementPointer element, RenderArgs* args) { } virtual float getSizeScale() const { return DEFAULT_OCTREE_SIZE_SCALE; } virtual int getBoundaryLevelAdjust() const { return 0; } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 692a320b4e..b8a97f0902 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -280,6 +280,7 @@ void ScriptEngine::init() { _controllerScriptingInterface->registerControllerTypes(this); } + qScriptRegisterMetaType(this, EntityPropertyFlagsToScriptValue, EntityPropertyFlagsFromScriptValue); qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly); qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); qScriptRegisterMetaType(this, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 83e65823a5..3cfeb3447e 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "AbstractControllerScriptingInterface.h" #include "ArrayBufferClass.h" @@ -46,7 +47,7 @@ public: QScriptValue scriptObject; }; -class ScriptEngine : public QScriptEngine, public ScriptUser { +class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScriptEngineProvider { Q_OBJECT public: ScriptEngine(const QString& scriptContents = NO_SCRIPT, diff --git a/libraries/shared/src/PropertyFlags.h b/libraries/shared/src/PropertyFlags.h index 8e74fd728b..2d512ece61 100644 --- a/libraries/shared/src/PropertyFlags.h +++ b/libraries/shared/src/PropertyFlags.h @@ -45,6 +45,7 @@ public: _maxFlag(INT_MIN), _minFlag(INT_MAX), _trailingFlipped(false), _encodedLength(0) { decode(fromEncoded); } void clear() { _flags.clear(); _maxFlag = INT_MIN; _minFlag = INT_MAX; _trailingFlipped = false; _encodedLength = 0; } + bool isEmpty() const { return _maxFlag == INT_MIN && _minFlag == INT_MAX && _trailingFlipped == false && _encodedLength == 0; } Enum firstFlag() const { return (Enum)_minFlag; } Enum lastFlag() const { return (Enum)_maxFlag; }