Merge branch 'master' of https://github.com/highfidelity/hifi into mouse_pick_in_oculus

This commit is contained in:
Atlante45 2014-12-04 17:26:22 -08:00
commit abe9704dbc
31 changed files with 2401 additions and 6044 deletions

View file

@ -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 // 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. // added it.
var modelMenuAddedDelete = false; var modelMenuAddedDelete = false;
var originalLightsArePickable = Entities.getLightsArePickable();
function setupModelMenus() { function setupModelMenus() {
print("setupModelMenus()"); print("setupModelMenus()");
// adj our menuitems // adj our menuitems
@ -2824,15 +2825,18 @@ function setupModelMenus() {
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" }); 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: "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 }); afterItem: "Paste Models", isCheckable: true });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Small Models", shortcutKey: "CTRL+META+S", Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S",
afterItem: "Allow Select Large Models", isCheckable: true }); 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: "Models", isSeparator: true, beforeItem: "Settings" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" }); 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" }); 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", "Model List...");
Menu.removeMenuItem("Edit", "Paste Models"); Menu.removeMenuItem("Edit", "Paste Models");
Menu.removeMenuItem("Edit", "Allow Select Large Models"); Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
Menu.removeMenuItem("Edit", "Allow Select Small Models"); Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
Menu.removeSeparator("File", "Models"); Menu.removeSeparator("File", "Models");
Menu.removeMenuItem("File", "Export Models"); Menu.removeMenuItem("File", "Export Models");
@ -2865,6 +2870,7 @@ function scriptEnding() {
if (exportMenu) { if (exportMenu) {
exportMenu.close(); exportMenu.close();
} }
Entities.setLightsArePickable(originalLightsArePickable);
} }
Script.scriptEnding.connect(scriptEnding); Script.scriptEnding.connect(scriptEnding);
@ -2890,10 +2896,12 @@ function showPropertiesForm(editModelID) {
function handeMenuEvent(menuItem) { function handeMenuEvent(menuItem) {
print("menuItemEvent() in JS... menuItem=" + menuItem); print("menuItemEvent() in JS... menuItem=" + menuItem);
if (menuItem == "Allow Select Small Models") { if (menuItem == "Allow Selecting of Small Models") {
allowSmallModels = Menu.isOptionChecked("Allow Select Small Models"); allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models");
} else if (menuItem == "Allow Select Large Models") { } else if (menuItem == "Allow Selecting of Large Models") {
allowLargeModels = Menu.isOptionChecked("Allow Select 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") { } else if (menuItem == "Delete") {
if (leftController.grabbing) { if (leftController.grabbing) {
print(" Delete Entity.... leftController.entityID="+ leftController.entityID); print(" Delete Entity.... leftController.entityID="+ leftController.entityID);

View file

@ -16,11 +16,15 @@ var AXIS_STRAFE = Joysticks.AXIS_LEFT_X;
var AXIS_FORWARD = Joysticks.AXIS_LEFT_Y; var AXIS_FORWARD = Joysticks.AXIS_LEFT_Y;
var AXIS_ROTATE = Joysticks.AXIS_RIGHT_X; 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_TURN_AROUND = Joysticks.BUTTON_RIGHT_STICK;
var BUTTON_FLY_UP = Joysticks.BUTTON_RIGHT_SHOULDER; var BUTTON_FLY_UP = Joysticks.BUTTON_RIGHT_SHOULDER;
var BUTTON_FLY_DOWN = Joysticks.BUTTON_LEFT_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_FORWARD = Joysticks.BUTTON_DPAD_UP;
var BUTTON_WARP_BACKWARD = Joysticks.BUTTON_DPAD_DOWN; var BUTTON_WARP_BACKWARD = Joysticks.BUTTON_DPAD_DOWN;
@ -31,7 +35,8 @@ var BUTTON_WARP_RIGHT = Joysticks.BUTTON_DPAD_RIGHT;
var WARP_DISTANCE = 1; var WARP_DISTANCE = 1;
// Walk speed in m/s // Walk speed in m/s
var MOVE_SPEED = 2; var MOVE_SPEED = 0.5;
var MOVE_SPRINT_SPEED = 2;
// Amount to rotate in radians // Amount to rotate in radians
var ROTATE_INCREMENT = Math.PI / 8; var ROTATE_INCREMENT = Math.PI / 8;
@ -46,9 +51,14 @@ var WARP_PICK_MAX_DISTANCE = 100;
var flyDownButtonState = false; var flyDownButtonState = false;
var flyUpButtonState = 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 // 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. // 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 moveDirection = { x: 0, y: 0, z: 0 };
var sprintButtonState = false;
var warpActive = false; var warpActive = false;
var warpPosition = { x: 0, y: 0, z: 0 }; var warpPosition = { x: 0, y: 0, z: 0 };
@ -173,6 +183,18 @@ function reportButtonValue(button, newValue, oldValue) {
MyAvatar.orientation = Quat.multiply( MyAvatar.orientation = Quat.multiply(
Quat.fromPitchYawRollRadians(0, Math.PI, 0), MyAvatar.orientation); 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) { } else if (newValue) {
var direction = null; var direction = null;
@ -209,9 +231,10 @@ function update(dt) {
var move = copyVec3(moveDirection); var move = copyVec3(moveDirection);
move.y = 0; move.y = 0;
if (Vec3.length(move) > 0) { if (Vec3.length(move) > 0) {
speed = sprintButtonState ? MOVE_SPRINT_SPEED : MOVE_SPEED;
velocity = Vec3.multiplyQbyV(Camera.getOrientation(), move); velocity = Vec3.multiplyQbyV(Camera.getOrientation(), move);
velocity.y = 0; velocity.y = 0;
velocity = Vec3.multiply(Vec3.normalize(velocity), MOVE_SPEED); velocity = Vec3.multiply(Vec3.normalize(velocity), speed);
} }
if (moveDirection.y != 0) { if (moveDirection.y != 0) {

View file

@ -33,10 +33,12 @@ SelectionManager = (function() {
that.localRotation = Quat.fromPitchYawRollDegrees(0, 0, 0); that.localRotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
that.localPosition = { x: 0, y: 0, z: 0 }; that.localPosition = { x: 0, y: 0, z: 0 };
that.localDimensions = { 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.worldRotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
that.worldPosition = { x: 0, y: 0, z: 0 }; that.worldPosition = { x: 0, y: 0, z: 0 };
that.worldDimensions = { 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.centerPosition = { x: 0, y: 0, z: 0 };
that.saveProperties = function() { that.saveProperties = function() {
@ -153,6 +155,7 @@ SelectionManager = (function() {
that.localDimensions = properties.dimensions; that.localDimensions = properties.dimensions;
that.localPosition = properties.position; that.localPosition = properties.position;
that.localRotation = properties.rotation; that.localRotation = properties.rotation;
that.localRegistrationPoint = properties.registrationPoint;
that.worldDimensions = properties.boundingBox.dimensions; that.worldDimensions = properties.boundingBox.dimensions;
that.worldPosition = properties.boundingBox.center; that.worldPosition = properties.boundingBox.center;
@ -215,6 +218,17 @@ function normalizeDegrees(degrees) {
return 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 () { SelectionDisplay = (function () {
var that = {}; var that = {};
@ -982,26 +996,36 @@ SelectionDisplay = (function () {
that.updateRotationHandles(); that.updateRotationHandles();
that.highlightSelectable(); that.highlightSelectable();
var rotation, dimensions, position; var rotation, dimensions, position, registrationPoint;
if (spaceMode == SPACE_LOCAL) { if (spaceMode == SPACE_LOCAL) {
rotation = SelectionManager.localRotation; rotation = SelectionManager.localRotation;
dimensions = SelectionManager.localDimensions; dimensions = SelectionManager.localDimensions;
position = SelectionManager.localPosition; position = SelectionManager.localPosition;
registrationPoint = SelectionManager.localRegistrationPoint;
} else { } else {
rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); rotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
dimensions = SelectionManager.worldDimensions; dimensions = SelectionManager.worldDimensions;
position = SelectionManager.worldPosition; 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; // Center of entity, relative to registration point
var right = halfDimensions.x; center = getRelativeCenterPosition(dimensions, registrationPoint);
var top = halfDimensions.y;
var bottom = -halfDimensions.y; // Distances in world coordinates relative to the registration point
var front = far = halfDimensions.z; var left = -registrationPointDimensions.x;
var near = -halfDimensions.z; 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; var worldTop = SelectionManager.worldDimensions.y / 2;
@ -1014,25 +1038,25 @@ SelectionDisplay = (function () {
var LTF = { x: left, y: top, z: far }; var LTF = { x: left, y: top, z: far };
var RTF = { x: right, y: top, z: far }; var RTF = { x: right, y: top, z: far };
var TOP = { x: 0, y: top, z: 0 }; var TOP = { x: center.x, y: top, z: center.z };
var BOTTOM = { x: 0, y: bottom, z: 0 }; var BOTTOM = { x: center.x, y: bottom, z: center.z };
var LEFT = { x: left, y: 0, z: 0 }; var LEFT = { x: left, y: center.y, z: center.z };
var RIGHT = { x: right, y: 0, z: 0 }; var RIGHT = { x: right, y: center.y, z: center.z };
var NEAR = { x: 0, y: 0, z: near }; var NEAR = { x: center.x, y: center.y, z: near };
var FAR = { x: 0, y: 0, z: far }; var FAR = { x: center.x, y: center.y, z: far };
var EdgeTR = { x: right, y: top, z: 0 }; var EdgeTR = { x: right, y: top, z: center.z };
var EdgeTL = { x: left, y: top, z: 0 }; var EdgeTL = { x: left, y: top, z: center.z };
var EdgeTF = { x: 0, y: top, z: front }; var EdgeTF = { x: center.x, y: top, z: front };
var EdgeTN = { x: 0, y: top, z: near }; var EdgeTN = { x: center.x, y: top, z: near };
var EdgeBR = { x: right, y: bottom, z: 0 }; var EdgeBR = { x: right, y: bottom, z: center.z };
var EdgeBL = { x: left, y: bottom, z: 0 }; var EdgeBL = { x: left, y: bottom, z: center.z };
var EdgeBF = { x: 0, y: bottom, z: front }; var EdgeBF = { x: center.x, y: bottom, z: front };
var EdgeBN = { x: 0, y: bottom, z: near }; var EdgeBN = { x: center.x, y: bottom, z: near };
var EdgeNR = { x: right, y: 0, z: near }; var EdgeNR = { x: right, y: center.y, z: near };
var EdgeNL = { x: left, y: 0, z: near }; var EdgeNL = { x: left, y: center.y, z: near };
var EdgeFR = { x: right, y: 0, z: front }; var EdgeFR = { x: right, y: center.y, z: front };
var EdgeFL = { x: left, y: 0, z: front }; var EdgeFL = { x: left, y: center.y, z: front };
LBN = Vec3.multiplyQbyV(rotation, LBN); LBN = Vec3.multiplyQbyV(rotation, LBN);
RBN = Vec3.multiplyQbyV(rotation, RBN); RBN = Vec3.multiplyQbyV(rotation, RBN);
@ -1111,8 +1135,10 @@ SelectionDisplay = (function () {
Overlays.editOverlay(grabberNEAR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: NEAR }); Overlays.editOverlay(grabberNEAR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: NEAR });
Overlays.editOverlay(grabberFAR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: FAR }); Overlays.editOverlay(grabberFAR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: FAR });
var boxPosition = Vec3.multiplyQbyV(rotation, center);
boxPosition = Vec3.sum(position, boxPosition);
Overlays.editOverlay(selectionBox, { Overlays.editOverlay(selectionBox, {
position: position, position: boxPosition,
dimensions: dimensions, dimensions: dimensions,
rotation: rotation, rotation: rotation,
visible: !(mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL"), visible: !(mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL"),
@ -1140,8 +1166,17 @@ SelectionDisplay = (function () {
if (selectionManager.selections.length > 1) { if (selectionManager.selections.length > 1) {
for (; i < selectionManager.selections.length; i++) { for (; i < selectionManager.selections.length; i++) {
var properties = Entities.getEntityProperties(selectionManager.selections[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], { Overlays.editOverlay(selectionBoxes[i], {
position: properties.position, position: boxPosition,
rotation: properties.rotation, rotation: properties.rotation,
dimensions: properties.dimensions, dimensions: properties.dimensions,
visible: true, visible: true,
@ -1400,6 +1435,8 @@ SelectionDisplay = (function () {
var initialDimensions = null; var initialDimensions = null;
var initialIntersection = null; var initialIntersection = null;
var initialProperties = null; var initialProperties = null;
var registrationPoint = null;
var deltaPivot = null;
var pickRayPosition = null; var pickRayPosition = null;
var rotation = null; var rotation = null;
@ -1412,18 +1449,29 @@ SelectionDisplay = (function () {
rotation = SelectionManager.localRotation; rotation = SelectionManager.localRotation;
initialPosition = SelectionManager.localPosition; initialPosition = SelectionManager.localPosition;
initialDimensions = SelectionManager.localDimensions; initialDimensions = SelectionManager.localDimensions;
registrationPoint = SelectionManager.localRegistrationPoint;
} else { } else {
rotation = SelectionManager.worldRotation; rotation = SelectionManager.worldRotation;
initialPosition = SelectionManager.worldPosition; initialPosition = SelectionManager.worldPosition;
initialDimensions = SelectionManager.worldDimensions; initialDimensions = SelectionManager.worldDimensions;
registrationPoint = SelectionManager.worldRegistrationPoint;
} }
var scaledOffset = { // Modify range of registrationPoint to be [-0.5, 0.5]
x: initialDimensions.x * offset.x * 0.5, var centeredRP = Vec3.subtract(registrationPoint, { x: 0.5, y: 0.5, z: 0.5 });
y: initialDimensions.y * offset.y * 0.5,
z: initialDimensions.z * offset.z * 0.5, // Scale pivot to be in the same range as registrationPoint
}; var scaledPivot = Vec3.multiply(0.5, pivot)
pickRayPosition = Vec3.sum(initialPosition, Vec3.multiplyQbyV(rotation, scaledOffset)); 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) { if (numDimensions == 1 && mask.x) {
var start = Vec3.multiplyQbyV(rotation, { x: -10000, y: 0, z: 0 }); 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.y = Math.max(newDimensions.y, MINIMUM_DIMENSION);
newDimensions.z = Math.max(newDimensions.z, MINIMUM_DIMENSION); newDimensions.z = Math.max(newDimensions.z, MINIMUM_DIMENSION);
var p = Vec3.multiply(0.5, pivot); var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(deltaPivot, changeInDimensions));
var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(p, changeInDimensions));
var newPosition = Vec3.sum(initialPosition, changeInPosition); var newPosition = Vec3.sum(initialPosition, changeInPosition);
for (var i = 0; i < SelectionManager.selections.length; i++) { for (var i = 0; i < SelectionManager.selections.length; i++) {
@ -1589,20 +1636,19 @@ SelectionDisplay = (function () {
function addStretchTool(overlay, mode, pivot, direction, offset) { function addStretchTool(overlay, mode, pivot, direction, offset) {
if (!pivot) { if (!pivot) {
pivot = Vec3.multiply(-1, direction); pivot = direction;
pivot.y = direction.y;
} }
var tool = makeStretchTool(mode, direction, pivot, offset); var tool = makeStretchTool(mode, direction, pivot, offset);
addGrabberTool(overlay, tool); 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(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(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(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(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(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(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(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 }); 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 }); 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++) { for (var i = 0; i < SelectionManager.selections.length; i++) {
var entityID = SelectionManager.selections[i]; var entityID = SelectionManager.selections[i];
var properties = Entities.getEntityProperties(entityID); var properties = Entities.getEntityProperties(entityID);
var initialProperties = SelectionManager.savedProperties[entityID.id]; var initialProperties = SelectionManager.savedProperties[entityID.id];
var dPos = Vec3.subtract(initialProperties.position, initialPosition);
dPos = Vec3.multiplyQbyV(yawChange, dPos);
Entities.editEntity(entityID, { var newProperties = {
position: Vec3.sum(initialPosition, dPos),
rotation: Quat.multiply(yawChange, initialProperties.rotation), 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, newProperties);
} }
updateRotationDegreesOverlay(angleFromZero, yawHandleRotation, yawCenter); updateRotationDegreesOverlay(angleFromZero, yawHandleRotation, yawCenter);

View file

@ -90,7 +90,6 @@ Grid = function(opts) {
} }
that.snapToSpacing = function(delta, majorOnly) { that.snapToSpacing = function(delta, majorOnly) {
print('snaptogrid? ' + snapToGrid);
if (!snapToGrid) { if (!snapToGrid) {
return delta; return delta;
} }

File diff suppressed because it is too large Load diff

View file

@ -1,225 +1,277 @@
// //
// walkFilters.js // walkFilters.js
// //
// version 1.001 // version 1.002
// //
// Created by David Wooldridge, Autumn 2014 // 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. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
AveragingFilter = function(length) { AveragingFilter = function(length) {
//this.name = name; //this.name = name;
this.pastValues = []; this.pastValues = [];
for(var i = 0; i < length; i++) { for(var i = 0; i < length; i++) {
this.pastValues.push(0); this.pastValues.push(0);
} }
// single arg is the nextInputValue // single arg is the nextInputValue
this.process = function() { this.process = function() {
if (this.pastValues.length === 0 && arguments[0]) { if (this.pastValues.length === 0 && arguments[0]) {
return arguments[0];
} else if (arguments[0]) { return arguments[0];
// apply quick and simple LP filtering
this.pastValues.push(arguments[0]); } else if (arguments[0] !== null) {
this.pastValues.shift();
var nextOutputValue = 0; // apply quick and simple LP filtering
for (var ea in this.pastValues) nextOutputValue += this.pastValues[ea]; this.pastValues.push(arguments[0]);
return nextOutputValue / this.pastValues.length; this.pastValues.shift();
} else { var nextOutputValue = 0;
return 0; for (var ea in this.pastValues) nextOutputValue += this.pastValues[ea];
} return nextOutputValue / this.pastValues.length;
};
}; } else {
// 2nd order Butterworth LP filter - calculate coeffs here: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html return 0;
// provides LP filtering with a more stable frequency / phase response }
ButterworthFilter = function(cutOff) { };
};
// cut off frequency = 5Hz
this.gain = 20.20612010;
this.coeffOne = -0.4775922501; // 1st order Butterworth filter - calculate coeffs here: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
this.coeffTwo = 1.2796324250; // provides LP filtering with a more stable frequency / phase response (-3 dB @ 3 Hz)
ButterworthFilter1 = function() {
// initialise the arrays
this.xv = []; this.gain = 7.313751515;
this.yv = []; this.coeff = 0.7265425280;
for(var i = 0; i < 3; i++) {
this.xv.push(0); // initialise the arrays
this.yv.push(0); this.xv = [];
} this.yv = [];
// process values for(var i = 0; i < 2; i++) {
this.process = function(nextInputValue) {
this.xv.push(0);
this.xv[0] = this.xv[1]; this.yv.push(0);
this.xv[1] = this.xv[2]; }
this.xv[2] = nextInputValue / this.gain;
// process values
this.yv[0] = this.yv[1]; this.process = function(nextInputValue) {
this.yv[1] = this.yv[2];
this.yv[2] = (this.xv[0] + this.xv[2]) + this.xv[0] = this.xv[1];
2 * this.xv[1] + this.xv[1] = nextInputValue / this.gain;
(this.coeffOne * this.yv[0]) +
(this.coeffTwo * this.yv[1]); this.yv[0] = this.yv[1];
this.yv[1] = this.xv[0] + this.xv[1] + this.coeff * this.yv[0];
return this.yv[2];
}; return this.yv[1];
}; // end Butterworth filter contructor };
// Add harmonics to a given sine wave to form square, sawtooth or triangle waves }; // end Butterworth filter constructor
// Geometric wave synthesis fundamentals taken from: http://hyperphysics.phy-astr.gsu.edu/hbase/audio/geowv.html
WaveSynth = function(waveShape, numHarmonics, smoothing) { // 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
this.numHarmonics = numHarmonics; ButterworthFilter2 = function(cutOff) {
this.waveShape = waveShape;
this.averagingFilter = new AveragingFilter(smoothing); switch(cutOff) {
// NB: frequency in radians case 5:
this.shapeWave = function(frequency) { default:
// make some shapes this.gain = 20.20612010;
var harmonics = 0; this.coeffOne = -0.4775922501;
var multiplier = 0; this.coeffTwo = 1.2796324250;
var iterations = this.numHarmonics * 2 + 2; break;
if (this.waveShape === TRIANGLE) { }
iterations++;
} // initialise the arrays
this.xv = [];
for(var n = 2; n < iterations; n++) { this.yv = [];
for(var i = 0; i < 3; i++) {
switch(this.waveShape) {
this.xv.push(0);
case SAWTOOTH: { this.yv.push(0);
}
multiplier = 1 / n;
harmonics += multiplier * Math.sin(n * frequency); // process values
break; this.process = function(nextInputValue) {
}
this.xv[0] = this.xv[1];
case TRIANGLE: { this.xv[1] = this.xv[2];
this.xv[2] = nextInputValue / this.gain;
if (n % 2 === 1) {
var mulitplier = 1 / (n * n); this.yv[0] = this.yv[1];
// multiply (4n-1)th harmonics by -1 this.yv[1] = this.yv[2];
if (n === 3 || n === 7 || n === 11 || n === 15) { this.yv[2] = (this.xv[0] + this.xv[2]) +
mulitplier *= -1; 2 * this.xv[1] +
} (this.coeffOne * this.yv[0]) +
harmonics += mulitplier * Math.sin(n * frequency); (this.coeffTwo * this.yv[1]);
}
break; return this.yv[2];
} };
}; // end Butterworth filter constructor
case SQUARE: {
if (n % 2 === 1) { // Add harmonics to a given sine wave to form square, sawtooth or triangle waves
multiplier = 1 / n; // Geometric wave synthesis fundamentals taken from: http://hyperphysics.phy-astr.gsu.edu/hbase/audio/geowv.html
harmonics += multiplier * Math.sin(n * frequency); WaveSynth = function(waveShape, numHarmonics, smoothing) {
}
break; this.numHarmonics = numHarmonics;
} this.waveShape = waveShape;
} this.smoothingFilter = new AveragingFilter(smoothing);
}
// NB: frequency in radians
// smooth the result and return this.calculate = function(frequency) {
return this.averagingFilter.process(harmonics);
}; // make some shapes
}; var harmonics = 0;
var multiplier = 0;
// Create a wave shape by summing pre-calcualted sinusoidal harmonics var iterations = this.numHarmonics * 2 + 2;
HarmonicsFilter = function(magnitudes, phaseAngles) { if (this.waveShape === TRIANGLE) {
iterations++;
this.magnitudes = magnitudes; }
this.phaseAngles = phaseAngles;
for(var n = 1; n < iterations; n++) {
this.calculate = function(twoPiFT) {
switch(this.waveShape) {
var harmonics = 0;
var numHarmonics = magnitudes.length; case SAWTOOTH: {
for(var n = 0; n < numHarmonics; n++) { multiplier = 1 / n;
harmonics += this.magnitudes[n] * Math.cos(n * twoPiFT - this.phaseAngles[n]); harmonics += multiplier * Math.sin(n * frequency);
} break;
return harmonics; }
};
}; case TRIANGLE: {
// the main filter object literal if (n % 2 === 1) {
filter = (function() { var mulitplier = 1 / (n * n);
// multiply (4n-1)th harmonics by -1
// Bezier private functions if (n === 3 || n === 7 || n === 11 || n === 15) {
function _B1(t) { return t * t * t }; mulitplier *= -1;
function _B2(t) { return 3 * t * t * (1 - t) }; }
function _B3(t) { return 3 * t * (1 - t) * (1 - t) }; harmonics += mulitplier * Math.sin(n * frequency);
function _B4(t) { return (1 - t) * (1 - t) * (1 - t) }; }
break;
return { }
// helper methods case SQUARE: {
degToRad: function(degrees) {
if (n % 2 === 1) {
var convertedValue = degrees * Math.PI / 180; multiplier = 1 / n;
return convertedValue; harmonics += multiplier * Math.sin(n * frequency);
}, }
break;
radToDeg: function(radians) { }
}
var convertedValue = radians * 180 / Math.PI; }
return convertedValue;
}, // smooth the result and return
return this.smoothingFilter.process(harmonics);
// these filters need instantiating, as they hold arrays of previous values };
createAveragingFilter: function(length) { };
var newAveragingFilter = new AveragingFilter(length); // Create a motion wave by summing pre-calcualted sinusoidal harmonics
return newAveragingFilter; HarmonicsFilter = function(magnitudes, phaseAngles) {
},
this.magnitudes = magnitudes;
createButterworthFilter: function(cutoff) { this.phaseAngles = phaseAngles;
var newButterworthFilter = new ButterworthFilter(cutoff); this.calculate = function(twoPiFT) {
return newButterworthFilter;
}, var harmonics = 0;
var numHarmonics = magnitudes.length;
createWaveSynth: function(waveShape, numHarmonics, smoothing) {
for(var n = 0; n < numHarmonics; n++) {
var newWaveSynth = new WaveSynth(waveShape, numHarmonics, smoothing); harmonics += this.magnitudes[n] * Math.cos(n * twoPiFT - this.phaseAngles[n]);
return newWaveSynth; }
}, return harmonics;
};
createHarmonicsFilter: function(magnitudes, phaseAngles) { };
var newHarmonicsFilter = new HarmonicsFilter(magnitudes, phaseAngles);
return newHarmonicsFilter; // the main filter object
}, filter = (function() {
// Bezier private functions
// the following filters do not need separate instances, as they hold no previous values function _B1(t) { return t * t * t };
bezier: function(percent, C1, C2, C3, C4) { function _B2(t) { return 3 * t * t * (1 - t) };
function _B3(t) { return 3 * t * (1 - t) * (1 - t) };
// Bezier functions for more natural transitions function _B4(t) { return (1 - t) * (1 - t) * (1 - t) };
// based on script by Dan Pupius (www.pupius.net) http://13thparallel.com/archive/bezier-curves/
var pos = {x: 0, y: 0}; return {
pos.x = C1.x * _B1(percent) + C2.x * _B2(percent) + C3.x * _B3(percent) + C4.x * _B4(percent);
pos.y = C1.y * _B1(percent) + C2.y * _B2(percent) + C3.y * _B3(percent) + C4.y * _B4(percent); // helper methods
return pos; degToRad: function(degrees) {
},
var convertedValue = degrees * Math.PI / 180;
// simple clipping filter (clips bottom of wave only, special case for hips y-axis skeleton offset) return convertedValue;
clipTrough: function(inputValue, peak, strength) { },
var outputValue = inputValue * strength; radToDeg: function(radians) {
if (outputValue < -peak) {
outputValue = -peak; var convertedValue = radians * 180 / Math.PI;
} return convertedValue;
return outputValue; },
}
} // these filters need instantiating, as they hold arrays of previous values
createAveragingFilter: function(length) {
var newAveragingFilter = new AveragingFilter(length);
return newAveragingFilter;
},
createButterworthFilter1: function() {
var newButterworthFilter = new ButterworthFilter1();
return newButterworthFilter;
},
createButterworthFilter2: function(cutoff) {
var newButterworthFilter = new ButterworthFilter2(cutoff);
return newButterworthFilter;
},
createWaveSynth: function(waveShape, numHarmonics, smoothing) {
var newWaveSynth = new WaveSynth(waveShape, numHarmonics, smoothing);
return newWaveSynth;
},
createHarmonicsFilter: function(magnitudes, phaseAngles) {
var newHarmonicsFilter = new HarmonicsFilter(magnitudes, phaseAngles);
return newHarmonicsFilter;
},
// the following filters do not need separate instances, as they hold no previous values
bezier: function(percent, C1, C2, C3, C4) {
// Bezier functions for more natural transitions
// based on script by Dan Pupius (www.pupius.net) http://13thparallel.com/archive/bezier-curves/
var pos = {x: 0, y: 0};
pos.x = C1.x * _B1(percent) + C2.x * _B2(percent) + C3.x * _B3(percent) + C4.x * _B4(percent);
pos.y = C1.y * _B1(percent) + C2.y * _B2(percent) + C3.y * _B3(percent) + C4.y * _B4(percent);
return pos;
},
// simple clipping filter (clips bottom of wave only)
clipTrough: function(inputValue, peak, strength) {
var outputValue = inputValue * strength;
if (outputValue < -peak) {
outputValue = -peak;
}
return outputValue;
}
}
})(); })();

File diff suppressed because it is too large Load diff

View file

@ -566,6 +566,10 @@ function mouseReleaseEvent(event) {
} }
function mouseClickEvent(event) { function mouseClickEvent(event) {
if (!isActive) {
return;
}
var result = findClickedEntity(event); var result = findClickedEntity(event);
if (result === null) { if (result === null) {
if (!event.isShifted) { 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 // 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. // added it.
var modelMenuAddedDelete = false; var modelMenuAddedDelete = false;
var originalLightsArePickable = Entities.getLightsArePickable();
function setupModelMenus() { function setupModelMenus() {
print("setupModelMenus()"); print("setupModelMenus()");
// adj our menuitems // adj our menuitems
@ -653,10 +658,12 @@ function setupModelMenus() {
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" }); 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: "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 }); afterItem: "Paste Models", isCheckable: true, isChecked: true });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Small Models", shortcutKey: "CTRL+META+S", Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S",
afterItem: "Allow Select Large Models", isCheckable: true, isChecked: true }); 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: "Models", isSeparator: true, beforeItem: "Settings" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" }); 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, Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED,
isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" }); isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" });
Entities.setLightsArePickable(false);
} }
setupModelMenus(); // do this when first running our script. setupModelMenus(); // do this when first running our script.
@ -679,8 +688,9 @@ function cleanupModelMenus() {
Menu.removeMenuItem("Edit", "Model List..."); Menu.removeMenuItem("Edit", "Model List...");
Menu.removeMenuItem("Edit", "Paste Models"); Menu.removeMenuItem("Edit", "Paste Models");
Menu.removeMenuItem("Edit", "Allow Select Large Models"); Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
Menu.removeMenuItem("Edit", "Allow Select Small Models"); Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
Menu.removeSeparator("File", "Models"); Menu.removeSeparator("File", "Models");
Menu.removeMenuItem("File", "Export Models"); Menu.removeMenuItem("File", "Export Models");
@ -704,6 +714,7 @@ Script.scriptEnding.connect(function() {
if (exportMenu) { if (exportMenu) {
exportMenu.close(); exportMenu.close();
} }
Entities.setLightsArePickable(originalLightsArePickable);
}); });
// Do some stuff regularly, like check for placement of various overlays // Do some stuff regularly, like check for placement of various overlays
@ -714,10 +725,12 @@ Script.update.connect(function (deltaTime) {
}); });
function handeMenuEvent(menuItem) { function handeMenuEvent(menuItem) {
if (menuItem == "Allow Select Small Models") { if (menuItem == "Allow Selecting of Small Models") {
allowSmallModels = Menu.isOptionChecked("Allow Select Small Models"); allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models");
} else if (menuItem == "Allow Select Large Models") { } else if (menuItem == "Allow Selecting of Large Models") {
allowLargeModels = Menu.isOptionChecked("Allow Select 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") { } else if (menuItem == "Delete") {
if (SelectionManager.hasSelection()) { if (SelectionManager.hasSelection()) {
print(" Delete Entities"); print(" Delete Entities");

File diff suppressed because it is too large Load diff

View file

@ -896,6 +896,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
case Qt::Key_Greater: case Qt::Key_Greater:
case Qt::Key_Comma: case Qt::Key_Comma:
case Qt::Key_Period: case Qt::Key_Period:
case Qt::Key_QuoteDbl:
Menu::getInstance()->handleViewFrustumOffsetKeyModifier(event->key()); Menu::getInstance()->handleViewFrustumOffsetKeyModifier(event->key());
break; break;
case Qt::Key_L: case Qt::Key_L:
@ -2090,12 +2091,6 @@ void Application::updateMouseRay() {
_mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), _mouseRayDirection) + _mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), _mouseRayDirection) +
_viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), _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() { void Application::updateFaceshift() {
@ -2916,7 +2911,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly, RenderAr
// transform by eye offset // transform by eye offset
// load the view frustum // load the view frustum
loadViewFrustum(whichCamera, _displayViewFrustum); loadViewFrustum(whichCamera, _viewFrustum);
// flip x if in mirror mode (also requires reversing winding order for backface culling) // flip x if in mirror mode (also requires reversing winding order for backface culling)
if (whichCamera.getMode() == CAMERA_MODE_MIRROR) { 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 { float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const {
// allow 3DTV/Oculus to override parameters from camera // 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()) { if (OculusManager::isConnected()) {
OculusManager::overrideOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); OculusManager::overrideOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);

View file

@ -196,7 +196,6 @@ public:
const AudioReflector* getAudioReflector() const { return &_audioReflector; } const AudioReflector* getAudioReflector() const { return &_audioReflector; }
Camera* getCamera() { return &_myCamera; } Camera* getCamera() { return &_myCamera; }
ViewFrustum* getViewFrustum() { return &_viewFrustum; } ViewFrustum* getViewFrustum() { return &_viewFrustum; }
ViewFrustum* getDisplayViewFrustum() { return &_displayViewFrustum; }
ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; } ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; }
VoxelImporter* getVoxelImporter() { return &_voxelImporter; } VoxelImporter* getVoxelImporter() { return &_voxelImporter; }
VoxelSystem* getVoxels() { return &_voxels; } VoxelSystem* getVoxels() { return &_voxels; }
@ -518,7 +517,6 @@ private:
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels) ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels)
ViewFrustum _displayViewFrustum;
ViewFrustum _shadowViewFrustum; ViewFrustum _shadowViewFrustum;
quint64 _lastQueriedTime; quint64 _lastQueriedTime;

View file

@ -835,6 +835,14 @@ void Menu::handleViewFrustumOffsetKeyModifier(int key) {
const float VIEW_FRUSTUM_OFFSET_UP_DELTA = 0.05f; const float VIEW_FRUSTUM_OFFSET_UP_DELTA = 0.05f;
switch (key) { 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: case Qt::Key_BracketLeft:
_viewFrustumOffset.yaw -= VIEW_FRUSTUM_OFFSET_DELTA; _viewFrustumOffset.yaw -= VIEW_FRUSTUM_OFFSET_DELTA;
break; break;

View file

@ -192,7 +192,7 @@ static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM;
void MetavoxelSystem::render() { void MetavoxelSystem::render() {
// update the frustum // update the frustum
ViewFrustum* viewFrustum = Application::getInstance()->getDisplayViewFrustum(); ViewFrustum* viewFrustum = Application::getInstance()->getViewFrustum();
_frustum.set(viewFrustum->getFarTopLeft(), viewFrustum->getFarTopRight(), viewFrustum->getFarBottomLeft(), _frustum.set(viewFrustum->getFarTopLeft(), viewFrustum->getFarTopRight(), viewFrustum->getFarBottomLeft(),
viewFrustum->getFarBottomRight(), viewFrustum->getNearTopLeft(), viewFrustum->getNearTopRight(), viewFrustum->getFarBottomRight(), viewFrustum->getNearTopLeft(), viewFrustum->getNearTopRight(),
viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight()); viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight());
@ -1896,7 +1896,7 @@ private:
SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) : SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(), SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>(), QVector<AttributePointer>(), lod, QVector<AttributePointer>(), QVector<AttributePointer>(), lod,
encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())),
_containmentDepth(INT_MAX) { _containmentDepth(INT_MAX) {
} }
@ -1932,7 +1932,7 @@ private:
BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute) : BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute) :
MetavoxelVisitor(QVector<AttributePointer>() << attribute), MetavoxelVisitor(QVector<AttributePointer>() << attribute),
_order(encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), _order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())),
_containmentDepth(INT_MAX) { _containmentDepth(INT_MAX) {
} }

View file

@ -66,8 +66,6 @@ Avatar::Avatar() :
_leanScale(0.5f), _leanScale(0.5f),
_scale(1.0f), _scale(1.0f),
_worldUpDirection(DEFAULT_UP_DIRECTION), _worldUpDirection(DEFAULT_UP_DIRECTION),
_mouseRayOrigin(0.0f, 0.0f, 0.0f),
_mouseRayDirection(0.0f, 0.0f, 0.0f),
_moving(false), _moving(false),
_collisionGroups(0), _collisionGroups(0),
_initialized(false), _initialized(false),
@ -250,11 +248,6 @@ void Avatar::measureMotionDerivatives(float deltaTime) {
_lastOrientation = getOrientation(); _lastOrientation = getOrientation();
} }
void Avatar::setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction) {
_mouseRayOrigin = origin;
_mouseRayDirection = direction;
}
enum TextRendererType { enum TextRendererType {
CHAT, CHAT,
DISPLAYNAME DISPLAYNAME

View file

@ -85,7 +85,6 @@ public:
//setters //setters
void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); } void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); }
void setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction);
void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; } void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; }
bool getIsLookAtTarget() const { return _isLookAtTarget; } bool getIsLookAtTarget() const { return _isLookAtTarget; }
//getters //getters
@ -213,8 +212,6 @@ protected:
float _leanScale; float _leanScale;
float _scale; float _scale;
glm::vec3 _worldUpDirection; glm::vec3 _worldUpDirection;
glm::vec3 _mouseRayOrigin;
glm::vec3 _mouseRayDirection;
float _stringLength; float _stringLength;
bool _moving; ///< set when position is changing bool _moving; ///< set when position is changing

View file

@ -87,7 +87,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
if (!shouldKillAvatar(sharedAvatar)) { if (!shouldKillAvatar(sharedAvatar)) {
// this avatar's mixer is still around, go ahead and simulate it // this avatar's mixer is still around, go ahead and simulate it
avatar->simulate(deltaTime); avatar->simulate(deltaTime);
avatar->setMouseRay(mouseOrigin, mouseDirection);
++avatarIterator; ++avatarIterator;
} else { } else {
// the mixer that owned this avatar is gone, give it to the vector of fades and kill it // the mixer that owned this avatar is gone, give it to the vector of fades and kill it

View file

@ -67,7 +67,6 @@ const int SCRIPTED_MOTOR_WORLD_FRAME = 2;
MyAvatar::MyAvatar() : MyAvatar::MyAvatar() :
Avatar(), Avatar(),
_mousePressed(false),
_turningKeyPressTime(0.0f), _turningKeyPressTime(0.0f),
_gravity(0.0f, 0.0f, 0.0f), _gravity(0.0f, 0.0f, 0.0f),
_distanceToNearestAvatar(std::numeric_limits<float>::max()), _distanceToNearestAvatar(std::numeric_limits<float>::max()),

View file

@ -55,15 +55,12 @@ public:
void renderHeadMouse(int screenWidth, int screenHeight) const; void renderHeadMouse(int screenWidth, int screenHeight) const;
// setters // setters
void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; }
void setLeanScale(float scale) { _leanScale = scale; } void setLeanScale(float scale) { _leanScale = scale; }
void setLocalGravity(glm::vec3 gravity); void setLocalGravity(glm::vec3 gravity);
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; } void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
// getters // getters
float getLeanScale() const { return _leanScale; } 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 getGravity() const { return _gravity; }
glm::vec3 getDefaultEyePosition() const; glm::vec3 getDefaultEyePosition() const;
bool getShouldRenderLocally() const { return _shouldRender; } bool getShouldRenderLocally() const { return _shouldRender; }
@ -203,7 +200,6 @@ protected:
virtual void renderAttachments(RenderMode renderMode); virtual void renderAttachments(RenderMode renderMode);
private: private:
bool _mousePressed;
float _turningKeyPressTime; float _turningKeyPressTime;
glm::vec3 _gravity; glm::vec3 _gravity;
float _distanceToNearestAvatar; // How close is the nearest avatar? float _distanceToNearestAvatar; // How close is the nearest avatar?

View file

@ -90,3 +90,14 @@ void RenderableLightEntityItem::render(RenderArgs* args) {
glPopMatrix(); 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();
}

View file

@ -34,6 +34,10 @@ public:
{ } { }
virtual void render(RenderArgs* args); 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;
}; };

View file

@ -232,8 +232,8 @@ void DeferredLightingEffect::render() {
// enlarge the scales slightly to account for tesselation // enlarge the scales slightly to account for tesselation
const float SCALE_EXPANSION = 0.05f; const float SCALE_EXPANSION = 0.05f;
const glm::vec3& eyePoint = Application::getInstance()->getDisplayViewFrustum()->getPosition(); const glm::vec3& eyePoint = Application::getInstance()->getViewFrustum()->getPosition();
float nearRadius = glm::distance(eyePoint, Application::getInstance()->getDisplayViewFrustum()->getNearTopLeft()); float nearRadius = glm::distance(eyePoint, Application::getInstance()->getViewFrustum()->getNearTopLeft());
if (!_pointLights.isEmpty()) { if (!_pointLights.isEmpty()) {
_pointLight.bind(); _pointLight.bind();

View file

@ -134,6 +134,11 @@ public:
virtual SimulationState computeSimulationState() const; virtual SimulationState computeSimulationState() const;
virtual void debugDump() const; 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 // attributes applicable to all entity types
EntityTypes::EntityType getType() const { return _type; } EntityTypes::EntityType getType() const { return _type; }

View file

@ -221,6 +221,19 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke
return result; 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() : RayToEntityIntersectionResult::RayToEntityIntersectionResult() :
intersects(false), intersects(false),

View file

@ -96,6 +96,8 @@ public slots:
/// order to return an accurate result /// order to return an accurate result
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray); Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray);
Q_INVOKABLE void setLightsArePickable(bool value);
Q_INVOKABLE bool getLightsArePickable() const;
Q_INVOKABLE void dumpTree() const; Q_INVOKABLE void dumpTree() const;

View file

@ -21,6 +21,7 @@
EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage), _simulation(NULL) { EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage), _simulation(NULL) {
_rootElement = createNewElement(); _rootElement = createNewElement();
_lightsArePickable = true; // assume they are by default
} }
EntityTree::~EntityTree() { EntityTree::~EntityTree() {

View file

@ -144,6 +144,8 @@ public:
void emitEntityScriptChanging(const EntityItemID& entityItemID); void emitEntityScriptChanging(const EntityItemID& entityItemID);
bool getLightsArePickable() const { return _lightsArePickable; }
void setLightsArePickable(bool value) { _lightsArePickable = value; }
void setSimulation(EntitySimulation* simulation); void setSimulation(EntitySimulation* simulation);
signals: signals:
@ -169,6 +171,8 @@ private:
EntityItemFBXService* _fbxService; EntityItemFBXService* _fbxService;
QHash<EntityItemID, EntityTreeElement*> _entityToElementMap; QHash<EntityItemID, EntityTreeElement*> _entityToElementMap;
bool _lightsArePickable;
EntitySimulation* _simulation; EntitySimulation* _simulation;
}; };

View file

@ -511,10 +511,28 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
// and testing intersection there. // and testing intersection there.
if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance, localFace)) { if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance, localFace)) {
if (localDistance < distance) { if (localDistance < distance) {
distance = localDistance; // now ask the entity if we actually intersect
face = localFace; if (entity->supportsDetailedRayIntersection()) {
*intersectedObject = (void*)entity;
somethingIntersected = true; if (entity->findDetailedRayIntersection(origin, direction, keepSearching, element, localDistance,
localFace, intersectedObject)) {
if (localDistance < distance) {
distance = localDistance;
face = localFace;
*intersectedObject = (void*)entity;
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;
}
}
} }
} }
} }

View file

@ -93,3 +93,26 @@ void SphereEntityItem::recalculateCollisionShape() {
float largestDiameter = glm::max(dimensionsInMeters.x, dimensionsInMeters.y, dimensionsInMeters.z); float largestDiameter = glm::max(dimensionsInMeters.x, dimensionsInMeters.y, dimensionsInMeters.z);
_sphereShape.setRadius(largestDiameter / 2.0f); _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;
}

View file

@ -56,6 +56,11 @@ public:
// TODO: implement proper contains for 3D ellipsoid // TODO: implement proper contains for 3D ellipsoid
//virtual bool contains(const glm::vec3& point) const; //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: protected:
virtual void recalculateCollisionShape(); virtual void recalculateCollisionShape();

View file

@ -10,9 +10,12 @@
// //
#include <glm/gtx/transform.hpp>
#include <QDebug> #include <QDebug>
#include <ByteCountCoding.h> #include <ByteCountCoding.h>
#include <PlaneShape.h>
#include "EntityTree.h" #include "EntityTree.h"
#include "EntityTreeElement.h" #include "EntityTreeElement.h"
@ -110,4 +113,48 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, appendValue, getLineHeight()); APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, appendValue, getLineHeight());
APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, appendColor, getTextColor()); APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, appendColor, getTextColor());
APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, appendColor, getBackgroundColor()); 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;
}

View file

@ -41,6 +41,11 @@ public:
ReadBitstreamToTreeParams& args, ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData); 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; static const QString DEFAULT_TEXT;
void setText(const QString& value) { _text = value; } void setText(const QString& value) { _text = value; }
const QString& getText() const { return _text; } const QString& getText() const { return _text; }