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

This commit is contained in:
gaitat 2014-02-14 20:12:39 -05:00
commit eee839f423
40 changed files with 1021 additions and 519 deletions

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSRequiresCarbon</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME} URL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>hifi</string>
</array>
</dict>
</array>
</dict>
</plist>

View file

@ -69,6 +69,12 @@ function checkCamera() {
var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER);
yaw -= viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime;
pitch += viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime;
if (yaw > 360) {
yaw -= 360;
}
if (yaw < -360) {
yaw += 360;
}
var orientation = Quat.fromPitchYawRoll(pitch, yaw, roll);
Camera.setOrientation(orientation);
}

View file

@ -7,9 +7,11 @@
//
// Captures mouse clicks and edits voxels accordingly.
//
// click = create a new voxel on this face, same color as old
// Alt + click = delete this voxel
// click = create a new voxel on this face, same color as old (default color picker state)
// right click or control + click = delete this voxel
// shift + click = recolor this voxel
// 1 - 8 = pick new color from palette
// 9 = create a new voxel in front of the camera
//
// Click and drag to create more new voxels in the same direction
//
@ -18,15 +20,41 @@ function vLength(v) {
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
function vMinus(a, b) {
var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
return rval;
}
var NEW_VOXEL_SIZE = 1.0;
var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0;
var ORBIT_RATE_ALTITUDE = 200.0;
var ORBIT_RATE_AZIMUTH = 90.0;
var PIXELS_PER_EXTRUDE_VOXEL = 16;
var oldMode = Camera.getMode();
var key_alt = false;
var key_shift = false;
var isAdding = false;
var isExtruding = false;
var isOrbiting = false;
var orbitAzimuth = 0.0;
var orbitAltitude = 0.0;
var orbitCenter = { x: 0, y: 0, z: 0 };
var orbitPosition = { x: 0, y: 0, z: 0 };
var orbitRadius = 0.0;
var extrudeDirection = { x: 0, y: 0, z: 0 };
var extrudeScale = 0.0;
var lastVoxelPosition = { x: 0, y: 0, z: 0 };
var lastVoxelColor = { red: 0, green: 0, blue: 0 };
var lastVoxelScale = 0;
var dragStart = { x: 0, y: 0 };
var mouseX = 0;
var mouseY = 0;
// Create a table of the different colors you can choose
var colors = new Array();
colors[0] = { red: 237, green: 175, blue: 0 };
@ -37,31 +65,70 @@ colors[4] = { red: 193, green: 99, blue: 122 };
colors[5] = { red: 255, green: 54, blue: 69 };
colors[6] = { red: 124, green: 36, blue: 36 };
colors[7] = { red: 63, green: 35, blue: 19 };
var numColors = 6;
var whichColor = 0;
var numColors = 8;
var whichColor = -1; // Starting color is 'Copy' mode
// Create sounds for adding, deleting, recoloring voxels
var addSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Electronic/ElectronicBurst1.raw");
var deleteSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Bubbles/bubbles1.raw");
var changeColorSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Electronic/ElectronicBurst6.raw");
var addSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create.raw");
var deleteSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+delete.raw");
var changeColorSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+edit.raw");
var clickSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Switches+and+sliders/toggle+switch+-+medium.raw");
var audioOptions = new AudioInjectionOptions();
audioOptions.volume = 0.5;
function setAudioPosition() {
var camera = Camera.getPosition();
var forwardVector = Quat.getFront(MyAvatar.orientation);
audioOptions.position = Vec3.sum(camera, forwardVector);
}
function getNewVoxelPosition() {
var camera = Camera.getPosition();
var forwardVector = Quat.getFront(MyAvatar.orientation);
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, NEW_VOXEL_DISTANCE_FROM_CAMERA));
return newPosition;
}
function fixEulerAngles(eulers) {
var rVal = { x: 0, y: 0, z: eulers.z };
if (eulers.x >= 90.0) {
rVal.x = 180.0 - eulers.x;
rVal.y = eulers.y - 180.0;
} else if (eulers.x <= -90.0) {
rVal.x = 180.0 - eulers.x;
rVal.y = eulers.y - 180.0;
}
return rVal;
}
function mousePressEvent(event) {
mouseX = event.x;
mouseY = event.y;
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Voxels.findRayIntersection(pickRay);
audioOptions.volume = 1.0;
audioOptions.position = { x: intersection.voxel.x, y: intersection.voxel.y, z: intersection.voxel.z };
audioOptions.position = Vec3.sum(pickRay.origin, pickRay.direction);
if (intersection.intersects) {
if (key_alt) {
if (event.isAlt) {
// start orbit camera!
var cameraPosition = Camera.getPosition();
oldMode = Camera.getMode();
Camera.setMode("independent");
isOrbiting = true;
Camera.keepLookingAt(intersection.intersection);
// get position for initial azimuth, elevation
orbitCenter = intersection.intersection;
var orbitVector = Vec3.subtract(cameraPosition, orbitCenter);
orbitRadius = vLength(orbitVector);
orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x);
orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector));
} else if (event.isRightButton || event.isControl) {
// Delete voxel
Voxels.eraseVoxel(intersection.voxel.x, intersection.voxel.y, intersection.voxel.z, intersection.voxel.s);
Audio.playSound(deleteSound, audioOptions);
} else if (key_shift) {
} else if (event.isShifted) {
// Recolor Voxel
whichColor++;
if (whichColor == numColors) whichColor = 0;
Voxels.setVoxel(intersection.voxel.x,
intersection.voxel.y,
intersection.voxel.z,
@ -70,7 +137,9 @@ function mousePressEvent(event) {
Audio.playSound(changeColorSound, audioOptions);
} else {
// Add voxel on face
var newVoxel = {
if (whichColor == -1) {
// Copy mode - use clicked voxel color
var newVoxel = {
x: intersection.voxel.x,
y: intersection.voxel.y,
z: intersection.voxel.z,
@ -78,6 +147,16 @@ function mousePressEvent(event) {
red: intersection.voxel.red,
green: intersection.voxel.green,
blue: intersection.voxel.blue };
} else {
var newVoxel = {
x: intersection.voxel.x,
y: intersection.voxel.y,
z: intersection.voxel.z,
s: intersection.voxel.s,
red: colors[whichColor].red,
green: colors[whichColor].green,
blue: colors[whichColor].blue };
}
if (intersection.face == "MIN_X_FACE") {
newVoxel.x -= newVoxel.s;
@ -108,55 +187,113 @@ function mousePressEvent(event) {
function keyPressEvent(event) {
key_alt = event.isAlt;
key_shift = event.isShifted;
var nVal = parseInt(event.text);
if (event.text == "0") {
print("Color = Copy");
whichColor = -1;
Audio.playSound(clickSound, audioOptions);
} else if ((nVal > 0) && (nVal <= numColors)) {
whichColor = nVal - 1;
print("Color = " + (whichColor + 1));
Audio.playSound(clickSound, audioOptions);
} else if (event.text == "9") {
// Create a brand new 1 meter voxel in front of your avatar
var color = whichColor;
if (color == -1) color = 0;
var newPosition = getNewVoxelPosition();
var newVoxel = {
x: newPosition.x,
y: newPosition.y ,
z: newPosition.z,
s: NEW_VOXEL_SIZE,
red: colors[color].red,
green: colors[color].green,
blue: colors[color].blue };
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
setAudioPosition();
Audio.playSound(addSound, audioOptions);
} else if (event.text == " ") {
// Reset my orientation!
var orientation = { x:0, y:0, z:0, w:1 };
Camera.setOrientation(orientation);
MyAvatar.orientation = orientation;
}
}
function keyReleaseEvent(event) {
key_alt = false;
key_shift = false;
}
function mouseMoveEvent(event) {
if (isOrbiting) {
var cameraOrientation = Camera.getOrientation();
var origEulers = Quat.safeEulerAngles(cameraOrientation);
var newEulers = fixEulerAngles(Quat.safeEulerAngles(cameraOrientation));
var dx = event.x - mouseX;
var dy = event.y - mouseY;
orbitAzimuth += dx / ORBIT_RATE_AZIMUTH;
orbitAltitude += dy / ORBIT_RATE_ALTITUDE;
var orbitVector = { x:(Math.cos(orbitAltitude) * Math.cos(orbitAzimuth)) * orbitRadius,
y:Math.sin(orbitAltitude) * orbitRadius,
z:(Math.cos(orbitAltitude) * Math.sin(orbitAzimuth)) * orbitRadius };
orbitPosition = Vec3.sum(orbitCenter, orbitVector);
Camera.setPosition(orbitPosition);
mouseX = event.x;
mouseY = event.y;
}
if (isAdding) {
var pickRay = Camera.computePickRay(event.x, event.y);
var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x,
// Watch the drag direction to tell which way to 'extrude' this voxel
if (!isExtruding) {
var pickRay = Camera.computePickRay(event.x, event.y);
var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x,
y: pickRay.origin.y - lastVoxelPosition.y,
z: pickRay.origin.z - lastVoxelPosition.z };
var distance = vLength(lastVoxelDistance);
var mouseSpot = { x: pickRay.direction.x * distance, y: pickRay.direction.y * distance, z: pickRay.direction.z * distance };
mouseSpot.x += pickRay.origin.x;
mouseSpot.y += pickRay.origin.y;
mouseSpot.z += pickRay.origin.z;
var dx = mouseSpot.x - lastVoxelPosition.x;
var dy = mouseSpot.y - lastVoxelPosition.y;
var dz = mouseSpot.z - lastVoxelPosition.z;
if (dx > lastVoxelScale) {
lastVoxelPosition.x += lastVoxelScale;
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
} else if (dx < -lastVoxelScale) {
lastVoxelPosition.x -= lastVoxelScale;
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
} else if (dy > lastVoxelScale) {
lastVoxelPosition.y += lastVoxelScale;
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
} else if (dy < -lastVoxelScale) {
lastVoxelPosition.y -= lastVoxelScale;
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
} else if (dz > lastVoxelScale) {
lastVoxelPosition.z += lastVoxelScale;
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
} else if (dz < -lastVoxelScale) {
lastVoxelPosition.z -= lastVoxelScale;
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
var distance = vLength(lastVoxelDistance);
var mouseSpot = { x: pickRay.direction.x * distance, y: pickRay.direction.y * distance, z: pickRay.direction.z * distance };
mouseSpot.x += pickRay.origin.x;
mouseSpot.y += pickRay.origin.y;
mouseSpot.z += pickRay.origin.z;
var dx = mouseSpot.x - lastVoxelPosition.x;
var dy = mouseSpot.y - lastVoxelPosition.y;
var dz = mouseSpot.z - lastVoxelPosition.z;
extrudeScale = lastVoxelScale;
extrudeDirection = { x: 0, y: 0, z: 0 };
isExtruding = true;
if (dx > lastVoxelScale) extrudeDirection.x = extrudeScale;
else if (dx < -lastVoxelScale) extrudeDirection.x = -extrudeScale;
else if (dy > lastVoxelScale) extrudeDirection.y = extrudeScale;
else if (dy < -lastVoxelScale) extrudeDirection.y = -extrudeScale;
else if (dz > lastVoxelScale) extrudeDirection.z = extrudeScale;
else if (dz < -lastVoxelScale) extrudeDirection.z = -extrudeScale;
else isExtruding = false;
} else {
// We have got an extrusion direction, now look for mouse move beyond threshold to add new voxel
var dx = event.x - mouseX;
var dy = event.y - mouseY;
if (Math.sqrt(dx*dx + dy*dy) > PIXELS_PER_EXTRUDE_VOXEL) {
lastVoxelPosition = Vec3.sum(lastVoxelPosition, extrudeDirection);
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
extrudeScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
mouseX = event.x;
mouseY = event.y;
}
}
}
}
function mouseReleaseEvent(event) {
if (isOrbiting) {
var cameraOrientation = Camera.getOrientation();
var eulers = Quat.safeEulerAngles(cameraOrientation);
MyAvatar.position = Camera.getPosition();
MyAvatar.orientation = cameraOrientation;
Camera.stopLooking();
Camera.setMode(oldMode);
Camera.setOrientation(cameraOrientation);
}
isAdding = false;
isOrbiting = false;
isExtruding = false;
}
Controller.mousePressEvent.connect(mousePressEvent);

210
examples/hydraMove.js Normal file
View file

@ -0,0 +1,210 @@
//
// hydraMove.js
// hifi
//
// Created by Brad Hefta-Gaub on 2/10/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates use of the Controller and MyAvatar classes to implement
// avatar flying through the hydra/controller joysticks
//
//
var damping = 0.9;
var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z };
var joysticksCaptured = false;
var THRUST_CONTROLLER = 0;
var VIEW_CONTROLLER = 1;
var INITIAL_THRUST_MULTPLIER = 1.0;
var THRUST_INCREASE_RATE = 1.05;
var MAX_THRUST_MULTIPLIER = 75.0;
var thrustMultiplier = INITIAL_THRUST_MULTPLIER;
var grabDelta = { x: 0, y: 0, z: 0};
var grabDeltaVelocity = { x: 0, y: 0, z: 0};
var grabStartRotation = { x: 0, y: 0, z: 0, w: 1};
var grabCurrentRotation = { x: 0, y: 0, z: 0, w: 1};
var grabbingWithRightHand = false;
var wasGrabbingWithRightHand = false;
var grabbingWithLeftHand = false;
var wasGrabbingWithLeftHand = false;
var EPSILON = 0.000001;
var velocity = { x: 0, y: 0, z: 0};
var deltaTime = 1/60; // approximately our FPS - maybe better to be elapsed time since last call
var THRUST_MAG_UP = 800.0;
var THRUST_MAG_DOWN = 300.0;
var THRUST_MAG_FWD = 500.0;
var THRUST_MAG_BACK = 300.0;
var THRUST_MAG_LATERAL = 250.0;
var THRUST_JUMP = 120.0;
var YAW_MAG = 500.0;
var PITCH_MAG = 100.0;
var THRUST_MAG_HAND_JETS = THRUST_MAG_FWD;
var JOYSTICK_YAW_MAG = YAW_MAG;
var JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5;
var LEFT_PALM = 0;
var LEFT_BUTTON_4 = 4;
var RIGHT_PALM = 2;
var RIGHT_BUTTON_4 = 10;
// Used by handleGrabBehavior() for managing the grab position changes
function getAndResetGrabDelta() {
var HAND_GRAB_SCALE_DISTANCE = 2.0;
var delta = Vec3.multiply(grabDelta, (MyAvatar.scale * HAND_GRAB_SCALE_DISTANCE));
grabDelta = { x: 0, y: 0, z: 0};
var avatarRotation = MyAvatar.orientation;
var result = Vec3.multiplyQbyV(avatarRotation, Vec3.multiply(delta, -1));
return result;
}
// Used by handleGrabBehavior() for managing the grab velocity feature
function getAndResetGrabDeltaVelocity() {
var HAND_GRAB_SCALE_VELOCITY = 50.0;
var delta = Vec3.multiply(grabDeltaVelocity, (MyAvatar.scale * HAND_GRAB_SCALE_VELOCITY));
grabDeltaVelocity = { x: 0, y: 0, z: 0};
var avatarRotation = MyAvatar.orientation;
var result = Quat.multiply(avatarRotation, Vec3.multiply(delta, -1));
return result;
}
// Used by handleGrabBehavior() for managing the grab rotation feature
function getAndResetGrabRotation() {
var quatDiff = Quat.multiply(grabCurrentRotation, Quat.inverse(grabStartRotation));
grabStartRotation = grabCurrentRotation;
return quatDiff;
}
// handles all the grab related behavior: position (crawl), velocity (flick), and rotate (twist)
function handleGrabBehavior() {
// check for and handle grab behaviors
grabbingWithRightHand = Controller.isButtonPressed(RIGHT_BUTTON_4);
grabbingWithLeftHand = Controller.isButtonPressed(LEFT_BUTTON_4);
stoppedGrabbingWithLeftHand = false;
stoppedGrabbingWithRightHand = false;
if (grabbingWithRightHand && !wasGrabbingWithRightHand) {
// Just starting grab, capture starting rotation
grabStartRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM);
}
if (grabbingWithRightHand) {
grabDelta = Vec3.sum(grabDelta, Vec3.multiply(Controller.getSpatialControlVelocity(RIGHT_PALM), deltaTime));
grabCurrentRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM);
}
if (!grabbingWithRightHand && wasGrabbingWithRightHand) {
// Just ending grab, capture velocity
grabDeltaVelocity = Controller.getSpatialControlVelocity(RIGHT_PALM);
stoppedGrabbingWithRightHand = true;
}
if (grabbingWithLeftHand && !wasGrabbingWithLeftHand) {
// Just starting grab, capture starting rotation
grabStartRotation = Controller.getSpatialControlRawRotation(LEFT_PALM);
}
if (grabbingWithLeftHand) {
grabDelta = Vec3.sum(grabDelta, Vec3.multiply(Controller.getSpatialControlVelocity(LEFT_PALM), deltaTime));
grabCurrentRotation = Controller.getSpatialControlRawRotation(LEFT_PALM);
}
if (!grabbingWithLeftHand && wasGrabbingWithLeftHand) {
// Just ending grab, capture velocity
grabDeltaVelocity = Controller.getSpatialControlVelocity(LEFT_PALM);
stoppedGrabbingWithLeftHand = true;
}
grabbing = grabbingWithRightHand || grabbingWithLeftHand;
stoppedGrabbing = stoppedGrabbingWithRightHand || stoppedGrabbingWithLeftHand;
if (grabbing) {
// move position
var moveFromGrab = getAndResetGrabDelta();
if (Vec3.length(moveFromGrab) > EPSILON) {
MyAvatar.position = Vec3.sum(MyAvatar.position, moveFromGrab);
velocity = { x: 0, y: 0, z: 0};
}
// add some rotation...
var deltaRotation = getAndResetGrabRotation();
var GRAB_CONTROLLER_TURN_SCALING = 0.5;
var euler = Vec3.multiply(Quat.safeEulerAngles(deltaRotation), GRAB_CONTROLLER_TURN_SCALING);
// Adjust body yaw by yaw from controller
var orientation = Quat.multiply(Quat.angleAxis(-euler.y, {x:0, y: 1, z:0}), MyAvatar.orientation);
MyAvatar.orientation = orientation;
// Adjust head pitch from controller
MyAvatar.headPitch = MyAvatar.headPitch - euler.x;
}
// add some velocity...
if (stoppedGrabbing) {
velocity = Vec3.sum(velocity, getAndResetGrabDeltaVelocity());
}
// handle residual velocity
if(Vec3.length(velocity) > EPSILON) {
MyAvatar.position = Vec3.sum(MyAvatar.position, Vec3.multiply(velocity, deltaTime));
// damp velocity
velocity = Vec3.multiply(velocity, damping);
}
wasGrabbingWithRightHand = grabbingWithRightHand;
wasGrabbingWithLeftHand = grabbingWithLeftHand;
}
// Main update function that handles flying and grabbing behaviort
function flyWithHydra() {
var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER);
if (thrustJoystickPosition.x != 0 || thrustJoystickPosition.y != 0) {
if (thrustMultiplier < MAX_THRUST_MULTIPLIER) {
thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE);
}
var currentOrientation = MyAvatar.orientation;
var front = Quat.getFront(currentOrientation);
var right = Quat.getRight(currentOrientation);
var up = Quat.getUp(currentOrientation);
var thrustFront = Vec3.multiply(front, MyAvatar.scale * THRUST_MAG_HAND_JETS *
thrustJoystickPosition.y * thrustMultiplier * deltaTime);
MyAvatar.addThrust(thrustFront);
var thrustRight = Vec3.multiply(right, MyAvatar.scale * THRUST_MAG_HAND_JETS *
thrustJoystickPosition.x * thrustMultiplier * deltaTime);
MyAvatar.addThrust(thrustRight);
} else {
thrustMultiplier = INITIAL_THRUST_MULTPLIER;
}
// View Controller
var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER);
if (viewJoystickPosition.x != 0 || viewJoystickPosition.y != 0) {
// change the body yaw based on our x controller
var orientation = MyAvatar.orientation;
var deltaOrientation = Quat.fromPitchYawRoll(0, (-1 * viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime), 0);
MyAvatar.orientation = Quat.multiply(orientation, deltaOrientation);
// change the headPitch based on our x controller
//pitch += viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime;
var newPitch = MyAvatar.headPitch + (viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime);
MyAvatar.headPitch = newPitch;
}
handleGrabBehavior();
}
Script.willSendVisualDataCallback.connect(flyWithHydra);
Controller.captureJoystick(THRUST_CONTROLLER);
Controller.captureJoystick(VIEW_CONTROLLER);
// Map keyPress and mouse move events to our callbacks
function scriptEnding() {
// re-enabled the standard application for touch events
Controller.releaseJoystick(THRUST_CONTROLLER);
Controller.releaseJoystick(VIEW_CONTROLLER);
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -90,7 +90,13 @@ qt5_wrap_ui(QT_UI_HEADERS ${QT_UI_FILES})
set(INTERFACE_SRCS ${INTERFACE_SRCS} ${QT_UI_HEADERS})
if (APPLE)
# configure CMake to use a custom Info.plist
SET_TARGET_PROPERTIES( ${this_target} PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in )
set(MACOSX_BUNDLE_BUNDLE_NAME Interface)
set(MACOSX_BUNDLE_GUI_IDENTIFIER io.highfidelity.Interface)
# set how the icon shows up in the Info.plist file
SET(MACOSX_BUNDLE_ICON_FILE interface.icns)

View file

@ -101,6 +101,8 @@ const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::D
const int STATS_PELS_PER_LINE = 20;
const QString CUSTOM_URL_SCHEME = "hifi:";
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (message.size() > 0) {
QString messageWithNewLine = message + "\n";
@ -679,6 +681,38 @@ void Application::controlledBroadcastToNodes(const QByteArray& packet, const Nod
}
}
bool Application::event(QEvent* event) {
// handle custom URL
if (event->type() == QEvent::FileOpen) {
QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
if (!fileEvent->url().isEmpty() && fileEvent->url().toLocalFile().startsWith(CUSTOM_URL_SCHEME)) {
QString destination = fileEvent->url().toLocalFile().remove(CUSTOM_URL_SCHEME);
QStringList urlParts = destination.split('/', QString::SkipEmptyParts);
if (urlParts.count() > 1) {
// if url has 2 or more parts, the first one is domain name
Menu::getInstance()->goToDomain(urlParts[0]);
// location coordinates
Menu::getInstance()->goToDestination(urlParts[1]);
if (urlParts.count() > 2) {
// location orientation
Menu::getInstance()->goToOrientation(urlParts[2]);
}
} else if (urlParts.count() == 1) {
// location coordinates
Menu::getInstance()->goToDestination(urlParts[0]);
}
}
return false;
}
return QApplication::event(event);
}
void Application::keyPressEvent(QKeyEvent* event) {
_controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts
@ -2146,15 +2180,6 @@ void Application::updateThreads(float deltaTime) {
}
}
void Application::updateParticles(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateParticles()");
if (Menu::getInstance()->isOptionChecked(MenuOption::ParticleCloud)) {
_cloud.simulate(deltaTime);
}
}
void Application::updateMetavoxels(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateMetavoxels()");
@ -2276,7 +2301,6 @@ void Application::update(float deltaTime) {
updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
_avatarManager.updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them...
updateParticles(deltaTime); // Simulate particle cloud movements
updateMetavoxels(deltaTime); // update metavoxels
updateCamera(deltaTime); // handle various camera tweaks like off axis projection
updateDialogs(deltaTime); // update various stats dialogs if present
@ -2711,10 +2735,6 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
// disable specular lighting for ground and voxels
glMaterialfv(GL_FRONT, GL_SPECULAR, NO_SPECULAR_COLOR);
// Draw Cloud Particles
if (Menu::getInstance()->isOptionChecked(MenuOption::ParticleCloud)) {
_cloud.render();
}
// Draw voxels
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
@ -3998,6 +4018,32 @@ void Application::saveScripts() {
settings->endArray();
}
void Application::stopAllScripts() {
// stops all current running scripts
QList<QAction*> scriptActions = Menu::getInstance()->getActiveScriptsMenu()->actions();
foreach (QAction* scriptAction, scriptActions) {
scriptAction->activate(QAction::Trigger);
qDebug() << "stopping script..." << scriptAction->text();
}
_activeScripts.clear();
}
void Application::reloadAllScripts() {
// remember all the current scripts so we can reload them
QStringList reloadList = _activeScripts;
// reloads all current running scripts
QList<QAction*> scriptActions = Menu::getInstance()->getActiveScriptsMenu()->actions();
foreach (QAction* scriptAction, scriptActions) {
scriptAction->activate(QAction::Trigger);
qDebug() << "stopping script..." << scriptAction->text();
}
_activeScripts.clear();
foreach (QString scriptName, reloadList){
qDebug() << "reloading script..." << scriptName;
loadScript(scriptName);
}
}
void Application::removeScriptName(const QString& fileNameString) {
_activeScripts.removeOne(fileNameString);
}
@ -4042,7 +4088,7 @@ void Application::loadScript(const QString& fileNameString) {
scriptEngine->getParticlesScriptingInterface()->setParticleTree(_particles.getTree());
// hook our avatar object into this script engine
scriptEngine->setAvatarData( static_cast<Avatar*>(_myAvatar), "MyAvatar");
scriptEngine->setAvatarData(_myAvatar, "MyAvatar"); // leave it as a MyAvatar class to expose thrust features
CameraScriptableObject* cameraScriptable = new CameraScriptableObject(&_myCamera, &_viewFrustum);
scriptEngine->registerGlobalObject("Camera", cameraScriptable);

View file

@ -32,7 +32,6 @@
#include "BandwidthMeter.h"
#include "Camera.h"
#include "Cloud.h"
#include "DatagramProcessor.h"
#include "Environment.h"
#include "GLCanvas.h"
@ -127,7 +126,9 @@ public:
void touchUpdateEvent(QTouchEvent* event);
void wheelEvent(QWheelEvent* event);
bool event(QEvent* event);
void makeVoxel(glm::vec3 position,
float scale,
unsigned char red,
@ -232,6 +233,8 @@ public slots:
void loadDialog();
void toggleLogDialog();
void initAvatarAndViewFrustum();
void stopAllScripts();
void reloadAllScripts();
private slots:
void timer();
@ -284,7 +287,6 @@ private:
void updateSixense(float deltaTime);
void updateSerialDevices(float deltaTime);
void updateThreads(float deltaTime);
void updateParticles(float deltaTime);
void updateMetavoxels(float deltaTime);
void updateCamera(float deltaTime);
void updateDialogs(float deltaTime);
@ -351,8 +353,6 @@ private:
Stars _stars;
Cloud _cloud;
VoxelSystem _voxels;
VoxelTree _clipboard; // if I copy/paste
VoxelImporter* _voxelImporter;

View file

@ -37,6 +37,8 @@ static const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_LENGTH_MSECS * NUM_AUDI
static const float AUDIO_CALLBACK_MSECS = (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float)SAMPLE_RATE * 1000.0;
static const int NUMBER_OF_NOISE_SAMPLE_FRAMES = 300;
// Mute icon configration
static const int ICON_SIZE = 24;
static const int ICON_LEFT = 0;
@ -65,7 +67,8 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* p
_measuredJitter(0),
_jitterBufferSamples(initialJitterBufferSamples),
_lastInputLoudness(0),
_averageInputLoudness(0),
_noiseGateMeasuredFloor(0),
_noiseGateSampleCounter(0),
_noiseGateOpen(false),
_noiseGateEnabled(true),
_noiseGateFramesToClose(0),
@ -82,6 +85,8 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* p
{
// clear the array of locally injected samples
memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL);
// Create the noise sample array
_noiseSampleFrames = new float[NUMBER_OF_NOISE_SAMPLE_FRAMES];
}
void Audio::init(QGLWidget *parent) {
@ -351,26 +356,66 @@ void Audio::handleAudioInput() {
NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL,
_inputFormat, _desiredInputFormat);
//
// Impose Noise Gate
//
// The Noise Gate is used to reject constant background noise by measuring the noise
// floor observed at the microphone and then opening the 'gate' to allow microphone
// signals to be transmitted when the microphone samples average level exceeds a multiple
// of the noise floor.
//
// NOISE_GATE_HEIGHT: How loud you have to speak relative to noise background to open the gate.
// Make this value lower for more sensitivity and less rejection of noise.
// NOISE_GATE_WIDTH: The number of samples in an audio frame for which the height must be exceeded
// to open the gate.
// NOISE_GATE_CLOSE_FRAME_DELAY: Once the noise is below the gate height for the frame, how many frames
// will we wait before closing the gate.
// NOISE_GATE_FRAMES_TO_AVERAGE: How many audio frames should we average together to compute noise floor.
// More means better rejection but also can reject continuous things like singing.
// NUMBER_OF_NOISE_SAMPLE_FRAMES: How often should we re-evaluate the noise floor?
float loudness = 0;
float thisSample = 0;
int samplesOverNoiseGate = 0;
const float NOISE_GATE_HEIGHT = 3.f;
const float NOISE_GATE_HEIGHT = 7.f;
const int NOISE_GATE_WIDTH = 5;
const int NOISE_GATE_CLOSE_FRAME_DELAY = 30;
const int NOISE_GATE_CLOSE_FRAME_DELAY = 5;
const int NOISE_GATE_FRAMES_TO_AVERAGE = 5;
for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
thisSample = fabsf(monoAudioSamples[i]);
loudness += thisSample;
// Noise Reduction: Count peaks above the average loudness
if (thisSample > (_averageInputLoudness * NOISE_GATE_HEIGHT)) {
if (thisSample > (_noiseGateMeasuredFloor * NOISE_GATE_HEIGHT)) {
samplesOverNoiseGate++;
}
}
_lastInputLoudness = loudness / NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
const float LOUDNESS_AVERAGING_FRAMES = 1000.f; // This will be about 10 seconds
_averageInputLoudness = (1.f - 1.f / LOUDNESS_AVERAGING_FRAMES) * _averageInputLoudness + (1.f / LOUDNESS_AVERAGING_FRAMES) * _lastInputLoudness;
float averageOfAllSampleFrames = 0.f;
_noiseSampleFrames[_noiseGateSampleCounter++] = _lastInputLoudness;
if (_noiseGateSampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) {
float smallestSample = FLT_MAX;
for (int i = 0; i <= NUMBER_OF_NOISE_SAMPLE_FRAMES - NOISE_GATE_FRAMES_TO_AVERAGE; i+= NOISE_GATE_FRAMES_TO_AVERAGE) {
float thisAverage = 0.0f;
for (int j = i; j < i + NOISE_GATE_FRAMES_TO_AVERAGE; j++) {
thisAverage += _noiseSampleFrames[j];
averageOfAllSampleFrames += _noiseSampleFrames[j];
}
thisAverage /= NOISE_GATE_FRAMES_TO_AVERAGE;
if (thisAverage < smallestSample) {
smallestSample = thisAverage;
}
}
averageOfAllSampleFrames /= NUMBER_OF_NOISE_SAMPLE_FRAMES;
_noiseGateMeasuredFloor = smallestSample;
_noiseGateSampleCounter = 0;
//qDebug("smallest sample = %.1f, avg of all = %.1f", _noiseGateMeasuredFloor, averageOfAllSampleFrames);
}
if (_noiseGateEnabled) {
if (samplesOverNoiseGate > NOISE_GATE_WIDTH) {
_noiseGateOpen = true;
@ -487,7 +532,7 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
if (!_ringBuffer.isStarved() && _audioOutput->bytesFree() == _audioOutput->bufferSize()) {
// we don't have any audio data left in the output buffer
// we just starved
qDebug() << "Audio output just starved.";
//qDebug() << "Audio output just starved.";
_ringBuffer.setIsStarved(true);
_numFramesDisplayStarve = 10;
}
@ -505,7 +550,7 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
if (!_ringBuffer.isNotStarvedOrHasMinimumSamples(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO
+ (_jitterBufferSamples * 2))) {
// starved and we don't have enough to start, keep waiting
qDebug() << "Buffer is starved and doesn't have enough samples to start. Held back.";
//qDebug() << "Buffer is starved and doesn't have enough samples to start. Held back.";
} else {
// We are either already playing back, or we have enough audio to start playing back.
_ringBuffer.setIsStarved(false);

View file

@ -45,7 +45,7 @@ public:
void render(int screenWidth, int screenHeight);
float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _averageInputLoudness, 0.f); }
float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGateMeasuredFloor, 0.f); }
float getAudioCardAverageInputLoudness() const { return _averageInputLoudness; } // saki
@ -111,7 +111,9 @@ private:
float _measuredJitter;
int16_t _jitterBufferSamples;
float _lastInputLoudness;
float _averageInputLoudness;
float _noiseGateMeasuredFloor;
float* _noiseSampleFrames;
int _noiseGateSampleCounter;
bool _noiseGateOpen;
bool _noiseGateEnabled;
int _noiseGateFramesToClose;

View file

@ -1,85 +0,0 @@
//
// Cloud.cpp
// interface
//
// Created by Philip Rosedale on 11/17/12.
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
//
#include <iostream>
#include <InterfaceConfig.h>
#include "Cloud.h"
#include "Util.h"
#include "Field.h"
const int NUM_PARTICLES = 100000;
const float FIELD_COUPLE = 0.001f;
const bool RENDER_FIELD = false;
Cloud::Cloud() {
glm::vec3 box = glm::vec3(PARTICLE_WORLD_SIZE);
_bounds = box;
_count = NUM_PARTICLES;
_particles = new Particle[_count];
_field = new Field(PARTICLE_WORLD_SIZE, FIELD_COUPLE);
for (unsigned int i = 0; i < _count; i++) {
_particles[i].position = randVector() * box;
const float INIT_VEL_SCALE = 0.03f;
_particles[i].velocity = randVector() * ((float)PARTICLE_WORLD_SIZE * INIT_VEL_SCALE);
_particles[i].color = randVector();
}
}
void Cloud::render() {
if (RENDER_FIELD) {
_field->render();
}
glPointSize(3.0f);
glDisable(GL_TEXTURE_2D);
glEnable(GL_POINT_SMOOTH);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FLOAT, 3 * sizeof(glm::vec3), &_particles[0].position);
glColorPointer(3, GL_FLOAT, 3 * sizeof(glm::vec3), &_particles[0].color);
glDrawArrays(GL_POINTS, 0, NUM_PARTICLES);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
}
void Cloud::simulate (float deltaTime) {
unsigned int i;
_field->simulate(deltaTime);
for (i = 0; i < _count; ++i) {
// Update position
_particles[i].position += _particles[i].velocity * deltaTime;
// Decay Velocity (Drag)
const float CONSTANT_DAMPING = 0.15f;
_particles[i].velocity *= (1.f - CONSTANT_DAMPING * deltaTime);
// Interact with Field
_field->interact(deltaTime, _particles[i].position, _particles[i].velocity);
// Update color to velocity
_particles[i].color = (glm::normalize(_particles[i].velocity) * 0.5f) + 0.5f;
// Bounce at bounds
if ((_particles[i].position.x > _bounds.x) || (_particles[i].position.x < 0.f)) {
_particles[i].position.x = glm::clamp(_particles[i].position.x, 0.f, _bounds.x);
_particles[i].velocity.x *= -1.f;
}
if ((_particles[i].position.y > _bounds.y) || (_particles[i].position.y < 0.f)) {
_particles[i].position.y = glm::clamp(_particles[i].position.y, 0.f, _bounds.y);
_particles[i].velocity.y *= -1.f;
}
if ((_particles[i].position.z > _bounds.z) || (_particles[i].position.z < 0.f)) {
_particles[i].position.z = glm::clamp(_particles[i].position.z, 0.f, _bounds.z);
_particles[i].velocity.z *= -1.f;
}
}
}

View file

@ -1,32 +0,0 @@
//
// Cloud.h
// interface
//
// Created by Philip Rosedale on 11/17/12.
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__Cloud__
#define __interface__Cloud__
#include "Field.h"
#define PARTICLE_WORLD_SIZE 256.0
class Cloud {
public:
Cloud();
void simulate(float deltaTime);
void render();
private:
struct Particle {
glm::vec3 position, velocity, color;
}* _particles;
unsigned int _count;
glm::vec3 _bounds;
Field* _field;
};
#endif

View file

@ -172,6 +172,21 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlVelocity(int controlInd
return glm::vec3(0); // bad index
}
glm::quat ControllerScriptingInterface::getSpatialControlRawRotation(int controlIndex) const {
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
const PalmData* palmData = getActivePalm(palmIndex);
if (palmData) {
switch (controlOfPalm) {
case PALM_SPATIALCONTROL:
return palmData->getRawRotation();
case TIP_SPATIALCONTROL:
return palmData->getRawRotation(); // currently the tip doesn't have a unique rotation, use the palm rotation
}
}
return glm::quat(); // bad index
}
glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex) const {
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;

View file

@ -58,7 +58,7 @@ public slots:
virtual glm::vec3 getSpatialControlPosition(int controlIndex) const;
virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const;
virtual glm::vec3 getSpatialControlNormal(int controlIndex) const;
virtual glm::quat getSpatialControlRawRotation(int controlIndex) const;
virtual void captureKeyEvents(const KeyEvent& event);
virtual void releaseKeyEvents(const KeyEvent& event);

View file

@ -93,6 +93,8 @@ Menu::Menu() :
addDisabledActionAndSeparator(fileMenu, "Scripts");
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, appInstance, SLOT(loadDialog()));
addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, appInstance, SLOT(stopAllScripts()));
addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, 0, appInstance, SLOT(reloadAllScripts()));
_activeScriptsMenu = fileMenu->addMenu("Running Scripts");
addDisabledActionAndSeparator(fileMenu, "Voxels");
@ -161,7 +163,7 @@ Menu::Menu() :
#endif
addDisabledActionAndSeparator(editMenu, "Physics");
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, true);
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, false);
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ClickToFly);
@ -238,9 +240,9 @@ Menu::Menu() :
SLOT(setFullscreen(bool)));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true,
appInstance,SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H,false,
appInstance,SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false,
appInstance, SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0,
false,
@ -266,14 +268,8 @@ Menu::Menu() :
appInstance->getAvatar(),
SLOT(resetSize()));
addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::OffAxisProjection,
0,
true);
addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::TurnWithHead,
0,
true);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::OffAxisProjection, 0, false);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::MoveWithLean, 0, false);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HeadMouse, 0, false);
@ -298,7 +294,6 @@ Menu::Menu() :
appInstance->getGlowEffect(),
SLOT(cycleRenderMode()));
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ParticleCloud, 0, false);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, false);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, false);
@ -339,14 +334,14 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollisionProxies);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu,
MenuOption::FaceshiftTCP,
0,
false,
appInstance->getFaceshift(),
SLOT(setTCPEnabled(bool)));
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false);
QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options");
@ -356,7 +351,7 @@ Menu::Menu() :
true,
appInstance->getSixenseManager(),
SLOT(setFilter(bool)));
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayLeapHands, 0, true);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::VoxelDrumming, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::PlaySlaps, 0, false);
@ -889,6 +884,17 @@ void Menu::editPreferences() {
sendFakeEnterEvent();
}
void Menu::goToDomain(const QString newDomain) {
if (NodeList::getInstance()->getDomainHostname() != newDomain) {
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
Application::getInstance()->getAvatar()->sendKillAvatar();
// give our nodeList the new domain-server hostname
NodeList::getInstance()->setDomainHostname(newDomain);
}
}
void Menu::goToDomain() {
QString currentDomainHostname = NodeList::getInstance()->getDomainHostname();
@ -913,17 +919,77 @@ void Menu::goToDomain() {
// the user input a new hostname, use that
newHostname = domainDialog.textValue();
}
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
Application::getInstance()->getAvatar()->sendKillAvatar();
// give our nodeList the new domain-server hostname
NodeList::getInstance()->setDomainHostname(domainDialog.textValue());
goToDomain(newHostname);
}
sendFakeEnterEvent();
}
void Menu::goToOrientation(QString orientation) {
if (orientation.isEmpty()) {
return;
}
QStringList orientationItems = orientation.split(QRegExp("_|,"), QString::SkipEmptyParts);
const int NUMBER_OF_ORIENTATION_ITEMS = 4;
const int W_ITEM = 0;
const int X_ITEM = 1;
const int Y_ITEM = 2;
const int Z_ITEM = 3;
if (orientationItems.size() == NUMBER_OF_ORIENTATION_ITEMS) {
double w = replaceLastOccurrence('-', '.', orientationItems[W_ITEM].trimmed()).toDouble();
double x = replaceLastOccurrence('-', '.', orientationItems[X_ITEM].trimmed()).toDouble();
double y = replaceLastOccurrence('-', '.', orientationItems[Y_ITEM].trimmed()).toDouble();
double z = replaceLastOccurrence('-', '.', orientationItems[Z_ITEM].trimmed()).toDouble();
glm::quat newAvatarOrientation(w, x, y, z);
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
glm::quat avatarOrientation = myAvatar->getOrientation();
if (newAvatarOrientation != avatarOrientation) {
myAvatar->setOrientation(newAvatarOrientation);
}
}
}
bool Menu::goToDestination(QString destination) {
QStringList coordinateItems = destination.split(QRegExp("_|,"), QString::SkipEmptyParts);
const int NUMBER_OF_COORDINATE_ITEMS = 3;
const int X_ITEM = 0;
const int Y_ITEM = 1;
const int Z_ITEM = 2;
if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) {
double x = replaceLastOccurrence('-', '.', coordinateItems[X_ITEM].trimmed()).toDouble();
double y = replaceLastOccurrence('-', '.', coordinateItems[Y_ITEM].trimmed()).toDouble();
double z = replaceLastOccurrence('-', '.', coordinateItems[Z_ITEM].trimmed()).toDouble();
glm::vec3 newAvatarPos(x, y, z);
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
glm::vec3 avatarPos = myAvatar->getPosition();
if (newAvatarPos != avatarPos) {
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
MyAvatar::sendKillAvatar();
qDebug("Going To Location: %f, %f, %f...", x, y, z);
myAvatar->setPosition(newAvatarPos);
}
return true;
}
// no coordinates were parsed
return false;
}
void Menu::goTo() {
QInputDialog gotoDialog(Application::getInstance()->getWindow());
@ -939,31 +1005,8 @@ void Menu::goTo() {
destination = gotoDialog.textValue();
QStringList coordinateItems = destination.split(QRegExp("_|,"), QString::SkipEmptyParts);
const int NUMBER_OF_COORDINATE_ITEMS = 3;
const int X_ITEM = 0;
const int Y_ITEM = 1;
const int Z_ITEM = 2;
if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) {
double x = replaceLastOccurrence('-', '.', coordinateItems[X_ITEM].trimmed()).toDouble();
double y = replaceLastOccurrence('-', '.', coordinateItems[Y_ITEM].trimmed()).toDouble();
double z = replaceLastOccurrence('-', '.', coordinateItems[Z_ITEM].trimmed()).toDouble();
glm::vec3 newAvatarPos(x, y, z);
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
glm::vec3 avatarPos = myAvatar->getPosition();
if (newAvatarPos != avatarPos) {
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
MyAvatar::sendKillAvatar();
qDebug("Going To Location: %f, %f, %f...", x, y, z);
myAvatar->setPosition(newAvatarPos);
}
} else {
// go to coordinate destination or to Username
if (!goToDestination(destination)) {
// there's a username entered by the user, make a request to the data-server
DataServerClient::getValuesForKeysAndUserString(
QStringList()
@ -994,29 +1037,7 @@ void Menu::goToLocation() {
int dialogReturn = coordinateDialog.exec();
if (dialogReturn == QDialog::Accepted && !coordinateDialog.textValue().isEmpty()) {
QByteArray newCoordinates;
QString delimiterPattern(",");
QStringList coordinateItems = coordinateDialog.textValue().split(delimiterPattern);
const int NUMBER_OF_COORDINATE_ITEMS = 3;
const int X_ITEM = 0;
const int Y_ITEM = 1;
const int Z_ITEM = 2;
if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) {
double x = coordinateItems[X_ITEM].toDouble();
double y = coordinateItems[Y_ITEM].toDouble();
double z = coordinateItems[Z_ITEM].toDouble();
glm::vec3 newAvatarPos(x, y, z);
if (newAvatarPos != avatarPos) {
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
MyAvatar::sendKillAvatar();
qDebug("Going To Location: %f, %f, %f...", x, y, z);
myAvatar->setPosition(newAvatarPos);
}
}
goToDestination(coordinateDialog.textValue());
}
sendFakeEnterEvent();

View file

@ -84,6 +84,9 @@ public:
const char* member = NULL,
QAction::MenuRole role = QAction::NoRole);
virtual void removeAction(QMenu* menu, const QString& actionName);
bool goToDestination(QString destination);
void goToOrientation(QString orientation);
void goToDomain(const QString newDomain);
public slots:
void bandwidthDetails();
@ -183,7 +186,7 @@ namespace MenuOption {
const QString DisableDeltaSending = "Disable Delta Sending";
const QString DisableLowRes = "Disable Lower Resolution While Moving";
const QString DisplayFrustum = "Display Frustum";
const QString DisplayLeapHands = "Display Leap Hands";
const QString DisplayHands = "Display Hands";
const QString DisplayHandTargets = "Display Hand Targets";
const QString FilterSixense = "Smooth Sixense Movement";
const QString DontRenderVoxels = "Don't call _voxels.render()";
@ -223,7 +226,6 @@ namespace MenuOption {
const QString KillLocalVoxels = "Kill Local Voxels";
const QString GoHome = "Go Home";
const QString Gravity = "Use Gravity";
const QString ParticleCloud = "Particle Cloud";
const QString LodTools = "LOD Tools";
const QString Log = "Log";
const QString Login = "Login";
@ -244,8 +246,10 @@ namespace MenuOption {
const QString PasteVoxels = "Paste";
const QString PasteToVoxel = "Paste to Voxel...";
const QString PipelineWarnings = "Show Render Pipeline Warnings";
const QString PlaySlaps = "Play Slaps";
const QString Preferences = "Preferences...";
const QString RandomizeVoxelColors = "Randomize Voxel TRUE Colors";
const QString ReloadAllScripts = "Reload All Scripts";
const QString ResetAvatarSize = "Reset Avatar Size";
const QString ResetSwatchColors = "Reset Swatch Colors";
const QString RunTimingTests = "Run Timing Tests";
@ -254,11 +258,10 @@ namespace MenuOption {
const QString SettingsExport = "Export Settings";
const QString ShowAllLocalVoxels = "Show All Local Voxels";
const QString ShowTrueColors = "Show TRUE Colors";
const QString VoxelDrumming = "Voxel Drumming";
const QString PlaySlaps = "Play Slaps";
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
const QString Stars = "Stars";
const QString Stats = "Stats";
const QString StopAllScripts = "Stop All Scripts";
const QString TestPing = "Test Ping";
const QString TreeStats = "Calculate Tree Stats";
const QString TransmitterDrive = "Transmitter Drive";
@ -269,6 +272,7 @@ namespace MenuOption {
const QString VoxelAddMode = "Add Voxel Mode";
const QString VoxelColorMode = "Color Voxel Mode";
const QString VoxelDeleteMode = "Delete Voxel Mode";
const QString VoxelDrumming = "Voxel Drumming";
const QString VoxelGetColorMode = "Get Color Mode";
const QString VoxelMode = "Cycle Voxel Mode";
const QString VoxelPaintColor = "Voxel Paint Color";

View file

@ -162,6 +162,7 @@ void Avatar::render(bool forceRenderHead) {
// render body
if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) {
_skeletonModel.renderCollisionProxies(1.f);
//_head.getFaceModel().renderCollisionProxies(0.5f);
}
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
@ -276,11 +277,14 @@ bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penet
bool didPenetrate = false;
glm::vec3 skeletonPenetration;
ModelCollisionInfo collisionInfo;
/* Temporarily disabling collisions against the skeleton because the collision proxies up
* near the neck are bad and prevent the hand from hitting the face.
if (_skeletonModel.findSphereCollision(penetratorCenter, penetratorRadius, collisionInfo, 1.0f, skeletonSkipIndex)) {
collisionInfo._model = &_skeletonModel;
collisions.push_back(collisionInfo);
didPenetrate = true;
}
*/
if (_head.getFaceModel().findSphereCollision(penetratorCenter, penetratorRadius, collisionInfo)) {
collisionInfo._model = &(_head.getFaceModel());
collisions.push_back(collisionInfo);
@ -445,15 +449,36 @@ float Avatar::getHeight() const {
return extents.maximum.y - extents.minimum.y;
}
bool Avatar::poke(ModelCollisionInfo& collision) {
// ATM poke() can only affect the Skeleton (not the head)
bool Avatar::collisionWouldMoveAvatar(ModelCollisionInfo& collision) const {
// ATM only the Skeleton is pokeable
// TODO: make poke affect head
if (!collision._model) {
return false;
}
if (collision._model == &_skeletonModel && collision._jointIndex != -1) {
return _skeletonModel.poke(collision);
// collision response of skeleton is temporarily disabled
return false;
//return _skeletonModel.collisionHitsMoveableJoint(collision);
}
if (collision._model == &(_head.getFaceModel())) {
return true;
}
return false;
}
void Avatar::applyCollision(ModelCollisionInfo& collision) {
if (!collision._model) {
return;
}
if (collision._model == &(_head.getFaceModel())) {
_head.applyCollision(collision);
}
// TODO: make skeleton respond to collisions
//if (collision._model == &_skeletonModel && collision._jointIndex != -1) {
// _skeletonModel.applyCollision(collision);
//}
}
float Avatar::getPelvisFloatingHeight() const {
return -_skeletonModel.getBindExtents().minimum.y;
}

View file

@ -128,9 +128,11 @@ public:
float getHeight() const;
/// \return true if we expect the avatar would move as a result of the collision
bool collisionWouldMoveAvatar(ModelCollisionInfo& collision) const;
/// \param collision a data structure for storing info about collisions against Models
/// \return true if the collision affects the Avatar models
bool poke(ModelCollisionInfo& collision);
void applyCollision(ModelCollisionInfo& collision);
public slots:
void updateCollisionFlags();

View file

@ -58,7 +58,7 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX
glm::mat3 axes = glm::mat3_cast(_rotation);
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) *
joint.preTransform * glm::mat4_cast(joint.preRotation)));
state.rotation = glm::angleAxis(-_owningHead->getRoll(), glm::normalize(inverse * axes[2])) *
state.rotation = glm::angleAxis(-_owningHead->getTweakedRoll(), glm::normalize(inverse * axes[2])) *
glm::angleAxis(_owningHead->getTweakedYaw(), glm::normalize(inverse * axes[1])) *
glm::angleAxis(-_owningHead->getTweakedPitch(), glm::normalize(inverse * axes[0])) * joint.rotation;
}

View file

@ -32,12 +32,7 @@ Hand::Hand(Avatar* owningAvatar) :
_ballColor(0.0, 0.0, 0.4),
_collisionCenter(0,0,0),
_collisionAge(0),
_collisionDuration(0),
_pitchUpdate(0),
_grabDelta(0, 0, 0),
_grabDeltaVelocity(0, 0, 0),
_grabStartRotation(0, 0, 0, 1),
_grabCurrentRotation(0, 0, 0, 1)
_collisionDuration(0)
{
}
@ -54,28 +49,6 @@ void Hand::init() {
void Hand::reset() {
}
glm::vec3 Hand::getAndResetGrabDelta() {
const float HAND_GRAB_SCALE_DISTANCE = 2.f;
glm::vec3 delta = _grabDelta * _owningAvatar->getScale() * HAND_GRAB_SCALE_DISTANCE;
_grabDelta = glm::vec3(0,0,0);
glm::quat avatarRotation = _owningAvatar->getOrientation();
return avatarRotation * -delta;
}
glm::vec3 Hand::getAndResetGrabDeltaVelocity() {
const float HAND_GRAB_SCALE_VELOCITY = 5.f;
glm::vec3 delta = _grabDeltaVelocity * _owningAvatar->getScale() * HAND_GRAB_SCALE_VELOCITY;
_grabDeltaVelocity = glm::vec3(0,0,0);
glm::quat avatarRotation = _owningAvatar->getOrientation();
return avatarRotation * -delta;
}
glm::quat Hand::getAndResetGrabRotation() {
glm::quat diff = _grabCurrentRotation * glm::inverse(_grabStartRotation);
_grabStartRotation = _grabCurrentRotation;
return diff;
}
void Hand::simulate(float deltaTime, bool isMine) {
if (_collisionAge > 0.f) {
@ -84,7 +57,6 @@ void Hand::simulate(float deltaTime, bool isMine) {
if (isMine) {
_buckyBalls.simulate(deltaTime);
updateCollisions();
}
calculateGeometry();
@ -99,19 +71,6 @@ void Hand::simulate(float deltaTime, bool isMine) {
_buckyBalls.grab(palm, fingerTipPosition, _owningAvatar->getOrientation(), deltaTime);
if (palm.getControllerButtons() & BUTTON_4) {
_grabDelta += palm.getRawVelocity() * deltaTime;
_grabCurrentRotation = palm.getRawRotation();
}
if ((palm.getLastControllerButtons() & BUTTON_4) && !(palm.getControllerButtons() & BUTTON_4)) {
// Just ending grab, capture velocity
_grabDeltaVelocity = palm.getRawVelocity();
}
if (!(palm.getLastControllerButtons() & BUTTON_4) && (palm.getControllerButtons() & BUTTON_4)) {
// Just starting grab, capture starting rotation
_grabStartRotation = palm.getRawRotation();
}
if (palm.getControllerButtons() & BUTTON_1) {
if (glm::length(fingerTipPosition - _lastFingerAddVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
QColor paintColor = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor)->data().value<QColor>();
@ -166,91 +125,105 @@ void Hand::simulate(float deltaTime, bool isMine) {
}
}
void Hand::updateCollisions() {
// use position to obtain the left and right palm indices
int leftPalmIndex, rightPalmIndex;
getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
ModelCollisionList collisions;
// check for collisions
void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
if (!avatar || avatar == _owningAvatar) {
// don't collide with our own hands (that is done elsewhere)
return;
}
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
for (size_t i = 0; i < getNumPalms(); i++) {
PalmData& palm = getPalms()[i];
if (!palm.isActive()) {
continue;
}
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
glm::vec3 totalPenetration;
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithAvatars)) {
// check other avatars
foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) {
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
if (avatar == _owningAvatar) {
// don't collid with our own hands
ModelCollisionList collisions;
if (isMyHand && Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
// Check for palm collisions
glm::vec3 myPalmPosition = palm.getPosition();
float palmCollisionDistance = 0.1f;
bool wasColliding = palm.getIsCollidingWithPalm();
palm.setIsCollidingWithPalm(false);
// If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound
for (size_t j = 0; j < avatar->getHand().getNumPalms(); j++) {
PalmData& otherPalm = avatar->getHand().getPalms()[j];
if (!otherPalm.isActive()) {
continue;
}
if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
// Check for palm collisions
glm::vec3 myPalmPosition = palm.getPosition();
float palmCollisionDistance = 0.1f;
bool wasColliding = palm.getIsCollidingWithPalm();
palm.setIsCollidingWithPalm(false);
// If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound
for (size_t j = 0; j < avatar->getHand().getNumPalms(); j++) {
PalmData& otherPalm = avatar->getHand().getPalms()[j];
if (!otherPalm.isActive()) {
continue;
}
glm::vec3 otherPalmPosition = otherPalm.getPosition();
if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) {
palm.setIsCollidingWithPalm(true);
if (!wasColliding) {
const float PALM_COLLIDE_VOLUME = 1.f;
const float PALM_COLLIDE_FREQUENCY = 1000.f;
const float PALM_COLLIDE_DURATION_MAX = 0.75f;
const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f;
Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME,
PALM_COLLIDE_FREQUENCY,
PALM_COLLIDE_DURATION_MAX,
PALM_COLLIDE_DECAY_PER_SAMPLE);
// If the other person's palm is in motion, move mine downward to show I was hit
const float MIN_VELOCITY_FOR_SLAP = 0.05f;
if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) {
// add slapback here
}
}
}
}
}
if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions)) {
for (size_t j = 0; j < collisions.size(); ++j) {
if (!avatar->poke(collisions[j])) {
totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration);
glm::vec3 otherPalmPosition = otherPalm.getPosition();
if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) {
palm.setIsCollidingWithPalm(true);
if (!wasColliding) {
const float PALM_COLLIDE_VOLUME = 1.f;
const float PALM_COLLIDE_FREQUENCY = 1000.f;
const float PALM_COLLIDE_DURATION_MAX = 0.75f;
const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f;
Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME,
PALM_COLLIDE_FREQUENCY,
PALM_COLLIDE_DURATION_MAX,
PALM_COLLIDE_DECAY_PER_SAMPLE);
// If the other person's palm is in motion, move mine downward to show I was hit
const float MIN_VELOCITY_FOR_SLAP = 0.05f;
if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) {
// add slapback here
}
}
}
}
}
if (Menu::getInstance()->isOptionChecked(MenuOption::HandsCollideWithSelf)) {
// and the current avatar (ignoring everything below the parent of the parent of the last free joint)
collisions.clear();
const Model& skeletonModel = _owningAvatar->getSkeletonModel();
int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex(
skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() :
(i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1)));
if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions, skipIndex)) {
for (size_t j = 0; j < collisions.size(); ++j) {
totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration);
if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions)) {
for (int j = 0; j < collisions.size(); ++j) {
if (isMyHand) {
if (!avatar->collisionWouldMoveAvatar(collisions[j])) {
// we resolve the hand from collision when it belongs to MyAvatar AND the other Avatar is
// not expected to respond to the collision (hand hit unmovable part of their Avatar)
totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration);
}
} else {
// when !isMyHand then avatar is MyAvatar and we apply the collision
// which might not do anything (hand hit unmovable part of MyAvatar) however
// we don't resolve the hand's penetration because we expect the remote
// simulation to do the right thing.
avatar->applyCollision(collisions[j]);
}
}
}
// un-penetrate
palm.addToPosition(-totalPenetration);
if (isMyHand) {
// resolve penetration
palm.addToPosition(-totalPenetration);
}
}
}
// we recycle the collisions container, so we clear it for the next loop
void Hand::collideAgainstOurself() {
if (!Menu::getInstance()->isOptionChecked(MenuOption::HandsCollideWithSelf)) {
return;
}
ModelCollisionList collisions;
int leftPalmIndex, rightPalmIndex;
getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
for (size_t i = 0; i < getNumPalms(); i++) {
PalmData& palm = getPalms()[i];
if (!palm.isActive()) {
continue;
}
glm::vec3 totalPenetration;
// and the current avatar (ignoring everything below the parent of the parent of the last free joint)
collisions.clear();
const Model& skeletonModel = _owningAvatar->getSkeletonModel();
int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex(
skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() :
(i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1)));
if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions, skipIndex)) {
for (int j = 0; j < collisions.size(); ++j) {
totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration);
}
}
// resolve penetration
palm.addToPosition(-totalPenetration);
}
}
@ -343,7 +316,7 @@ void Hand::render(bool isMine) {
}
}
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayLeapHands)) {
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) {
renderLeapHands(isMine);
}

