overte/examples/controllers/toybox.js
2015-08-07 18:07:14 -07:00

430 lines
No EOL
16 KiB
JavaScript

// handGrab.js
// examples
//
// Created by Sam Gondelman on 8/3/2015
// Copyright 2015 High Fidelity, Inc.
//
// Allow avatar to grab the closest object to each hand and throw them
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("http://s3.amazonaws.com/hifi-public/scripts/libraries/toolBars.js");
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var nullActionID = "00000000-0000-0000-0000-000000000000";
var controllerID;
var controllerActive;
var leftHandObjectID = null;
var rightHandObjectID = null;
var leftHandActionID = nullActionID;
var rightHandActionID = nullActionID;
var TRIGGER_THRESHOLD = 0.2;
var GRAB_RADIUS = 0.15;
var LEFT_HAND_CLICK = Controller.findAction("LEFT_HAND_CLICK");
var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK");
var ACTION1 = Controller.findAction("ACTION1");
var ACTION2 = Controller.findAction("ACTION2");
var rightHandGrabAction = RIGHT_HAND_CLICK;
var leftHandGrabAction = LEFT_HAND_CLICK;
var rightHandGrabValue = 0;
var leftHandGrabValue = 0;
var prevRightHandGrabValue = 0
var prevLeftHandGrabValue = 0;
var grabColor = { red: 0, green: 255, blue: 0};
var releaseColor = { red: 0, green: 0, blue: 255};
var toolBar = new ToolBar(0, 0, ToolBar.vertical, "highfidelity.toybox.toolbar", function() {
return {
x: 100,
y: 380
};
});
var BUTTON_SIZE = 32;
var SWORD_IMAGE = "https://hifi-public.s3.amazonaws.com/images/sword/sword.svg"; // TODO: replace this with a table icon
var CLEANUP_IMAGE = "http://s3.amazonaws.com/hifi-public/images/delete.png";
var tableButton = toolBar.addOverlay("image", {
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: SWORD_IMAGE,
alpha: 1
});
var cleanupButton = toolBar.addOverlay("image", {
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: CLEANUP_IMAGE,
alpha: 1
});
var overlays = false;
var leftHandOverlay;
var rightHandOverlay;
if (overlays) {
leftHandOverlay = Overlays.addOverlay("sphere", {
position: MyAvatar.getLeftPalmPosition(),
size: GRAB_RADIUS,
color: releaseColor,
alpha: 0.5,
solid: false
});
rightHandOverlay = Overlays.addOverlay("sphere", {
position: MyAvatar.getRightPalmPosition(),
size: GRAB_RADIUS,
color: releaseColor,
alpha: 0.5,
solid: false
});
}
var OBJECT_HEIGHT_OFFSET = 0.5;
var MIN_OBJECT_SIZE = 0.05;
var MAX_OBJECT_SIZE = 0.3;
var TABLE_DIMENSIONS = {
x: 10.0,
y: 0.2,
z: 5.0
};
var GRAVITY = {
x: 0.0,
y: -2.0,
z: 0.0
}
var LEFT = 0;
var RIGHT = 1;
var tableCreated = false;
var NUM_OBJECTS = 100;
var tableEntities = Array(NUM_OBJECTS + 1); // Also includes table
var VELOCITY_MAG = 0.3;
var entitiesToResize = [];
var MODELS = Array(
{ modelURL: "https://hifi-public.s3.amazonaws.com/ozan/props/sword/sword.fbx" },
{ modelURL: "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Vehicles/clara/spaceshuttle.fbx" },
{ modelURL: "https://s3.amazonaws.com/hifi-public/cozza13/apartment/Stargate.fbx" },
{ modelURL: "https://dl.dropboxusercontent.com/u/17344741/kelectricguitar10/kelectricguitar10.fbx" },
{ modelURL: "https://dl.dropboxusercontent.com/u/17344741/ktoilet10/ktoilet10.fbx" },
{ modelURL: "https://hifi-public.s3.amazonaws.com/models/props/MidCenturyModernLivingRoom/Interior/BilliardsTable.fbx" },
{ modelURL: "https://hifi-public.s3.amazonaws.com/ozan/avatars/robotMedic/robotMedicRed/robotMedicRed.fst" },
{ modelURL: "https://hifi-public.s3.amazonaws.com/ozan/avatars/robotMedic/robotMedicFaceRig/robotMedic.fst" },
{ modelURL: "https://hifi-public.s3.amazonaws.com/marketplace/contents/029db3d4-da2c-4cb2-9c08-b9612ba576f5/02949063e7c4aed42ad9d1a58461f56d.fst?1427169842" },
{ modelURL: "https://hifi-public.s3.amazonaws.com/models/props/MidCenturyModernLivingRoom/Interior/Bar.fbx" },
{ modelURL: "https://hifi-public.s3.amazonaws.com/marketplace/contents/96124d04-d603-4707-a5b3-e03bf47a53b2/1431770eba362c1c25c524126f2970fb.fst?1436924721" }
// Complex models:
// { modelURL: "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Architecture/sketchfab/cudillero.fbx" },
// { modelURL: "https://hifi-public.s3.amazonaws.com/ozan/sets/musicality/musicality.fbx" },
// { modelURL: "https://hifi-public.s3.amazonaws.com/ozan/sets/statelyHome/statelyHome.fbx" }
);
var COLLISION_SOUNDS = Array(
"http://public.highfidelity.io/sounds/Collisions-ballhitsandcatches/pingpong_TableBounceMono.wav",
"http://public.highfidelity.io/sounds/Collisions-ballhitsandcatches/billiards/collision1.wav"
);
var RESIZE_TIMER = 0.0;
var RESIZE_WAIT = 0.05; // 50 milliseconds
var leftFist = Entities.addEntity( {
type: "Sphere",
shapeType: 'sphere',
position: MyAvatar.getLeftPalmPosition(),
dimensions: { x: GRAB_RADIUS, y: GRAB_RADIUS, z: GRAB_RADIUS },
rotation: MyAvatar.getLeftPalmRotation(),
visible: false,
collisionsWillMove: false,
ignoreForCollisions: true
});
var rightFist = Entities.addEntity( {
type: "Sphere",
shapeType: 'sphere',
position: MyAvatar.getRightPalmPosition(),
dimensions: { x: GRAB_RADIUS, y: GRAB_RADIUS, z: GRAB_RADIUS },
rotation: MyAvatar.getRightPalmRotation(),
visible: false,
collisionsWillMove: false,
ignoreForCollisions: true
});
function letGo(hand) {
var actionIDToRemove = (hand == LEFT) ? leftHandActionID : rightHandActionID;
var entityIDToEdit = (hand == LEFT) ? leftHandObjectID : rightHandObjectID;
var handVelocity = (hand == LEFT) ? MyAvatar.getLeftPalmVelocity() : MyAvatar.getRightPalmVelocity();
var handAngularVelocity = (hand == LEFT) ? MyAvatar.getLeftPalmAngularVelocity() :
MyAvatar.getRightPalmAngularVelocity();
if (actionIDToRemove != nullActionID && entityIDToEdit != null) {
Entities.deleteAction(entityIDToEdit, actionIDToRemove);
// TODO: upon successful letGo, restore collision groups
if (hand == LEFT) {
leftHandObjectID = null;
leftHandActionID = nullActionID;
} else {
rightHandObjectID = null;
rightHandActionID = nullActionID;
}
}
}
function setGrabbedObject(hand) {
var handPosition = (hand == LEFT) ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition();
var entities = Entities.findEntities(handPosition, GRAB_RADIUS);
var objectID = null;
var minDistance = GRAB_RADIUS;
for (var i = 0; i < entities.length; i++) {
// Don't grab the object in your other hands, your fists, or the table
if ((hand == LEFT && entities[i] == rightHandObjectID) ||
(hand == RIGHT && entities[i] == leftHandObjectID) ||
entities[i] == leftFist || entities[i] == rightFist ||
(tableCreated && entities[i] == tableEntities[0])) {
continue;
} else {
var distance = Vec3.distance(Entities.getEntityProperties(entities[i]).position, handPosition);
if (distance <= minDistance) {
objectID = entities[i];
minDistance = distance;
}
}
}
if (objectID == null) {
return false;
}
if (hand == LEFT) {
leftHandObjectID = objectID;
} else {
rightHandObjectID = objectID;
}
return true;
}
function grab(hand) {
if (!setGrabbedObject(hand)) {
// If you don't grab an object, make a fist
Entities.editEntity((hand == LEFT) ? leftFist : rightFist, { ignoreForCollisions: false } );
return;
}
var objectID = (hand == LEFT) ? leftHandObjectID : rightHandObjectID;
var handRotation = (hand == LEFT) ? MyAvatar.getLeftPalmRotation() : MyAvatar.getRightPalmRotation();
var handPosition = (hand == LEFT) ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition();
var objectRotation = Entities.getEntityProperties(objectID).rotation;
var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
var objectPosition = Entities.getEntityProperties(objectID).position;
var offset = Vec3.subtract(objectPosition, handPosition);
var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset);
// print(JSON.stringify(offsetPosition));
var actionID = Entities.addAction("hold", objectID, {
relativePosition: { x: 0, y: 0, z: 0 },
relativeRotation: offsetRotation,
hand: (hand == LEFT) ? "left" : "right",
timeScale: 0.05
});
if (actionID == nullActionID) {
if (hand == LEFT) {
leftHandObjectID = null;
} else {
rightHandObjectID = null;
}
} else {
// TODO: upon successful grab, add to collision group so object doesn't collide with immovable entities
if (hand == LEFT) {
leftHandActionID = actionID;
} else {
rightHandActionID = actionID;
}
}
}
function resizeModels() {
var newEntitiesToResize = [];
for (var i = 0; i < entitiesToResize.length; i++) {
var naturalDimensions = Entities.getEntityProperties(entitiesToResize[i]).naturalDimensions;
if (naturalDimensions.x != 1.0 || naturalDimensions.y != 1.0 || naturalDimensions.z != 1.0) {
// bigger range of sizes for models
var dimensions = Vec3.multiply(randFloat(MIN_OBJECT_SIZE, 3.0*MAX_OBJECT_SIZE), Vec3.normalize(naturalDimensions));
Entities.editEntity(entitiesToResize[i], {
dimensions: dimensions,
shapeType: "box"
});
} else {
newEntitiesToResize.push(entitiesToResize[i]);
}
}
entitiesToResize = newEntitiesToResize;
}
function update(deltaTime) {
if (overlays) {
Overlays.editOverlay(leftHandOverlay, { position: MyAvatar.getLeftPalmPosition() });
Overlays.editOverlay(rightHandOverlay, { position: MyAvatar.getRightPalmPosition() });
}
// if (tableCreated && RESIZE_TIMER < RESIZE_WAIT) {
// RESIZE_TIMER += deltaTime;
// } else if (tableCreated) {
// resizeModels();
// }
rightHandGrabValue = Controller.getActionValue(rightHandGrabAction);
leftHandGrabValue = Controller.getActionValue(leftHandGrabAction);
Entities.editEntity(leftFist, { position: MyAvatar.getLeftPalmPosition() });
Entities.editEntity(rightFist, { position: MyAvatar.getRightPalmPosition() });
if (rightHandGrabValue > TRIGGER_THRESHOLD &&
prevRightHandGrabValue < TRIGGER_THRESHOLD) {
if (overlays) {
Overlays.editOverlay(rightHandOverlay, { color: grabColor });
}
grab(RIGHT);
} else if (rightHandGrabValue < TRIGGER_THRESHOLD &&
prevRightHandGrabValue > TRIGGER_THRESHOLD) {
Entities.editEntity(rightFist, { ignoreForCollisions: true } );
if (overlays) {
Overlays.editOverlay(rightHandOverlay, { color: releaseColor });
}
letGo(RIGHT);
}
if (leftHandGrabValue > TRIGGER_THRESHOLD &&
prevLeftHandGrabValue < TRIGGER_THRESHOLD) {
if (overlays) {
Overlays.editOverlay(leftHandOverlay, { color: grabColor });
}
grab(LEFT);
} else if (leftHandGrabValue < TRIGGER_THRESHOLD &&
prevLeftHandGrabValue > TRIGGER_THRESHOLD) {
Entities.editEntity(leftFist, { ignoreForCollisions: true } );
if (overlays) {
Overlays.editOverlay(leftHandOverlay, { color: releaseColor });
}
letGo(LEFT);
}
prevRightHandGrabValue = rightHandGrabValue;
prevLeftHandGrabValue = leftHandGrabValue;
}
function cleanUp() {
letGo(RIGHT);
letGo(LEFT);
if (overlays) {
Overlays.deleteOverlay(leftHandOverlay);
Overlays.deleteOverlay(rightHandOverlay);
}
Entities.deleteEntity(leftFist);
Entities.deleteEntity(rightFist);
removeTable();
toolBar.cleanup();
}
function onClick(event) {
if (event.deviceID != 0) {
return;
}
switch (Overlays.getOverlayAtPoint(event)) {
case tableButton:
if (!tableCreated) {
createTable();
tableCreated = true;
}
break;
case cleanupButton:
if (tableCreated) {
removeTable();
tableCreated = false;
}
break;
}
}
randFloat = function(low, high) {
return low + Math.random() * (high - low);
}
randInt = function(low, high) {
return Math.floor(randFloat(low, high));
}
function createTable() {
var tablePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(MyAvatar.orientation)));
tableEntities[0] = Entities.addEntity( {
type: "Model",
shapeType: 'box',
position: tablePosition,
dimensions: TABLE_DIMENSIONS,
rotation: MyAvatar.orientation,
// color: { red: 102, green: 51, blue: 0 },
modelURL: HIFI_PUBLIC_BUCKET + 'eric/models/woodFloor.fbx',
collisionSoundURL: "http://public.highfidelity.io/sounds/dice/diceCollide.wav"
});
for (var i = 1; i < NUM_OBJECTS + 1; i++) {
var objectOffset = { x: TABLE_DIMENSIONS.x/2.0 * randFloat(-1, 1),
y: OBJECT_HEIGHT_OFFSET,
z: TABLE_DIMENSIONS.z/2.0 * randFloat(-1, 1) };
var objectPosition = Vec3.sum(tablePosition, Vec3.multiplyQbyV(MyAvatar.orientation, objectOffset));
var type;
var randType = randInt(0, 3);
switch (randType) {
case 0:
type = "Box";
break;
case 1:
type = "Sphere";
// break;
case 2:
type = "Model";
break;
}
tableEntities[i] = Entities.addEntity( {
type: type,
position: objectPosition,
velocity: { x: randFloat(-VELOCITY_MAG, VELOCITY_MAG),
y: randFloat(-VELOCITY_MAG, VELOCITY_MAG),
z: randFloat(-VELOCITY_MAG, VELOCITY_MAG) },
dimensions: { x: randFloat(MIN_OBJECT_SIZE, MAX_OBJECT_SIZE),
y: randFloat(MIN_OBJECT_SIZE, MAX_OBJECT_SIZE),
z: randFloat(MIN_OBJECT_SIZE, MAX_OBJECT_SIZE) },
rotation: MyAvatar.orientation,
gravity: GRAVITY,
damping: 0.1,
restitution: 0.01,
density: 0.5,
collisionsWillMove: true,
color: { red: randInt(0, 255), green: randInt(0, 255), blue: randInt(0, 255) },
// collisionSoundURL: COLLISION_SOUNDS[randInt(0, COLLISION_SOUNDS.length)]
});
if (type == "Model") {
var randModel = randInt(0, MODELS.length);
Entities.editEntity(tableEntities[i], {
shapeType: "box",
modelURL: MODELS[randModel].modelURL
});
entitiesToResize.push(tableEntities[i]);
}
}
}
function removeTable() {
RESIZE_TIMER = 0.0;
for (var i = 0; i < tableEntities.length; i++) {
Entities.deleteEntity(tableEntities[i]);
}
}
Script.scriptEnding.connect(cleanUp);
Script.update.connect(update);
Controller.mousePressEvent.connect(onClick);