From 18ce5ba30f5c1e0a0487e00a4942acd866be374f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 14 Sep 2017 08:53:20 +1200 Subject: [PATCH] Add undo/redo for physics --- scripts/vr-edit/modules/history.js | 27 +++++++++++++++ scripts/vr-edit/modules/selection.js | 50 ++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/scripts/vr-edit/modules/history.js b/scripts/vr-edit/modules/history.js index 6fb5416df3..a2b403f87a 100644 --- a/scripts/vr-edit/modules/history.js +++ b/scripts/vr-edit/modules/history.js @@ -48,6 +48,27 @@ History = (function () { MAX_HISTORY_ITEMS = 100, undoPosition = -1; // The next history item to undo; the next history item to redo = undoIndex + 1. + function doKick(entityID) { + var properties, + NO_KICK_ENTITY_TYPES = ["Text", "Web"], // These entities don't respond to gravity so don't kick them. + DYNAMIC_VELOCITY_THRESHOLD = 0.05, // See EntityMotionState.cpp DYNAMIC_LINEAR_VELOCITY_THRESHOLD + DYNAMIC_VELOCITY_KICK = { x: 0, y: 0.1, z: 0 }; + + properties = Entities.getEntityProperties(entityID, ["type", "dynamic", "gravity", "velocity"]); + if (NO_KICK_ENTITY_TYPES.indexOf(properties.type) === -1 && properties.dynamic + && Vec3.length(properties.gravity) > 0 && Vec3.length(properties.velocity) < DYNAMIC_VELOCITY_THRESHOLD) { + Entities.editEntity(entityID, { velocity: DYNAMIC_VELOCITY_KICK }); + } + } + + function kickPhysics(entityID) { + // Gives entities a small kick to start off physics, if necessary. + var KICK_DELAY = 500; // ms + + // Give physics a chance to catch up. Avoids some erratic behavior. + Script.setTimeout(function () { doKick(entityID); }, KICK_DELAY); + } + function push(undoData, redoData) { // Wipe any redo history after current undo position. if (undoPosition < history.length - 1) { @@ -118,6 +139,9 @@ History = (function () { if (undoData.setProperties) { for (i = 0, length = undoData.setProperties.length; i < length; i += 1) { Entities.editEntity(undoData.setProperties[i].entityID, undoData.setProperties[i].properties); + if (undoData.setProperties[i].properties.gravity) { + kickPhysics(undoData.setProperties[i].entityID); + } } } @@ -151,6 +175,9 @@ History = (function () { if (redoData.setProperties) { for (i = 0, length = redoData.setProperties.length; i < length; i += 1) { Entities.editEntity(redoData.setProperties[i].entityID, redoData.setProperties[i].properties); + if (redoData.setProperties[i].properties.gravity) { + kickPhysics(redoData.setProperties[i].entityID); + } } } diff --git a/scripts/vr-edit/modules/selection.js b/scripts/vr-edit/modules/selection.js index 60ef02285b..2b6155a032 100644 --- a/scripts/vr-edit/modules/selection.js +++ b/scripts/vr-edit/modules/selection.js @@ -652,6 +652,9 @@ Selection = function (side) { // - Requires child entities to be collisionless, otherwise the entity tree can become self-propelled. // See also: Groups.group() and ungroup(). var properties, + property, + undoData = [], + redoData = [], i, length; @@ -661,14 +664,61 @@ Selection = function (side) { collisionless: physicsProperties.dynamic || physicsProperties.collisionless }; for (i = 1, length = selection.length; i < length; i += 1) { + undoData.push({ + entityID: selection[i].id, + properties: { + dynamic: selection[i].dynamic, + collisionless: selection[i].collisionless + } + }); Entities.editEntity(selection[i].id, properties); + undoData.push({ + entityID: selection[i].id, + properties: properties + }); } + // Undo data. + properties = { + position: selection[0].position, + rotation: selection[0].rotation, + velocity: Vec3.ZERO, + angularVelocity: Vec3.ZERO + }; + for (property in physicsProperties) { + if (physicsProperties.hasOwnProperty(property)) { + properties[property] = selectionProperties[0].properties[property]; + } + } + if (properties.userData === undefined) { + properties.userData = ""; + } + undoData.push({ + entityID: selection[0].id, + properties: properties + }); + // Set root per physicsProperties. properties = Object.clone(physicsProperties); properties.userData = updatePhysicsUserData(selection[intersectedEntityIndex].userData, physicsProperties.userData); Entities.editEntity(rootEntityID, properties); + // Redo data. + properties.position = selection[0].position; + properties.rotation = selection[0].rotation; + properties.velocity = Vec3.ZERO; + properties.angularVelocity = Vec3.ZERO; + redoData.push({ + entityID: selection[0].id, + properties: properties + }); + + // Add history entry. + History.push( + { setProperties: undoData }, + { setProperties: redoData } + ); + // Kick off physics if necessary. if (physicsProperties.dynamic) { kickPhysics(rootEntityID);