View file

@ -58,14 +58,8 @@ public:
const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;}
const glm::vec3& getLeapFingerRootBallPosition(int ball) const { return _leapFingerRootBalls[ball].position;}
// Pitch from controller input to view
const float getPitchUpdate() const { return _pitchUpdate; }
void setPitchUpdate(float pitchUpdate) { _pitchUpdate = pitchUpdate; }
// Get the drag distance to move
glm::vec3 getAndResetGrabDelta();
glm::vec3 getAndResetGrabDeltaVelocity();
glm::quat getAndResetGrabRotation();
void collideAgainstAvatar(Avatar* avatar, bool isMyHand);
void collideAgainstOurself();
private:
// disallow copies of the Hand, copy of owning Avatar is disallowed too
@ -96,17 +90,9 @@ private:
void renderLeapHands(bool isMine);
void renderLeapFingerTrails();
void updateCollisions();
void calculateGeometry();
void handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime);
float _pitchUpdate;
glm::vec3 _grabDelta;
glm::vec3 _grabDeltaVelocity;
glm::quat _grabStartRotation;
glm::quat _grabCurrentRotation;
};
#endif

View file

@ -219,6 +219,34 @@ float Head::getTweakedRoll() const {
return glm::clamp(_roll + _tweakedRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL);
}
void Head::applyCollision(ModelCollisionInfo& collisionInfo) {
// HACK: the collision proxies for the FaceModel are bad. As a temporary workaround
// we collide against a hard coded collision proxy.
// TODO: get a better collision proxy here.
const float HEAD_RADIUS = 0.15f;
const glm::vec3 HEAD_CENTER = _position;
// collide the contactPoint against the collision proxy to obtain a new penetration
// NOTE: that penetration is in opposite direction (points the way out for the point, not the sphere)
glm::vec3 penetration;
if (findPointSpherePenetration(collisionInfo._contactPoint, HEAD_CENTER, HEAD_RADIUS, penetration)) {
// compute lean angles
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
glm::quat bodyRotation = owningAvatar->getOrientation();
glm::vec3 neckPosition;
if (owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) {
glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f);
glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f);
float neckLength = glm::length(_position - neckPosition);
if (neckLength > 0.f) {
float forward = glm::dot(collisionInfo._penetration, zAxis) / neckLength;
float sideways = - glm::dot(collisionInfo._penetration, xAxis) / neckLength;
addLean(sideways, forward);
}
}
}
}
void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {
Application::getInstance()->getGlowEffect()->begin();

View file

@ -79,6 +79,8 @@ public:
float getTweakedPitch() const;
float getTweakedYaw() const;
float getTweakedRoll() const;
void applyCollision(ModelCollisionInfo& collisionInfo);
private:
// disallow copies of the Head, copy of owning Avatar is disallowed too

View file

@ -112,23 +112,7 @@ void MyAvatar::updateTransmitter(float deltaTime) {
void MyAvatar::update(float deltaTime) {
updateTransmitter(deltaTime);
// TODO: resurrect touch interactions between avatars
//// rotate body yaw for yaw received from multitouch
//setOrientation(getOrientation() * glm::quat(glm::vec3(0, _yawFromTouch, 0)));
//_yawFromTouch = 0.f;
//
//// apply pitch from touch
//_head.setPitch(_head.getPitch() + _pitchFromTouch);
//_pitchFromTouch = 0.0f;
//
//float TOUCH_YAW_SCALE = -0.25f;
//float TOUCH_PITCH_SCALE = -12.5f;
//float FIXED_TOUCH_TIMESTEP = 0.016f;
//_yawFromTouch += ((_touchAvgX - _lastTouchAvgX) * TOUCH_YAW_SCALE * FIXED_TOUCH_TIMESTEP);
//_pitchFromTouch += ((_touchAvgY - _lastTouchAvgY) * TOUCH_PITCH_SCALE * FIXED_TOUCH_TIMESTEP);
// Update my avatar's state from gyros
updateFromGyros(Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead));
updateFromGyros(deltaTime);
// Update head mouse from faceshift if active
Faceshift* faceshift = Application::getInstance()->getFaceshift();
@ -227,8 +211,6 @@ void MyAvatar::simulate(float deltaTime) {
updateCollisionWithVoxels(deltaTime, radius);
}
if (_collisionFlags & COLLISION_GROUP_AVATARS) {
// Note, hand-vs-avatar collisions are done elsewhere
// This is where we avatar-vs-avatar bounding capsule
updateCollisionWithAvatars(deltaTime);
}
}
@ -330,24 +312,10 @@ void MyAvatar::simulate(float deltaTime) {
updateChatCircle(deltaTime);
// Get any position, velocity, or rotation update from Grab Drag controller
glm::vec3 moveFromGrab = _hand.getAndResetGrabDelta();
if (glm::length(moveFromGrab) > EPSILON) {
_position += moveFromGrab;
_velocity = glm::vec3(0, 0, 0);
}
_velocity += _hand.getAndResetGrabDeltaVelocity();
glm::quat deltaRotation = _hand.getAndResetGrabRotation();
const float GRAB_CONTROLLER_TURN_SCALING = 0.5f;
glm::vec3 euler = safeEulerAngles(deltaRotation) * GRAB_CONTROLLER_TURN_SCALING;
// Adjust body yaw by yaw from controller
setOrientation(glm::angleAxis(-euler.y, glm::vec3(0, 1, 0)) * getOrientation());
// Adjust head pitch from controller
getHead().setPitch(getHead().getPitch() - euler.x);
_position += _velocity * deltaTime;
// update avatar skeleton and simulate hand and head
_hand.collideAgainstOurself();
_hand.simulate(deltaTime, true);
_skeletonModel.simulate(deltaTime);
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
@ -367,7 +335,7 @@ void MyAvatar::simulate(float deltaTime) {
const float MAX_PITCH = 90.0f;
// Update avatar head rotation with sensor data
void MyAvatar::updateFromGyros(bool turnWithHead) {
void MyAvatar::updateFromGyros(float deltaTime) {
Faceshift* faceshift = Application::getInstance()->getFaceshift();
glm::vec3 estimatedPosition, estimatedRotation;
@ -375,7 +343,7 @@ void MyAvatar::updateFromGyros(bool turnWithHead) {
estimatedPosition = faceshift->getHeadTranslation();
estimatedRotation = safeEulerAngles(faceshift->getHeadRotation());
// Rotate the body if the head is turned beyond the screen
if (turnWithHead) {
if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) {
const float FACESHIFT_YAW_TURN_SENSITIVITY = 0.5f;
const float FACESHIFT_MIN_YAW_TURN = 15.f;
const float FACESHIFT_MAX_YAW_TURN = 50.f;
@ -390,11 +358,12 @@ void MyAvatar::updateFromGyros(bool turnWithHead) {
}
} else {
// restore rotation, lean to neutral positions
const float RESTORE_RATE = 0.05f;
_head.setYaw(glm::mix(_head.getYaw(), 0.0f, RESTORE_RATE));
_head.setRoll(glm::mix(_head.getRoll(), 0.0f, RESTORE_RATE));
_head.setLeanSideways(glm::mix(_head.getLeanSideways(), 0.0f, RESTORE_RATE));
_head.setLeanForward(glm::mix(_head.getLeanForward(), 0.0f, RESTORE_RATE));
const float RESTORE_PERIOD = 1.f; // seconds
float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f);
_head.setYaw(glm::mix(_head.getYaw(), 0.0f, restorePercentage));
_head.setRoll(glm::mix(_head.getRoll(), 0.0f, restorePercentage));
_head.setLeanSideways(glm::mix(_head.getLeanSideways(), 0.0f, restorePercentage));
_head.setLeanForward(glm::mix(_head.getLeanForward(), 0.0f, restorePercentage));
return;
}
@ -809,39 +778,6 @@ void MyAvatar::updateThrust(float deltaTime) {
up;
}
}
// Add thrust and rotation from hand controllers
const float THRUST_MAG_HAND_JETS = THRUST_MAG_FWD;
const float JOYSTICK_YAW_MAG = YAW_MAG;
const float JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5f;
const int THRUST_CONTROLLER = 0;
const int VIEW_CONTROLLER = 1;
for (size_t i = 0; i < getHand().getPalms().size(); ++i) {
PalmData& palm = getHand().getPalms()[i];
// If the script hasn't captured this joystick, then let the default behavior work
if (!Application::getInstance()->getControllerScriptingInterface()->isJoystickCaptured(palm.getSixenseID())) {
if (palm.isActive() && (palm.getSixenseID() == THRUST_CONTROLLER)) {
if (palm.getJoystickY() != 0.f) {
FingerData& finger = palm.getFingers()[0];
if (finger.isActive()) {
}
_thrust += front * _scale * THRUST_MAG_HAND_JETS * palm.getJoystickY() * _thrustMultiplier * deltaTime;
}
if (palm.getJoystickX() != 0.f) {
_thrust += right * _scale * THRUST_MAG_HAND_JETS * palm.getJoystickX() * _thrustMultiplier * deltaTime;
}
} else if (palm.isActive() && (palm.getSixenseID() == VIEW_CONTROLLER)) {
if (palm.getJoystickX() != 0.f) {
_bodyYawDelta -= palm.getJoystickX() * JOYSTICK_YAW_MAG * deltaTime;
}
if (palm.getJoystickY() != 0.f) {
getHand().setPitchUpdate(getHand().getPitchUpdate() +
(palm.getJoystickY() * JOYSTICK_PITCH_MAG * deltaTime));
}
}
}
}
// Update speed brake status
const float MIN_SPEED_BRAKE_VELOCITY = _scale * 0.4f;
@ -906,7 +842,6 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
}
}
void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
const float VOXEL_ELASTICITY = 0.4f;
const float VOXEL_DAMPING = 0.0f;
@ -976,7 +911,43 @@ void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTim
}
}
const float DEFAULT_HAND_RADIUS = 0.1f;
bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float heightA,
const glm::vec3 positionB, float radiusB, float heightB, glm::vec3& penetration) {
glm::vec3 positionBA = positionB - positionA;
float xzDistance = sqrt(positionBA.x * positionBA.x + positionBA.z * positionBA.z);
if (xzDistance < (radiusA + radiusB)) {
float yDistance = fabs(positionBA.y);
float halfHeights = 0.5 * (heightA + heightB);
if (yDistance < halfHeights) {
// cylinders collide
if (xzDistance > 0.f) {
positionBA.y = 0.f;
// note, penetration should point from A into B
penetration = positionBA * ((radiusA + radiusB - xzDistance) / xzDistance);
return true;
} else {
// exactly coaxial -- we'll return false for this case
return false;
}
} else if (yDistance < halfHeights + radiusA + radiusB) {
// caps collide
if (positionBA.y < 0.f) {
// A is above B
positionBA.y += halfHeights;
float BA = glm::length(positionBA);
penetration = positionBA * (radiusA + radiusB - BA) / BA;
return true;
} else {
// A is below B
positionBA.y -= halfHeights;
float BA = glm::length(positionBA);
penetration = positionBA * (radiusA + radiusB - BA) / BA;
return true;
}
}
}
return false;
}
void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
// Reset detector for nearest avatar
@ -986,7 +957,14 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
// no need to compute a bunch of stuff if we have one or fewer avatars
return;
}
float myRadius = getHeight();
float myBoundingRadius = 0.5f * getHeight();
// HACK: body-body collision uses two coaxial capsules with axes parallel to y-axis
// TODO: make the collision work without assuming avatar orientation
Extents myStaticExtents = _skeletonModel.getStaticExtents();
glm::vec3 staticScale = myStaticExtents.maximum - myStaticExtents.minimum;
float myCapsuleRadius = 0.25f * (staticScale.x + staticScale.z);
float myCapsuleHeight = staticScale.y;
CollisionInfo collisionInfo;
foreach (const AvatarSharedPointer& avatarPointer, avatars) {
@ -999,9 +977,25 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
if (_distanceToNearestAvatar > distance) {
_distanceToNearestAvatar = distance;
}
float theirRadius = avatar->getHeight();
if (distance < myRadius + theirRadius) {
// TODO: Andrew to make avatar-avatar capsule collisions work here
float theirBoundingRadius = 0.5f * avatar->getHeight();
if (distance < myBoundingRadius + theirBoundingRadius) {
Extents theirStaticExtents = _skeletonModel.getStaticExtents();
glm::vec3 staticScale = theirStaticExtents.maximum - theirStaticExtents.minimum;
float theirCapsuleRadius = 0.25f * (staticScale.x + staticScale.z);
float theirCapsuleHeight = staticScale.y;
glm::vec3 penetration(0.f);
if (findAvatarAvatarPenetration(_position, myCapsuleRadius, myCapsuleHeight,
avatar->getPosition(), theirCapsuleRadius, theirCapsuleHeight, penetration)) {
// move the avatar out by half the penetration
setPosition(_position - 0.5f * penetration);
}
// collide our hands against them
_hand.collideAgainstAvatar(avatar, true);
// collide their hands against us
avatar->getHand().collideAgainstAvatar(this, false);
}
}
}

