Add undo/redo for physics

This commit is contained in:
David Rowe 2017-09-14 08:53:20 +12:00
parent 709e7e7d90
commit 18ce5ba30f
2 changed files with 77 additions and 0 deletions

View file

@ -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);
}
}
}

View file

@ -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);