mirror of
https://github.com/overte-org/overte.git
synced 2025-06-21 22:40:15 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into mouse_pick_in_oculus
This commit is contained in:
commit
abe9704dbc
31 changed files with 2401 additions and 6044 deletions
|
@ -2807,6 +2807,7 @@ function mouseReleaseEvent(event) {
|
|||
// exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that
|
||||
// added it.
|
||||
var modelMenuAddedDelete = false;
|
||||
var originalLightsArePickable = Entities.getLightsArePickable();
|
||||
function setupModelMenus() {
|
||||
print("setupModelMenus()");
|
||||
// adj our menuitems
|
||||
|
@ -2824,15 +2825,18 @@ function setupModelMenus() {
|
|||
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Large Models", shortcutKey: "CTRL+META+L",
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L",
|
||||
afterItem: "Paste Models", isCheckable: true });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Small Models", shortcutKey: "CTRL+META+S",
|
||||
afterItem: "Allow Select Large Models", isCheckable: true });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S",
|
||||
afterItem: "Allow Selecting of Large Models", isCheckable: true });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L",
|
||||
afterItem: "Allow Selecting of Small Models", isCheckable: true });
|
||||
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" });
|
||||
|
||||
Entities.setLightsArePickable(false);
|
||||
|
||||
}
|
||||
|
||||
|
@ -2846,8 +2850,9 @@ function cleanupModelMenus() {
|
|||
|
||||
Menu.removeMenuItem("Edit", "Model List...");
|
||||
Menu.removeMenuItem("Edit", "Paste Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Select Large Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Select Small Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
|
||||
|
||||
Menu.removeSeparator("File", "Models");
|
||||
Menu.removeMenuItem("File", "Export Models");
|
||||
|
@ -2865,6 +2870,7 @@ function scriptEnding() {
|
|||
if (exportMenu) {
|
||||
exportMenu.close();
|
||||
}
|
||||
Entities.setLightsArePickable(originalLightsArePickable);
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
|
@ -2890,10 +2896,12 @@ function showPropertiesForm(editModelID) {
|
|||
|
||||
function handeMenuEvent(menuItem) {
|
||||
print("menuItemEvent() in JS... menuItem=" + menuItem);
|
||||
if (menuItem == "Allow Select Small Models") {
|
||||
allowSmallModels = Menu.isOptionChecked("Allow Select Small Models");
|
||||
} else if (menuItem == "Allow Select Large Models") {
|
||||
allowLargeModels = Menu.isOptionChecked("Allow Select Large Models");
|
||||
if (menuItem == "Allow Selecting of Small Models") {
|
||||
allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models");
|
||||
} else if (menuItem == "Allow Selecting of Large Models") {
|
||||
allowLargeModels = Menu.isOptionChecked("Allow Selecting of Large Models");
|
||||
} else if (menuItem == "Allow Selecting of Lights") {
|
||||
Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights"));
|
||||
} else if (menuItem == "Delete") {
|
||||
if (leftController.grabbing) {
|
||||
print(" Delete Entity.... leftController.entityID="+ leftController.entityID);
|
||||
|
|
|
@ -16,11 +16,15 @@ var AXIS_STRAFE = Joysticks.AXIS_LEFT_X;
|
|||
var AXIS_FORWARD = Joysticks.AXIS_LEFT_Y;
|
||||
var AXIS_ROTATE = Joysticks.AXIS_RIGHT_X;
|
||||
|
||||
var BUTTON_SPRINT = Joysticks.BUTTON_LEFT_STICK;
|
||||
|
||||
var BUTTON_TOGGLE_MIRROR = Joysticks.BUTTON_FACE_LEFT;
|
||||
|
||||
var BUTTON_TURN_AROUND = Joysticks.BUTTON_RIGHT_STICK;
|
||||
|
||||
var BUTTON_FLY_UP = Joysticks.BUTTON_RIGHT_SHOULDER;
|
||||
var BUTTON_FLY_DOWN = Joysticks.BUTTON_LEFT_SHOULDER;
|
||||
var BUTTON_WARP = Joysticks.BUTTON_FACE_RIGHT;
|
||||
var BUTTON_WARP = null; // Disable for now
|
||||
|
||||
var BUTTON_WARP_FORWARD = Joysticks.BUTTON_DPAD_UP;
|
||||
var BUTTON_WARP_BACKWARD = Joysticks.BUTTON_DPAD_DOWN;
|
||||
|
@ -31,7 +35,8 @@ var BUTTON_WARP_RIGHT = Joysticks.BUTTON_DPAD_RIGHT;
|
|||
var WARP_DISTANCE = 1;
|
||||
|
||||
// Walk speed in m/s
|
||||
var MOVE_SPEED = 2;
|
||||
var MOVE_SPEED = 0.5;
|
||||
var MOVE_SPRINT_SPEED = 2;
|
||||
|
||||
// Amount to rotate in radians
|
||||
var ROTATE_INCREMENT = Math.PI / 8;
|
||||
|
@ -46,9 +51,14 @@ var WARP_PICK_MAX_DISTANCE = 100;
|
|||
var flyDownButtonState = false;
|
||||
var flyUpButtonState = false;
|
||||
|
||||
// When toggling to mirror mode, this stores the mode the user was previously in
|
||||
// so it can be toggled back to.
|
||||
var toggledFromCameraMode = 'first person';
|
||||
|
||||
// Current move direction, axis aligned - that is, looking down and moving forward
|
||||
// will not move you into the ground, but instead will keep you on the horizontal plane.
|
||||
var moveDirection = { x: 0, y: 0, z: 0 };
|
||||
var sprintButtonState = false;
|
||||
|
||||
var warpActive = false;
|
||||
var warpPosition = { x: 0, y: 0, z: 0 };
|
||||
|
@ -173,6 +183,18 @@ function reportButtonValue(button, newValue, oldValue) {
|
|||
MyAvatar.orientation = Quat.multiply(
|
||||
Quat.fromPitchYawRollRadians(0, Math.PI, 0), MyAvatar.orientation);
|
||||
}
|
||||
} else if (button == BUTTON_SPRINT) {
|
||||
sprintButtonState = newValue;
|
||||
} else if (button == BUTTON_TOGGLE_MIRROR) {
|
||||
if (newValue) {
|
||||
var currentMode = Camera.mode;
|
||||
if (currentMode != "mirror") {
|
||||
toggledFromCameraMode = currentMode;
|
||||
}
|
||||
Camera.mode = "mirror";
|
||||
} else {
|
||||
Camera.mode = toggledFromCameraMode;
|
||||
}
|
||||
} else if (newValue) {
|
||||
var direction = null;
|
||||
|
||||
|
@ -209,9 +231,10 @@ function update(dt) {
|
|||
var move = copyVec3(moveDirection);
|
||||
move.y = 0;
|
||||
if (Vec3.length(move) > 0) {
|
||||
speed = sprintButtonState ? MOVE_SPRINT_SPEED : MOVE_SPEED;
|
||||
velocity = Vec3.multiplyQbyV(Camera.getOrientation(), move);
|
||||
velocity.y = 0;
|
||||
velocity = Vec3.multiply(Vec3.normalize(velocity), MOVE_SPEED);
|
||||
velocity = Vec3.multiply(Vec3.normalize(velocity), speed);
|
||||
}
|
||||
|
||||
if (moveDirection.y != 0) {
|
||||
|
|
|
@ -33,10 +33,12 @@ SelectionManager = (function() {
|
|||
that.localRotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||
that.localPosition = { x: 0, y: 0, z: 0 };
|
||||
that.localDimensions = { x: 0, y: 0, z: 0 };
|
||||
that.localRegistrationPoint = { x: 0.5, y: 0.5, z: 0.5 };
|
||||
|
||||
that.worldRotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||
that.worldPosition = { x: 0, y: 0, z: 0 };
|
||||
that.worldDimensions = { x: 0, y: 0, z: 0 };
|
||||
that.worldRegistrationPoint = { x: 0.5, y: 0.5, z: 0.5 };
|
||||
that.centerPosition = { x: 0, y: 0, z: 0 };
|
||||
|
||||
that.saveProperties = function() {
|
||||
|
@ -153,6 +155,7 @@ SelectionManager = (function() {
|
|||
that.localDimensions = properties.dimensions;
|
||||
that.localPosition = properties.position;
|
||||
that.localRotation = properties.rotation;
|
||||
that.localRegistrationPoint = properties.registrationPoint;
|
||||
|
||||
that.worldDimensions = properties.boundingBox.dimensions;
|
||||
that.worldPosition = properties.boundingBox.center;
|
||||
|
@ -215,6 +218,17 @@ function normalizeDegrees(degrees) {
|
|||
return degrees;
|
||||
}
|
||||
|
||||
// Return the enter position of an entity relative to it's registrationPoint
|
||||
// A registration point of (0.5, 0.5, 0.5) will have an offset of (0, 0, 0)
|
||||
// A registration point of (1.0, 1.0, 1.0) will have an offset of (-dimensions.x / 2, -dimensions.y / 2, -dimensions.z / 2)
|
||||
function getRelativeCenterPosition(dimensions, registrationPoint) {
|
||||
return {
|
||||
x: -dimensions.x * (registrationPoint.x - 0.5),
|
||||
y: -dimensions.y * (registrationPoint.y - 0.5),
|
||||
z: -dimensions.z * (registrationPoint.z - 0.5),
|
||||
}
|
||||
}
|
||||
|
||||
SelectionDisplay = (function () {
|
||||
var that = {};
|
||||
|
||||
|
@ -982,26 +996,36 @@ SelectionDisplay = (function () {
|
|||
that.updateRotationHandles();
|
||||
that.highlightSelectable();
|
||||
|
||||
var rotation, dimensions, position;
|
||||
var rotation, dimensions, position, registrationPoint;
|
||||
|
||||
if (spaceMode == SPACE_LOCAL) {
|
||||
rotation = SelectionManager.localRotation;
|
||||
dimensions = SelectionManager.localDimensions;
|
||||
position = SelectionManager.localPosition;
|
||||
registrationPoint = SelectionManager.localRegistrationPoint;
|
||||
} else {
|
||||
rotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||
dimensions = SelectionManager.worldDimensions;
|
||||
position = SelectionManager.worldPosition;
|
||||
registrationPoint = SelectionManager.worldRegistrationPoint;
|
||||
}
|
||||
|
||||
var halfDimensions = Vec3.multiply(0.5, dimensions);
|
||||
var registrationPointDimensions = {
|
||||
x: dimensions.x * registrationPoint.x,
|
||||
y: dimensions.y * registrationPoint.y,
|
||||
z: dimensions.z * registrationPoint.z,
|
||||
};
|
||||
|
||||
var left = -halfDimensions.x;
|
||||
var right = halfDimensions.x;
|
||||
var top = halfDimensions.y;
|
||||
var bottom = -halfDimensions.y;
|
||||
var front = far = halfDimensions.z;
|
||||
var near = -halfDimensions.z;
|
||||
// Center of entity, relative to registration point
|
||||
center = getRelativeCenterPosition(dimensions, registrationPoint);
|
||||
|
||||
// Distances in world coordinates relative to the registration point
|
||||
var left = -registrationPointDimensions.x;
|
||||
var right = dimensions.x - registrationPointDimensions.x;
|
||||
var bottom = -registrationPointDimensions.y;
|
||||
var top = dimensions.y - registrationPointDimensions.y;
|
||||
var near = -registrationPointDimensions.z;
|
||||
var front = far = dimensions.z - registrationPointDimensions.z;
|
||||
|
||||
var worldTop = SelectionManager.worldDimensions.y / 2;
|
||||
|
||||
|
@ -1014,25 +1038,25 @@ SelectionDisplay = (function () {
|
|||
var LTF = { x: left, y: top, z: far };
|
||||
var RTF = { x: right, y: top, z: far };
|
||||
|
||||
var TOP = { x: 0, y: top, z: 0 };
|
||||
var BOTTOM = { x: 0, y: bottom, z: 0 };
|
||||
var LEFT = { x: left, y: 0, z: 0 };
|
||||
var RIGHT = { x: right, y: 0, z: 0 };
|
||||
var NEAR = { x: 0, y: 0, z: near };
|
||||
var FAR = { x: 0, y: 0, z: far };
|
||||
var TOP = { x: center.x, y: top, z: center.z };
|
||||
var BOTTOM = { x: center.x, y: bottom, z: center.z };
|
||||
var LEFT = { x: left, y: center.y, z: center.z };
|
||||
var RIGHT = { x: right, y: center.y, z: center.z };
|
||||
var NEAR = { x: center.x, y: center.y, z: near };
|
||||
var FAR = { x: center.x, y: center.y, z: far };
|
||||
|
||||
var EdgeTR = { x: right, y: top, z: 0 };
|
||||
var EdgeTL = { x: left, y: top, z: 0 };
|
||||
var EdgeTF = { x: 0, y: top, z: front };
|
||||
var EdgeTN = { x: 0, y: top, z: near };
|
||||
var EdgeBR = { x: right, y: bottom, z: 0 };
|
||||
var EdgeBL = { x: left, y: bottom, z: 0 };
|
||||
var EdgeBF = { x: 0, y: bottom, z: front };
|
||||
var EdgeBN = { x: 0, y: bottom, z: near };
|
||||
var EdgeNR = { x: right, y: 0, z: near };
|
||||
var EdgeNL = { x: left, y: 0, z: near };
|
||||
var EdgeFR = { x: right, y: 0, z: front };
|
||||
var EdgeFL = { x: left, y: 0, z: front };
|
||||
var EdgeTR = { x: right, y: top, z: center.z };
|
||||
var EdgeTL = { x: left, y: top, z: center.z };
|
||||
var EdgeTF = { x: center.x, y: top, z: front };
|
||||
var EdgeTN = { x: center.x, y: top, z: near };
|
||||
var EdgeBR = { x: right, y: bottom, z: center.z };
|
||||
var EdgeBL = { x: left, y: bottom, z: center.z };
|
||||
var EdgeBF = { x: center.x, y: bottom, z: front };
|
||||
var EdgeBN = { x: center.x, y: bottom, z: near };
|
||||
var EdgeNR = { x: right, y: center.y, z: near };
|
||||
var EdgeNL = { x: left, y: center.y, z: near };
|
||||
var EdgeFR = { x: right, y: center.y, z: front };
|
||||
var EdgeFL = { x: left, y: center.y, z: front };
|
||||
|
||||
LBN = Vec3.multiplyQbyV(rotation, LBN);
|
||||
RBN = Vec3.multiplyQbyV(rotation, RBN);
|
||||
|
@ -1111,8 +1135,10 @@ SelectionDisplay = (function () {
|
|||
Overlays.editOverlay(grabberNEAR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: NEAR });
|
||||
Overlays.editOverlay(grabberFAR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: FAR });
|
||||
|
||||
var boxPosition = Vec3.multiplyQbyV(rotation, center);
|
||||
boxPosition = Vec3.sum(position, boxPosition);
|
||||
Overlays.editOverlay(selectionBox, {
|
||||
position: position,
|
||||
position: boxPosition,
|
||||
dimensions: dimensions,
|
||||
rotation: rotation,
|
||||
visible: !(mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL"),
|
||||
|
@ -1140,8 +1166,17 @@ SelectionDisplay = (function () {
|
|||
if (selectionManager.selections.length > 1) {
|
||||
for (; i < selectionManager.selections.length; i++) {
|
||||
var properties = Entities.getEntityProperties(selectionManager.selections[i]);
|
||||
|
||||
// Adjust overlay position to take registrationPoint into account
|
||||
// centeredRP = registrationPoint with range [-0.5, 0.5]
|
||||
var centeredRP = Vec3.subtract(properties.registrationPoint, { x: 0.5, y: 0.5, z: 0.5 });
|
||||
var offset = vec3Mult(properties.dimensions, centeredRP);
|
||||
offset = Vec3.multiply(-1, offset);
|
||||
offset = Vec3.multiplyQbyV(properties.rotation, offset);
|
||||
var boxPosition = Vec3.sum(properties.position, offset);
|
||||
|
||||
Overlays.editOverlay(selectionBoxes[i], {
|
||||
position: properties.position,
|
||||
position: boxPosition,
|
||||
rotation: properties.rotation,
|
||||
dimensions: properties.dimensions,
|
||||
visible: true,
|
||||
|
@ -1400,6 +1435,8 @@ SelectionDisplay = (function () {
|
|||
var initialDimensions = null;
|
||||
var initialIntersection = null;
|
||||
var initialProperties = null;
|
||||
var registrationPoint = null;
|
||||
var deltaPivot = null;
|
||||
var pickRayPosition = null;
|
||||
var rotation = null;
|
||||
|
||||
|
@ -1412,18 +1449,29 @@ SelectionDisplay = (function () {
|
|||
rotation = SelectionManager.localRotation;
|
||||
initialPosition = SelectionManager.localPosition;
|
||||
initialDimensions = SelectionManager.localDimensions;
|
||||
registrationPoint = SelectionManager.localRegistrationPoint;
|
||||
} else {
|
||||
rotation = SelectionManager.worldRotation;
|
||||
initialPosition = SelectionManager.worldPosition;
|
||||
initialDimensions = SelectionManager.worldDimensions;
|
||||
registrationPoint = SelectionManager.worldRegistrationPoint;
|
||||
}
|
||||
|
||||
var scaledOffset = {
|
||||
x: initialDimensions.x * offset.x * 0.5,
|
||||
y: initialDimensions.y * offset.y * 0.5,
|
||||
z: initialDimensions.z * offset.z * 0.5,
|
||||
};
|
||||
pickRayPosition = Vec3.sum(initialPosition, Vec3.multiplyQbyV(rotation, scaledOffset));
|
||||
// Modify range of registrationPoint to be [-0.5, 0.5]
|
||||
var centeredRP = Vec3.subtract(registrationPoint, { x: 0.5, y: 0.5, z: 0.5 });
|
||||
|
||||
// Scale pivot to be in the same range as registrationPoint
|
||||
var scaledPivot = Vec3.multiply(0.5, pivot)
|
||||
deltaPivot = Vec3.subtract(centeredRP, scaledPivot);
|
||||
|
||||
var scaledOffset = Vec3.multiply(0.5, offset);
|
||||
|
||||
// Offset from the registration point
|
||||
offsetRP = Vec3.subtract(scaledOffset, centeredRP);
|
||||
|
||||
// Scaled offset in world coordinates
|
||||
var scaledOffsetWorld = vec3Mult(initialDimensions, offsetRP);
|
||||
pickRayPosition = Vec3.sum(initialPosition, Vec3.multiplyQbyV(rotation, scaledOffsetWorld));
|
||||
|
||||
if (numDimensions == 1 && mask.x) {
|
||||
var start = Vec3.multiplyQbyV(rotation, { x: -10000, y: 0, z: 0 });
|
||||
|
@ -1550,8 +1598,7 @@ SelectionDisplay = (function () {
|
|||
newDimensions.y = Math.max(newDimensions.y, MINIMUM_DIMENSION);
|
||||
newDimensions.z = Math.max(newDimensions.z, MINIMUM_DIMENSION);
|
||||
|
||||
var p = Vec3.multiply(0.5, pivot);
|
||||
var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(p, changeInDimensions));
|
||||
var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(deltaPivot, changeInDimensions));
|
||||
var newPosition = Vec3.sum(initialPosition, changeInPosition);
|
||||
|
||||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
||||
|
@ -1589,20 +1636,19 @@ SelectionDisplay = (function () {
|
|||
|
||||
function addStretchTool(overlay, mode, pivot, direction, offset) {
|
||||
if (!pivot) {
|
||||
pivot = Vec3.multiply(-1, direction);
|
||||
pivot.y = direction.y;
|
||||
pivot = direction;
|
||||
}
|
||||
var tool = makeStretchTool(mode, direction, pivot, offset);
|
||||
|
||||
addGrabberTool(overlay, tool);
|
||||
}
|
||||
|
||||
addStretchTool(grabberNEAR, "STRETCH_NEAR", { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 });
|
||||
addStretchTool(grabberFAR, "STRETCH_FAR", { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 });
|
||||
addStretchTool(grabberTOP, "STRETCH_TOP", { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 });
|
||||
addStretchTool(grabberBOTTOM, "STRETCH_BOTTOM", { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 });
|
||||
addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 });
|
||||
addStretchTool(grabberLEFT, "STRETCH_LEFT", { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 });
|
||||
addStretchTool(grabberNEAR, "STRETCH_NEAR", { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 });
|
||||
addStretchTool(grabberFAR, "STRETCH_FAR", { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 });
|
||||
addStretchTool(grabberTOP, "STRETCH_TOP", { x: 0, y: -1, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 });
|
||||
addStretchTool(grabberBOTTOM, "STRETCH_BOTTOM", { x: 0, y: 1, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 });
|
||||
addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { x: -1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 });
|
||||
addStretchTool(grabberLEFT, "STRETCH_LEFT", { x: 1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 });
|
||||
|
||||
addStretchTool(grabberLBN, "STRETCH_LBN", null, {x: 1, y: 0, z: 1}, { x: -1, y: -1, z: -1 });
|
||||
addStretchTool(grabberRBN, "STRETCH_RBN", null, {x: -1, y: 0, z: 1}, { x: 1, y: -1, z: -1 });
|
||||
|
@ -1740,17 +1786,27 @@ SelectionDisplay = (function () {
|
|||
}
|
||||
|
||||
var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 });
|
||||
|
||||
// Entities should only reposition if we are rotating multiple selections around
|
||||
// the selections center point. Otherwise, the rotation will be around the entities
|
||||
// registration point which does not need repositioning.
|
||||
var reposition = SelectionManager.selections.length > 1;
|
||||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
||||
var entityID = SelectionManager.selections[i];
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
var initialProperties = SelectionManager.savedProperties[entityID.id];
|
||||
|
||||
var newProperties = {
|
||||
rotation: Quat.multiply(yawChange, initialProperties.rotation),
|
||||
};
|
||||
|
||||
if (reposition) {
|
||||
var dPos = Vec3.subtract(initialProperties.position, initialPosition);
|
||||
dPos = Vec3.multiplyQbyV(yawChange, dPos);
|
||||
newProperties.position = Vec3.sum(initialPosition, dPos);
|
||||
}
|
||||
|
||||
Entities.editEntity(entityID, {
|
||||
position: Vec3.sum(initialPosition, dPos),
|
||||
rotation: Quat.multiply(yawChange, initialProperties.rotation),
|
||||
});
|
||||
Entities.editEntity(entityID, newProperties);
|
||||
}
|
||||
|
||||
updateRotationDegreesOverlay(angleFromZero, yawHandleRotation, yawCenter);
|
||||
|
|
|
@ -90,7 +90,6 @@ Grid = function(opts) {
|
|||
}
|
||||
|
||||
that.snapToSpacing = function(delta, majorOnly) {
|
||||
print('snaptogrid? ' + snapToGrid);
|
||||
if (!snapToGrid) {
|
||||
return delta;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//
|
||||
// walkObjects.js
|
||||
//
|
||||
// version 1.001
|
||||
// version 1.003
|
||||
//
|
||||
// Created by David Wooldridge, Autumn 2014
|
||||
//
|
||||
// Motion, state and Transition objects for use by the walk.js script v1.1
|
||||
// Motion, state and Transition objects for use by the walk.js script v1.12
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -14,41 +14,21 @@
|
|||
// constructor for the Motion object
|
||||
Motion = function() {
|
||||
|
||||
this.setGender = function(gender) {
|
||||
|
||||
this.avatarGender = gender;
|
||||
|
||||
switch(this.avatarGender) {
|
||||
|
||||
case MALE:
|
||||
|
||||
this.selWalk = walkAssets.maleStandardWalk;
|
||||
this.selStand = walkAssets.maleStandOne;
|
||||
this.selFlyUp = walkAssets.maleFlyingUp;
|
||||
this.selFly = walkAssets.maleFlying;
|
||||
this.selFlyDown = walkAssets.maleFlyingDown;
|
||||
this.selSideStepLeft = walkAssets.maleSideStepLeft;
|
||||
this.selSideStepRight = walkAssets.maleSideStepRight;
|
||||
this.curAnim = this.selStand;
|
||||
return;
|
||||
|
||||
case FEMALE:
|
||||
|
||||
this.selWalk = walkAssets.femaleStandardWalk;
|
||||
this.selStand = walkAssets.femaleStandOne;
|
||||
this.selFlyUp = walkAssets.femaleFlyingUp;
|
||||
this.selFly = walkAssets.femaleFlying;
|
||||
this.selFlyDown = walkAssets.femaleFlyingDown;
|
||||
this.selSideStepLeft = walkAssets.femaleSideStepLeft;
|
||||
this.selSideStepRight = walkAssets.femaleSideStepRight;
|
||||
this.curAnim = this.selStand;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// selected animations
|
||||
this.selectedWalk = undefined;
|
||||
this.selectedStand = undefined;
|
||||
this.selectedFly = undefined;
|
||||
this.selectedFlyUp = undefined;
|
||||
this.selectedFlyDown = undefined;
|
||||
this.selectedFlyBlend = undefined;
|
||||
this.selectedHovering = undefined;
|
||||
this.selectedSideStepLeft = undefined;
|
||||
this.selectedSideStepRight = undefined;
|
||||
|
||||
// if Hydras are connected, the only way to enable use is by never setting any rotations on the arm joints
|
||||
this.hydraCheck = function() {
|
||||
|
||||
// function courtesy of Thijs Wenker, frisbee.js
|
||||
// function courtesy of Thijs Wenker (frisbee.js)
|
||||
var numberOfButtons = Controller.getNumberOfButtons();
|
||||
var numberOfTriggers = Controller.getNumberOfTriggers();
|
||||
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
|
||||
|
@ -60,33 +40,65 @@ Motion = function() {
|
|||
// settings
|
||||
this.armsFree = this.hydraCheck(); // automatically sets true for Hydra support - temporary fix
|
||||
this.makesFootStepSounds = true;
|
||||
this.avatarGender = MALE;
|
||||
this.motionPitchMax = 60;
|
||||
this.motionRollMax = 40;
|
||||
this.avatarGender = undefined;
|
||||
this.setGender = function(gender) {
|
||||
|
||||
// timing
|
||||
this.frameStartTime = 0; // used for measuring frame execution times
|
||||
this.frameExecutionTimeMax = 0; // keep track of the longest frame execution time
|
||||
this.cumulativeTime = 0.0;
|
||||
this.lastWalkStartTime = 0;
|
||||
this.avatarGender = gender;
|
||||
|
||||
// selected animations
|
||||
this.selWalk = walkAssets.maleStandardWalk;
|
||||
this.selStand = walkAssets.maleStandOne;
|
||||
this.selFlyUp = walkAssets.maleFlyingUp;
|
||||
this.selFly = walkAssets.maleFlying;
|
||||
this.selFlyDown = walkAssets.maleFlyingDown;
|
||||
this.selSideStepLeft = walkAssets.maleSideStepLeft;
|
||||
this.selSideStepRight = walkAssets.maleSideStepRight;
|
||||
switch(this.avatarGender) {
|
||||
|
||||
// the currently selected animation, joint and transition
|
||||
this.curAnim = this.selStand;
|
||||
this.curJointIndex = 0;
|
||||
this.curTransition = null;
|
||||
case MALE:
|
||||
|
||||
// zero out avi's joints, curl the fingers nicely then take some measurements
|
||||
this.selectedWalk = walkAssets.maleStandardWalk;
|
||||
this.selectedStand = walkAssets.maleStandOne;
|
||||
this.selectedFlyUp = walkAssets.maleFlyingUp;
|
||||
this.selectedFly = walkAssets.maleFlying;
|
||||
this.selectedFlyDown = walkAssets.maleFlyingDown;
|
||||
this.selectedFlyBlend = walkAssets.maleFlyingBlend;
|
||||
this.selectedHovering = walkAssets.maleHovering;
|
||||
this.selectedSideStepLeft = walkAssets.maleSideStepLeft;
|
||||
this.selectedSideStepRight = walkAssets.maleSideStepRight;
|
||||
this.currentAnimation = this.selectedStand;
|
||||
return;
|
||||
|
||||
case FEMALE:
|
||||
|
||||
this.selectedWalk = walkAssets.femaleStandardWalk;
|
||||
this.selectedStand = walkAssets.femaleStandOne;
|
||||
this.selectedFlyUp = walkAssets.femaleFlyingUp;
|
||||
this.selectedFly = walkAssets.femaleFlying;
|
||||
this.selectedFlyDown = walkAssets.femaleFlyingDown;
|
||||
this.selectedFlyBlend = walkAssets.femaleFlyingBlend;
|
||||
this.selectedHovering = walkAssets.femaleHovering;
|
||||
this.selectedSideStepLeft = walkAssets.femaleSideStepLeft;
|
||||
this.selectedSideStepRight = walkAssets.femaleSideStepRight;
|
||||
this.currentAnimation = this.selectedStand;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.setGender(MALE);
|
||||
|
||||
// calibration
|
||||
this.calibration = {
|
||||
|
||||
pitchMax: 8,
|
||||
maxWalkAcceleration: 5,
|
||||
maxWalkDeceleration: 25,
|
||||
rollMax: 80,
|
||||
angularVelocityMax: 70,
|
||||
hipsToFeet: MyAvatar.getJointPosition("Hips").y - MyAvatar.getJointPosition("RightFoot").y,
|
||||
}
|
||||
|
||||
// used to make sure at least one step has been taken when transitioning from a walk cycle
|
||||
this.elapsedFTDegrees = 0;
|
||||
|
||||
// the current animation and transition
|
||||
this.currentAnimation = this.selectedStand;
|
||||
this.currentTransition = null;
|
||||
|
||||
// zero out avi's joints and pose the fingers
|
||||
this.avatarJointNames = MyAvatar.getJointNames();
|
||||
if (!this.armsFree) {
|
||||
this.poseFingers = function() {
|
||||
|
||||
for (var i = 0; i < this.avatarJointNames.length; i++) {
|
||||
|
||||
|
@ -102,38 +114,138 @@ Motion = function() {
|
|||
} else if (i > 57 || i < 62) {
|
||||
// right hand thumb
|
||||
MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(4, 0, 0));
|
||||
} else {
|
||||
// zero out the remaining joints
|
||||
MyAvatar.clearJointData(this.avatarJointNames[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!this.armsFree) {
|
||||
this.poseFingers();
|
||||
}
|
||||
|
||||
this.footRPos = MyAvatar.getJointPosition("RightFoot");
|
||||
this.hipsToFeet = MyAvatar.getJointPosition("Hips").y - this.footRPos.y;
|
||||
|
||||
// walkwheel (foot / ground speed matching)
|
||||
// frequency time wheel (foot / ground speed matching)
|
||||
this.direction = FORWARDS;
|
||||
this.nextStep = RIGHT;
|
||||
this.nFrames = 0;
|
||||
this.strideLength = this.selWalk.calibration.strideLengthForwards;
|
||||
this.walkWheelPos = 0;
|
||||
this.strideLength = this.selectedWalk.calibration.strideLengthForwards;
|
||||
this.frequencyTimeWheelPos = 0;
|
||||
this.frequencyTimeWheelRadius = 0.5;
|
||||
this.recentFrequencyTimeIncrements = [];
|
||||
for(var i = 0; i < 8; i++) {
|
||||
|
||||
this.advanceWalkWheel = function(angle){
|
||||
this.walkWheelPos += angle;
|
||||
if (motion.walkWheelPos >= 360) {
|
||||
this.walkWheelPos = this.walkWheelPos % 360;
|
||||
this.recentFrequencyTimeIncrements.push(0);
|
||||
}
|
||||
this.averageFrequencyTimeIncrement = 0;
|
||||
|
||||
this.advanceFrequencyTimeWheel = function(angle){
|
||||
|
||||
this.elapsedFTDegrees += angle;
|
||||
|
||||
this.recentFrequencyTimeIncrements.push(angle);
|
||||
this.recentFrequencyTimeIncrements.shift();
|
||||
for(increment in this.recentFrequencyTimeIncrements) {
|
||||
this.averageFrequencyTimeIncrement += this.recentFrequencyTimeIncrements[increment];
|
||||
}
|
||||
this.averageFrequencyTimeIncrement /= this.recentFrequencyTimeIncrements.length;
|
||||
|
||||
this.frequencyTimeWheelPos += angle;
|
||||
if (this.frequencyTimeWheelPos >= 360) {
|
||||
this.frequencyTimeWheelPos = this.frequencyTimeWheelPos % 360;
|
||||
}
|
||||
}
|
||||
|
||||
// last frame history
|
||||
// currently disabled due to breaking changes in js audio native objects
|
||||
this.playFootStepSound = function(side) {
|
||||
|
||||
//var options = new AudioInjectionOptions();
|
||||
//options.position = Camera.getPosition();
|
||||
//options.volume = 0.3;
|
||||
//var soundNumber = 2; // 0 to 2
|
||||
//if (side === RIGHT && motion.makesFootStepSounds) {
|
||||
// Audio.playS ound(walkAssets.footsteps[soundNumber + 1], options);
|
||||
//} else if (side === LEFT && motion.makesFootStepSounds) {
|
||||
// Audio.playSound(walkAssets.footsteps[soundNumber], options);
|
||||
//}
|
||||
}
|
||||
|
||||
// history
|
||||
this.lastDirection = 0;
|
||||
this.lastVelocity = 0;
|
||||
this.lastStrideLength = 0; // kept for use during transitions
|
||||
this.lastSpeed = 0;
|
||||
this.transitionCount = 0;
|
||||
this.lastDistanceToVoxels = 0;
|
||||
|
||||
}; // end Motion constructor
|
||||
|
||||
// finite state machine
|
||||
|
||||
spatialInformation = (function() {
|
||||
|
||||
return {
|
||||
|
||||
distanceToVoxels: function() {
|
||||
|
||||
// use the blocking version of findRayIntersection to avoid errors
|
||||
var pickRay = {origin: MyAvatar.position, direction: {x:0, y:-1, z:0}};
|
||||
return Voxels.findRayIntersectionBlocking(pickRay).distance - motion.calibration.hipsToFeet;;
|
||||
}
|
||||
}
|
||||
|
||||
})(); // end spatialInformation object literal
|
||||
|
||||
|
||||
// animation file manipulation
|
||||
animation = (function() {
|
||||
|
||||
return {
|
||||
|
||||
zeroAnimation: function(animation) {
|
||||
|
||||
for (i in animation.joints) {
|
||||
|
||||
animation.joints[i].pitch = 0;
|
||||
animation.joints[i].yaw = 0;
|
||||
animation.joints[i].roll = 0;
|
||||
animation.joints[i].pitchPhase = 0;
|
||||
animation.joints[i].yawPhase = 0;
|
||||
animation.joints[i].rollPhase = 0;
|
||||
animation.joints[i].pitchOffset = 0;
|
||||
animation.joints[i].yawOffset = 0;
|
||||
animation.joints[i].rollOffset = 0;
|
||||
if (i === 0) {
|
||||
// Hips only
|
||||
animation.joints[i].thrust = 0;
|
||||
animation.joints[i].sway = 0;
|
||||
animation.joints[i].bob = 0;
|
||||
animation.joints[i].thrustPhase = 0;
|
||||
animation.joints[i].swayPhase = 0;
|
||||
animation.joints[i].bobPhase = 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
blendAnimation: function(sourceAnimation, targetAnimation, percent) {
|
||||
|
||||
for (i in targetAnimation.joints) {
|
||||
|
||||
targetAnimation.joints[i].pitch += percent * sourceAnimation.joints[i].pitch;
|
||||
targetAnimation.joints[i].yaw += percent * sourceAnimation.joints[i].yaw;
|
||||
targetAnimation.joints[i].roll += percent * sourceAnimation.joints[i].roll;
|
||||
targetAnimation.joints[i].pitchPhase += percent * sourceAnimation.joints[i].pitchPhase;
|
||||
targetAnimation.joints[i].yawPhase += percent * sourceAnimation.joints[i].yawPhase;
|
||||
targetAnimation.joints[i].rollPhase += percent * sourceAnimation.joints[i].rollPhase;
|
||||
targetAnimation.joints[i].pitchOffset += percent * sourceAnimation.joints[i].pitchOffset;
|
||||
targetAnimation.joints[i].yawOffset += percent * sourceAnimation.joints[i].yawOffset;
|
||||
targetAnimation.joints[i].rollOffset += percent * sourceAnimation.joints[i].rollOffset;
|
||||
if (i === 0) {
|
||||
// Hips only
|
||||
targetAnimation.joints[i].thrust += percent * sourceAnimation.joints[i].thrust;
|
||||
targetAnimation.joints[i].sway += percent * sourceAnimation.joints[i].sway;
|
||||
targetAnimation.joints[i].bob += percent * sourceAnimation.joints[i].bob;
|
||||
targetAnimation.joints[i].thrustPhase += percent * sourceAnimation.joints[i].thrustPhase;
|
||||
targetAnimation.joints[i].swayPhase += percent * sourceAnimation.joints[i].swayPhase;
|
||||
targetAnimation.joints[i].bobPhase += percent * sourceAnimation.joints[i].bobPhase;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})(); // end animation object literal
|
||||
|
||||
// finite state machine. Avatar locomotion modes represent states in the FSM
|
||||
state = (function () {
|
||||
|
||||
return {
|
||||
|
@ -143,151 +255,36 @@ state = (function () {
|
|||
WALKING: 2,
|
||||
SIDE_STEP: 3,
|
||||
FLYING: 4,
|
||||
EDIT_WALK_STYLES: 5,
|
||||
EDIT_WALK_TWEAKS: 6,
|
||||
EDIT_WALK_JOINTS: 7,
|
||||
EDIT_STANDING: 8,
|
||||
EDIT_FLYING: 9,
|
||||
EDIT_FLYING_UP: 10,
|
||||
EDIT_FLYING_DOWN: 11,
|
||||
EDIT_SIDESTEP_LEFT: 12,
|
||||
EDIT_SIDESTEP_RIGHT: 14,
|
||||
currentState: this.STANDING,
|
||||
|
||||
// status vars
|
||||
powerOn: true,
|
||||
minimised: true,
|
||||
editing: false,
|
||||
editingTranslation: false,
|
||||
|
||||
setInternalState: function(newInternalState) {
|
||||
|
||||
motion.elapsedFTDegrees = 0;
|
||||
|
||||
switch (newInternalState) {
|
||||
|
||||
case this.WALKING:
|
||||
|
||||
this.currentState = this.WALKING;
|
||||
this.editing = false;
|
||||
motion.lastWalkStartTime = new Date().getTime();
|
||||
walkInterface.updateMenu();
|
||||
return;
|
||||
|
||||
case this.FLYING:
|
||||
|
||||
this.currentState = this.FLYING;
|
||||
this.editing = false;
|
||||
motion.lastWalkStartTime = 0;
|
||||
walkInterface.updateMenu();
|
||||
return;
|
||||
|
||||
case this.SIDE_STEP:
|
||||
|
||||
this.currentState = this.SIDE_STEP;
|
||||
this.editing = false;
|
||||
motion.lastWalkStartTime = new Date().getTime();
|
||||
walkInterface.updateMenu();
|
||||
return;
|
||||
|
||||
case this.EDIT_WALK_STYLES:
|
||||
|
||||
this.currentState = this.EDIT_WALK_STYLES;
|
||||
this.editing = true;
|
||||
motion.lastWalkStartTime = new Date().getTime();
|
||||
motion.curAnim = motion.selWalk;
|
||||
walkInterface.updateMenu();
|
||||
return;
|
||||
|
||||
case this.EDIT_WALK_TWEAKS:
|
||||
|
||||
this.currentState = this.EDIT_WALK_TWEAKS;
|
||||
this.editing = true;
|
||||
motion.lastWalkStartTime = new Date().getTime();
|
||||
motion.curAnim = motion.selWalk;
|
||||
walkInterface.updateMenu();
|
||||
return;
|
||||
|
||||
case this.EDIT_WALK_JOINTS:
|
||||
|
||||
this.currentState = this.EDIT_WALK_JOINTS;
|
||||
this.editing = true;
|
||||
motion.lastWalkStartTime = new Date().getTime();
|
||||
motion.curAnim = motion.selWalk;
|
||||
walkInterface.updateMenu();
|
||||
return;
|
||||
|
||||
case this.EDIT_STANDING:
|
||||
|
||||
this.currentState = this.EDIT_STANDING;
|
||||
this.editing = true;
|
||||
motion.lastWalkStartTime = 0;
|
||||
motion.curAnim = motion.selStand;
|
||||
walkInterface.updateMenu();
|
||||
return;
|
||||
|
||||
case this.EDIT_SIDESTEP_LEFT:
|
||||
|
||||
this.currentState = this.EDIT_SIDESTEP_LEFT;
|
||||
this.editing = true;
|
||||
motion.lastWalkStartTime = new Date().getTime();
|
||||
motion.curAnim = motion.selSideStepLeft;
|
||||
walkInterface.updateMenu();
|
||||
return;
|
||||
|
||||
case this.EDIT_SIDESTEP_RIGHT:
|
||||
|
||||
this.currentState = this.EDIT_SIDESTEP_RIGHT;
|
||||
this.editing = true;
|
||||
motion.lastWalkStartTime = new Date().getTime();
|
||||
motion.curAnim = motion.selSideStepRight;
|
||||
walkInterface.updateMenu();
|
||||
return;
|
||||
|
||||
case this.EDIT_FLYING:
|
||||
|
||||
this.currentState = this.EDIT_FLYING;
|
||||
this.editing = true;
|
||||
motion.lastWalkStartTime = 0;
|
||||
motion.curAnim = motion.selFly;
|
||||
walkInterface.updateMenu();
|
||||
return;
|
||||
|
||||
case this.EDIT_FLYING_UP:
|
||||
|
||||
this.currentState = this.EDIT_FLYING_UP;
|
||||
this.editing = true;
|
||||
motion.lastWalkStartTime = 0;
|
||||
motion.curAnim = motion.selFlyUp;
|
||||
walkInterface.updateMenu();
|
||||
return;
|
||||
|
||||
case this.EDIT_FLYING_DOWN:
|
||||
|
||||
this.currentState = this.EDIT_FLYING_DOWN;
|
||||
this.editing = true;
|
||||
motion.lastWalkStartTime = 0;
|
||||
motion.curAnim = motion.selFlyDown;
|
||||
walkInterface.updateMenu();
|
||||
return;
|
||||
|
||||
case this.STANDING:
|
||||
default:
|
||||
|
||||
this.currentState = this.STANDING;
|
||||
this.editing = false;
|
||||
motion.lastWalkStartTime = 0;
|
||||
motion.curAnim = motion.selStand;
|
||||
walkInterface.updateMenu();
|
||||
|
||||
// initialisation - runs at script startup only
|
||||
if (motion.strideLength === 0) {
|
||||
|
||||
motion.setGender(MALE);
|
||||
if (motion.direction === BACKWARDS) {
|
||||
motion.strideLength = motion.selWalk.calibration.strideLengthBackwards;
|
||||
} else {
|
||||
motion.strideLength = motion.selWalk.calibration.strideLengthForwards;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -295,109 +292,302 @@ state = (function () {
|
|||
})(); // end state object literal
|
||||
|
||||
// constructor for animation Transition
|
||||
Transition = function(lastAnimation, nextAnimation, reachPoses, transitionDuration, easingLower, easingUpper) {
|
||||
Transition = function(nextAnimation, lastAnimation, lastTransition) {
|
||||
|
||||
this.lastAnim = lastAnimation; // name of last animation
|
||||
this.nextAnimation = nextAnimation; // name of next animation
|
||||
if (lastAnimation === motion.selWalk ||
|
||||
nextAnimation === motion.selSideStepLeft ||
|
||||
nextAnimation === motion.selSideStepRight) {
|
||||
// boolean - is the last animation a walking animation?
|
||||
this.walkingAtStart = true;
|
||||
} else {
|
||||
this.walkingAtStart = false;
|
||||
// record the current state of animation
|
||||
this.nextAnimation = nextAnimation;
|
||||
this.lastAnimation = lastAnimation;
|
||||
this.lastTransition = lastTransition;
|
||||
|
||||
// deal with transition recursion (overlapping transitions)
|
||||
this.id = motion.transitionCount++; // serial number for this transition
|
||||
this.recursionDepth = 0;
|
||||
this.incrementRecursion = function() {
|
||||
|
||||
this.recursionDepth += 1;
|
||||
|
||||
if(this.lastTransition !== nullTransition) {
|
||||
|
||||
this.lastTransition.incrementRecursion();
|
||||
if(this.lastTransition.recursionDepth > 5) {
|
||||
|
||||
delete this.lastTransition;
|
||||
this.lastTransition = nullTransition;
|
||||
}
|
||||
if (nextAnimation === motion.selWalk ||
|
||||
nextAnimation === motion.selSideStepLeft ||
|
||||
nextAnimation === motion.selSideStepRight) {
|
||||
// boolean - is the next animation a walking animation?
|
||||
this.walkingAtEnd = true;
|
||||
} else {
|
||||
this.walkingAtEnd = false;
|
||||
}
|
||||
this.reachPoses = reachPoses; // placeholder / stub: array of reach poses for squash and stretch techniques
|
||||
this.transitionDuration = transitionDuration; // length of transition (seconds)
|
||||
this.easingLower = easingLower; // Bezier curve handle (normalised)
|
||||
this.easingUpper = easingUpper; // Bezier curve handle (normalised)
|
||||
}
|
||||
if(lastTransition !== nullTransition) {
|
||||
|
||||
this.lastTransition.incrementRecursion();
|
||||
}
|
||||
|
||||
this.reachPoses = []; // placeholder / stub: work in progress - array of reach poses for squash and stretch techniques
|
||||
this.transitionDuration = 0.5; // length of transition (seconds)
|
||||
this.easingLower = {x:0.60, y:0.03}; // Bezier curve handle
|
||||
this.easingUpper = {x:0.87, y:1.35}; // Bezier curve handle
|
||||
this.startTime = new Date().getTime(); // Starting timestamp (seconds)
|
||||
this.progress = 0; // how far are we through the transition?
|
||||
this.walkWheelIncrement = 3; // how much to turn the walkwheel each frame when transitioning to / from walking
|
||||
this.walkWheelAdvance = 0; // how many degrees the walk wheel has been advanced during the transition
|
||||
this.walkStopAngle = 0; // what angle should we stop the walk cycle?
|
||||
this.lastDirection = motion.lastDirection;
|
||||
|
||||
// collect information about the currently playing animation
|
||||
this.lastDirection = motion.lastDirection;
|
||||
this.lastFrequencyTimeWheelPos = motion.frequencyTimeWheelPos;
|
||||
this.lastFrequencyTimeIncrement = motion.averageFrequencyTimeIncrement;
|
||||
this.lastFrequencyTimeWheelRadius = motion.frequencyTimeWheelRadius;
|
||||
this.stopAngle = 0; // what angle should we stop turning this frequency time wheel?
|
||||
this.percentToMove = 0; // if we need to keep moving to complete a step, this will be set to 100
|
||||
this.lastElapsedFTDegrees = motion.elapsedFTDegrees;
|
||||
this.advancePreviousFrequencyTimeWheel = function() {
|
||||
|
||||
this.lastFrequencyTimeWheelPos += this.lastFrequencyTimeIncrement;
|
||||
|
||||
if (this.lastFrequencyTimeWheelPos >= 360) {
|
||||
|
||||
this.lastFrequencyTimeWheelPos = this.lastFrequencyTimeWheelPos % 360;
|
||||
}
|
||||
|
||||
if(this.lastTransition !== nullTransition) {
|
||||
|
||||
this.lastTransition.advancePreviousFrequencyTimeWheel();
|
||||
}
|
||||
};
|
||||
|
||||
this.updateProgress = function() {
|
||||
|
||||
var elapasedTime = (new Date().getTime() - this.startTime) / 1000;
|
||||
this.progress = elapasedTime / this.transitionDuration;
|
||||
|
||||
this.progress = filter.bezier((1 - this.progress),
|
||||
{x: 0, y: 0},
|
||||
this.easingLower,
|
||||
this.easingUpper,
|
||||
{x: 1, y: 1}).y;
|
||||
|
||||
// ensure there is at least some progress
|
||||
if(this.progress <= 0) {
|
||||
|
||||
this.progress = VERY_SHORT_TIME;
|
||||
}
|
||||
if(this.lastTransition !== nullTransition) {
|
||||
|
||||
if(this.lastTransition.updateProgress() >= 1) {
|
||||
|
||||
// the previous transition is now complete
|
||||
delete this.lastTransition;
|
||||
this.lastTransition = nullTransition;
|
||||
}
|
||||
}
|
||||
return this.progress;
|
||||
};
|
||||
|
||||
this.blendTranslations = function(frequencyTimeWheelPos, direction) {
|
||||
|
||||
var lastTranslations = {x:0, y:0, z:0};
|
||||
if(!isDefined(this.nextAnimation)) {
|
||||
|
||||
return lastTranslations;
|
||||
}
|
||||
var nextTranslations = calculateTranslations(this.nextAnimation,
|
||||
frequencyTimeWheelPos,
|
||||
direction);
|
||||
|
||||
// are we blending with a previous, still live transition?
|
||||
if(this.lastTransition !== nullTransition) {
|
||||
|
||||
lastTranslations = this.lastTransition.blendTranslations(this.lastFrequencyTimeWheelPos,
|
||||
this.lastDirection);
|
||||
|
||||
} else {
|
||||
|
||||
lastTranslations = calculateTranslations(this.lastAnimation,
|
||||
this.lastFrequencyTimeWheelPos,
|
||||
this.lastDirection);
|
||||
}
|
||||
|
||||
nextTranslations.x = this.progress * nextTranslations.x +
|
||||
(1 - this.progress) * lastTranslations.x;
|
||||
|
||||
nextTranslations.y = this.progress * nextTranslations.y +
|
||||
(1 - this.progress) * lastTranslations.y;
|
||||
|
||||
nextTranslations.z = this.progress * nextTranslations.z +
|
||||
(1 - this.progress) * lastTranslations.z;
|
||||
|
||||
return nextTranslations;
|
||||
};
|
||||
|
||||
this.blendRotations = function(jointName, frequencyTimeWheelPos, direction) {
|
||||
|
||||
var lastRotations = {x:0, y:0, z:0};
|
||||
var nextRotations = calculateRotations(jointName,
|
||||
this.nextAnimation,
|
||||
frequencyTimeWheelPos,
|
||||
direction);
|
||||
|
||||
// are we blending with a previous, still live transition?
|
||||
if(this.lastTransition !== nullTransition) {
|
||||
|
||||
lastRotations = this.lastTransition.blendRotations(jointName,
|
||||
this.lastFrequencyTimeWheelPos,
|
||||
this.lastDirection);
|
||||
} else {
|
||||
|
||||
lastRotations = calculateRotations(jointName,
|
||||
this.lastAnimation,
|
||||
this.lastFrequencyTimeWheelPos,
|
||||
this.lastDirection);
|
||||
}
|
||||
|
||||
nextRotations.x = this.progress * nextRotations.x +
|
||||
(1 - this.progress) * lastRotations.x;
|
||||
|
||||
nextRotations.y = this.progress * nextRotations.y +
|
||||
(1 - this.progress) * lastRotations.y;
|
||||
|
||||
nextRotations.z = this.progress * nextRotations.z +
|
||||
(1 - this.progress) * lastRotations.z;
|
||||
|
||||
return nextRotations;
|
||||
};
|
||||
|
||||
}; // end Transition constructor
|
||||
|
||||
|
||||
// individual joint modiers (mostly used to provide symmetry between left and right limbs)
|
||||
JointModifiers = function(joint, direction) {
|
||||
|
||||
// gather modifiers and multipliers
|
||||
this.pitchFrequencyMultiplier = 1;
|
||||
this.pitchPhaseModifier = 0;
|
||||
this.pitchReverseModifier = 0;
|
||||
this.yawReverseModifier = 0;
|
||||
this.rollReverseModifier = 0;
|
||||
this.pitchSign = 1; // for sidestepping and incorrectly rigged Ron ToeBases
|
||||
this.yawSign = 1;
|
||||
this.rollSign = 1;
|
||||
this.pitchReverseInvert = 1;
|
||||
this.pitchOffsetSign = 1;
|
||||
this.yawOffsetSign = 1;
|
||||
this.rollOffsetSign = 1;
|
||||
this.bobReverseModifier = 0;
|
||||
this.bobFrequencyMultiplier = 1;
|
||||
this.thrustFrequencyMultiplier = 1;
|
||||
|
||||
if (isDefined(joint.pitchFrequencyMultiplier)) {
|
||||
this.pitchFrequencyMultiplier = joint.pitchFrequencyMultiplier;
|
||||
}
|
||||
if (isDefined(joint.pitchPhaseModifier)) {
|
||||
this.pitchPhaseModifier = joint.pitchPhaseModifier;
|
||||
}
|
||||
if (isDefined(joint.pitchSign)) {
|
||||
this.pitchSign = joint.pitchSign;
|
||||
}
|
||||
if (isDefined(joint.yawSign)) {
|
||||
this.yawSign = joint.yawSign;
|
||||
}
|
||||
if (isDefined(joint.rollSign)) {
|
||||
this.rollSign = joint.rollSign;
|
||||
}
|
||||
if (isDefined(joint.pitchReverseInvert) && direction === BACKWARDS) {
|
||||
this.pitchReverseInvert = joint.pitchReverseInvert;
|
||||
}
|
||||
if (isDefined(joint.pitchReverseModifier) && direction === BACKWARDS) {
|
||||
this.pitchReverseModifier = joint.pitchReverseModifier;
|
||||
}
|
||||
if (isDefined(joint.yawReverseModifier) && direction === BACKWARDS) {
|
||||
this.yawReverseModifier = joint.yawReverseModifier;
|
||||
}
|
||||
if (isDefined(joint.rollReverseModifier) && direction === BACKWARDS) {
|
||||
this.rollReverseModifier = joint.rollReverseModifier;
|
||||
}
|
||||
if (isDefined(joint.pitchOffsetSign)) {
|
||||
this.pitchOffsetSign = joint.pitchOffsetSign;
|
||||
}
|
||||
if (isDefined(joint.yawOffsetSign)) {
|
||||
this.yawOffsetSign = joint.yawOffsetSign;
|
||||
}
|
||||
if (isDefined(joint.rollOffsetSign)) {
|
||||
this.rollOffsetSign = joint.rollOffsetSign;
|
||||
}
|
||||
if (isDefined(joint.bobReverseModifier) && direction === BACKWARDS) {
|
||||
this.bobReverseModifier = joint.bobReverseModifier;
|
||||
}
|
||||
if (isDefined(joint.bobFrequencyMultiplier)) {
|
||||
this.bobFrequencyMultiplier = joint.bobFrequencyMultiplier;
|
||||
}
|
||||
if (isDefined(joint.thrustFrequencyMultiplier)) {
|
||||
this.thrustFrequencyMultiplier = joint.thrustFrequencyMultiplier;
|
||||
}
|
||||
};
|
||||
|
||||
walkAssets = (function () {
|
||||
|
||||
// path to the sounds used for the footsteps
|
||||
var _pathToSounds = 'https://s3.amazonaws.com/hifi-public/sounds/Footsteps/';
|
||||
// load the sounds - currently disabled due to breaking changes in js audio native objects
|
||||
//var _pathToSounds = 'https://s3.amazonaws.com/hifi-public/sounds/Footsteps/';
|
||||
|
||||
// read in the sounds
|
||||
var _footsteps = [];
|
||||
_footsteps.push(SoundCache.getSound(_pathToSounds+"FootstepW2Left-12db.wav"));
|
||||
_footsteps.push(SoundCache.getSound(_pathToSounds+"FootstepW2Right-12db.wav"));
|
||||
_footsteps.push(SoundCache.getSound(_pathToSounds+"FootstepW3Left-12db.wav"));
|
||||
_footsteps.push(SoundCache.getSound(_pathToSounds+"FootstepW3Right-12db.wav"));
|
||||
_footsteps.push(SoundCache.getSound(_pathToSounds+"FootstepW5Left-12db.wav"));
|
||||
_footsteps.push(SoundCache.getSound(_pathToSounds+"FootstepW5Right-12db.wav"));
|
||||
//var _footsteps = [];
|
||||
//_footsteps.push(new Sound(_pathToSounds+"FootstepW2Left-12db.wav"));
|
||||
//_footsteps.push(new Sound(_pathToSounds+"FootstepW2Right-12db.wav"));
|
||||
//_footsteps.push(new Sound(_pathToSounds+"FootstepW3Left-12db.wav"));
|
||||
//_footsteps.push(new Sound(_pathToSounds+"FootstepW3Right-12db.wav"));
|
||||
//_footsteps.push(new Sound(_pathToSounds+"FootstepW5Left-12db.wav"));
|
||||
//_footsteps.push(new Sound(_pathToSounds+"FootstepW5Right-12db.wav"));
|
||||
|
||||
// load the animation datafiles
|
||||
Script.include(pathToAssets+"animations/dd-female-standard-walk-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-female-standing-one-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-female-flying-up-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-female-flying-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-female-flying-down-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-female-standing-one-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-female-flying-blend-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-female-hovering-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-female-sidestep-left-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-female-sidestep-right-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-male-standard-walk-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-male-standing-one-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-male-flying-up-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-male-flying-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-male-flying-down-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-male-standing-one-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-male-flying-blend-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-male-hovering-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-male-sidestep-left-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-male-sidestep-right-animation.js");
|
||||
Script.include(pathToAssets+"animations/dd-animation-reference.js");
|
||||
|
||||
// read in the animation files
|
||||
var _FemaleStandardWalkFile = new FemaleStandardWalk();
|
||||
var _femaleStandardWalk = _FemaleStandardWalkFile.loadAnimation();
|
||||
var _FemaleFlyingUpFile = new FemaleFlyingUp();
|
||||
var _femaleFlyingUp = _FemaleFlyingUpFile.loadAnimation();
|
||||
var _FemaleFlyingFile = new FemaleFlying();
|
||||
var _femaleFlying = _FemaleFlyingFile.loadAnimation();
|
||||
var _FemaleFlyingDownFile = new FemaleFlyingDown();
|
||||
var _femaleFlyingDown = _FemaleFlyingDownFile.loadAnimation();
|
||||
var _FemaleStandOneFile = new FemaleStandingOne();
|
||||
var _femaleStandOne = _FemaleStandOneFile.loadAnimation();
|
||||
var _FemaleSideStepLeftFile = new FemaleSideStepLeft();
|
||||
var _femaleSideStepLeft = _FemaleSideStepLeftFile.loadAnimation();
|
||||
var _FemaleSideStepRightFile = new FemaleSideStepRight();
|
||||
var _femaleSideStepRight = _FemaleSideStepRightFile.loadAnimation();
|
||||
var _MaleStandardWalkFile = new MaleStandardWalk(filter);
|
||||
var _maleStandardWalk = _MaleStandardWalkFile.loadAnimation();
|
||||
var _MaleFlyingUpFile = new MaleFlyingUp();
|
||||
var _maleFlyingUp = _MaleFlyingUpFile.loadAnimation();
|
||||
var _MaleFlyingFile = new MaleFlying();
|
||||
var _maleFlying = _MaleFlyingFile.loadAnimation();
|
||||
var _MaleFlyingDownFile = new MaleFlyingDown();
|
||||
var _maleFlyingDown = _MaleFlyingDownFile.loadAnimation();
|
||||
var _MaleStandOneFile = new MaleStandingOne();
|
||||
var _maleStandOne = _MaleStandOneFile.loadAnimation();
|
||||
var _MaleSideStepLeftFile = new MaleSideStepLeft();
|
||||
var _maleSideStepLeft = _MaleSideStepLeftFile.loadAnimation();
|
||||
var _MaleSideStepRightFile = new MaleSideStepRight();
|
||||
var _maleSideStepRight = _MaleSideStepRightFile.loadAnimation();
|
||||
var _femaleStandardWalk = new FemaleStandardWalk();
|
||||
var _femaleFlyingUp = new FemaleFlyingUp();
|
||||
var _femaleFlying = new FemaleFlying();
|
||||
var _femaleFlyingDown = new FemaleFlyingDown();
|
||||
var _femaleStandOne = new FemaleStandingOne();
|
||||
var _femaleSideStepLeft = new FemaleSideStepLeft();
|
||||
var _femaleSideStepRight = new FemaleSideStepRight();
|
||||
var _femaleFlyingBlend = new FemaleFlyingBlend();
|
||||
var _femaleHovering = new FemaleHovering();
|
||||
|
||||
var _maleStandardWalk = new MaleStandardWalk(filter);
|
||||
var _maleStandOne = new MaleStandingOne();
|
||||
var _maleSideStepLeft = new MaleSideStepLeft();
|
||||
var _maleSideStepRight = new MaleSideStepRight();
|
||||
var _maleFlying = new MaleFlying();
|
||||
var _maleFlyingDown = new MaleFlyingDown();
|
||||
var _maleFlyingUp = new MaleFlyingUp();
|
||||
var _maleFlyingBlend = new MaleFlyingBlend();
|
||||
var _maleHovering = new MaleHovering();
|
||||
|
||||
var _animationReference = new AnimationReference();
|
||||
|
||||
return {
|
||||
|
||||
// expose the sound assets
|
||||
footsteps: _footsteps,
|
||||
//footsteps: _footsteps,
|
||||
|
||||
// expose the animation assets
|
||||
femaleStandardWalk: _femaleStandardWalk,
|
||||
femaleFlyingUp: _femaleFlyingUp,
|
||||
femaleFlying: _femaleFlying,
|
||||
femaleFlyingDown: _femaleFlyingDown,
|
||||
femaleFlyingBlend: _femaleFlyingBlend,
|
||||
femaleHovering: _femaleHovering,
|
||||
femaleStandOne: _femaleStandOne,
|
||||
femaleSideStepLeft: _femaleSideStepLeft,
|
||||
femaleSideStepRight: _femaleSideStepRight,
|
||||
|
@ -405,8 +595,12 @@ walkAssets = (function () {
|
|||
maleFlyingUp: _maleFlyingUp,
|
||||
maleFlying: _maleFlying,
|
||||
maleFlyingDown: _maleFlyingDown,
|
||||
maleFlyingBlend: _maleFlyingBlend,
|
||||
maleHovering: _maleHovering,
|
||||
maleStandOne: _maleStandOne,
|
||||
maleSideStepLeft: _maleSideStepLeft,
|
||||
maleSideStepRight: _maleSideStepRight,
|
||||
animationReference: _animationReference,
|
||||
}
|
||||
|
||||
})();
|
|
@ -1,11 +1,11 @@
|
|||
//
|
||||
// walkFilters.js
|
||||
//
|
||||
// version 1.001
|
||||
// version 1.002
|
||||
//
|
||||
// Created by David Wooldridge, Autumn 2014
|
||||
//
|
||||
// Provides a variety of filters for use by the walk.js script v1.1
|
||||
// Provides a variety of filters for use by the walk.js script v1.12
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -24,33 +24,77 @@ AveragingFilter = function(length) {
|
|||
this.process = function() {
|
||||
|
||||
if (this.pastValues.length === 0 && arguments[0]) {
|
||||
|
||||
return arguments[0];
|
||||
} else if (arguments[0]) {
|
||||
|
||||
} else if (arguments[0] !== null) {
|
||||
|
||||
// apply quick and simple LP filtering
|
||||
this.pastValues.push(arguments[0]);
|
||||
this.pastValues.shift();
|
||||
var nextOutputValue = 0;
|
||||
for (var ea in this.pastValues) nextOutputValue += this.pastValues[ea];
|
||||
return nextOutputValue / this.pastValues.length;
|
||||
|
||||
} else {
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// 1st order Butterworth filter - calculate coeffs here: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
|
||||
// provides LP filtering with a more stable frequency / phase response (-3 dB @ 3 Hz)
|
||||
ButterworthFilter1 = function() {
|
||||
|
||||
this.gain = 7.313751515;
|
||||
this.coeff = 0.7265425280;
|
||||
|
||||
// initialise the arrays
|
||||
this.xv = [];
|
||||
this.yv = [];
|
||||
|
||||
for(var i = 0; i < 2; i++) {
|
||||
|
||||
this.xv.push(0);
|
||||
this.yv.push(0);
|
||||
}
|
||||
|
||||
// process values
|
||||
this.process = function(nextInputValue) {
|
||||
|
||||
this.xv[0] = this.xv[1];
|
||||
this.xv[1] = nextInputValue / this.gain;
|
||||
|
||||
this.yv[0] = this.yv[1];
|
||||
this.yv[1] = this.xv[0] + this.xv[1] + this.coeff * this.yv[0];
|
||||
|
||||
return this.yv[1];
|
||||
};
|
||||
|
||||
}; // end Butterworth filter constructor
|
||||
|
||||
// 2nd order Butterworth LP filter - calculate coeffs here: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
|
||||
// provides LP filtering with a more stable frequency / phase response
|
||||
ButterworthFilter = function(cutOff) {
|
||||
ButterworthFilter2 = function(cutOff) {
|
||||
|
||||
switch(cutOff) {
|
||||
|
||||
case 5:
|
||||
default:
|
||||
|
||||
// cut off frequency = 5Hz
|
||||
this.gain = 20.20612010;
|
||||
this.coeffOne = -0.4775922501;
|
||||
this.coeffTwo = 1.2796324250;
|
||||
break;
|
||||
}
|
||||
|
||||
// initialise the arrays
|
||||
this.xv = [];
|
||||
this.yv = [];
|
||||
for(var i = 0; i < 3; i++) {
|
||||
|
||||
this.xv.push(0);
|
||||
this.yv.push(0);
|
||||
}
|
||||
|
@ -71,7 +115,8 @@ ButterworthFilter = function(cutOff) {
|
|||
|
||||
return this.yv[2];
|
||||
};
|
||||
}; // end Butterworth filter contructor
|
||||
}; // end Butterworth filter constructor
|
||||
|
||||
|
||||
// Add harmonics to a given sine wave to form square, sawtooth or triangle waves
|
||||
// Geometric wave synthesis fundamentals taken from: http://hyperphysics.phy-astr.gsu.edu/hbase/audio/geowv.html
|
||||
|
@ -79,10 +124,10 @@ WaveSynth = function(waveShape, numHarmonics, smoothing) {
|
|||
|
||||
this.numHarmonics = numHarmonics;
|
||||
this.waveShape = waveShape;
|
||||
this.averagingFilter = new AveragingFilter(smoothing);
|
||||
this.smoothingFilter = new AveragingFilter(smoothing);
|
||||
|
||||
// NB: frequency in radians
|
||||
this.shapeWave = function(frequency) {
|
||||
this.calculate = function(frequency) {
|
||||
|
||||
// make some shapes
|
||||
var harmonics = 0;
|
||||
|
@ -92,7 +137,7 @@ WaveSynth = function(waveShape, numHarmonics, smoothing) {
|
|||
iterations++;
|
||||
}
|
||||
|
||||
for(var n = 2; n < iterations; n++) {
|
||||
for(var n = 1; n < iterations; n++) {
|
||||
|
||||
switch(this.waveShape) {
|
||||
|
||||
|
@ -128,11 +173,11 @@ WaveSynth = function(waveShape, numHarmonics, smoothing) {
|
|||
}
|
||||
|
||||
// smooth the result and return
|
||||
return this.averagingFilter.process(harmonics);
|
||||
return this.smoothingFilter.process(harmonics);
|
||||
};
|
||||
};
|
||||
|
||||
// Create a wave shape by summing pre-calcualted sinusoidal harmonics
|
||||
// Create a motion wave by summing pre-calcualted sinusoidal harmonics
|
||||
HarmonicsFilter = function(magnitudes, phaseAngles) {
|
||||
|
||||
this.magnitudes = magnitudes;
|
||||
|
@ -150,7 +195,8 @@ HarmonicsFilter = function(magnitudes, phaseAngles) {
|
|||
};
|
||||
};
|
||||
|
||||
// the main filter object literal
|
||||
|
||||
// the main filter object
|
||||
filter = (function() {
|
||||
|
||||
// Bezier private functions
|
||||
|
@ -181,9 +227,15 @@ filter = (function() {
|
|||
return newAveragingFilter;
|
||||
},
|
||||
|
||||
createButterworthFilter: function(cutoff) {
|
||||
createButterworthFilter1: function() {
|
||||
|
||||
var newButterworthFilter = new ButterworthFilter(cutoff);
|
||||
var newButterworthFilter = new ButterworthFilter1();
|
||||
return newButterworthFilter;
|
||||
},
|
||||
|
||||
createButterworthFilter2: function(cutoff) {
|
||||
|
||||
var newButterworthFilter = new ButterworthFilter2(cutoff);
|
||||
return newButterworthFilter;
|
||||
},
|
||||
|
||||
|
@ -211,7 +263,7 @@ filter = (function() {
|
|||
return pos;
|
||||
},
|
||||
|
||||
// simple clipping filter (clips bottom of wave only, special case for hips y-axis skeleton offset)
|
||||
// simple clipping filter (clips bottom of wave only)
|
||||
clipTrough: function(inputValue, peak, strength) {
|
||||
|
||||
var outputValue = inputValue * strength;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -566,6 +566,10 @@ function mouseReleaseEvent(event) {
|
|||
}
|
||||
|
||||
function mouseClickEvent(event) {
|
||||
if (!isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
var result = findClickedEntity(event);
|
||||
if (result === null) {
|
||||
if (!event.isShifted) {
|
||||
|
@ -636,6 +640,7 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
|||
// exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that
|
||||
// added it.
|
||||
var modelMenuAddedDelete = false;
|
||||
var originalLightsArePickable = Entities.getLightsArePickable();
|
||||
function setupModelMenus() {
|
||||
print("setupModelMenus()");
|
||||
// adj our menuitems
|
||||
|
@ -653,10 +658,12 @@ function setupModelMenus() {
|
|||
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Large Models", shortcutKey: "CTRL+META+L",
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L",
|
||||
afterItem: "Paste Models", isCheckable: true, isChecked: true });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Small Models", shortcutKey: "CTRL+META+S",
|
||||
afterItem: "Allow Select Large Models", isCheckable: true, isChecked: true });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S",
|
||||
afterItem: "Allow Selecting of Large Models", isCheckable: true, isChecked: true });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L",
|
||||
afterItem: "Allow Selecting of Small Models", isCheckable: true });
|
||||
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
||||
|
@ -665,6 +672,8 @@ function setupModelMenus() {
|
|||
|
||||
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED,
|
||||
isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" });
|
||||
|
||||
Entities.setLightsArePickable(false);
|
||||
}
|
||||
|
||||
setupModelMenus(); // do this when first running our script.
|
||||
|
@ -679,8 +688,9 @@ function cleanupModelMenus() {
|
|||
|
||||
Menu.removeMenuItem("Edit", "Model List...");
|
||||
Menu.removeMenuItem("Edit", "Paste Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Select Large Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Select Small Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
|
||||
|
||||
Menu.removeSeparator("File", "Models");
|
||||
Menu.removeMenuItem("File", "Export Models");
|
||||
|
@ -704,6 +714,7 @@ Script.scriptEnding.connect(function() {
|
|||
if (exportMenu) {
|
||||
exportMenu.close();
|
||||
}
|
||||
Entities.setLightsArePickable(originalLightsArePickable);
|
||||
});
|
||||
|
||||
// Do some stuff regularly, like check for placement of various overlays
|
||||
|
@ -714,10 +725,12 @@ Script.update.connect(function (deltaTime) {
|
|||
});
|
||||
|
||||
function handeMenuEvent(menuItem) {
|
||||
if (menuItem == "Allow Select Small Models") {
|
||||
allowSmallModels = Menu.isOptionChecked("Allow Select Small Models");
|
||||
} else if (menuItem == "Allow Select Large Models") {
|
||||
allowLargeModels = Menu.isOptionChecked("Allow Select Large Models");
|
||||
if (menuItem == "Allow Selecting of Small Models") {
|
||||
allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models");
|
||||
} else if (menuItem == "Allow Selecting of Large Models") {
|
||||
allowLargeModels = Menu.isOptionChecked("Allow Selecting of Large Models");
|
||||
} else if (menuItem == "Allow Selecting of Lights") {
|
||||
Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights"));
|
||||
} else if (menuItem == "Delete") {
|
||||
if (SelectionManager.hasSelection()) {
|
||||
print(" Delete Entities");
|
||||
|
|
2883
examples/walk.js
2883
examples/walk.js
File diff suppressed because it is too large
Load diff
|
@ -896,6 +896,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
case Qt::Key_Greater:
|
||||
case Qt::Key_Comma:
|
||||
case Qt::Key_Period:
|
||||
case Qt::Key_QuoteDbl:
|
||||
Menu::getInstance()->handleViewFrustumOffsetKeyModifier(event->key());
|
||||
break;
|
||||
case Qt::Key_L:
|
||||
|
@ -2090,12 +2091,6 @@ void Application::updateMouseRay() {
|
|||
_mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), _mouseRayDirection) +
|
||||
_viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), _mouseRayDirection));
|
||||
}
|
||||
|
||||
// tell my avatar if the mouse is being pressed...
|
||||
_myAvatar->setMousePressed(_mousePressed);
|
||||
|
||||
// tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position
|
||||
_myAvatar->setMouseRay(_mouseRayOrigin, _mouseRayDirection);
|
||||
}
|
||||
|
||||
void Application::updateFaceshift() {
|
||||
|
@ -2916,7 +2911,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly, RenderAr
|
|||
// transform by eye offset
|
||||
|
||||
// load the view frustum
|
||||
loadViewFrustum(whichCamera, _displayViewFrustum);
|
||||
loadViewFrustum(whichCamera, _viewFrustum);
|
||||
|
||||
// flip x if in mirror mode (also requires reversing winding order for backface culling)
|
||||
if (whichCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
|
@ -3190,7 +3185,7 @@ void Application::computeOffAxisFrustum(float& left, float& right, float& bottom
|
|||
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const {
|
||||
|
||||
// allow 3DTV/Oculus to override parameters from camera
|
||||
_displayViewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
|
||||
_viewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
|
||||
if (OculusManager::isConnected()) {
|
||||
OculusManager::overrideOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
|
||||
|
||||
|
|
|
@ -196,7 +196,6 @@ public:
|
|||
const AudioReflector* getAudioReflector() const { return &_audioReflector; }
|
||||
Camera* getCamera() { return &_myCamera; }
|
||||
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
|
||||
ViewFrustum* getDisplayViewFrustum() { return &_displayViewFrustum; }
|
||||
ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; }
|
||||
VoxelImporter* getVoxelImporter() { return &_voxelImporter; }
|
||||
VoxelSystem* getVoxels() { return &_voxels; }
|
||||
|
@ -518,7 +517,6 @@ private:
|
|||
|
||||
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
|
||||
ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels)
|
||||
ViewFrustum _displayViewFrustum;
|
||||
ViewFrustum _shadowViewFrustum;
|
||||
quint64 _lastQueriedTime;
|
||||
|
||||
|
|
|
@ -835,6 +835,14 @@ void Menu::handleViewFrustumOffsetKeyModifier(int key) {
|
|||
const float VIEW_FRUSTUM_OFFSET_UP_DELTA = 0.05f;
|
||||
|
||||
switch (key) {
|
||||
case Qt::Key_QuoteDbl:
|
||||
_viewFrustumOffset.yaw = 0.0f;
|
||||
_viewFrustumOffset.pitch = 0.0f;
|
||||
_viewFrustumOffset.roll = 0.0f;
|
||||
_viewFrustumOffset.up = 0.0f;
|
||||
_viewFrustumOffset.distance = 0.0f;
|
||||
break;
|
||||
|
||||
case Qt::Key_BracketLeft:
|
||||
_viewFrustumOffset.yaw -= VIEW_FRUSTUM_OFFSET_DELTA;
|
||||
break;
|
||||
|
|
|
@ -192,7 +192,7 @@ static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM;
|
|||
|
||||
void MetavoxelSystem::render() {
|
||||
// update the frustum
|
||||
ViewFrustum* viewFrustum = Application::getInstance()->getDisplayViewFrustum();
|
||||
ViewFrustum* viewFrustum = Application::getInstance()->getViewFrustum();
|
||||
_frustum.set(viewFrustum->getFarTopLeft(), viewFrustum->getFarTopRight(), viewFrustum->getFarBottomLeft(),
|
||||
viewFrustum->getFarBottomRight(), viewFrustum->getNearTopLeft(), viewFrustum->getNearTopRight(),
|
||||
viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight());
|
||||
|
@ -1896,7 +1896,7 @@ private:
|
|||
SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) :
|
||||
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
|
||||
QVector<AttributePointer>(), QVector<AttributePointer>(), lod,
|
||||
encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())),
|
||||
encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())),
|
||||
_containmentDepth(INT_MAX) {
|
||||
}
|
||||
|
||||
|
@ -1932,7 +1932,7 @@ private:
|
|||
|
||||
BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << attribute),
|
||||
_order(encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())),
|
||||
_order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())),
|
||||
_containmentDepth(INT_MAX) {
|
||||
}
|
||||
|
||||
|
|
|
@ -66,8 +66,6 @@ Avatar::Avatar() :
|
|||
_leanScale(0.5f),
|
||||
_scale(1.0f),
|
||||
_worldUpDirection(DEFAULT_UP_DIRECTION),
|
||||
_mouseRayOrigin(0.0f, 0.0f, 0.0f),
|
||||
_mouseRayDirection(0.0f, 0.0f, 0.0f),
|
||||
_moving(false),
|
||||
_collisionGroups(0),
|
||||
_initialized(false),
|
||||
|
@ -250,11 +248,6 @@ void Avatar::measureMotionDerivatives(float deltaTime) {
|
|||
_lastOrientation = getOrientation();
|
||||
}
|
||||
|
||||
void Avatar::setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction) {
|
||||
_mouseRayOrigin = origin;
|
||||
_mouseRayDirection = direction;
|
||||
}
|
||||
|
||||
enum TextRendererType {
|
||||
CHAT,
|
||||
DISPLAYNAME
|
||||
|
|
|
@ -85,7 +85,6 @@ public:
|
|||
|
||||
//setters
|
||||
void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); }
|
||||
void setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction);
|
||||
void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; }
|
||||
bool getIsLookAtTarget() const { return _isLookAtTarget; }
|
||||
//getters
|
||||
|
@ -213,8 +212,6 @@ protected:
|
|||
float _leanScale;
|
||||
float _scale;
|
||||
glm::vec3 _worldUpDirection;
|
||||
glm::vec3 _mouseRayOrigin;
|
||||
glm::vec3 _mouseRayDirection;
|
||||
float _stringLength;
|
||||
bool _moving; ///< set when position is changing
|
||||
|
||||
|
|
|
@ -87,7 +87,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
if (!shouldKillAvatar(sharedAvatar)) {
|
||||
// this avatar's mixer is still around, go ahead and simulate it
|
||||
avatar->simulate(deltaTime);
|
||||
avatar->setMouseRay(mouseOrigin, mouseDirection);
|
||||
++avatarIterator;
|
||||
} else {
|
||||
// the mixer that owned this avatar is gone, give it to the vector of fades and kill it
|
||||
|
|
|
@ -67,7 +67,6 @@ const int SCRIPTED_MOTOR_WORLD_FRAME = 2;
|
|||
|
||||
MyAvatar::MyAvatar() :
|
||||
Avatar(),
|
||||
_mousePressed(false),
|
||||
_turningKeyPressTime(0.0f),
|
||||
_gravity(0.0f, 0.0f, 0.0f),
|
||||
_distanceToNearestAvatar(std::numeric_limits<float>::max()),
|
||||
|
|
|
@ -55,15 +55,12 @@ public:
|
|||
void renderHeadMouse(int screenWidth, int screenHeight) const;
|
||||
|
||||
// setters
|
||||
void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; }
|
||||
void setLeanScale(float scale) { _leanScale = scale; }
|
||||
void setLocalGravity(glm::vec3 gravity);
|
||||
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
|
||||
|
||||
// getters
|
||||
float getLeanScale() const { return _leanScale; }
|
||||
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
|
||||
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
|
||||
glm::vec3 getGravity() const { return _gravity; }
|
||||
glm::vec3 getDefaultEyePosition() const;
|
||||
bool getShouldRenderLocally() const { return _shouldRender; }
|
||||
|
@ -203,7 +200,6 @@ protected:
|
|||
virtual void renderAttachments(RenderMode renderMode);
|
||||
|
||||
private:
|
||||
bool _mousePressed;
|
||||
float _turningKeyPressTime;
|
||||
glm::vec3 _gravity;
|
||||
float _distanceToNearestAvatar; // How close is the nearest avatar?
|
||||
|
|
|
@ -90,3 +90,14 @@ void RenderableLightEntityItem::render(RenderArgs* args) {
|
|||
glPopMatrix();
|
||||
}
|
||||
};
|
||||
|
||||
bool RenderableLightEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject) const {
|
||||
|
||||
// TODO: this isn't really correct because we don't know if we actually live in the main tree of the applications's
|
||||
// EntityTreeRenderer. But we probably do. Technically we could be on the clipboard and someone might be trying to
|
||||
// use the ray intersection API there. Anyway... if you ever try to do ray intersection testing off of trees other
|
||||
// than the main tree of the main entity renderer, then you'll need to fix this mechanism.
|
||||
return Application::getInstance()->getEntities()->getTree()->getLightsArePickable();
|
||||
}
|
||||
|
|
|
@ -34,6 +34,10 @@ public:
|
|||
{ }
|
||||
|
||||
virtual void render(RenderArgs* args);
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject) const;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -232,8 +232,8 @@ void DeferredLightingEffect::render() {
|
|||
// enlarge the scales slightly to account for tesselation
|
||||
const float SCALE_EXPANSION = 0.05f;
|
||||
|
||||
const glm::vec3& eyePoint = Application::getInstance()->getDisplayViewFrustum()->getPosition();
|
||||
float nearRadius = glm::distance(eyePoint, Application::getInstance()->getDisplayViewFrustum()->getNearTopLeft());
|
||||
const glm::vec3& eyePoint = Application::getInstance()->getViewFrustum()->getPosition();
|
||||
float nearRadius = glm::distance(eyePoint, Application::getInstance()->getViewFrustum()->getNearTopLeft());
|
||||
|
||||
if (!_pointLights.isEmpty()) {
|
||||
_pointLight.bind();
|
||||
|
|
|
@ -135,6 +135,11 @@ public:
|
|||
|
||||
virtual void debugDump() const;
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const { return false; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject) const { return true; }
|
||||
|
||||
// attributes applicable to all entity types
|
||||
EntityTypes::EntityType getType() const { return _type; }
|
||||
const glm::vec3& getPosition() const { return _position; } /// get position in domain scale units (0.0 - 1.0)
|
||||
|
|
|
@ -221,6 +221,19 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke
|
|||
return result;
|
||||
}
|
||||
|
||||
void EntityScriptingInterface::setLightsArePickable(bool value) {
|
||||
if (_entityTree) {
|
||||
_entityTree->setLightsArePickable(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::getLightsArePickable() const {
|
||||
if (_entityTree) {
|
||||
return _entityTree->getLightsArePickable();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
RayToEntityIntersectionResult::RayToEntityIntersectionResult() :
|
||||
intersects(false),
|
||||
|
|
|
@ -96,6 +96,8 @@ public slots:
|
|||
/// order to return an accurate result
|
||||
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray);
|
||||
|
||||
Q_INVOKABLE void setLightsArePickable(bool value);
|
||||
Q_INVOKABLE bool getLightsArePickable() const;
|
||||
|
||||
Q_INVOKABLE void dumpTree() const;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage), _simulation(NULL) {
|
||||
_rootElement = createNewElement();
|
||||
_lightsArePickable = true; // assume they are by default
|
||||
}
|
||||
|
||||
EntityTree::~EntityTree() {
|
||||
|
|
|
@ -144,6 +144,8 @@ public:
|
|||
|
||||
void emitEntityScriptChanging(const EntityItemID& entityItemID);
|
||||
|
||||
bool getLightsArePickable() const { return _lightsArePickable; }
|
||||
void setLightsArePickable(bool value) { _lightsArePickable = value; }
|
||||
void setSimulation(EntitySimulation* simulation);
|
||||
|
||||
signals:
|
||||
|
@ -169,6 +171,8 @@ private:
|
|||
EntityItemFBXService* _fbxService;
|
||||
|
||||
QHash<EntityItemID, EntityTreeElement*> _entityToElementMap;
|
||||
|
||||
bool _lightsArePickable;
|
||||
EntitySimulation* _simulation;
|
||||
};
|
||||
|
||||
|
|
|
@ -510,6 +510,13 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
|
|||
// we can use the AABox's ray intersection by mapping our origin and direction into the entity frame
|
||||
// and testing intersection there.
|
||||
if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance, localFace)) {
|
||||
if (localDistance < distance) {
|
||||
// now ask the entity if we actually intersect
|
||||
if (entity->supportsDetailedRayIntersection()) {
|
||||
|
||||
if (entity->findDetailedRayIntersection(origin, direction, keepSearching, element, localDistance,
|
||||
localFace, intersectedObject)) {
|
||||
|
||||
if (localDistance < distance) {
|
||||
distance = localDistance;
|
||||
face = localFace;
|
||||
|
@ -517,6 +524,17 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
|
|||
somethingIntersected = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if the entity type doesn't support a detailed intersection, then just return the non-AABox results
|
||||
if (localDistance < distance) {
|
||||
distance = localDistance;
|
||||
face = localFace;
|
||||
*intersectedObject = (void*)entity;
|
||||
somethingIntersected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++entityItr;
|
||||
|
|
|
@ -93,3 +93,26 @@ void SphereEntityItem::recalculateCollisionShape() {
|
|||
float largestDiameter = glm::max(dimensionsInMeters.x, dimensionsInMeters.y, dimensionsInMeters.z);
|
||||
_sphereShape.setRadius(largestDiameter / 2.0f);
|
||||
}
|
||||
|
||||
bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject) const {
|
||||
|
||||
// NOTE: origin and direction are in tree units. But our _sphereShape is in meters, so we need to
|
||||
// do a little math to make these match each other.
|
||||
RayIntersectionInfo rayInfo;
|
||||
rayInfo._rayStart = origin * (float)TREE_SCALE;
|
||||
rayInfo._rayDirection = direction;
|
||||
|
||||
// TODO: Note this is really doing ray intersections against a sphere, which is fine except in cases
|
||||
// where our dimensions actually make us an ellipsoid. But we'll live with this for now until we
|
||||
// get a more full fledged physics library
|
||||
if (_sphereShape.findRayIntersection(rayInfo)) {
|
||||
distance = rayInfo._hitDistance / (float)TREE_SCALE;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -56,6 +56,11 @@ public:
|
|||
// TODO: implement proper contains for 3D ellipsoid
|
||||
//virtual bool contains(const glm::vec3& point) const;
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject) const;
|
||||
|
||||
protected:
|
||||
virtual void recalculateCollisionShape();
|
||||
|
||||
|
|
|
@ -10,9 +10,12 @@
|
|||
//
|
||||
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <ByteCountCoding.h>
|
||||
#include <PlaneShape.h>
|
||||
|
||||
#include "EntityTree.h"
|
||||
#include "EntityTreeElement.h"
|
||||
|
@ -111,3 +114,47 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
|
|||
APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, appendColor, getTextColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, appendColor, getBackgroundColor());
|
||||
}
|
||||
|
||||
|
||||
bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject) const {
|
||||
|
||||
RayIntersectionInfo rayInfo;
|
||||
rayInfo._rayStart = origin;
|
||||
rayInfo._rayDirection = direction;
|
||||
rayInfo._rayLength = std::numeric_limits<float>::max();
|
||||
|
||||
PlaneShape plane;
|
||||
|
||||
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
|
||||
glm::vec3 normal = _rotation * UNROTATED_NORMAL;
|
||||
plane.setNormal(normal);
|
||||
plane.setPoint(_position); // the position is definitely a point on our plane
|
||||
|
||||
bool intersects = plane.findRayIntersection(rayInfo);
|
||||
|
||||
if (intersects) {
|
||||
glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance);
|
||||
// now we know the point the ray hit our plane
|
||||
|
||||
glm::mat4 rotation = glm::mat4_cast(getRotation());
|
||||
glm::mat4 translation = glm::translate(getPosition());
|
||||
glm::mat4 entityToWorldMatrix = translation * rotation;
|
||||
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
|
||||
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::vec3 registrationPoint = getRegistrationPoint();
|
||||
glm::vec3 corner = -(dimensions * registrationPoint);
|
||||
AABox entityFrameBox(corner, dimensions);
|
||||
|
||||
glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f));
|
||||
|
||||
intersects = entityFrameBox.contains(entityFrameHitAt);
|
||||
}
|
||||
|
||||
if (intersects) {
|
||||
distance = rayInfo._hitDistance;
|
||||
}
|
||||
return intersects;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,11 @@ public:
|
|||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
|
||||
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject) const;
|
||||
|
||||
static const QString DEFAULT_TEXT;
|
||||
void setText(const QString& value) { _text = value; }
|
||||
const QString& getText() const { return _text; }
|
||||
|
|
Loading…
Reference in a new issue