View file

@ -34,7 +34,7 @@ public:
void reset();
void update(float deltaTime);
void simulate(float deltaTime);
void updateFromGyros(bool turnWithHead);
void updateFromGyros(float deltaTime);
void updateTransmitter(float deltaTime);
void render(bool forceRenderHead);
@ -45,7 +45,6 @@ public:
// setters
void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; }
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
void setLeanScale(float scale) { _leanScale = scale; }
void setGravity(glm::vec3 gravity);
@ -78,9 +77,6 @@ public:
static void sendKillAvatar();
// Set/Get update the thrust that will move the avatar around
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
glm::vec3 getThrust() { return _thrust; };
void orbit(const glm::vec3& position, int deltaX, int deltaY);
@ -94,9 +90,13 @@ public slots:
void increaseSize();
void decreaseSize();
void resetSize();
void sendIdentityPacket();
// Set/Get update the thrust that will move the avatar around
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
glm::vec3 getThrust() { return _thrust; };
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
private:
bool _mousePressed;
float _bodyPitchDelta;

View file

@ -36,23 +36,24 @@ void SkeletonModel::simulate(float deltaTime) {
HandData& hand = _owningAvatar->getHand();
hand.getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
const float HAND_RESTORATION_RATE = 0.25f;
const float HAND_RESTORATION_PERIOD = 1.f; // seconds
float handRestorePercent = glm::clamp(deltaTime / HAND_RESTORATION_PERIOD, 0.f, 1.f);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (leftPalmIndex == -1) {
// no Leap data; set hands from mouse
if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
restoreRightHandPosition(HAND_RESTORATION_RATE);
restoreRightHandPosition(handRestorePercent);
} else {
applyHandPosition(geometry.rightHandJointIndex, _owningAvatar->getHandPosition());
}
restoreLeftHandPosition(HAND_RESTORATION_RATE);
restoreLeftHandPosition(handRestorePercent);
} else if (leftPalmIndex == rightPalmIndex) {
// right hand only
applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices,
hand.getPalms()[leftPalmIndex]);
restoreLeftHandPosition(HAND_RESTORATION_RATE);
restoreLeftHandPosition(handRestorePercent);
} else {
applyPalmData(geometry.leftHandJointIndex, geometry.leftFingerJointIndices, geometry.leftFingertipJointIndices,

View file

@ -1274,6 +1274,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
geometry.bindExtents.minimum = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX);
geometry.bindExtents.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
geometry.staticExtents.minimum = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX);
geometry.staticExtents.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
QVariantHash springs = mapping.value("spring").toHash();
QVariant defaultSpring = springs.value("default");
@ -1430,6 +1432,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
boneDirection /= boneLength;
}
}
bool jointIsStatic = joint.freeLineage.isEmpty();
glm::vec3 jointTranslation = extractTranslation(geometry.offset * joint.bindTransform);
float radiusScale = extractUniformScale(joint.transform * fbxCluster.inverseBindMatrix);
float totalWeight = 0.0f;
for (int j = 0; j < cluster.indices.size(); j++) {
@ -1447,6 +1451,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
joint.boneRadius = glm::max(joint.boneRadius, radiusScale * glm::distance(
vertex, boneEnd + boneDirection * proj));
}
if (jointIsStatic) {
// expand the extents of static (nonmovable) joints
geometry.staticExtents.minimum = glm::min(geometry.staticExtents.minimum, vertex + jointTranslation);
geometry.staticExtents.maximum = glm::max(geometry.staticExtents.maximum, vertex + jointTranslation);
}
}
// look for an unused slot in the weights vector

