mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:44:02 +02:00
Merge branch 'master' into homer
This commit is contained in:
commit
488a449cb1
10 changed files with 408 additions and 189 deletions
|
@ -53,6 +53,10 @@ var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/";
|
|||
var toolHeight = 50;
|
||||
var toolWidth = 50;
|
||||
|
||||
var DEGREES_TO_RADIANS = Math.PI / 180.0;
|
||||
var RADIANS_TO_DEGREES = 180.0 / Math.PI;
|
||||
var epsilon = 0.001;
|
||||
|
||||
var MIN_ANGULAR_SIZE = 2;
|
||||
var MAX_ANGULAR_SIZE = 45;
|
||||
var allowLargeModels = true;
|
||||
|
@ -658,7 +662,7 @@ function handleIdleMouse() {
|
|||
idleMouseTimerId = null;
|
||||
if (isActive) {
|
||||
highlightEntityUnderCursor(lastMousePosition, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function highlightEntityUnderCursor(position, accurateRay) {
|
||||
|
@ -1207,6 +1211,49 @@ PropertiesTool = function(opts) {
|
|||
webView.setVisible(visible);
|
||||
};
|
||||
|
||||
vecToPolar = function(direction) {
|
||||
var x = direction.x;
|
||||
var y = direction.y;
|
||||
var z = direction.z;
|
||||
var pitch, yaw;
|
||||
pitch = -Math.asin(y);
|
||||
var c = Math.cos(-pitch);
|
||||
if (Math.abs(pitch) > (Math.PI / 2.0 - epsilon)) {
|
||||
//handle gymbal lock
|
||||
if (pitch > 0) {
|
||||
pitch = Math.PI / 2.0;
|
||||
} else {
|
||||
pitch = -Math.PI / 2.0;
|
||||
}
|
||||
yaw = 0.0;
|
||||
} else {
|
||||
if (z < 0) {
|
||||
if(x > 0 && x < 1) {
|
||||
yaw = Math.PI - Math.asin(x / c);
|
||||
} else {
|
||||
yaw = -Math.asin(x / c) - Math.PI;
|
||||
}
|
||||
} else {
|
||||
yaw = Math.asin(x / c);
|
||||
}
|
||||
}
|
||||
return {
|
||||
x: pitch * RADIANS_TO_DEGREES,
|
||||
y: yaw * RADIANS_TO_DEGREES,
|
||||
z: 0.0 //discard roll component
|
||||
};
|
||||
};
|
||||
|
||||
polarToVec = function(orientation) {
|
||||
var pitch = orientation.x * DEGREES_TO_RADIANS;
|
||||
var yaw = orientation.y * DEGREES_TO_RADIANS;
|
||||
return {
|
||||
x: Math.cos(pitch) * Math.sin(yaw),
|
||||
y: Math.sin(-pitch),
|
||||
z: Math.cos(pitch) * Math.cos(yaw)
|
||||
};
|
||||
}
|
||||
|
||||
selectionManager.addEventListener(function() {
|
||||
data = {
|
||||
type: 'update',
|
||||
|
@ -1216,7 +1263,12 @@ PropertiesTool = function(opts) {
|
|||
var entity = {};
|
||||
entity.id = selectionManager.selections[i];
|
||||
entity.properties = Entities.getEntityProperties(selectionManager.selections[i]);
|
||||
entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation);
|
||||
if (entity.properties.rotation !== undefined) {
|
||||
entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation);
|
||||
}
|
||||
if (entity.properties.keyLightDirection !== undefined) {
|
||||
entity.properties.keyLightDirection = vecToPolar(entity.properties.keyLightDirection);
|
||||
}
|
||||
selections.push(entity);
|
||||
}
|
||||
data.selections = selections;
|
||||
|
@ -1244,6 +1296,9 @@ PropertiesTool = function(opts) {
|
|||
var rotation = data.properties.rotation;
|
||||
data.properties.rotation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z);
|
||||
}
|
||||
if (data.properties.keyLightDirection !== undefined) {
|
||||
data.properties.keyLightDirection = polarToVec(data.properties.keyLightDirection);
|
||||
}
|
||||
Entities.editEntity(selectionManager.selections[0], data.properties);
|
||||
if (data.properties.name != undefined) {
|
||||
entityListTool.sendUpdate();
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// stick.js
|
||||
// sword.js
|
||||
// examples
|
||||
//
|
||||
// Created by Seth Alves on 2015-6-10
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Allow avatar to hold a stick
|
||||
// Allow avatar to hold a sword
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -14,32 +14,48 @@
|
|||
var Script, Entities, MyAvatar, Window, Overlays, Controller, Vec3, Quat, print, ToolBar, Settings; // Referenced globals provided by High Fidelity.
|
||||
Script.include("http://s3.amazonaws.com/hifi-public/scripts/libraries/toolBars.js");
|
||||
|
||||
var hand = Settings.getValue("highfidelity.sword.hand", "right");
|
||||
var zombieFight;
|
||||
|
||||
var hand = "right";
|
||||
|
||||
var nullActionID = "00000000-0000-0000-0000-000000000000";
|
||||
var controllerID;
|
||||
var controllerActive;
|
||||
var stickID = null;
|
||||
var swordID = null;
|
||||
var actionID = nullActionID;
|
||||
var targetIDs = [];
|
||||
var dimensions = { x: 0.3, y: 0.15, z: 2.0 };
|
||||
var dimensions = {
|
||||
x: 0.3,
|
||||
y: 0.15,
|
||||
z: 2.0
|
||||
};
|
||||
var BUTTON_SIZE = 32;
|
||||
|
||||
var stickModel = "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx";
|
||||
var health = 100;
|
||||
var healthLossOnHit = 10;
|
||||
|
||||
var swordModel = "https://hifi-public.s3.amazonaws.com/ozan/props/sword/sword.fbx";
|
||||
var swordCollisionShape = "https://hifi-public.s3.amazonaws.com/ozan/props/sword/sword.obj";
|
||||
// var swordCollisionShape = "https://hifi-public.s3.amazonaws.com/ozan/props/sword/sword.obj";
|
||||
var swordCollisionShape = "https://hifi-public.s3.amazonaws.com/eric/models/noHandleSwordCollisionShape.obj?=v2";
|
||||
var swordCollisionSoundURL = "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/swordStrike1.wav";
|
||||
var avatarCollisionSoundURL = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit1.wav";
|
||||
var avatarCollisionSoundURL = "https://hifi-public.s3.amazonaws.com/eric/sounds/blankSound.wav"; //Just to avoid no collision callback bug
|
||||
var zombieGameScriptURL = "https://hifi-public.s3.amazonaws.com/eric/scripts/zombieFight.js";
|
||||
var whichModel = "sword";
|
||||
var originalAvatarCollisionSound;
|
||||
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.vertical, "highfidelity.sword.toolbar", function () {
|
||||
return {x: 100, y: 380};
|
||||
var avatarCollisionSounds = [SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/avatarHit.wav"), SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/avatarHit2.wav?=v2")];
|
||||
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.vertical, "highfidelity.sword.toolbar", function() {
|
||||
return {
|
||||
x: 100,
|
||||
y: 380
|
||||
};
|
||||
});
|
||||
|
||||
var SWORD_IMAGE = "http://s3.amazonaws.com/hifi-public/images/billiardsReticle.png"; // Toggle between brandishing/sheathing sword (creating if necessary)
|
||||
var TARGET_IMAGE = "http://s3.amazonaws.com/hifi-public/images/puck.png"; // Create a target dummy
|
||||
var gameStarted = false;
|
||||
|
||||
var SWORD_IMAGE = "https://hifi-public.s3.amazonaws.com/images/sword/sword.svg"; // Toggle between brandishing/sheathing sword (creating if necessary)
|
||||
var TARGET_IMAGE = "https://hifi-public.s3.amazonaws.com/images/sword/dummy2.svg"; // Create a target dummy
|
||||
var CLEANUP_IMAGE = "http://s3.amazonaws.com/hifi-public/images/delete.png"; // Remove sword and all target dummies.f
|
||||
var SWITCH_HANDS_IMAGE = "http://s3.amazonaws.com/hifi-public/images/up-arrow.svg"; // Toggle left vs right hand. Persists in settings.
|
||||
var swordButton = toolBar.addOverlay("image", {
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
|
@ -52,12 +68,7 @@ var targetButton = toolBar.addOverlay("image", {
|
|||
imageURL: TARGET_IMAGE,
|
||||
alpha: 1
|
||||
});
|
||||
var switchHandsButton = toolBar.addOverlay("image", {
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: SWITCH_HANDS_IMAGE,
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var cleanupButton = toolBar.addOverlay("image", {
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
|
@ -66,6 +77,24 @@ var cleanupButton = toolBar.addOverlay("image", {
|
|||
});
|
||||
|
||||
var flasher;
|
||||
|
||||
var leftTriggerButton = 0;
|
||||
var leftTriggerValue = 0;
|
||||
var prevLeftTriggerValue = 0;
|
||||
|
||||
|
||||
var LEFT = 0;
|
||||
var RIGHT = 1;
|
||||
|
||||
var leftPalm = 2 * LEFT;
|
||||
var rightPalm = 2 * RIGHT;
|
||||
var rightTriggerButton = 1;
|
||||
var prevRightTriggerValue = 0;
|
||||
var rightTriggerValue = 0;
|
||||
var TRIGGER_THRESHOLD = 0.2;
|
||||
|
||||
var swordHeld = false;
|
||||
|
||||
function clearFlash() {
|
||||
if (!flasher) {
|
||||
return;
|
||||
|
@ -74,7 +103,9 @@ function clearFlash() {
|
|||
Overlays.deleteOverlay(flasher.overlay);
|
||||
flasher = null;
|
||||
}
|
||||
|
||||
function flash(color) {
|
||||
return;
|
||||
clearFlash();
|
||||
flasher = {};
|
||||
flasher.overlay = Overlays.addOverlay("text", {
|
||||
|
@ -86,42 +117,74 @@ function flash(color) {
|
|||
flasher.timer = Script.setTimeout(clearFlash, 500);
|
||||
}
|
||||
|
||||
var health = 100;
|
||||
|
||||
var display2d, display3d;
|
||||
|
||||
function trackAvatarWithText() {
|
||||
Entities.editEntity(display3d, {
|
||||
position: Vec3.sum(MyAvatar.position, {x: 0, y: 1.5, z: 0}),
|
||||
position: Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: 1.5,
|
||||
z: 0
|
||||
}),
|
||||
rotation: Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollDegrees(0, 180, 0))
|
||||
});
|
||||
}
|
||||
|
||||
function updateDisplay() {
|
||||
var text = health.toString();
|
||||
if (!display2d) {
|
||||
health = 100;
|
||||
display2d = Overlays.addOverlay("text", {
|
||||
text: text,
|
||||
font: { size: 20 },
|
||||
color: {red: 0, green: 255, blue: 0},
|
||||
backgroundColor: {red: 100, green: 100, blue: 100}, // Why doesn't this and the next work?
|
||||
font: {
|
||||
size: 20
|
||||
},
|
||||
color: {
|
||||
red: 0,
|
||||
green: 255,
|
||||
blue: 0
|
||||
},
|
||||
backgroundColor: {
|
||||
red: 100,
|
||||
green: 100,
|
||||
blue: 100
|
||||
}, // Why doesn't this and the next work?
|
||||
backgroundAlpha: 0.9,
|
||||
x: toolBar.x - 5, // I'd like to add the score to the toolBar and have it drag with it, but toolBar doesn't support text (just buttons).
|
||||
y: toolBar.y - 30 // So next best thing is to position it each time as if it were on top.
|
||||
});
|
||||
display3d = Entities.addEntity({
|
||||
name: MyAvatar.displayName + " score",
|
||||
textColor: {red: 255, green: 255, blue: 255},
|
||||
textColor: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
type: "Text",
|
||||
text: text,
|
||||
lineHeight: 0.14,
|
||||
backgroundColor: {red: 64, green: 64, blue: 64},
|
||||
dimensions: {x: 0.3, y: 0.2, z: 0.01},
|
||||
backgroundColor: {
|
||||
red: 64,
|
||||
green: 64,
|
||||
blue: 64
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.3,
|
||||
y: 0.2,
|
||||
z: 0.01
|
||||
},
|
||||
});
|
||||
Script.update.connect(trackAvatarWithText);
|
||||
} else {
|
||||
Overlays.editOverlay(display2d, {text: text});
|
||||
Entities.editEntity(display3d, {text: text});
|
||||
Overlays.editOverlay(display2d, {
|
||||
text: text
|
||||
});
|
||||
Entities.editEntity(display3d, {
|
||||
text: text
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function removeDisplay() {
|
||||
if (display2d) {
|
||||
Overlays.deleteOverlay(display2d);
|
||||
|
@ -131,97 +194,57 @@ function removeDisplay() {
|
|||
display3d = null;
|
||||
}
|
||||
}
|
||||
function computeEnergy(collision, entityID) {
|
||||
var id = entityID || collision.idA || collision.idB;
|
||||
var entity = id && Entities.getEntityProperties(id);
|
||||
var mass = entity ? (entity.density * entity.dimensions.x * entity.dimensions.y * entity.dimensions.z) : 1;
|
||||
var linearVelocityChange = Vec3.length(collision.velocityChange);
|
||||
var energy = 0.5 * mass * linearVelocityChange * linearVelocityChange;
|
||||
return Math.min(Math.max(1.0, Math.round(energy)), 20);
|
||||
}
|
||||
|
||||
|
||||
function gotHit(collision) {
|
||||
var energy = computeEnergy(collision);
|
||||
print("Got hit - " + energy + " from " + collision.idA + " " + collision.idB);
|
||||
health -= energy;
|
||||
flash({red: 255, green: 0, blue: 0});
|
||||
updateDisplay();
|
||||
}
|
||||
function scoreHit(idA, idB, collision) {
|
||||
var energy = computeEnergy(collision, idA);
|
||||
print("Score + " + energy + " from " + JSON.stringify(idA) + " " + JSON.stringify(idB));
|
||||
health += energy;
|
||||
flash({red: 0, green: 255, blue: 0});
|
||||
Audio.playSound(avatarCollisionSounds[randInt(0, avatarCollisionSounds.length)], {
|
||||
position: MyAvatar.position,
|
||||
volume: 0.5
|
||||
});
|
||||
health -= healthLossOnHit;
|
||||
if (health <= 30) {
|
||||
Overlays.editOverlay(display2d, {
|
||||
color: {
|
||||
red: 200,
|
||||
green: 10,
|
||||
blue: 10
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (health <= 0 && zombieFight) {
|
||||
zombieFight.loseGame();
|
||||
}
|
||||
flash({
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 0
|
||||
});
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
|
||||
function isFighting() {
|
||||
return stickID && (actionID !== nullActionID);
|
||||
return swordID && (actionID !== nullActionID);
|
||||
}
|
||||
|
||||
function initControls() {
|
||||
print("Sword hand is " + hand);
|
||||
if (hand === "right") {
|
||||
controllerID = 3; // right handed
|
||||
} else {
|
||||
controllerID = 4; // left handed
|
||||
}
|
||||
}
|
||||
|
||||
var inHand = false;
|
||||
function positionStick(stickOrientation) {
|
||||
var reorient = Quat.fromPitchYawRollDegrees(0, -90, 0);
|
||||
var baseOffset = {x: -dimensions.z * 0.8, y: 0, z: 0};
|
||||
var offset = Vec3.multiplyQbyV(reorient, baseOffset);
|
||||
stickOrientation = Quat.multiply(reorient, stickOrientation);
|
||||
inHand = false;
|
||||
Entities.updateAction(stickID, actionID, {
|
||||
relativePosition: offset,
|
||||
relativeRotation: stickOrientation,
|
||||
hand: "right"
|
||||
});
|
||||
}
|
||||
function resetToHand() { // For use with controllers, puts the sword in contact with the hand.
|
||||
// Maybe coordinate with positionStick?
|
||||
if (inHand) { // Optimization: bail if we're already inHand.
|
||||
return;
|
||||
}
|
||||
print('Reset to hand');
|
||||
Entities.updateAction(stickID, actionID, {
|
||||
relativePosition: {x: 0.0, y: 0.0, z: -dimensions.z * 0.5},
|
||||
relativeRotation: Quat.fromVec3Degrees({x: 45.0, y: 0.0, z: 0.0}),
|
||||
hand: hand, // It should not be necessary to repeat these two, but there seems to be a bug in that that
|
||||
timeScale: 0.05 // they do not retain their earlier values if you don't repeat them.
|
||||
});
|
||||
inHand = true;
|
||||
}
|
||||
|
||||
|
||||
function isControllerActive() {
|
||||
// I don't think the hydra API provides any reliable way to know whether a particular controller is active. Ask for both.
|
||||
controllerActive = (Vec3.length(Controller.getSpatialControlPosition(3)) > 0) || Vec3.length(Controller.getSpatialControlPosition(4)) > 0;
|
||||
return controllerActive;
|
||||
}
|
||||
function mouseMoveEvent(event) {
|
||||
// When a controller like the hydra gives a mouse event, the x/y is not meaningful to us, but we can detect with a truty deviceID
|
||||
if (event.deviceID || !isFighting() || isControllerActive()) {
|
||||
print('Attempting attachment reset');
|
||||
resetToHand();
|
||||
return;
|
||||
}
|
||||
var windowCenterX = Window.innerWidth / 2;
|
||||
var windowCenterY = Window.innerHeight / 2;
|
||||
var mouseXCenterOffset = event.x - windowCenterX;
|
||||
var mouseYCenterOffset = event.y - windowCenterY;
|
||||
var mouseXRatio = mouseXCenterOffset / windowCenterX;
|
||||
var mouseYRatio = mouseYCenterOffset / windowCenterY;
|
||||
|
||||
var stickOrientation = Quat.fromPitchYawRollDegrees(mouseYRatio * 90, mouseXRatio * 90, 0);
|
||||
positionStick(stickOrientation);
|
||||
}
|
||||
|
||||
function removeSword() {
|
||||
if (stickID) {
|
||||
print('deleting action ' + actionID + ' and entity ' + stickID);
|
||||
Entities.deleteAction(stickID, actionID);
|
||||
Entities.deleteEntity(stickID);
|
||||
stickID = null;
|
||||
if (swordID) {
|
||||
print('deleting action ' + actionID + ' and entity ' + swordID);
|
||||
Entities.deleteAction(swordID, actionID);
|
||||
Entities.deleteEntity(swordID);
|
||||
swordID = null;
|
||||
actionID = nullActionID;
|
||||
Controller.mouseMoveEvent.disconnect(mouseMoveEvent);
|
||||
MyAvatar.collisionWithEntity.disconnect(gotHit);
|
||||
|
@ -232,101 +255,219 @@ function removeSword() {
|
|||
MyAvatar.collisionSoundURL = originalAvatarCollisionSound;
|
||||
}
|
||||
removeDisplay();
|
||||
swordHeld = false;
|
||||
}
|
||||
|
||||
function cleanUp(leaveButtons) {
|
||||
removeSword();
|
||||
targetIDs.forEach(function (id) {
|
||||
Entities.deleteAction(id.entity, id.action);
|
||||
Entities.deleteEntity(id.entity);
|
||||
});
|
||||
targetIDs = [];
|
||||
if (!leaveButtons) {
|
||||
toolBar.cleanup();
|
||||
}
|
||||
removeSword();
|
||||
gameStarted = false;
|
||||
zombieFight.cleanup();
|
||||
}
|
||||
|
||||
function makeSword() {
|
||||
initControls();
|
||||
var swordPosition;
|
||||
if (!isControllerActive()) { // Dont' knock yourself with sword
|
||||
swordPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(MyAvatar.orientation)));
|
||||
} else if (hand === 'right') {
|
||||
swordPosition = MyAvatar.getRightPalmPosition();
|
||||
} else {
|
||||
swordPosition = MyAvatar.getLeftPalmPosition();
|
||||
}
|
||||
stickID = Entities.addEntity({
|
||||
var swordPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(5, Quat.getFront(MyAvatar.orientation)));
|
||||
var orientationAdjustment = Quat.fromPitchYawRollDegrees(90, 0, 0);
|
||||
|
||||
swordID = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "sword",
|
||||
modelURL: swordModel,
|
||||
compoundShapeURL: swordCollisionShape,
|
||||
dimensions: dimensions,
|
||||
position: swordPosition,
|
||||
rotation: MyAvatar.orientation,
|
||||
rotation: Quat.fromPitchYawRollDegrees(90, 0, 0),
|
||||
damping: 0.1,
|
||||
collisionSoundURL: swordCollisionSoundURL,
|
||||
restitution: 0.01,
|
||||
collisionsWillMove: true
|
||||
collisionsWillMove: true,
|
||||
});
|
||||
actionID = Entities.addAction("hold", stickID, {
|
||||
relativePosition: {x: 0.0, y: 0.0, z: -dimensions.z * 0.5},
|
||||
relativeRotation: Quat.fromVec3Degrees({x: 45.0, y: 0.0, z: 0.0}),
|
||||
|
||||
if (originalAvatarCollisionSound === undefined) {
|
||||
originalAvatarCollisionSound = MyAvatar.collisionSoundURL; // We won't get MyAvatar.collisionWithEntity unless there's a sound URL. (Bug.)
|
||||
SoundCache.getSound(avatarCollisionSoundURL); // Interface does not currently "preload" this? (Bug?)
|
||||
}
|
||||
|
||||
if (!isControllerActive()) {
|
||||
grabSword("right");
|
||||
}
|
||||
MyAvatar.collisionSoundURL = avatarCollisionSoundURL;
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
MyAvatar.collisionWithEntity.connect(gotHit);
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function grabSword(hand) {
|
||||
if (!swordID) {
|
||||
print("Create a sword by clicking on sword icon!")
|
||||
return;
|
||||
}
|
||||
var handRotation;
|
||||
if (hand === "right") {
|
||||
handRotation = MyAvatar.getRightPalmRotation();
|
||||
|
||||
} else if (hand === "left") {
|
||||
handRotation = MyAvatar.getLeftPalmRotation();
|
||||
}
|
||||
var swordRotation = Entities.getEntityProperties(swordID).rotation;
|
||||
var offsetRotation = Quat.multiply(Quat.inverse(handRotation), swordRotation);
|
||||
actionID = Entities.addAction("hold", swordID, {
|
||||
relativePosition: {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: -dimensions.z * 0.5
|
||||
},
|
||||
relativeRotation: offsetRotation,
|
||||
hand: hand,
|
||||
timeScale: 0.05
|
||||
});
|
||||
if (actionID === nullActionID) {
|
||||
print('*** FAILED TO MAKE SWORD ACTION ***');
|
||||
cleanUp();
|
||||
} else {
|
||||
swordHeld = true;
|
||||
}
|
||||
if (originalAvatarCollisionSound === undefined) {
|
||||
originalAvatarCollisionSound = MyAvatar.collisionSoundURL; // We won't get MyAvatar.collisionWithEntity unless there's a sound URL. (Bug.)
|
||||
SoundCache.getSound(avatarCollisionSoundURL); // Interface does not currently "preload" this? (Bug?)
|
||||
}
|
||||
MyAvatar.collisionSoundURL = avatarCollisionSoundURL;
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
MyAvatar.collisionWithEntity.connect(gotHit);
|
||||
Script.addEventHandler(stickID, 'collisionWithEntity', scoreHit);
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
function releaseSword() {
|
||||
Entities.deleteAction(swordID, actionID);
|
||||
actionID = nullActionID;
|
||||
Entities.editEntity(swordID, {
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
angularVelocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
}
|
||||
});
|
||||
swordHeld = false;
|
||||
}
|
||||
|
||||
function update() {
|
||||
updateControllerState();
|
||||
|
||||
}
|
||||
|
||||
function updateControllerState() {
|
||||
rightTriggerValue = Controller.getTriggerValue(rightTriggerButton);
|
||||
leftTriggerValue = Controller.getTriggerValue(leftTriggerButton);
|
||||
|
||||
if (rightTriggerValue > TRIGGER_THRESHOLD && !swordHeld) {
|
||||
grabSword("right")
|
||||
} else if (rightTriggerValue < TRIGGER_THRESHOLD && prevRightTriggerValue > TRIGGER_THRESHOLD && swordHeld) {
|
||||
releaseSword();
|
||||
}
|
||||
|
||||
if (leftTriggerValue > TRIGGER_THRESHOLD && !swordHeld) {
|
||||
grabSword("left")
|
||||
} else if (leftTriggerValue < TRIGGER_THRESHOLD && prevLeftTriggerValue > TRIGGER_THRESHOLD && swordHeld) {
|
||||
releaseSword();
|
||||
}
|
||||
|
||||
prevRightTriggerValue = rightTriggerValue;
|
||||
prevLeftTriggerValue = leftTriggerValue;
|
||||
}
|
||||
|
||||
randFloat = function(low, high) {
|
||||
return low + Math.random() * (high - low);
|
||||
}
|
||||
|
||||
|
||||
randInt = function(low, high) {
|
||||
return Math.floor(randFloat(low, high));
|
||||
}
|
||||
|
||||
function positionSword(swordOrientation) {
|
||||
var reorient = Quat.fromPitchYawRollDegrees(0, -90, 0);
|
||||
var baseOffset = {
|
||||
x: -dimensions.z * 0.8,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
var offset = Vec3.multiplyQbyV(reorient, baseOffset);
|
||||
swordOrientation = Quat.multiply(reorient, swordOrientation);
|
||||
inHand = false;
|
||||
Entities.updateAction(swordID, actionID, {
|
||||
relativePosition: offset,
|
||||
relativeRotation: swordOrientation,
|
||||
hand: "right"
|
||||
});
|
||||
}
|
||||
|
||||
function resetToHand() { // For use with controllers, puts the sword in contact with the hand.
|
||||
// Maybe coordinate with positionSword?
|
||||
if (inHand) { // Optimization: bail if we're already inHand.
|
||||
return;
|
||||
}
|
||||
print('Reset to hand');
|
||||
Entities.updateAction(swordID, actionID, {
|
||||
relativePosition: {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: -dimensions.z * 0.5
|
||||
},
|
||||
relativeRotation: Quat.fromVec3Degrees({
|
||||
x: 45.0,
|
||||
y: 0.0,
|
||||
z: 0.0
|
||||
}),
|
||||
hand: "right", // It should not be necessary to repeat these two, but there seems to be a bug in that that
|
||||
timeScale: 0.05 // they do not retain their earlier values if you don't repeat them.
|
||||
});
|
||||
inHand = true;
|
||||
}
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
//When a controller like the hydra gives a mouse event, the x/y is not meaningful to us, but we can detect with a truty deviceID
|
||||
if (event.deviceID || !isFighting() || isControllerActive()) {
|
||||
resetToHand();
|
||||
return;
|
||||
}
|
||||
var windowCenterX = Window.innerWidth / 2;
|
||||
var windowCenterY = Window.innerHeight / 2;
|
||||
var mouseXCenterOffset = event.x - windowCenterX;
|
||||
var mouseYCenterOffset = event.y - windowCenterY;
|
||||
var mouseXRatio = mouseXCenterOffset / windowCenterX;
|
||||
var mouseYRatio = mouseYCenterOffset / windowCenterY;
|
||||
|
||||
var swordOrientation = Quat.fromPitchYawRollDegrees(mouseYRatio * 90, mouseXRatio * 90, 0);
|
||||
positionSword(swordOrientation);
|
||||
}
|
||||
|
||||
|
||||
function onClick(event) {
|
||||
switch (Overlays.getOverlayAtPoint(event)) {
|
||||
case swordButton:
|
||||
if (!stickID) {
|
||||
makeSword();
|
||||
} else {
|
||||
removeSword();
|
||||
}
|
||||
break;
|
||||
case targetButton:
|
||||
var position = Vec3.sum(MyAvatar.position, {x: 1.0, y: 0.4, z: 0.0});
|
||||
var boxId = Entities.addEntity({
|
||||
type: "Box",
|
||||
name: "dummy",
|
||||
position: position,
|
||||
dimensions: {x: 0.3, y: 0.7, z: 0.3},
|
||||
gravity: {x: 0.0, y: -3.0, z: 0.0},
|
||||
damping: 0.2,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
case swordButton:
|
||||
if (!swordID) {
|
||||
makeSword();
|
||||
} else {
|
||||
removeSword();
|
||||
}
|
||||
break;
|
||||
case targetButton:
|
||||
if (gameStarted) {
|
||||
return;
|
||||
}
|
||||
Script.include("https://hifi-public.s3.amazonaws.com/eric/scripts/zombieFight.js");
|
||||
zombieFight = new ZombieFight();
|
||||
zombieFight.initiateZombieApocalypse();
|
||||
gameStarted = true;
|
||||
|
||||
var pointToOffsetFrom = Vec3.sum(position, {x: 0.0, y: 2.0, z: 0.0});
|
||||
var action = Entities.addAction("offset", boxId, {pointToOffsetFrom: pointToOffsetFrom,
|
||||
linearDistance: 2.0,
|
||||
// linearTimeScale: 0.005
|
||||
linearTimeScale: 0.1
|
||||
});
|
||||
targetIDs.push({entity: boxId, action: action});
|
||||
break;
|
||||
case switchHandsButton:
|
||||
cleanUp('leaveButtons');
|
||||
hand = hand === "right" ? "left" : "right";
|
||||
Settings.setValue("highfidelity.sword.hand", hand);
|
||||
makeSword();
|
||||
break;
|
||||
case cleanupButton:
|
||||
cleanUp('leaveButtons');
|
||||
break;
|
||||
break;
|
||||
case cleanupButton:
|
||||
cleanUp('leaveButtons');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanUp);
|
||||
Controller.mousePressEvent.connect(onClick);
|
||||
Script.update.connect(update);
|
||||
Controller.mousePressEvent.connect(onClick);
|
|
@ -286,6 +286,8 @@ var usersWindow = (function () {
|
|||
MENU_ITEM = "Users Online",
|
||||
MENU_ITEM_AFTER = "Chat...",
|
||||
|
||||
SETTING_USERS_WINDOW_MINIMIZED = "UsersWindow.Minimized",
|
||||
|
||||
isVisible = true,
|
||||
isMinimized = false,
|
||||
|
||||
|
@ -869,9 +871,13 @@ var usersWindow = (function () {
|
|||
|
||||
pollUsers();
|
||||
|
||||
// Set minimized at end - setup code does not handle `minimized == false` correctly
|
||||
setMinimized(Settings.getValue(SETTING_USERS_WINDOW_MINIMIZED, false));
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
Settings.setValue(SETTING_USERS_WINDOW_MINIMIZED, isMinimized);
|
||||
|
||||
Menu.removeMenuItem(MENU_NAME, MENU_ITEM);
|
||||
|
||||
Script.clearTimeout(usersTimer);
|
||||
|
|
|
@ -331,6 +331,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_lastNackTime(usecTimestampNow()),
|
||||
_lastSendDownstreamAudioStats(usecTimestampNow()),
|
||||
_isVSyncOn(true),
|
||||
_isThrottleFPSEnabled(false),
|
||||
_aboutToQuit(false),
|
||||
_notifiedPacketVersionMismatchThisDomain(false),
|
||||
_domainConnectionRefusals(QList<QString>()),
|
||||
|
@ -2025,13 +2026,6 @@ void Application::setActiveFaceTracker() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void Application::toggleFaceTrackerMute() {
|
||||
FaceTracker* faceTracker = getSelectedFaceTracker();
|
||||
if (faceTracker) {
|
||||
faceTracker->toggleMute();
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) {
|
||||
QVector<EntityItemPointer> entities;
|
||||
|
||||
|
@ -2463,7 +2457,13 @@ void Application::update(float deltaTime) {
|
|||
{
|
||||
PerformanceTimer perfTimer("devices");
|
||||
DeviceTracker::updateAll();
|
||||
FaceTracker* tracker = getActiveFaceTracker();
|
||||
|
||||
FaceTracker* tracker = getSelectedFaceTracker();
|
||||
if (tracker && Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking) != tracker->isMuted()) {
|
||||
tracker->toggleMute();
|
||||
}
|
||||
|
||||
tracker = getActiveFaceTracker();
|
||||
if (tracker && !tracker->isMuted()) {
|
||||
tracker->update(deltaTime);
|
||||
|
||||
|
@ -4387,6 +4387,10 @@ void Application::setVSyncEnabled() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void Application::setThrottleFPSEnabled() {
|
||||
_isThrottleFPSEnabled = Menu::getInstance()->isOptionChecked(MenuOption::ThrottleFPSIfNotFocus);
|
||||
}
|
||||
|
||||
bool Application::isVSyncOn() const {
|
||||
#if defined(Q_OS_WIN)
|
||||
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
||||
|
|
|
@ -418,10 +418,12 @@ public slots:
|
|||
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
|
||||
|
||||
void setVSyncEnabled();
|
||||
|
||||
void setThrottleFPSEnabled();
|
||||
bool isThrottleFPSEnabled() { return _isThrottleFPSEnabled; }
|
||||
|
||||
void resetSensors();
|
||||
void setActiveFaceTracker();
|
||||
void toggleFaceTrackerMute();
|
||||
|
||||
void aboutApp();
|
||||
void showEditEntitiesHelp();
|
||||
|
@ -634,6 +636,7 @@ private:
|
|||
quint64 _lastSendDownstreamAudioStats;
|
||||
|
||||
bool _isVSyncOn;
|
||||
bool _isThrottleFPSEnabled;
|
||||
|
||||
bool _aboutToQuit;
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@ void GLCanvas::stopFrameTimer() {
|
|||
}
|
||||
|
||||
bool GLCanvas::isThrottleRendering() const {
|
||||
return _throttleRendering || Application::getInstance()->getWindow()->isMinimized();
|
||||
return (_throttleRendering
|
||||
|| (Application::getInstance()->getWindow()->isMinimized() && Application::getInstance()->isThrottleFPSEnabled()));
|
||||
}
|
||||
|
||||
int GLCanvas::getDeviceWidth() const {
|
||||
|
@ -59,7 +60,8 @@ void GLCanvas::initializeGL() {
|
|||
|
||||
void GLCanvas::paintGL() {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
if (!_throttleRendering && !Application::getInstance()->getWindow()->isMinimized()) {
|
||||
if (!_throttleRendering
|
||||
&& (!Application::getInstance()->getWindow()->isMinimized()) || !Application::getInstance()->isThrottleFPSEnabled()) {
|
||||
Application::getInstance()->paintGL();
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +87,8 @@ void GLCanvas::activeChanged(Qt::ApplicationState state) {
|
|||
|
||||
default:
|
||||
// Otherwise, throttle.
|
||||
if (!_throttleRendering && !Application::getInstance()->isAboutToQuit()) {
|
||||
if (!_throttleRendering && !Application::getInstance()->isAboutToQuit()
|
||||
&& Application::getInstance()->isThrottleFPSEnabled()) {
|
||||
_frameTimer.start(_idleRenderInterval);
|
||||
_throttleRendering = true;
|
||||
}
|
||||
|
|
|
@ -370,6 +370,8 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderTargetFramerateVSyncOn, 0, true,
|
||||
qApp, SLOT(setVSyncEnabled()));
|
||||
#endif
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, false,
|
||||
qApp, SLOT(setThrottleFPSEnabled()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -433,8 +435,7 @@ Menu::Menu() {
|
|||
#if defined(HAVE_FACESHIFT) || defined(HAVE_DDE)
|
||||
faceTrackingMenu->addSeparator();
|
||||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_F, true, // DDE face tracking is on by default
|
||||
qApp, SLOT(toggleFaceTrackerMute()));
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_F, true); // DDE face tracking is on by default
|
||||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, false);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -280,6 +280,7 @@ namespace MenuOption {
|
|||
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
|
||||
const QString TestPing = "Test Ping";
|
||||
const QString ThirdPerson = "Third Person";
|
||||
const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus";
|
||||
const QString ToolWindow = "Tool Window";
|
||||
const QString TransmitterDrive = "Transmitter Drive";
|
||||
const QString TurnWithHead = "Turn using Head";
|
||||
|
|
|
@ -93,6 +93,7 @@ void AudioInjector::injectAudio() {
|
|||
}
|
||||
|
||||
void AudioInjector::restart() {
|
||||
_isPlaying = true;
|
||||
connect(this, &AudioInjector::finished, this, &AudioInjector::restartPortionAfterFinished);
|
||||
if (!_isStarted || _isFinished) {
|
||||
emit finished();
|
||||
|
@ -270,6 +271,7 @@ void AudioInjector::injectToMixer() {
|
|||
}
|
||||
|
||||
setIsFinished(true);
|
||||
_isPlaying = !_isFinished; // Which can be false if a restart was requested
|
||||
}
|
||||
|
||||
void AudioInjector::stop() {
|
||||
|
@ -277,6 +279,7 @@ void AudioInjector::stop() {
|
|||
|
||||
if (_options.localOnly) {
|
||||
// we're only a local injector, so we can say we are finished right away too
|
||||
_isPlaying = false;
|
||||
setIsFinished(true);
|
||||
}
|
||||
}
|
||||
|
@ -334,6 +337,7 @@ AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInj
|
|||
injectorThread->setObjectName("Audio Injector Thread");
|
||||
|
||||
AudioInjector* injector = new AudioInjector(buffer, options);
|
||||
injector->_isPlaying = true;
|
||||
injector->setLocalAudioInterface(localInterface);
|
||||
|
||||
injector->moveToThread(injectorThread);
|
||||
|
|
|
@ -62,7 +62,7 @@ public slots:
|
|||
|
||||
void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; }
|
||||
float getLoudness() const { return _loudness; }
|
||||
bool isPlaying() const { return !_isFinished; }
|
||||
bool isPlaying() const { return _isPlaying; }
|
||||
void restartPortionAfterFinished();
|
||||
|
||||
signals:
|
||||
|
@ -78,6 +78,7 @@ private:
|
|||
AudioInjectorOptions _options;
|
||||
bool _shouldStop = false;
|
||||
float _loudness = 0.0f;
|
||||
bool _isPlaying = false;
|
||||
bool _isStarted = false;
|
||||
bool _isFinished = false;
|
||||
bool _shouldDeleteAfterFinish = false;
|
||||
|
|
Loading…
Reference in a new issue