diff --git a/examples/acScripts/botProceduralWayPoints.js b/examples/acScripts/botProceduralWayPoints.js index fbe8b03c3d..1642d0f4db 100644 --- a/examples/acScripts/botProceduralWayPoints.js +++ b/examples/acScripts/botProceduralWayPoints.js @@ -21,7 +21,7 @@ //For procedural walk animation HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -Script.include(HIFI_PUBLIC_BUCKET + "scripts/proceduralAnimationAPI.js"); +Script.include(HIFI_PUBLIC_BUCKET + "scripts/acScripts/proceduralAnimationAPI.js"); var procAnimAPI = new ProcAnimAPI(); diff --git a/examples/attachedEntitiesManager.js b/examples/attachedEntitiesManager.js index 1cdd61ad39..8cd159ec14 100644 --- a/examples/attachedEntitiesManager.js +++ b/examples/attachedEntitiesManager.js @@ -18,7 +18,7 @@ var DEFAULT_WEARABLE_DATA = { }; -var MINIMUM_DROP_DISTANCE_FROM_JOINT = 0.4; +var MINIMUM_DROP_DISTANCE_FROM_JOINT = 0.8; var ATTACHED_ENTITY_SEARCH_DISTANCE = 10.0; var ATTACHED_ENTITIES_SETTINGS_KEY = "ATTACHED_ENTITIES"; var DRESSING_ROOM_DISTANCE = 2.0; @@ -161,7 +161,7 @@ function AttachedEntitiesManager() { continue; } var jointIndex = MyAvatar.getJointIndex(jointName); - if (jointIndex > 0) { + if (jointIndex >= 0) { var jointPosition = MyAvatar.getJointPosition(jointIndex); var distanceFromJoint = Vec3.distance(jointPosition, props.position); if (distanceFromJoint <= MINIMUM_DROP_DISTANCE_FROM_JOINT) { @@ -182,7 +182,9 @@ function AttachedEntitiesManager() { }; if (bestJointOffset && bestJointOffset.constructor === Array && bestJointOffset.length > 1) { - if (!this.avatarIsInDressingRoom()) { + if (this.avatarIsInDressingRoom()) { + this.updateRelativeOffsets(grabbedEntity); + } else { // don't snap the entity to the preferred position if the avatar is in the dressing room. wearProps.localPosition = bestJointOffset[0]; wearProps.localRotation = bestJointOffset[1]; diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 4b022e5a84..61c57fd86f 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -19,6 +19,7 @@ Script.include("../libraries/utils.js"); // var WANT_DEBUG = false; var WANT_DEBUG_STATE = false; +var WANT_DEBUG_SEARCH_NAME = null; // // these tune time-averaging and "on" value for analog trigger @@ -214,6 +215,10 @@ function getTag() { return "grab-" + MyAvatar.sessionUUID; } +function entityHasActions(entityID) { + return Entities.getActionIDs(entityID).length > 0; +} + function entityIsGrabbedByOther(entityID) { // by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*. var actionIDs = Entities.getActionIDs(entityID); @@ -774,6 +779,7 @@ function MyController(hand) { this.search = function() { this.grabbedEntity = null; this.isInitialGrab = false; + this.doubleParentGrab = false; if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { this.setState(STATE_RELEASE); @@ -867,36 +873,58 @@ function MyController(hand) { grabbable = false; } } + if ("grabbable" in grabbableDataForCandidate) { // if userData indicates that this is grabbable or not, override the default. grabbable = grabbableDataForCandidate.grabbable; } if (!grabbable && !grabbableDataForCandidate.wantsTrigger) { + if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) { + print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': not grabbable."); + } continue; } if (forbiddenTypes.indexOf(propsForCandidate.type) >= 0) { + if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) { + print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': forbidden entity type."); + } continue; } if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) { + if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) { + print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': locked and not triggerable."); + } continue; } if (forbiddenNames.indexOf(propsForCandidate.name) >= 0) { + if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) { + print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': forbidden name."); + } continue; } distance = Vec3.distance(propsForCandidate.position, handPosition); if (distance > PICK_MAX_DISTANCE) { // too far away, don't grab + if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) { + print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': too far away."); + } continue; } if (propsForCandidate.parentID != NULL_UUID && this.state == STATE_EQUIP_SEARCHING) { // don't allow a double-equip + if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) { + print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': it's a child"); + } continue; } if (this.state == STATE_SEARCHING && !isPhysical && distance > NEAR_PICK_MAX_DISTANCE) { // we can't distance-grab non-physical + if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) { + print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': not physical and too far for near-grab"); + } continue; } @@ -918,7 +946,7 @@ function MyController(hand) { return; } // near grab or equip with action - if (near) { + if (near && (grabbableData.refCount < 1 || entityHasActions(this.grabbedEntity))) { this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP); return; } @@ -926,6 +954,9 @@ function MyController(hand) { if ((isPhysical || this.state == STATE_EQUIP_SEARCHING) && !near) { if (entityIsGrabbedByOther(intersection.entityID)) { // don't distance grab something that is already grabbed. + if (WANT_DEBUG_SEARCH_NAME && props.name == WANT_DEBUG_SEARCH_NAME) { + print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': already grabbed by another."); + } return; } this.temporaryPositionOffset = null; @@ -951,6 +982,18 @@ function MyController(hand) { if (grabbableData.refCount < 1) { this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP); return; + } else { + // it's not physical and it's already held via parenting. go ahead and grab it, but + // save off the current parent and joint. this wont always be right if there are more than + // two grabs and the order of release isn't opposite of the order of grabs. + this.doubleParentGrab = true; + this.previousParentID = props.parentID; + this.previousParentJointIndex = props.parentJointIndex; + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP); + return; + } + if (WANT_DEBUG_SEARCH_NAME && props.name == WANT_DEBUG_SEARCH_NAME) { + print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': fell through."); } } @@ -979,7 +1022,9 @@ function MyController(hand) { }; this.distanceGrabTimescale = function(mass, distance) { - var timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME * mass / DISTANCE_HOLDING_UNITY_MASS * distance / DISTANCE_HOLDING_UNITY_DISTANCE; + var timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME * mass / + DISTANCE_HOLDING_UNITY_MASS * distance / + DISTANCE_HOLDING_UNITY_DISTANCE; if (timeScale < DISTANCE_HOLDING_ACTION_TIMEFRAME) { timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME; } @@ -1310,7 +1355,8 @@ function MyController(hand) { }); } - var handRotation = this.getHandRotation(); + // var handRotation = this.getHandRotation(); + var handRotation = (this.hand === RIGHT_HAND) ? MyAvatar.getRightPalmRotation() : MyAvatar.getLeftPalmRotation(); var handPosition = this.getHandPosition(); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); @@ -1337,7 +1383,7 @@ function MyController(hand) { } } - var isPhysical = this.propsArePhysical(grabbedProperties); + var isPhysical = this.propsArePhysical(grabbedProperties) || entityHasActions(this.grabbedEntity); if (isPhysical && this.state == STATE_NEAR_GRABBING) { // grab entity via action if (!this.setupHoldAction()) { @@ -1403,6 +1449,17 @@ function MyController(hand) { return; } + var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID"]); + if (props.parentID == MyAvatar.sessionUUID && + Vec3.length(props.localPosition) > NEAR_PICK_MAX_DISTANCE * 2.0) { + // for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip. + print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand."); + this.setState(STATE_RELEASE); + this.callEntityMethodOnGrabbed(this.state == STATE_NEAR_GRABBING ? "releaseGrab" : "releaseEquip", + [JSON.stringify(this.hand)]); + return; + } + // Keep track of the fingertip velocity to impart when we release the object. // Note that the idea of using a constant 'tip' velocity regardless of the // object's actual held offset is an idea intended to make it easier to throw things: @@ -1602,21 +1659,14 @@ function MyController(hand) { if (this.grabbedEntity !== null) { if (this.actionID !== null) { Entities.deleteAction(this.grabbedEntity, this.actionID); - //sometimes we want things to stay right where they are when we let go. + // sometimes we want things to stay right where they are when we let go. + var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {}); var releaseVelocityData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - if (releaseVelocityData.disableReleaseVelocity === true || !this.isInitialGrab) { - Entities.editEntity(this.grabbedEntity, { - velocity: { - x: 0, - y: 0, - z: 0 - }, - angularVelocity: { - x: 0, - y: 0, - z: 0 - } - }); + if (releaseVelocityData.disableReleaseVelocity === true || + // this next line allowed both: + // (1) far-grab, pull to self, near grab, then throw + // (2) equip something physical and adjust it with a other-hand grab without the thing drifting + (!this.isInitialGrab && grabData.refCount > 1)) { noVelocity = true; } } @@ -1721,15 +1771,27 @@ function MyController(hand) { data["dynamic"] && data["parentID"] == NULL_UUID && !data["collisionless"]) { - forceVelocity = true; + deactiveProps["velocity"] = {x: 0.0, y: 0.1, z: 0.0}; + } + if (noVelocity) { + deactiveProps["velocity"] = {x: 0.0, y: 0.0, z: 0.0}; + deactiveProps["angularVelocity"] = {x: 0.0, y: 0.0, z: 0.0}; } Entities.editEntity(entityID, deactiveProps); - - if (forceVelocity) { - Entities.editEntity(entityID, {velocity:{x:0, y:0.1, z:0}}); - } data = null; + } else if (this.doubleParentGrab) { + // we parent-grabbed this from another parent grab. try to put it back where we found it. + var deactiveProps = { + parentID: this.previousParentID, + parentJointIndex: this.previousParentJointIndex, + velocity: {x: 0.0, y: 0.0, z: 0.0}, + angularVelocity: {x: 0.0, y: 0.0, z: 0.0} + }; + Entities.editEntity(entityID, deactiveProps); + } else if (noVelocity) { + Entities.editEntity(entityID, {velocity: {x: 0.0, y: 0.0, z: 0.0}, + angularVelocity: {x: 0.0, y: 0.0, z: 0.0}}); } } else { data = null; diff --git a/examples/dressing_room/createPlatformWithLights.js b/examples/dressing_room/createPlatformWithLights.js new file mode 100644 index 0000000000..10061163fd --- /dev/null +++ b/examples/dressing_room/createPlatformWithLights.js @@ -0,0 +1,160 @@ +// +// createDressingPlatform.js +// +// Created by James B. Pollack @imgntn on 1/7/2016 +// Copyright 2016 High Fidelity, Inc. +// +// This script shows how to hook up a model entity to your avatar to act as a doppelganger. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var basePlatform; +var basePosition = Vec3.sum(Vec3.sum(MyAvatar.position, { + x: 0, + y: -1, + z: 0 +}), Vec3.multiply(2, Quat.getFront(Camera.getOrientation()))); + +var loadArea; +var LOAD_AREA_SCRIPT_URL = Script.resolvePath('loadingAreaEntity.js'); + +function createBasePlatform() { + var properties = { + type: 'Box', + name: 'Hifi-Dressing-Room-Base', + dimensions: { + x: 4, + y: 0.10, + z: 4 + }, + color: { + red: 255, + green: 0, + blue: 255 + }, + position: basePosition, + collisionsWillMove: false, + ignoreForCollisions: false, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + } + basePlatform = Entities.addEntity(properties); +} + +function createLoadArea() { + // on enter, load the wearables manager and the doppelganger manager; + // on exit, stop the scripts (at least call cleanup); + var properties = { + type: 'Box', + shapeType: 'box', + name: 'Hifi-Dressing-Room-Load-Area', + dimensions: { + x: 0.25, + y: 0.25, + z: 0.25 + }, + color: { + red: 0, + green: 255, + blue: 0 + }, + visible: true, + position: basePosition, + collisionsWillMove: false, + ignoreForCollisions: true, + script: LOAD_AREA_SCRIPT_URL, + + } + loadArea = Entities.addEntity(properties); +} +var lights = []; + +function createLightAtPosition(position) { + var lightProperties = { + name: 'Hifi-Spotlight', + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 8 + }, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 0.035, + exponent: 1, + cutoff: 40, + lifetime: -1, + position: position, + rotation: getLightRotation(position) + }; + + light = Entities.addEntity(lightProperties); + lights.push(light); +} + +function createLights() { + var lightPosition = { + x: basePosition.x - 2, + y: basePosition.y + 3, + z: basePosition.z + } + createLightAtPosition(lightPosition); + + var lightPosition = { + x: basePosition.x + 2, + y: basePosition.y + 3, + z: basePosition.z + } + + createLightAtPosition(lightPosition); + var lightPosition = { + x: basePosition.x, + y: basePosition.y + 3, + z: basePosition.z + 2 + } + + createLightAtPosition(lightPosition); + var lightPosition = { + x: basePosition.x, + y: basePosition.y + 3, + z: basePosition.z - 2 + } + + createLightAtPosition(lightPosition); + +} + +function getLightRotation(myPosition) { + + var sourceToTargetVec = Vec3.subtract(basePosition, myPosition); + var emitOrientation = Quat.rotationBetween(Vec3.UNIT_NEG_Z, sourceToTargetVec); + + return emitOrientation +} + +function init() { + createBasePlatform(); + createLights(); + // createLoadArea(); +} + + +function cleanup() { + Entities.deleteEntity(basePlatform); + + while (lights.length > 0) { + Entities.deleteEntity(lights.pop()); + } + // Entities.deleteEntity(loadArea); +} +init(); +Script.scriptEnding.connect(cleanup) \ No newline at end of file diff --git a/examples/dressing_room/createTableWithItems.js b/examples/dressing_room/createTableWithItems.js new file mode 100644 index 0000000000..b8119e3077 --- /dev/null +++ b/examples/dressing_room/createTableWithItems.js @@ -0,0 +1,117 @@ +// +// createTableWithItems.js +// +// Created by James B. Pollack @imgntn on 1/7/2016 +// Copyright 2016 High Fidelity, Inc. +// +// This script shows how to hook up a model entity to your avatar to act as a doppelganger. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +var table, wearable; + +var TABLE_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/doppelganger/table.FBX'; +var TABLE_DIMENSIONS = { + x: 0.76, + y: 1.06, + z: 0.76 +}; + +function createTable() { + var avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0); + var position, rotation; + var ids = Entities.findEntities(MyAvatar.position, 20); + var hasBase = false; + for (var i = 0; i < ids.length; i++) { + var entityID = ids[i]; + var props = Entities.getEntityProperties(entityID, "name"); + var name = props.name; + if (name === "Hifi-Dressing-Room-Base") { + var details = Entities.getEntityProperties(entityID, ["position", "dimensions", "rotation"]); + var rightVector = Quat.getRight(details.rotation); + var rightDistance = 1.5; + position = Vec3.sum(Vec3.multiply(rightDistance, rightVector), details.position); + position.y = details.position.y += TABLE_DIMENSIONS.y / 2 + rotation = details.rotation; + hasBase = true; + } + } + + if (hasBase === false) { + position = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(avatarRot))); + rotation = avatarRot; + } + + var tableProperties = { + name: 'Hifi-Dressing-Room-Table', + type: 'Model', + shapeType:'box', + modelURL: TABLE_MODEL_URL, + dimensions: TABLE_DIMENSIONS, + position: position, + rotation: rotation, + collisionsWillMove: false, + ignoreForCollisions: false, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + } + print('TABLE PROPS', JSON.stringify(tableProperties)) + table = Entities.addEntity(tableProperties); +} + + +function createWearable() { + var tableProperties = Entities.getEntityProperties(table); + var properties = { + type: 'Model', + modelURL: 'https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx', + name: 'Hifi-Wearable', + dimensions: { + x: 0.25, + y: 0.25, + z: 0.25 + }, + color: { + red: 0, + green: 255, + blue: 0 + }, + position: { + x: tableProperties.position.x, + y: tableProperties.position.y + tableProperties.dimensions.y / 1.5, + z: tableProperties.position.z + }, + userData: JSON.stringify({ + "grabbableKey": { + "invertSolidWhileHeld": false + }, + "wearable": { + "joints": ["head", "Head", "hair", "neck"] + }, + handControllerKey: { + disableReleaseVelocity: true, + disableMoveWithHead: true, + } + }) + } + wearable = Entities.addEntity(properties); +} + +function init() { + createTable(); + createWearable(); +} + +function cleanup() { + Entities.deleteEntity(table); + Entities.deleteEntity(wearable); + +} +init(); +Script.scriptEnding.connect(cleanup) \ No newline at end of file diff --git a/examples/example/avatarcontrol/doppelganger.js b/examples/dressing_room/doppelganger.js similarity index 56% rename from examples/example/avatarcontrol/doppelganger.js rename to examples/dressing_room/doppelganger.js index 09462156b1..10f6468e9f 100644 --- a/examples/example/avatarcontrol/doppelganger.js +++ b/examples/dressing_room/doppelganger.js @@ -13,9 +13,12 @@ var TEST_MODEL_URL = 'https://s3.amazonaws.com/hifi-public/ozan/avatars/albert/albert/albert.fbx'; -var doppelgangers = []; - var MIRROR_JOINT_DATA = true; +var MIRRORED_ENTITY_SCRIPT_URL = Script.resolvePath('mirroredEntity.js'); +var FREEZE_TOGGLER_SCRIPT_URL = Script.resolvePath('freezeToggler.js?' + Math.random(0, 1000)) +var THROTTLE = false; +var THROTTLE_RATE = 100; +var doppelgangers = []; function Doppelganger(avatar) { this.initialProperties = { @@ -25,13 +28,21 @@ function Doppelganger(avatar) { // dimensions: getAvatarDimensions(avatar), position: putDoppelgangerAcrossFromAvatar(this, avatar), rotation: rotateDoppelgangerTowardAvatar(this, avatar), + collisionsWillMove: false, + ignoreForCollisions: false, + script: FREEZE_TOGGLER_SCRIPT_URL, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false, + wantsTrigger: true + } + }) }; this.id = createDoppelgangerEntity(this); this.avatar = avatar; return this; } - function getJointData(avatar) { var allJointData = []; var jointNames = MyAvatar.jointNames; @@ -297,30 +308,113 @@ function createDoppelgangerEntity(doppelganger) { function putDoppelgangerAcrossFromAvatar(doppelganger, avatar) { var avatarRot = Quat.fromPitchYawRollDegrees(0, avatar.bodyYaw, 0.0); - var basePosition = Vec3.sum(avatar.position, Vec3.multiply(1.5, Quat.getFront(avatarRot))); - return basePosition; + var position; + + var ids = Entities.findEntities(MyAvatar.position, 20); + var hasBase = false; + for (var i = 0; i < ids.length; i++) { + var entityID = ids[i]; + var props = Entities.getEntityProperties(entityID, "name"); + var name = props.name; + if (name === "Hifi-Dressing-Room-Base") { + var details = Entities.getEntityProperties(entityID, ["position", "dimensions"]); + details.position.y += getAvatarFootOffset(); + details.position.y += details.dimensions.y / 2; + position = details.position; + hasBase = true; + } + } + + if (hasBase === false) { + position = Vec3.sum(avatar.position, Vec3.multiply(1.5, Quat.getFront(avatarRot))); + + } + + return position; } +function getAvatarFootOffset() { + var data = getJointData(); + var upperLeg, lowerLeg, foot, toe, toeTop; + data.forEach(function(d) { + + var jointName = d.joint; + if (jointName === "RightUpLeg") { + upperLeg = d.translation.y; + } + if (jointName === "RightLeg") { + lowerLeg = d.translation.y; + } + if (jointName === "RightFoot") { + foot = d.translation.y; + } + if (jointName === "RightToeBase") { + toe = d.translation.y; + } + if (jointName === "RightToe_End") { + toeTop = d.translation.y + } + }) + + var myPosition = MyAvatar.position; + var offset = upperLeg + lowerLeg + foot + toe + toeTop; + offset = offset / 100; + return offset +} + + function getAvatarDimensions(avatar) { return dimensions; } function rotateDoppelgangerTowardAvatar(doppelganger, avatar) { var avatarRot = Quat.fromPitchYawRollDegrees(0, avatar.bodyYaw, 0.0); - avatarRot = Vec3.multiply(-1, avatarRot); + + var ids = Entities.findEntities(MyAvatar.position, 20); + var hasBase = false; + for (var i = 0; i < ids.length; i++) { + var entityID = ids[i]; + var props = Entities.getEntityProperties(entityID, "name"); + var name = props.name; + if (name === "Hifi-Dressing-Room-Base") { + var details = Entities.getEntityProperties(entityID, "rotation"); + avatarRot = details.rotation; + } + } + if (hasBase === false) { + avatarRot = Vec3.multiply(-1, avatarRot); + } return avatarRot; } +var isConnected = false; + function connectDoppelgangerUpdates() { - // Script.update.connect(updateDoppelganger); - Script.setInterval(updateDoppelganger, 100); + Script.update.connect(updateDoppelganger); + isConnected = true; } function disconnectDoppelgangerUpdates() { - Script.update.disconnect(updateDoppelganger); + print('SHOULD DISCONNECT') + if (isConnected === true) { + Script.update.disconnect(updateDoppelganger); + } + isConnected = false; } -function updateDoppelganger() { +var sinceLastUpdate = 0; + +function updateDoppelganger(deltaTime) { + if (THROTTLE === true) { + sinceLastUpdate = sinceLastUpdate + deltaTime * 100; + if (sinceLastUpdate > THROTTLE_RATE) { + sinceLastUpdate = 0; + } else { + return; + } + } + + var absoluteXforms = buildAbsoluteXformsFromMyAvatar(); if (MIRROR_JOINT_DATA) { var mirroredAbsoluteXforms = []; @@ -346,14 +440,190 @@ function makeDoppelgangerForMyAvatar() { connectDoppelgangerUpdates(); } +function subscribeToWearableMessages() { + Messages.subscribe('Hifi-Doppelganger-Wearable'); + Messages.messageReceived.connect(handleWearableMessages); +} + +function subscribeToFreezeMessages() { + Messages.subscribe('Hifi-Doppelganger-Freeze'); + Messages.messageReceived.connect(handleFreezeMessages); +} + +function handleFreezeMessages(channel, message, sender) { + if (channel !== 'Hifi-Doppelganger-Freeze') { + return; + } + if (sender !== MyAvatar.sessionUUID) { + return; + } + + var parsedMessage = null; + + try { + parsedMessage = JSON.parse(message); + } catch (e) { + print('error parsing wearable message'); + } + print('MESSAGE ACTION::' + parsedMessage.action) + if (parsedMessage.action === 'freeze') { + print('ACTUAL FREEZE') + disconnectDoppelgangerUpdates(); + } + if (parsedMessage.action === 'unfreeze') { + print('ACTUAL UNFREEZE') + + connectDoppelgangerUpdates(); + } + +} + +var wearablePairs = []; + +function handleWearableMessages(channel, message, sender) { + if (channel !== 'Hifi-Doppelganger-Wearable' || 'Hifi-Doppelganger-Wearable-Avatar') { + return; + } + + if (sender !== MyAvatar.sessionUUID) { + return; + } + + var parsedMessage = null; + + try { + parsedMessage = JSON.parse(message); + } catch (e) { + print('error parsing wearable message'); + } + print('parsed message!!!') + + if (channel === 'Hifi-Doppelganger-Wearable') { + mirrorEntitiesForDoppelganger(doppelgangers[0], parsedMessage); + } + if (channel === 'Hifi-Doppelganger-Wearable') { + mirrorEntitiesForAvatar(parsedMessge); + } + +} + +function mirrorEntitiesForDoppelganger(doppelganger, parsedMessage) { + var doppelgangerProps = Entities.getEntityProperties(doppelganger.id); + + var action = parsedMessage.action; + print('IN MIRROR ENTITIES CALL' + action) + + var baseEntity = parsedMessage.baseEntity; + + var wearableProps = Entities.getEntityProperties(baseEntity); + + print('WEARABLE PROPS::') + delete wearableProps.id; + delete wearableProps.created; + delete wearableProps.age; + delete wearableProps.ageAsText; + //delete wearableProps.position; + // add to dg + // add to avatar + // moved item on dg + // moved item on avatar + // remove item from dg + // remove item from avatar + + var joint = wearableProps.parentJointIndex; + if (action === 'add') { + print('IN DOPPELGANGER ADD'); + + wearableProps.parentID = doppelganger.id; + wearableProps.parentJointIndex = joint; + + //create a new one + wearableProps.script = MIRRORED_ENTITY_SCRIPT_URL; + wearableProps.name = 'Hifi-Doppelganger-Mirrored-Entity'; + wearableProps.userData = JSON.stringify({ + doppelgangerKey: { + baseEntity: baseEntity, + doppelganger: doppelganger.id + } + }) + var mirrorEntity = Entities.addEntity(wearableProps); + + var mirrorEntityProps = Entities.getEntityProperties(mirrorEntity) + print('MIRROR PROPS::' + JSON.stringify(mirrorEntityProps)) + var wearablePair = { + baseEntity: baseEntity, + mirrorEntity: mirrorEntity + } + + wearablePairs.push(wearablePair); + } + + if (action === 'update') { + wearableProps.parentID = doppelganger.id; + + var mirrorEntity = getMirrorEntityForBaseEntity(baseEntity); + // print('MIRROR ENTITY, newPosition' + mirrorEntity + ":::" + JSON.stringify(newPosition)) + Entities.editEntity(mirrorEntity, wearableProps) + } + + if (action === 'remove') { + Entities.deleteEntity(getMirrorEntityForBaseEntity(baseEntity)) + wearablePairs = wearablePairs.filter(function(obj) { + return obj.baseEntity !== baseEntity; + }); + } + + if (action === 'updateBase') { + //this gets called when the mirrored entity gets grabbed. now we move the + var mirrorEntityProperties = Entities.getEntityProperties(message.mirrorEntity) + var doppelgangerToMirrorEntity = Vec3.subtract(doppelgangerProps.position, mirrorEntityProperties.position); + var newPosition = Vec3.sum(MyAvatar.position, doppelgangerToMirrorEntity); + + delete mirrorEntityProperties.id; + delete mirrorEntityProperties.created; + delete mirrorEntityProperties.age; + delete mirrorEntityProperties.ageAsText; + mirrorEntityProperties.position = newPosition; + mirrorEntityProperties.parentID = MyAvatar.sessionUUID; + Entities.editEntity(message.baseEntity, mirrorEntityProperties); + } +} + +function getMirrorEntityForBaseEntity(baseEntity) { + var result = wearablePairs.filter(function(obj) { + return obj.baseEntity === baseEntity; + }); + if (result.length === 0) { + return false; + } else { + return result[0].mirrorEntity + } +} + +function getBaseEntityForMirrorEntity(mirrorEntity) { + var result = wearablePairs.filter(function(obj) { + return obj.mirrorEntity === mirrorEntity; + }); + if (result.length === 0) { + return false; + } else { + return result[0].baseEntity + } +} + makeDoppelgangerForMyAvatar(); +subscribeToWearableMessages(); +subscribeToWearableMessagesForAvatar(); +subscribeToFreezeMessages(); function cleanup() { - //disconnectDoppelgangerUpdates(); + if (isConnected === true) { + disconnectDoppelgangerUpdates(); + } doppelgangers.forEach(function(doppelganger) { + print('DOPPELGANGER' + doppelganger.id) Entities.deleteEntity(doppelganger.id); }); } - -Script.scriptEnding.connect(cleanup); +Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/examples/dressing_room/freezeToggler.js b/examples/dressing_room/freezeToggler.js new file mode 100644 index 0000000000..6b83a606da --- /dev/null +++ b/examples/dressing_room/freezeToggler.js @@ -0,0 +1,76 @@ +// +// dopppelgangerEntity.js +// +// Created by James B. Pollack @imgntn on 1/6/2016 +// Copyright 2016 High Fidelity, Inc. +// +// for freezing / unfreezing the doppelganger +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + var COUNTDOWN_LENGTH = 0; + var _this; + + Dopppelganger = function() { + + _this = this; + }; + + Dopppelganger.prototype = { + isFrozen: false, + startNearTrigger: function() { + print('DOPPELGANGER NEAR TRIGGER') + }, + startFarTrigger: function() { + print('DOPPELGANGER FAR TRIGGER') + if (this.isFrozen === false) { + this.freeze(); + } else { + this.unfreeze(); + } + + }, + clickReleaseOnEntity: function(entityID, mouseEvent) { + print('DOPPELGANGER CLICK') + if (!mouseEvent.isLeftButton) { + return; + } + if (this.isFrozen === false) { + this.freeze(); + } else { + this.unfreeze(); + } + + }, + freeze: function() { + print('FREEZE YO') + this.isFrozen = true; + var data = { + action: 'freeze' + } + + Script.setTimeout(function() { + Messages.sendMessage('Hifi-Doppelganger-Freeze', JSON.stringify(data)); + }, COUNTDOWN_LENGTH) + + }, + unfreeze: function() { + this.isFrozen = false; + var data = { + action: 'unfreeze' + } + Messages.sendMessage('Hifi-Doppelganger-Freeze', JSON.stringify(data)); + }, + + preload: function(entityID) { + this.entityID = entityID; + this.initialProperties = Entities.getEntityProperties(this.entityID); + this.userData = JSON.parse(this.initialProperties.userData); + }, + }; + + return new Dopppelganger(); +}) \ No newline at end of file diff --git a/examples/dressing_room/loadingAreaEntity.js b/examples/dressing_room/loadingAreaEntity.js new file mode 100644 index 0000000000..73ea142d59 --- /dev/null +++ b/examples/dressing_room/loadingAreaEntity.js @@ -0,0 +1,34 @@ +// +// loadingAreaEntity.js +// +// Created by James B. Pollack @imgntn on 1/7/2016 +// Copyright 2016 High Fidelity, Inc. +// +// This script shows how to hook up a model entity to your avatar to act as a doppelganger. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + + var WEARABLES_MANAGER_SCRIPT = Script.resolvePath('wearablesManager.js'); + var DOPPELGANGER_SCRIPT = Script.resolvePath('doppelganger.js'); + var CREATE_TEST_WEARABLE_SCRIPT = Script.resolvePath('createTestWearable.js'); + this.preload = function(entityID) { + print("preload(" + entityID + ")"); + }; + + this.enterEntity = function(entityID) { + print("enterEntity(" + entityID + ")"); + // Script.load(WEARABLES_MANAGER_SCRIPT); + // Script.load(DOPPELGANGER_SCRIPT); + // Script.load(CREATE_TEST_WEARABLE_SCRIPT); + + }; + + this.leaveEntity = function(entityID) { + print("leaveEntity(" + entityID + ")"); + }; + +}) \ No newline at end of file diff --git a/examples/dressing_room/mirroredEntity.js b/examples/dressing_room/mirroredEntity.js new file mode 100644 index 0000000000..0310b21f96 --- /dev/null +++ b/examples/dressing_room/mirroredEntity.js @@ -0,0 +1,48 @@ +// +// mirroredEntity.js +// +// Created by James B. Pollack @imgntn on 1/6/2016 +// Copyright 2016 High Fidelity, Inc. +// +// when grabbed, this entity relays updates to update the base entity +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + + var _this; + + MirroredEntity = function() { + _this = this; + }; + + MirroredEntity.prototype = { + startNearGrab: function () { + print("I was just grabbed... entity:" + this.entityID); + }, + continueNearGrab: function () { + print("I am still being grabbed... entity:" + this.entityID); + var data = { + action:'updateBase', + baseEntity:this.userData.doppelgangerKey.baseEntity, + mirrorEntity:this.entityID, + doppelganger:this.userData.doppelgangerKey.doppelganger + } + Messages.sendMessage('Hifi-Doppelganger-Wearable',data) + }, + + releaseGrab: function () { + print("I was released... entity:" + this.entityID); + }, + + preload: function(entityID) { + this.entityID = entityID; + this.initialProperties = Entities.getEntityProperties(this.entityID); + this.userData = JSON.parse(this.initialProperties.userData); + }, + }; + + return new MirroredEntity(); +}) diff --git a/examples/dressing_room/setupDressingRoom.js b/examples/dressing_room/setupDressingRoom.js new file mode 100644 index 0000000000..6de346ace2 --- /dev/null +++ b/examples/dressing_room/setupDressingRoom.js @@ -0,0 +1,11 @@ +var createPlatformWithLights = Script.resolvePath('createPlatformWithLights.js?'+Math.random(0,100)); +Script.include(createPlatformWithLights); +var createTableWithItems = Script.resolvePath('createTableWithItems.js?'+Math.random(0,100)); +Script.include(createTableWithItems); +var doppelganger = Script.resolvePath('doppelganger.js?'+Math.random(0,100)); +Script.include(doppelganger); +var wearablesManager = Script.resolvePath('wearablesManager.js?'+Math.random(0,100)); +Script.include(wearablesManager); +var handControllerGrab = Script.resolvePath('../controllers/handControllerGrab.js?'+Math.random(0,100)); +Script.include(handControllerGrab); +//put it in an interior diff --git a/examples/dressing_room/wearablesManager.js b/examples/dressing_room/wearablesManager.js new file mode 100644 index 0000000000..99623023d0 --- /dev/null +++ b/examples/dressing_room/wearablesManager.js @@ -0,0 +1,138 @@ +// +// wearablesManager.js +// +// Created by James B. Pollack @imgntn on 1/7/2016 +// Copyright 2016 High Fidelity, Inc. +// +// This script handles messages from the grab script related to wearables, and interacts with a doppelganger. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// todo: +// add camera countdown / freezing unfreezing the doppelganger +// add ability to drop wearables on doppelganger +// which means creating a mirror entity on the avatar ... + +Script.include("../libraries/utils.js"); + +var NULL_UUID = "{00000000-0000-0000-0000-000000000000}"; +var DEFAULT_WEARABLE_DATA = { + joints: {} +}; + +function WearablesManager() { + + this.wearables = []; + + this.subscribeToMessages = function() { + Messages.subscribe('Hifi-Object-Manipulation'); + Messages.messageReceived.connect(this.handleWearableMessages); + } + + this.handleWearableMessages = function(channel, message, sender) { + // print('wearablesManager messageReceived ::: ' + channel + " ::: " + message) + if (channel !== 'Hifi-Object-Manipulation') { + return; + } + // if (sender !== MyAvatar.sessionUUID) { + // print('wearablesManager got message from wrong sender'); + // return; + // } + + var parsedMessage = null; + + try { + parsedMessage = JSON.parse(message); + } catch (e) { + print('error parsing wearable message'); + } + + if (parsedMessage.action === 'update' && manager.wearables.length !== 0) { + manager.updateWearable(parsedMessage.grabbedEntity) + } else if (parsedMessage.action === 'update' && manager.wearables.length === 0) { + } else if (parsedMessage.action === 'release') { + manager.checkIfWearable(parsedMessage.grabbedEntity) + } else { + print('unknown actions: ' + parsedMessage.action); + } + } + + this.updateWearable = function(grabbedEntity) { + if (this.wearables.length > 0) { + + //only do this check if we already have some wearables for the doppelganger + var hasWearableAlready = this.wearables.indexOf(grabbedEntity); + var props = Entities.getEntityProperties(grabbedEntity); + + if (hasWearableAlready > -1) { + var data = { + action: 'update', + baseEntity: grabbedEntity, + } + + Messages.sendMessage('Hifi-Doppelganger-Wearable', JSON.stringify(data)) + } + } + } + + this.checkIfWearableOnDoppelganger = function(grabbedEntity) { + var allowedJoints = getEntityCustomData('wearable', grabbedEntity, DEFAULT_WEARABLE_DATA).joints; + + var props = Entities.getEntityProperties(grabbedEntity, ["position", "parentID"]); + if (props.parentID === NULL_UUID || props.parentID === MyAvatar.sessionUUID) { + var bestJointName = ""; + var bestJointIndex = -1; + var bestJointDistance = 0; + for (var jointName in allowedJoints) { + //do this for the model + var jointIndex = Entities.getJointIndex(doppelganger.id,jointName); + var jointPosition = Entities.getJointPosition(doppelganger.id,jointIndex); + var distanceFromJoint = Vec3.distance(jointPosition, props.position); + if (distanceFromJoint < 0.4) { + if (bestJointIndex == -1 || distanceFromJoint < bestJointDistance) { + bestJointName = jointName; + bestJointIndex = jointIndex; + bestJointDistance = distanceFromJoint; + } + } + } + + if (bestJointIndex != -1) { + Entities.editEntity(grabbedEntity, { + parentID: doppelganger.id, + parentJointIndex: bestJointIndex + }); + + if (this.wearables.indexOf(grabbedEntity) < 0) { + var data = { + action: 'addToDoppelganger', + baseEntity: grabbedEntity, + } + Messages.sendMessage('Hifi-Doppelganger-Wearable-Avatar', JSON.stringify(data)); + this.wearables.push(grabbedEntity) + } + } else { + Entities.editEntity(grabbedEntity, { + parentID: NULL_UUID + }); + + var hasWearableAlready = this.wearables.indexOf(grabbedEntity); + if (hasWearableAlready > -1) { + var data = { + action: 'removeFromDoppelganger', + baseEntity: grabbedEntity + } + + Messages.sendMessage('Hifi-Doppelganger-Wearable-Avatar', JSON.stringify(data)); + } + + this.wearables.splice(hasWearableAlready, 1) + } + } + } +} + +var manager = new WearablesManager(); +manager.subscribeToMessages(); diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 8980abf740..a4c0a7c446 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -447,7 +447,7 @@ "interpType": "snapshotPrev", "transitions": [ { "var": "isAway", "state": "awayIntro" }, - { "var": "isNotInAir", "state": "idle" } + { "var": "isNotInAir", "state": "landStandImpact" } ] }, { @@ -457,7 +457,51 @@ "interpType": "snapshotPrev", "transitions": [ { "var": "isAway", "state": "awayIntro" }, - { "var": "isNotInAir", "state": "idle" } + { "var": "isNotInAir", "state": "landRun" } + ] + }, + { + "id": "landStandImpact", + "interpTarget": 6, + "interpDuration": 4, + "transitions": [ + { "var": "isAway", "state": "awayIntro" }, + { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoffStand", "state": "takeoffStand" }, + { "var": "isTakeoffRun", "state": "takeoffRun" }, + { "var": "landStandImpactOnDone", "state": "landStand" } + ] + }, + { + "id": "landStand", + "interpTarget": 0, + "interpDuration": 1, + "transitions": [ + { "var": "isMovingForward", "state": "idleToWalkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" }, + { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoffStand", "state": "takeoffStand" }, + { "var": "isTakeoffRun", "state": "takeoffRun" }, + { "var": "isInAirStand", "state": "inAirStand" }, + { "var": "isInAirRun", "state": "inAirRun" }, + { "var": "landStandOnDone", "state": "idle" } + ] + }, + { + "id": "landRun", + "interpTarget": 1, + "interpDuration": 7, + "transitions": [ + { "var": "isAway", "state": "awayIntro" }, + { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoffStand", "state": "takeoffStand" }, + { "var": "isTakeoffRun", "state": "takeoffRun" }, + { "var": "landRunOnDone", "state": "walkFwd" } ] } ] @@ -873,6 +917,42 @@ "children": [] } ] + }, + { + "id": "landStandImpact", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_standing_land.fbx", + "startFrame": 1.0, + "endFrame": 6.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "landStand", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_standing_land.fbx", + "startFrame": 6.0, + "endFrame": 28.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "landRun", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_land.fbx", + "startFrame": 1.0, + "endFrame": 6.0, + "timeScale": 0.65, + "loopFlag": false + }, + "children": [] } ] } diff --git a/interface/resources/qml/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml similarity index 90% rename from interface/resources/qml/dialogs/RunningScripts.qml rename to interface/resources/qml/hifi/dialogs/RunningScripts.qml index c9c9062bbd..f61bf3f96d 100644 --- a/interface/resources/qml/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -3,9 +3,9 @@ import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import Qt.labs.settings 1.0 -import "../styles" as Hifi -import "../controls" as HifiControls -import "../windows" +import "../../styles" as Hifi +import "../../controls" as HifiControls +import "../../windows" Window { id: root @@ -19,12 +19,6 @@ Window { property var scripts: ScriptDiscoveryService; property var scriptsModel: scripts.scriptsModelFilter property var runningScriptsModel: ListModel { } - property var fileFilters: ListModel { - id: jsFilters - ListElement { text: "Javascript Files (*.js)"; filter: "*.js" } - ListElement { text: "All Files (*.*)"; filter: "*.*" } - } - Settings { category: "Overlay.RunningScripts" @@ -249,7 +243,30 @@ Window { } } model: scriptsModel - TableViewColumn { title: "Name"; role: "display"; } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton + onClicked: treeView.foo(); + } + + function foo() { + var localRect = Qt.rect(0, 0, width, height); + var rect = desktop.mapFromItem(treeView, 0, 0, width, height) + console.log("Local Rect " + localRect) + console.log("Rect " + rect) + console.log("Desktop size " + Qt.size(desktop.width, desktop.height)); + } + + TableViewColumn { + title: "Name"; + role: "display"; +// delegate: Text { +// text: styleData.value +// renderType: Text.QtRendering +// elite: styleData.elideMode +// } + } } HifiControls.TextField { diff --git a/interface/resources/qml/windows/Window.qml b/interface/resources/qml/windows/Window.qml index ce1834d464..d7891da2ea 100644 --- a/interface/resources/qml/windows/Window.qml +++ b/interface/resources/qml/windows/Window.qml @@ -45,6 +45,30 @@ Fadable { // The content to place inside the window, determined by the client default property var content + property var rectifier: Timer { + property bool executing: false; + interval: 100 + repeat: false + running: false + + onTriggered: { + executing = true; + x = Math.floor(x); + y = Math.floor(y); + executing = false; + } + + function begin() { + if (!executing) { + restart(); + } + } + } + + + onXChanged: rectifier.begin(); + onYChanged: rectifier.begin(); + // This mouse area serves to raise the window. To function, it must live // in the window and have a higher Z-order than the content, but follow // the position and size of frame decoration diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bf145d0e29..6db1b26db7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1260,7 +1260,7 @@ void Application::initializeUi() { auto resultVec = _compositor.screenToOverlay(toGlm(pt)); result = QPointF(resultVec.x, resultVec.y); } - return result; + return result.toPoint(); }); offscreenUi->resume(); connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect& r){ @@ -4418,7 +4418,7 @@ bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) } void Application::toggleRunningScriptsWidget() { - static const QUrl url("dialogs/RunningScripts.qml"); + static const QUrl url("hifi/dialogs/RunningScripts.qml"); DependencyManager::get()->show(url, "RunningScripts"); //if (_runningScriptsWidget->isVisible()) { // if (_runningScriptsWidget->hasFocus()) { diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index e8f9c944b7..41d8a94b0a 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -125,7 +125,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointe assert(false); } -#if WANT_DEBUG +#ifdef WANT_DEBUG qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget << "interpType = " << (int)_interpType; #endif diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 7b788fc2c3..4146d8e1cf 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -616,7 +616,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _desiredStateAge += deltaTime; if (_state == RigRole::Move) { - if (glm::length(localVel) > MOVE_ENTER_SPEED_THRESHOLD) { + glm::vec3 horizontalVel = localVel - glm::vec3(0.0f, localVel.y, 0.0f); + if (glm::length(horizontalVel) > MOVE_ENTER_SPEED_THRESHOLD) { if (fabsf(forwardSpeed) > 0.5f * fabsf(lateralSpeed)) { if (forwardSpeed > 0.0f) { // forward @@ -651,18 +652,19 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotMoving", false); } } - _animVars.set("isTurningLeft", false); - _animVars.set("isTurningRight", false); - _animVars.set("isNotTurning", true); - _animVars.set("isFlying", false); - _animVars.set("isNotFlying", true); - _animVars.set("isTakeoffStand", false); - _animVars.set("isTakeoffRun", false); - _animVars.set("isNotTakeoff", true); - _animVars.set("isInAirStand", false); - _animVars.set("isInAirRun", false); - _animVars.set("isNotInAir", true); } + _animVars.set("isTurningLeft", false); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", true); + _animVars.set("isFlying", false); + _animVars.set("isNotFlying", true); + _animVars.set("isTakeoffStand", false); + _animVars.set("isTakeoffRun", false); + _animVars.set("isNotTakeoff", true); + _animVars.set("isInAirStand", false); + _animVars.set("isInAirRun", false); + _animVars.set("isNotInAir", true); + } else if (_state == RigRole::Turn) { if (turningSpeed > 0.0f) { // turning right @@ -782,7 +784,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos // compute blend based on velocity const float JUMP_SPEED = 3.5f; - float alpha = glm::clamp(-worldVelocity.y / JUMP_SPEED, -1.0f, 1.0f) + 1.0f; + float alpha = glm::clamp(-_lastVelocity.y / JUMP_SPEED, -1.0f, 1.0f) + 1.0f; _animVars.set("inAirAlpha", alpha); } diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.h b/libraries/gl/src/gl/OffscreenQmlSurface.h index fb916178ad..9e3ee06f92 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.h +++ b/libraries/gl/src/gl/OffscreenQmlSurface.h @@ -35,7 +35,7 @@ public: OffscreenQmlSurface(); virtual ~OffscreenQmlSurface(); - using MouseTranslator = std::function; + using MouseTranslator = std::function; virtual void create(QOpenGLContext* context); void resize(const QSize& size); @@ -94,7 +94,7 @@ private: bool _polish{ true }; bool _paused{ true }; uint8_t _maxFps{ 60 }; - MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p; } }; + MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p.toPoint(); } }; };