View file

@ -159,6 +159,7 @@ public:
glm::vec3 neckPivot;
Extents bindExtents;
Extents staticExtents;
QVector<FBXAttachment> attachments;
};

View file

@ -305,6 +305,15 @@ Extents Model::getBindExtents() const {
return scaledExtents;
}
Extents Model::getStaticExtents() const {
if (!isActive()) {
return Extents();
}
const Extents& staticExtents = _geometry->getFBXGeometry().staticExtents;
Extents scaledExtents = { staticExtents.minimum * _scale, staticExtents.maximum * _scale };
return scaledExtents;
}
int Model::getParentJointIndex(int jointIndex) const {
return (isActive() && jointIndex != -1) ? _geometry->getFBXGeometry().joints.at(jointIndex).parentIndex : -1;
}
@ -468,7 +477,10 @@ bool Model::findSphereCollision(const glm::vec3& penetratorCenter, float penetra
if (findSphereCapsuleConePenetration(relativeCenter, penetratorRadius, start, end,
startRadius, endRadius, bonePenetration)) {
totalPenetration = addPenetrations(totalPenetration, bonePenetration);
// TODO: Andrew to try to keep the joint furthest toward the root
// BUG: we currently overwrite the jointIndex with the last one found
// which can cause incorrect collisions when colliding against more than
// one joint.
// TODO: fix this.
jointIndex = i;
}
outerContinue: ;
@ -713,7 +725,19 @@ void Model::renderCollisionProxies(float alpha) {
glPopMatrix();
}
bool Model::poke(ModelCollisionInfo& collision) {
bool Model::collisionHitsMoveableJoint(ModelCollisionInfo& collision) const {
// the joint is pokable by a collision if it exists and is free to move
const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._jointIndex];
if (joint.parentIndex == -1 || _jointStates.isEmpty()) {
return false;
}
// an empty freeLineage means the joint can't move
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const QVector<int>& freeLineage = geometry.joints.at(collision._jointIndex).freeLineage;
return !freeLineage.isEmpty();
}
void Model::applyCollision(ModelCollisionInfo& collision) {
// This needs work. At the moment it can wiggle joints that are free to move (such as arms)
// but unmovable joints (such as torso) cannot be influenced at all.
glm::vec3 jointPosition(0.f);
@ -737,11 +761,10 @@ bool Model::poke(ModelCollisionInfo& collision) {
getJointPosition(jointIndex, end);
glm::vec3 newEnd = start + glm::angleAxis(glm::degrees(angle), axis) * (end - start);
// try to move it
return setJointPosition(jointIndex, newEnd, -1, true);
setJointPosition(jointIndex, newEnd, -1, true);
}
}
}
return false;
}
void Model::deleteGeometry() {

View file

@ -66,6 +66,9 @@ public:
/// Returns the extents of the model in its bind pose.
Extents getBindExtents() const;
/// Returns the extents of the unmovable joints of the model.
Extents getStaticExtents() const;
/// Returns a reference to the shared geometry.
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
@ -164,9 +167,12 @@ public:
void renderCollisionProxies(float alpha);
/// \return true if the collision is against a moveable joint
bool collisionHitsMoveableJoint(ModelCollisionInfo& collision) const;
/// \param collisionInfo info about the collision
/// \return true if collision affects the Model
bool poke(ModelCollisionInfo& collisionInfo);
/// Use the collisionInfo to affect the model
void applyCollision(ModelCollisionInfo& collisionInfo);
protected:

View file

@ -67,6 +67,7 @@ class AvatarData : public NodeData {
Q_OBJECT
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
Q_PROPERTY(float scale READ getTargetScale WRITE setTargetScale)
Q_PROPERTY(glm::vec3 handPosition READ getHandPosition WRITE setHandPosition)
Q_PROPERTY(float bodyYaw READ getBodyYaw WRITE setBodyYaw)
Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch)

View file

@ -2,7 +2,7 @@
// AvatarHashMap.cpp
// hifi
//
// Created by Stephen AndrewMeadows on 1/28/2014.
// Created by AndrewMeadows on 1/28/2014.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//

View file

@ -45,10 +45,3 @@ void HeadData::addLean(float sideways, float forwards) {
_leanForward += forwards;
}
bool HeadData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration) const {
// we would like to update this to determine collisions/penetrations with the Avatar's head sphere...
// but right now it does not appear as if the HeadData has a position and radius.
// this is a placeholder for now.
return false;
}

View file

@ -62,13 +62,6 @@ public:
void setLookAtPosition(const glm::vec3& lookAtPosition) { _lookAtPosition = lookAtPosition; }
friend class AvatarData;
/// Checks for penetration between the described sphere and the hand.
/// \param penetratorCenter the center of the penetration test sphere
/// \param penetratorRadius the radius of the penetration test sphere
/// \param penetration[out] the vector in which to store the penetration
/// \return whether or not the sphere penetrated
bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration) const;
protected:
float _yaw;

View file

@ -11,13 +11,19 @@
#include "OctreeScriptingInterface.h"
OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packetSender,
JurisdictionListener* jurisdictionListener)
JurisdictionListener* jurisdictionListener) :
_packetSender(NULL),
_jurisdictionListener(NULL),
_managedPacketSender(false),
_managedJurisdictionListener(false),
_initialized(false)
{
setPacketSender(packetSender);
setJurisdictionListener(jurisdictionListener);
}
OctreeScriptingInterface::~OctreeScriptingInterface() {
qDebug() << "OctreeScriptingInterface::~OctreeScriptingInterface() this=" << this;
cleanupManagedObjects();
}
@ -45,6 +51,9 @@ void OctreeScriptingInterface::setJurisdictionListener(JurisdictionListener* jur
}
void OctreeScriptingInterface::init() {
if (_initialized) {
return;
}
if (_jurisdictionListener) {
_managedJurisdictionListener = false;
} else {
@ -64,5 +73,5 @@ void OctreeScriptingInterface::init() {
if (QCoreApplication::instance()) {
connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(cleanupManagedObjects()));
}
_initialized = true;
}

View file

@ -93,6 +93,7 @@ protected:
JurisdictionListener* _jurisdictionListener;
bool _managedPacketSender;
bool _managedJurisdictionListener;
bool _initialized;
};
#endif /* defined(__hifi__OctreeScriptingInterface__) */

View file

@ -12,6 +12,7 @@
#include <QtCore/QObject>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include "EventTypes.h"
@ -37,6 +38,7 @@ public slots:
virtual glm::vec3 getSpatialControlPosition(int controlIndex) const = 0;
virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const = 0;
virtual glm::vec3 getSpatialControlNormal(int controlIndex) const = 0;
virtual glm::quat getSpatialControlRawRotation(int controlIndex) const = 0;
virtual void captureKeyEvents(const KeyEvent& event) = 0;
virtual void releaseKeyEvents(const KeyEvent& event) = 0;

View file

@ -9,7 +9,10 @@
//
//
#include <glm/gtx/vector_angle.hpp>
#include <OctreeConstants.h>
#include <SharedUtil.h>
#include "Quat.h"
glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) {
@ -24,6 +27,11 @@ glm::quat Quat::fromPitchYawRoll(float pitch, float yaw, float roll) {
return glm::quat(glm::radians(glm::vec3(pitch, yaw, roll)));
}
glm::quat Quat::inverse(const glm::quat& q) {
return glm::inverse(q);
}
glm::vec3 Quat::getFront(const glm::quat& orientation) {
return orientation * IDENTITY_FRONT;
}
@ -35,3 +43,12 @@ glm::vec3 Quat::getRight(const glm::quat& orientation) {
glm::vec3 Quat::getUp(const glm::quat& orientation) {
return orientation * IDENTITY_UP;
}
glm::vec3 Quat::safeEulerAngles(const glm::quat& orientation) {
return ::safeEulerAngles(orientation);
}
glm::quat Quat::angleAxis(float angle, const glm::vec3& v) {
return glm::angleAxis(angle, v);
}

View file

@ -23,11 +23,12 @@ public slots:
glm::quat multiply(const glm::quat& q1, const glm::quat& q2);
glm::quat fromVec3(const glm::vec3& vec3);
glm::quat fromPitchYawRoll(float pitch, float yaw, float roll);
glm::quat inverse(const glm::quat& q);
glm::vec3 getFront(const glm::quat& orientation);
glm::vec3 getRight(const glm::quat& orientation);
glm::vec3 getUp(const glm::quat& orientation);
glm::vec3 safeEulerAngles(const glm::quat& orientation);
glm::quat angleAxis(float angle, const glm::vec3& v);
};
#endif /* defined(__hifi__Quat__) */

View file

@ -19,6 +19,16 @@ glm::vec3 Vec3::multiply(const glm::vec3& v1, float f) {
return v1 * f;
}
glm::vec3 Vec3::multiplyQbyV(const glm::quat& q, const glm::vec3& v) {
return q * v;
}
glm::vec3 Vec3::sum(const glm::vec3& v1, const glm::vec3& v2) {
return v1 + v2;
}
glm::vec3 Vec3::subtract(const glm::vec3& v1, const glm::vec3& v2) {
return v1 - v2;
}
float Vec3::length(const glm::vec3& v) {
return glm::length(v);
}

View file

@ -23,7 +23,10 @@ class Vec3 : public QObject {
public slots:
glm::vec3 multiply(const glm::vec3& v1, const glm::vec3& v2);
glm::vec3 multiply(const glm::vec3& v1, float f);
glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v);
glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2);
glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2);
float length(const glm::vec3& v);
};