Conflicts:
	libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
	libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp
	libraries/shared/src/GLMHelpers.cpp
This commit is contained in:
Andrew Meadows 2015-09-01 11:17:08 -07:00
commit fe2d03fd37
53 changed files with 3051 additions and 793 deletions

View file

@ -0,0 +1,582 @@
//
// breakdanceToy.js
// examples
//
// Created by Brad Hefta-Gaub on August 24, 2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
// helpers
// Computes the penetration between a point and a sphere (centered at the origin)
// if point is inside sphere: returns true and stores the result in 'penetration'
// (the vector that would move the point outside the sphere)
// otherwise returns false
function findSphereHit(point, sphereRadius) {
var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations
var vectorLength = Vec3.length(point);
if (vectorLength < EPSILON) {
return true;
}
var distance = vectorLength - sphereRadius;
if (distance < 0.0) {
return true;
}
return false;
}
function findSpherePointHit(sphereCenter, sphereRadius, point) {
return findSphereHit(Vec3.subtract(point,sphereCenter), sphereRadius);
}
function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadius) {
return findSpherePointHit(firstCenter, firstRadius + secondRadius, secondCenter);
}
function getPositionPuppet() {
var DISTANCE_IN_FRONT = 2;
var DISTANCE_UP = 0.4;
var DISTANCE_TO_SIDE = 0.0;
var up = Quat.getUp(MyAvatar.orientation);
var front = Quat.getFront(MyAvatar.orientation);
var right = Quat.getRight(MyAvatar.orientation);
var left = Vec3.multiply(right, -1);
var upOffset = Vec3.multiply(up, DISTANCE_UP);
var leftOffset = Vec3.multiply(left, DISTANCE_TO_SIDE);
var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT);
var offset = Vec3.sum(Vec3.sum(leftOffset, frontOffset), upOffset);
var position = Vec3.sum(MyAvatar.position, offset);
return position;
}
function getPositionLeftFront() {
var DISTANCE_IN_FRONT = 0.6;
var DISTANCE_UP = 0.4;
var DISTANCE_TO_SIDE = 0.3;
var up = Quat.getUp(MyAvatar.orientation);
var front = Quat.getFront(MyAvatar.orientation);
var right = Quat.getRight(MyAvatar.orientation);
var left = Vec3.multiply(right, -1);
var upOffset = Vec3.multiply(up, DISTANCE_UP);
var leftOffset = Vec3.multiply(left, DISTANCE_TO_SIDE);
var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT);
var offset = Vec3.sum(Vec3.sum(leftOffset, frontOffset), upOffset);
var position = Vec3.sum(MyAvatar.position, offset);
return position;
}
function getPositionLeftSide() {
var DISTANCE_IN_FRONT = 0.0;
var DISTANCE_UP = 0.5;
var DISTANCE_TO_SIDE = 0.9;
var up = Quat.getUp(MyAvatar.orientation);
var front = Quat.getFront(MyAvatar.orientation);
var right = Quat.getRight(MyAvatar.orientation);
var left = Vec3.multiply(right, -1);
var upOffset = Vec3.multiply(up, DISTANCE_UP);
var leftOffset = Vec3.multiply(left, DISTANCE_TO_SIDE);
var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT);
var offset = Vec3.sum(Vec3.sum(leftOffset, frontOffset), upOffset);
var position = Vec3.sum(MyAvatar.position, offset);
return position;
}
function getPositionLeftOverhead() {
var DISTANCE_IN_FRONT = 0.2;
var DISTANCE_UP = 1;
var DISTANCE_TO_SIDE = 0.3;
var up = Quat.getUp(MyAvatar.orientation);
var front = Quat.getFront(MyAvatar.orientation);
var right = Quat.getRight(MyAvatar.orientation);
var left = Vec3.multiply(right, -1);
var upOffset = Vec3.multiply(up, DISTANCE_UP);
var leftOffset = Vec3.multiply(left, DISTANCE_TO_SIDE);
var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT);
var offset = Vec3.sum(Vec3.sum(leftOffset, frontOffset), upOffset);
var position = Vec3.sum(MyAvatar.position, offset);
return position;
}
function getPositionLeftLowered() {
var DISTANCE_IN_FRONT = 0.2;
var DISTANCE_DOWN = 0.1;
var DISTANCE_TO_SIDE = 0.3;
var up = Quat.getUp(MyAvatar.orientation);
var front = Quat.getFront(MyAvatar.orientation);
var right = Quat.getRight(MyAvatar.orientation);
var left = Vec3.multiply(right, -1);
var downOffset = Vec3.multiply(up, DISTANCE_DOWN);
var leftOffset = Vec3.multiply(left, DISTANCE_TO_SIDE);
var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT);
var offset = Vec3.sum(Vec3.sum(leftOffset, frontOffset), downOffset );
var position = Vec3.sum(MyAvatar.position, offset);
return position;
}
function getPositionLeftOnBase() {
var DISTANCE_IN_FRONT = 0.2;
var DISTANCE_DOWN = -0.4;
var DISTANCE_TO_SIDE = 0.3;
var up = Quat.getUp(MyAvatar.orientation);
var front = Quat.getFront(MyAvatar.orientation);
var right = Quat.getRight(MyAvatar.orientation);
var left = Vec3.multiply(right, -1);
var downOffset = Vec3.multiply(up, DISTANCE_DOWN);
var leftOffset = Vec3.multiply(left, DISTANCE_TO_SIDE);
var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT);
var offset = Vec3.sum(Vec3.sum(leftOffset, frontOffset), downOffset );
var position = Vec3.sum(MyAvatar.position, offset);
return position;
}
function getPositionRightFront() {
var DISTANCE_IN_FRONT = 0.6;
var DISTANCE_UP = 0.4;
var DISTANCE_TO_SIDE = 0.3;
var up = Quat.getUp(MyAvatar.orientation);
var front = Quat.getFront(MyAvatar.orientation);
var right = Quat.getRight(MyAvatar.orientation);
var upOffset = Vec3.multiply(up, DISTANCE_UP);
var rightOffset = Vec3.multiply(right, DISTANCE_TO_SIDE);
var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT);
var offset = Vec3.sum(Vec3.sum(rightOffset, frontOffset), upOffset);
var position = Vec3.sum(MyAvatar.position, offset);
return position;
}
function getPositionRightSide() {
var DISTANCE_IN_FRONT = 0.0;
var DISTANCE_UP = 0.5;
var DISTANCE_TO_SIDE = 0.9;
var up = Quat.getUp(MyAvatar.orientation);
var front = Quat.getFront(MyAvatar.orientation);
var right = Quat.getRight(MyAvatar.orientation);
var upOffset = Vec3.multiply(up, DISTANCE_UP);
var rightOffset = Vec3.multiply(right, DISTANCE_TO_SIDE);
var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT);
var offset = Vec3.sum(Vec3.sum(rightOffset, frontOffset), upOffset);
var position = Vec3.sum(MyAvatar.position, offset);
return position;
}
function getPositionRightOverhead() {
var DISTANCE_IN_FRONT = 0.2;
var DISTANCE_UP = 1;
var DISTANCE_TO_SIDE = 0.3;
var up = Quat.getUp(MyAvatar.orientation);
var front = Quat.getFront(MyAvatar.orientation);
var right = Quat.getRight(MyAvatar.orientation);
var upOffset = Vec3.multiply(up, DISTANCE_UP);
var rightOffset = Vec3.multiply(right, DISTANCE_TO_SIDE);
var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT);
var offset = Vec3.sum(Vec3.sum(rightOffset, frontOffset), upOffset);
var position = Vec3.sum(MyAvatar.position, offset);
return position;
}
function getPositionRightLowered() {
var DISTANCE_IN_FRONT = 0.2;
var DISTANCE_DOWN = 0.1;
var DISTANCE_TO_SIDE = 0.3;
var up = Quat.getUp(MyAvatar.orientation);
var front = Quat.getFront(MyAvatar.orientation);
var right = Quat.getRight(MyAvatar.orientation);
var downOffset = Vec3.multiply(up, DISTANCE_DOWN);
var rightOffset = Vec3.multiply(right, DISTANCE_TO_SIDE);
var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT);
var offset = Vec3.sum(Vec3.sum(rightOffset, frontOffset), downOffset );
var position = Vec3.sum(MyAvatar.position, offset);
return position;
}
function getPositionRightOnBase() {
var DISTANCE_IN_FRONT = 0.2;
var DISTANCE_DOWN = -0.4;
var DISTANCE_TO_SIDE = 0.3;
var up = Quat.getUp(MyAvatar.orientation);
var front = Quat.getFront(MyAvatar.orientation);
var right = Quat.getRight(MyAvatar.orientation);
var downOffset = Vec3.multiply(up, DISTANCE_DOWN);
var rightOffset = Vec3.multiply(right, DISTANCE_TO_SIDE);
var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT);
var offset = Vec3.sum(Vec3.sum(rightOffset, frontOffset), downOffset );
var position = Vec3.sum(MyAvatar.position, offset);
return position;
}
// We will also demonstrate some 3D overlays. We will create a couple of cubes, spheres, and lines
// our 3D cube that moves around...
var handSize = 0.25;
var leftCubePosition = MyAvatar.getLeftPalmPosition();
var rightCubePosition = MyAvatar.getRightPalmPosition();
var text = Overlays.addOverlay("text", {
x: 100,
y: 300,
width: 900,
height: 50,
backgroundColor: { red: 0, green: 0, blue: 0},
color: { red: 255, green: 255, blue: 255},
topMargin: 4,
leftMargin: 4,
text: "POSE...",
alpha: 1,
backgroundAlpha: 0.5
});
var leftHand= Overlays.addOverlay("cube", {
position: leftCubePosition,
size: handSize,
color: { red: 0, green: 0, blue: 255},
alpha: 1,
solid: false
});
var rightHand= Overlays.addOverlay("cube", {
position: rightCubePosition,
size: handSize,
color: { red: 255, green: 0, blue: 0},
alpha: 1,
solid: false
});
var targetSize = 0.3;
var targetColor = { red: 128, green: 128, blue: 128};
var targetColorHit = { red: 0, green: 255, blue: 0};
var moveCycleColor = { red: 255, green: 255, blue: 0};
var TEMPORARY_LIFETIME = 60;
var animationSettings = JSON.stringify({
fps: 30,
running: true,
loop: true,
firstFrame: 1,
lastFrame: 10000
});
var naturalDimensions = { x: 1.63, y: 1.67, z: 0.31 };
var dimensions = Vec3.multiply(naturalDimensions, 0.3);
var puppetEntityID = Entities.addEntity({
type: "Model",
modelURL: "https://hifi-public.s3.amazonaws.com/models/Bboys/bboy1/bboy1.fbx",
animationURL: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx",
animationSettings: animationSettings,
position: getPositionPuppet(),
ignoreForCollisions: true,
dimensions: dimensions,
lifetime: TEMPORARY_LIFETIME
});
var leftOnBase = Overlays.addOverlay("cube", {
position: getPositionLeftOnBase(),
size: targetSize,
color: targetColor,
alpha: 1,
solid: false
});
var leftLowered = Overlays.addOverlay("cube", {
position: getPositionLeftLowered(),
size: targetSize,
color: targetColor,
alpha: 1,
solid: false
});
var leftOverhead = Overlays.addOverlay("cube", {
position: getPositionLeftOverhead(),
size: targetSize,
color: targetColor,
alpha: 1,
solid: false
});
var leftSide= Overlays.addOverlay("cube", {
position: getPositionLeftSide(),
size: targetSize,
color: targetColor,
alpha: 1,
solid: false
});
var leftFront= Overlays.addOverlay("cube", {
position: getPositionLeftFront(),
size: targetSize,
color: targetColor,
alpha: 1,
solid: false
});
var rightOnBase = Overlays.addOverlay("cube", {
position: getPositionRightOnBase(),
size: targetSize,
color: targetColor,
alpha: 1,
solid: false
});
var rightLowered = Overlays.addOverlay("cube", {
position: getPositionRightLowered(),
size: targetSize,
color: targetColor,
alpha: 1,
solid: false
});
var rightOverhead = Overlays.addOverlay("cube", {
position: getPositionRightOverhead(),
size: targetSize,
color: targetColor,
alpha: 1,
solid: false
});
var rightSide= Overlays.addOverlay("cube", {
position: getPositionRightSide(),
size: targetSize,
color: targetColor,
alpha: 1,
solid: false
});
var rightFront= Overlays.addOverlay("cube", {
position: getPositionRightFront(),
size: targetSize,
color: targetColor,
alpha: 1,
solid: false
});
var startDate = new Date();
var lastTime = startDate.getTime();
var NO_POSE = 0;
var LEFT_ON_BASE = 1;
var LEFT_OVERHEAD = 2;
var LEFT_LOWERED = 4;
var LEFT_SIDE = 8;
var LEFT_FRONT = 16;
var RIGHT_ON_BASE = 32;
var RIGHT_OVERHEAD = 64;
var RIGHT_LOWERED = 128;
var RIGHT_SIDE = 256;
var RIGHT_FRONT = 512;
var lastPoseValue = NO_POSE;
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/bboy_pose_to_idle.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/bboy_uprock.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/bboy_uprock_start.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_1.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_2.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_3.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_to_freeze.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_to_idle.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_freeze_var_1.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_freeze_var_2.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_freeze_var_3.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_freeze_var_4.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_freezes.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_swipes.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_uprock.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_uprock_var_1.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_uprock_var_1_end.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_uprock_var_1_start.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_uprock_var_2.fbx
//http://s3.amazonaws.com/hifi-public/animations/Breakdancing/flair.fbx
var poses = Array();
/*
poses[0 ] = { name: "no pose", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[LEFT_OVERHEAD ] = { name: "Left Overhead" , animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[LEFT_LOWERED ] = { name: "Left Lowered", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[LEFT_SIDE ] = { name: "Left Side", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[LEFT_FRONT ] = { name: "Left Front", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[RIGHT_OVERHEAD ] = { name: "Right Overhead", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[RIGHT_LOWERED ] = { name: "Right Lowered", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[RIGHT_SIDE ] = { name: "Right Side", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[RIGHT_FRONT ] = { name: "Right Front", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
*/
poses[LEFT_ON_BASE + RIGHT_ON_BASE ] = { name: "Left On Base + Right On Base", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[LEFT_OVERHEAD + RIGHT_ON_BASE ] = { name: "Left Overhead + Right On Base", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[LEFT_LOWERED + RIGHT_ON_BASE ] = { name: "Left Lowered + Right On Base", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[LEFT_SIDE + RIGHT_ON_BASE ] = { name: "Left Side + Right On Base", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[LEFT_FRONT + RIGHT_ON_BASE ] = { name: "Left Front + Right On Base", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[LEFT_ON_BASE + RIGHT_OVERHEAD ] = { name: "Left On Base + Right Overhead", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[LEFT_ON_BASE + RIGHT_LOWERED ] = { name: "Left On Base + Right Lowered", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[LEFT_ON_BASE + RIGHT_SIDE ] = { name: "Left On Base + Right Side", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[LEFT_ON_BASE + RIGHT_FRONT ] = { name: "Left On Base + Right Front", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_ready.fbx" };
poses[LEFT_OVERHEAD + RIGHT_OVERHEAD ] = { name: "Left Overhead + Right Overhead", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/bboy_uprock.fbx" };
poses[LEFT_LOWERED + RIGHT_OVERHEAD ] = { name: "Left Lowered + Right Overhead", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_1.fbx" };
poses[LEFT_SIDE + RIGHT_OVERHEAD ] = { name: "Left Side + Right Overhead", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_2.fbx" };
poses[LEFT_FRONT + RIGHT_OVERHEAD ] = { name: "Left Front + Right Overhead", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_3.fbx" };
poses[LEFT_OVERHEAD + RIGHT_LOWERED ] = { name: "Left Overhead + Right Lowered", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_to_freeze.fbx" };
poses[LEFT_LOWERED + RIGHT_LOWERED ] = { name: "Left Lowered + Right Lowered", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_footwork_to_idle.fbx" };
poses[LEFT_SIDE + RIGHT_LOWERED ] = { name: "Left Side + Right Lowered", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_freeze_var_1.fbx" };
poses[LEFT_FRONT + RIGHT_LOWERED ] = { name: "Left Front + Right Lowered", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_freeze_var_2.fbx" };
poses[LEFT_OVERHEAD + RIGHT_SIDE ] = { name: "Left Overhead + Right Side", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_freeze_var_3.fbx" };
poses[LEFT_LOWERED + RIGHT_SIDE ] = { name: "Left Lowered + Right Side", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_freeze_var_4.fbx" };
poses[LEFT_SIDE + RIGHT_SIDE ] = { name: "Left Side + Right Side", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_freezes.fbx" };
poses[LEFT_FRONT + RIGHT_SIDE ] = { name: "Left Front + Right Side", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_swipes.fbx" };
poses[LEFT_OVERHEAD + RIGHT_FRONT ] = { name: "Left Overhead + Right Front", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_uprock.fbx" };
poses[LEFT_LOWERED + RIGHT_FRONT ] = { name: "Left Lowered + Right Front", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_uprock_var_1.fbx" };
poses[LEFT_SIDE + RIGHT_FRONT ] = { name: "Left Side + Right Front", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_uprock_var_2.fbx" };
poses[LEFT_FRONT + RIGHT_FRONT ] = { name: "Left Front + Right Front", animation: "http://s3.amazonaws.com/hifi-public/animations/Breakdancing/breakdance_uprock_var_1_end.fbx" };
Script.update.connect(function(deltaTime) {
var date= new Date();
var now= date.getTime();
var elapsed = now - lastTime;
var inMoveCycle = false;
var leftHandPos = MyAvatar.getLeftPalmPosition();
var rightHandPos = MyAvatar.getRightPalmPosition();
Overlays.editOverlay(leftHand, { position: leftHandPos } );
Overlays.editOverlay(rightHand, { position: rightHandPos } );
var hitTargetLeftOnBase = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftOnBase(), targetSize/2);
var hitTargetLeftOverhead = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftOverhead(), targetSize/2);
var hitTargetLeftLowered = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftLowered(), targetSize/2);
var hitTargetLeftSide = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftSide(), targetSize/2);
var hitTargetLeftFront = findSphereSphereHit(leftHandPos, handSize/2, getPositionLeftFront(), targetSize/2);
var hitTargetRightOnBase = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightOnBase(), targetSize/2);
var hitTargetRightOverhead = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightOverhead(), targetSize/2);
var hitTargetRightLowered = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightLowered(), targetSize/2);
var hitTargetRightSide = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightSide(), targetSize/2);
var hitTargetRightFront = findSphereSphereHit(rightHandPos, handSize/2, getPositionRightFront(), targetSize/2);
// determine target colors
var targetColorLeftOnBase = hitTargetLeftOnBase ? targetColorHit : targetColor;
var targetColorLeftOverhead = hitTargetLeftOverhead ? targetColorHit : targetColor;
var targetColorLeftLowered = hitTargetLeftLowered ? targetColorHit : targetColor;
var targetColorLeftSide = hitTargetLeftSide ? targetColorHit : targetColor;
var targetColorLeftFront = hitTargetLeftFront ? targetColorHit : targetColor;
var targetColorRightOnBase = hitTargetRightOnBase ? targetColorHit : targetColor;
var targetColorRightOverhead = hitTargetRightOverhead ? targetColorHit : targetColor;
var targetColorRightLowered = hitTargetRightLowered ? targetColorHit : targetColor;
var targetColorRightSide = hitTargetRightSide ? targetColorHit : targetColor;
var targetColorRightFront = hitTargetRightFront ? targetColorHit : targetColor;
// calculate a combined arm pose based on left and right hits
var poseValue = NO_POSE;
poseValue += hitTargetLeftOnBase ? LEFT_ON_BASE : 0;
poseValue += hitTargetLeftOverhead ? LEFT_OVERHEAD : 0;
poseValue += hitTargetLeftLowered ? LEFT_LOWERED : 0;
poseValue += hitTargetLeftSide ? LEFT_SIDE : 0;
poseValue += hitTargetLeftFront ? LEFT_FRONT : 0;
poseValue += hitTargetRightOnBase ? RIGHT_ON_BASE : 0;
poseValue += hitTargetRightOverhead ? RIGHT_OVERHEAD : 0;
poseValue += hitTargetRightLowered ? RIGHT_LOWERED : 0;
poseValue += hitTargetRightSide ? RIGHT_SIDE : 0;
poseValue += hitTargetRightFront ? RIGHT_FRONT : 0;
if (poses[poseValue] == undefined) {
Overlays.editOverlay(text, { text: "no pose -- value:" + poseValue });
} else {
Overlays.editOverlay(text, { text: "pose:" + poses[poseValue].name + "\n" + "animation:" + poses[poseValue].animation });
var props = Entities.getEntityProperties(puppetEntityID);
Entities.editEntity(puppetEntityID, {
animationURL: poses[poseValue].animation,
lifetime: TEMPORARY_LIFETIME + props.age // renew lifetime
});
}
lastPoseValue = poseValue;
Overlays.editOverlay(leftOnBase, { position: getPositionLeftOnBase(), color: targetColorLeftOnBase } );
Overlays.editOverlay(leftOverhead, { position: getPositionLeftOverhead(), color: targetColorLeftOverhead } );
Overlays.editOverlay(leftLowered, { position: getPositionLeftLowered(), color: targetColorLeftLowered } );
Overlays.editOverlay(leftSide, { position: getPositionLeftSide() , color: targetColorLeftSide } );
Overlays.editOverlay(leftFront, { position: getPositionLeftFront() , color: targetColorLeftFront } );
Overlays.editOverlay(rightOnBase, { position: getPositionRightOnBase(), color: targetColorRightOnBase } );
Overlays.editOverlay(rightOverhead, { position: getPositionRightOverhead(), color: targetColorRightOverhead } );
Overlays.editOverlay(rightLowered, { position: getPositionRightLowered(), color: targetColorRightLowered } );
Overlays.editOverlay(rightSide, { position: getPositionRightSide() , color: targetColorRightSide } );
Overlays.editOverlay(rightFront, { position: getPositionRightFront() , color: targetColorRightFront } );
});
Script.scriptEnding.connect(function() {
Overlays.deleteOverlay(leftHand);
Overlays.deleteOverlay(rightHand);
Overlays.deleteOverlay(text);
Overlays.deleteOverlay(leftOnBase);
Overlays.deleteOverlay(leftOverhead);
Overlays.deleteOverlay(leftLowered);
Overlays.deleteOverlay(leftSide);
Overlays.deleteOverlay(leftFront);
Overlays.deleteOverlay(rightOnBase);
Overlays.deleteOverlay(rightOverhead);
Overlays.deleteOverlay(rightLowered);
Overlays.deleteOverlay(rightSide);
Overlays.deleteOverlay(rightFront);
print("puppetEntityID:"+puppetEntityID);
Entities.deleteEntity(puppetEntityID);
});

View file

@ -448,12 +448,31 @@ var toolBar = (function () {
}
if (newPolyVoxButton === toolBar.clicked(clickedOverlay)) {
createNewEntity({
var polyVoxId = createNewEntity({
type: "PolyVox",
dimensions: { x: 10, y: 10, z: 10 },
voxelVolumeSize: {x:16, y:16, z:16},
voxelSurfaceStyle: 1
voxelSurfaceStyle: 2
});
for (var x = 1; x <= 14; x++) {
Entities.setVoxel(polyVoxId, {x: x, y: 1, z: 1}, 255);
Entities.setVoxel(polyVoxId, {x: x, y: 14, z: 1}, 255);
Entities.setVoxel(polyVoxId, {x: x, y: 1, z: 14}, 255);
Entities.setVoxel(polyVoxId, {x: x, y: 14, z: 14}, 255);
}
for (var y = 2; y <= 13; y++) {
Entities.setVoxel(polyVoxId, {x: 1, y: y, z: 1}, 255);
Entities.setVoxel(polyVoxId, {x: 14, y: y, z: 1}, 255);
Entities.setVoxel(polyVoxId, {x: 1, y: y, z: 14}, 255);
Entities.setVoxel(polyVoxId, {x: 14, y: y, z: 14}, 255);
}
for (var z = 2; z <= 13; z++) {
Entities.setVoxel(polyVoxId, {x: 1, y: 1, z: z}, 255);
Entities.setVoxel(polyVoxId, {x: 14, y: 1, z: z}, 255);
Entities.setVoxel(polyVoxId, {x: 1, y: 14, z: z}, 255);
Entities.setVoxel(polyVoxId, {x: 14, y: 14, z: z}, 255);
}
return true;
}

View file

@ -83,5 +83,11 @@ EntityListTool = function(opts) {
}
});
webView.visibilityChanged.connect(function (visible) {
if (visible) {
that.sendUpdate();
}
});
return that;
};

View file

@ -10,7 +10,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Overlay2D = function(properties, overlay) { // overlay is an optionnal variable
Overlay2D = function(properties, overlay) { // overlay is an optional variable
if (!(typeof(properties) === 'undefined')) {
if(typeof(overlay) === 'undefined') {
overlay = Overlays.addOverlay("image", properties);

View file

@ -0,0 +1,103 @@
Script.include("handController.js");
Script.include("highlighter.js");
BallController = function(side, magBalls) {
HandController.call(this, side);
this.magBalls = magBalls;
this.highlighter = new Highlighter();
this.highlighter.setSize(BALL_SIZE);
this.ghostEdges = {};
}
BallController.prototype = Object.create( HandController.prototype );
BallController.prototype.onUpdate = function(deltaTime) {
HandController.prototype.onUpdate.call(this, deltaTime);
if (!this.selected) {
// Find the highlight target and set it.
var target = this.magBalls.findNearestNode(this.tipPosition, BALL_SELECTION_RADIUS);
this.highlighter.highlight(target);
return;
}
this.highlighter.highlight(null);
Entities.editEntity(this.selected, { position: this.tipPosition });
var targetBalls = this.magBalls.findPotentialEdges(this.selected);
for (var ballId in targetBalls) {
if (!this.ghostEdges[ballId]) {
// create the ovleray
this.ghostEdges[ballId] = Overlays.addOverlay("line3d", {
start: this.magBalls.getNodePosition(ballId),
end: this.tipPosition,
color: COLORS.RED,
alpha: 1,
lineWidth: 5,
visible: true,
});
} else {
Overlays.editOverlay(this.ghostEdges[ballId], {
end: this.tipPosition,
});
}
}
for (var ballId in this.ghostEdges) {
if (!targetBalls[ballId]) {
Overlays.deleteOverlay(this.ghostEdges[ballId]);
delete this.ghostEdges[ballId];
}
}
}
BallController.prototype.onClick = function() {
this.selected = this.magBalls.grabBall(this.tipPosition, BALL_SELECTION_RADIUS);
this.highlighter.highlight(null);
}
BallController.prototype.onRelease = function() {
this.clearGhostEdges();
this.magBalls.releaseBall(this.selected);
this.selected = null;
}
BallController.prototype.clearGhostEdges = function() {
for(var ballId in this.ghostEdges) {
Overlays.deleteOverlay(this.ghostEdges[ballId]);
delete this.ghostEdges[ballId];
}
}
BallController.prototype.onCleanup = function() {
HandController.prototype.onCleanup.call(this);
this.clearGhostEdges();
}
BallController.prototype.onAltClick = function() {
return;
var target = this.magBalls.findNearestNode(this.tipPosition, BALL_SELECTION_RADIUS);
if (!target) {
logDebug(target);
return;
}
// FIXME move to delete shape
var toDelete = {};
var deleteQueue = [ target ];
while (deleteQueue.length) {
var curNode = deleteQueue.shift();
if (toDelete[curNode]) {
continue;
}
toDelete[curNode] = true;
for (var nodeId in this.magBalls.getConnectedNodes(curNode)) {
deleteQueue.push(nodeId);
}
}
for (var nodeId in toDelete) {
this.magBalls.destroyNode(nodeId);
}
}
BallController.prototype.onAltRelease = function() {
}

View file

@ -0,0 +1,140 @@
//
// Created by Bradley Austin Davis on 2015/08/27
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
STICK_URL = HIFI_PUBLIC_BUCKET + "models/props/geo_stick.fbx";
// FIXME make this editable through some script UI, so the user can customize the size of the structure built
SCALE = 0.5;
BALL_SIZE = 0.08 * SCALE;
STICK_LENGTH = 0.24 * SCALE;
DEBUG_MAGSTICKS = true;
CUSTOM_DATA_NAME = "magBalls";
BALL_NAME = "MagBall";
EDGE_NAME = "MagStick";
ZERO_VECTOR = { x: 0, y: 0, z: 0 };
COLORS = {
WHITE: {
red: 255,
green: 255,
blue: 255,
},
BLACK: {
red: 0,
green: 0,
blue: 0,
},
GREY: {
red: 128,
green: 128,
blue: 128,
},
RED: {
red: 255,
green: 0,
blue: 0
},
BLUE: {
red: 0,
green: 0,
blue: 255
},
GREEN: {
red: 0,
green: 255,
blue: 0
},
CYAN: {
red: 0,
green: 255,
blue: 255
},
YELLOW: {
red: 255,
green: 255,
blue: 0
},
MAGENTA: {
red: 255,
green: 0,
blue: 255
}
}
BALL_RADIUS = BALL_SIZE / 2.0;
BALL_SELECTION_RADIUS = BALL_RADIUS * 1.5;
BALL_DIMENSIONS = {
x: BALL_SIZE,
y: BALL_SIZE,
z: BALL_SIZE
};
BALL_COLOR = {
red: 128,
green: 128,
blue: 128
};
STICK_DIMENSIONS = {
x: STICK_LENGTH / 6,
y: STICK_LENGTH / 6,
z: STICK_LENGTH
};
BALL_DISTANCE = STICK_LENGTH + BALL_SIZE;
BALL_PROTOTYPE = {
type: "Sphere",
name: BALL_NAME,
dimensions: BALL_DIMENSIONS,
color: BALL_COLOR,
ignoreCollisions: true,
collisionsWillMove: false
};
// 2 millimeters
BALL_EPSILON = (.002) / BALL_DISTANCE;
LINE_DIMENSIONS = {
x: 5,
y: 5,
z: 5
}
LINE_PROTOTYPE = {
type: "Line",
name: EDGE_NAME,
color: COLORS.CYAN,
dimensions: LINE_DIMENSIONS,
lineWidth: 5,
visible: true,
ignoreCollisions: true,
collisionsWillMove: false,
}
EDGE_PROTOTYPE = LINE_PROTOTYPE;
// var EDGE_PROTOTYPE = {
// type: "Sphere",
// name: EDGE_NAME,
// color: { red: 0, green: 255, blue: 255 },
// //dimensions: STICK_DIMENSIONS,
// dimensions: { x: 0.02, y: 0.02, z: 0.02 },
// rotation: rotation,
// visible: true,
// ignoreCollisions: true,
// collisionsWillMove: false
// }

View file

@ -0,0 +1,95 @@
findMatchingNode = function(position, nodePositions) {
for (var nodeId in nodePositions) {
var nodePos = nodePositions[nodeId];
var distance = Vec3.distance(position, nodePos);
if (distance < 0.03) {
return nodeId;
}
}
}
repairConnections = function() {
var ids = Entities.findEntities(MyAvatar.position, 50);
// Find all the balls and record their positions
var nodePositions = {};
for (var i in ids) {
var id = ids[i];
var properties = Entities.getEntityProperties(id);
if (properties.name == BALL_NAME) {
nodePositions[id] = properties.position;
}
}
// Now check all the edges to see if they're valid (point to balls)
// and ensure that the balls point back to them
var ballsToEdges = {};
for (var i in ids) {
var id = ids[i];
var properties = Entities.getEntityProperties(id);
if (properties.name == EDGE_NAME) {
var startPos = properties.position;
var endPos = Vec3.sum(startPos, properties.linePoints[1]);
var magBallData = getMagBallsData(id);
var update = false;
if (!magBallData.start) {
var startNode = findMatchingNode(startPos, nodePositions);
if (startNode) {
logDebug("Found start node " + startNode)
magBallData.start = startNode;
update = true;
}
}
if (!magBallData.end) {
var endNode = findMatchingNode(endPos, nodePositions);
if (endNode) {
logDebug("Found end node " + endNode)
magBallData.end = endNode;
update = true;
}
}
if (!magBallData.start || !magBallData.end) {
logDebug("Didn't find both ends");
Entities.deleteEntity(id);
continue;
}
if (!ballsToEdges[magBallData.start]) {
ballsToEdges[magBallData.start] = [ id ];
} else {
ballsToEdges[magBallData.start].push(id);
}
if (!ballsToEdges[magBallData.end]) {
ballsToEdges[magBallData.end] = [ id ];
} else {
ballsToEdges[magBallData.end].push(id);
}
if (update) {
logDebug("Updating incomplete edge " + id);
magBallData.length = BALL_DISTANCE;
setMagBallsData(id, magBallData);
}
}
}
for (var nodeId in ballsToEdges) {
var magBallData = getMagBallsData(nodeId);
var edges = magBallData.edges || [];
var edgeHash = {};
for (var i in edges) {
edgeHash[edges[i]] = true;
}
var update = false;
for (var i in ballsToEdges[nodeId]) {
var edgeId = ballsToEdges[nodeId][i];
if (!edgeHash[edgeId]) {
update = true;
edgeHash[edgeId] = true;
edges.push(edgeId);
}
}
if (update) {
logDebug("Fixing node with missing edge data");
magBallData.edges = edges;
setMagBallsData(nodeId, magBallData);
}
}
}

View file

@ -0,0 +1,45 @@
EdgeSpring = function(edgeId, graph) {
this.edgeId = edgeId;
this.graph = graph;
var magBallsData = getMagBallsData(this.edgeId);
this.start = magBallsData.start;
this.end = magBallsData.end;
this.desiredLength = magBallsData.length || BALL_DISTANCE;
}
EdgeSpring.prototype.adjust = function(results) {
var startPos = this.getAdjustedPosition(this.start, results);
var endPos = this.getAdjustedPosition(this.end, results);
var vector = Vec3.subtract(endPos, startPos);
var length = Vec3.length(vector);
var variance = this.getVariance(length);
if (Math.abs(variance) <= this.MAX_VARIANCE) {
return false;
}
// adjust by halves until we fall below our variance
var adjustmentVector = Vec3.multiply(variance / 4, vector);
var newStartPos = Vec3.sum(Vec3.multiply(-1, adjustmentVector), startPos);
var newEndPos = Vec3.sum(adjustmentVector, endPos);
results[this.start] = newStartPos;
results[this.end] = newEndPos;
return true;
}
EdgeSpring.prototype.MAX_VARIANCE = 0.005;
EdgeSpring.prototype.getAdjustedPosition = function(nodeId, results) {
if (results[nodeId]) {
return results[nodeId];
}
return this.graph.getNodePosition(nodeId);
}
EdgeSpring.prototype.getVariance = function(length) {
var difference = this.desiredLength - length;
return difference / this.desiredLength;
}

View file

@ -0,0 +1,281 @@
//
// Created by Bradley Austin Davis on 2015/08/29
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// A collection of nodes and edges connecting them.
Graph = function() {
/* Structure of nodes tree
this.nodes: {
nodeId1: {
edgeId1: true
}
nodeId2: {
edgeId1: true
},
// Nodes can many edges
nodeId3: {
edgeId2: true
edgeId3: true
edgeId4: true
edgeId5: true
},
// Nodes can have 0 edges
nodeId5: {
},
...
}
*/
this.nodes = {};
/* Structure of edge tree
this.edges: {
edgeId1: {
// Every edge should have exactly two
nodeId1: true
nodeId2: true
},
edgeId2: {
nodeId3: true
nodeId4: true
},
...
}
*/
this.edges = {};
}
Graph.prototype.createNodeEntity = function(properties) {
throw "Unimplemented";
}
Graph.prototype.createNode = function(properties) {
var nodeId = this.createNodeEntity(properties);
this.nodes[nodeId] = {};
this.validate();
return nodeId;
}
Graph.prototype.createEdgeEntity = function(nodeA, nodeB) {
throw "Unimplemented";
}
Graph.prototype.createEdge = function(nodeA, nodeB) {
if (nodeA == nodeB) {
throw "Error: self connection not supported";
}
var newEdgeId = this.createEdgeEntity(nodeA, nodeB);
// Create the bidirectional linkage
this.edges[newEdgeId] = {};
this.edges[newEdgeId][nodeA] = true;
this.edges[newEdgeId][nodeB] = true;
this.nodes[nodeA][newEdgeId] = true;
this.nodes[nodeB][newEdgeId] = true;
this.validate();
}
Graph.prototype.getEdges = function(nodeId) {
var edges = this.nodes[nodeId];
var result = {};
for (var edgeId in edges) {
for (var otherNodeId in this.edges[edgeId]) {
if (otherNodeId != nodeId) {
result[edgeId] = otherNodeId;
}
}
}
return result;
}
Graph.prototype.getConnectedNodes = function(nodeId) {
var edges = this.getEdges(nodeId);
var result = {};
for (var edgeId in edges) {
var otherNodeId = edges[edgeId];
result[otherNodeId] = edgeId;
}
return result;
}
Graph.prototype.getEdgeLength = function(edgeId) {
var nodesInEdge = Object.keys(this.edges[edgeId]);
return this.getNodeDistance(nodesInEdge[0], nodesInEdge[1]);
}
Graph.prototype.getNodeDistance = function(a, b) {
var apos = this.getNodePosition(a);
var bpos = this.getNodePosition(b);
return Vec3.distance(apos, bpos);
}
Graph.prototype.getNodePosition = function(node) {
var properties = Entities.getEntityProperties(node);
return properties.position;
}
Graph.prototype.breakEdges = function(nodeId) {
for (var edgeId in this.nodes[nodeId]) {
this.destroyEdge(edgeId);
}
}
Graph.prototype.findNearestNode = function(position, maxDist) {
var resultId = null;
var resultDist = 0;
for (var nodeId in this.nodes) {
var nodePosition = this.getNodePosition(nodeId);
var curDist = Vec3.distance(nodePosition, position);
if (!maxDist || curDist <= maxDist) {
if (!resultId || curDist < resultDist) {
resultId = nodeId;
resultDist = curDist;
}
}
}
return resultId;
}
Graph.prototype.findMatchingNodes = function(selector) {
var result = {};
for (var nodeId in this.nodes) {
if (selector(nodeId)) {
result[nodeId] = true;
}
}
return result;
}
Graph.prototype.destroyEdge = function(edgeId) {
logDebug("Deleting edge " + edgeId);
for (var nodeId in this.edges[edgeId]) {
delete this.nodes[nodeId][edgeId];
}
delete this.edges[edgeId];
Entities.deleteEntity(edgeId);
this.validate();
}
Graph.prototype.destroyNode = function(nodeId) {
logDebug("Deleting node " + nodeId);
this.breakEdges(nodeId);
delete this.nodes[nodeId];
Entities.deleteEntity(nodeId);
this.validate();
}
Graph.prototype.deleteAll = function() {
var nodeIds = Object.keys(this.nodes);
for (var i in nodeIds) {
var nodeId = nodeIds[i];
this.destroyNode(nodeId);
}
}
Graph.prototype.areConnected = function(nodeIdA, nodeIdB) {
for (var edgeId in this.nodes[nodeIdA]) {
if (this.nodes[nodeIdB][edgeId]) {
return true;
}
}
return false;
}
forEachValue = function(val, operation) {
if( typeof val === 'string' ) {
operation(val);
} else if (typeof val === 'object') {
if (val.constructor === Array) {
for (var i in val) {
operation(val[i]);
}
} else {
for (var v in val) {
operation(v);
}
}
}
}
Graph.prototype.findShortestPath = function(start, end, options) {
var queue = [ start ];
var prev = {};
if (options && options.exclude) {
forEachValue(options.exclude, function(value) {
prev[value] = value;
});
logDebug("exclude " + prev);
}
var found = false;
while (!found && Object.keys(queue).length) {
var current = queue.shift();
for (var ballId in this.getConnectedNodes(current)) {
if (prev[ballId]) {
// already visited node
continue;
}
// record optimal path
prev[ballId] = current;
if (ballId == end) {
found = true;
break;
}
queue.push(ballId);
}
}
if (!found) {
logDebug("Exhausted search");
return;
}
var result = [ end ];
while (result[0] != start) {
result.unshift(prev[result[0]]);
}
return result;
}
Graph.prototype.validate = function() {
var error = false;
for (nodeId in this.nodes) {
for (edgeId in this.nodes[nodeId]) {
var edge = this.edges[edgeId];
if (!edge) {
logError("Error: node " + nodeId + " refers to unknown edge " + edgeId);
error = true;
continue;
}
if (!edge[nodeId]) {
logError("Error: node " + nodeId + " refers to edge " + edgeId + " but not vice versa");
error = true;
continue;
}
}
}
for (edgeId in this.edges) {
for (nodeId in this.edges[edgeId]) {
var node = this.nodes[nodeId];
if (!node) {
logError("Error: edge " + edgeId + " refers to unknown node " + nodeId);
error = true;
continue;
}
if (!node[edgeId]) {
logError("Error: edge " + edgeId + " refers to node " + nodeId + " but not vice versa");
error = true;
continue;
}
}
}
if (error) {
logDebug(JSON.stringify({ edges: this.edges, balls: this.nodes }, null, 2));
}
return error;
}

View file

@ -0,0 +1,127 @@
//
// Created by Bradley Austin Davis on 2015/08/29
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
LEFT_CONTROLLER = 0;
RIGHT_CONTROLLER = 1;
// FIXME add a customizable wand model and a mechanism to switch between wands
HandController = function(side) {
this.side = side;
this.palm = 2 * side;
this.tip = 2 * side + 1;
this.action = findAction(side ? "ACTION2" : "ACTION1");
this.altAction = findAction(side ? "ACTION1" : "ACTION2");
this.active = false;
this.tipScale = 1.4;
this.pointer = Overlays.addOverlay("sphere", {
position: ZERO_VECTOR,
size: 0.01,
color: COLORS.YELLOW,
alpha: 1.0,
solid: true,
visible: false,
});
// Connect to desired events
var _this = this;
Controller.actionEvent.connect(function(action, state) {
_this.onActionEvent(action, state);
});
Script.update.connect(function(deltaTime) {
_this.onUpdate(deltaTime);
});
Script.scriptEnding.connect(function() {
_this.onCleanup();
});
}
HandController.prototype.onActionEvent = function(action, state) {
var spatialControlCount = Controller.getNumberOfSpatialControls();
// If only 2 spacial controls, then we only have one controller active, so use either button
// otherwise, only use the specified action
if (action == this.action) {
if (state) {
this.onClick();
} else {
this.onRelease();
}
}
if (action == this.altAction) {
if (state) {
this.onAltClick();
} else {
this.onAltRelease();
}
}
}
HandController.prototype.setActive = function(active) {
if (active == this.active) {
return;
}
logDebug("Hand controller changing active state: " + active);
this.active = active;
Overlays.editOverlay(this.pointer, {
visible: this.active
});
Entities.editEntity(this.wand, {
visible: this.active
});
}
HandController.prototype.updateControllerState = function() {
// FIXME this returns data if either the left or right controller is not on the base
this.palmPos = Controller.getSpatialControlPosition(this.palm);
var tipPos = Controller.getSpatialControlPosition(this.tip);
this.tipPosition = scaleLine(this.palmPos, tipPos, this.tipScale);
// When on the base, hydras report a position of 0
this.setActive(Vec3.length(this.palmPos) > 0.001);
//logDebug(Controller.getTriggerValue(0) + " " + Controller.getTriggerValue(1));
//if (this.active) {
// logDebug("#ctrls " + Controller.getNumberOfSpatialControls() + " Side: " + this.side + " Palm: " + this.palm + " " + vec3toStr(this.palmPos))
//}
}
HandController.prototype.onCleanup = function() {
Overlays.deleteOverlay(this.pointer);
}
HandController.prototype.onUpdate = function(deltaTime) {
this.updateControllerState();
if (this.active) {
Overlays.editOverlay(this.pointer, {
position: this.tipPosition
});
Entities.editEntity(this.wand, {
position: this.tipPosition
});
}
}
HandController.prototype.onClick = function() {
logDebug("Base hand controller does nothing on click");
}
HandController.prototype.onRelease = function() {
logDebug("Base hand controller does nothing on release");
}
HandController.prototype.onAltClick = function() {
logDebug("Base hand controller does nothing on alt click");
}
HandController.prototype.onAltRelease = function() {
logDebug("Base hand controller does nothing on alt click");
}

View file

@ -0,0 +1,70 @@
//
// Created by Bradley Austin Davis on 2015/08/29
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var SELECTION_OVERLAY = {
position: {
x: 0,
y: 0,
z: 0
},
color: {
red: 255,
green: 255,
blue: 0
},
alpha: 1,
size: 1.0,
solid: false,
//colorPulse: 1.0,
//pulseMin: 0.5,
//pulseMax: 1.0,
visible: false,
lineWidth: 1.0,
borderSize: 1.4,
};
Highlighter = function() {
this.highlightCube = Overlays.addOverlay("cube", this.SELECTION_OVERLAY);
this.hightlighted = null;
var _this = this;
Script.scriptEnding.connect(function() {
_this.onCleanup();
});
};
Highlighter.prototype.onCleanup = function() {
Overlays.deleteOverlay(this.highlightCube);
}
Highlighter.prototype.highlight = function(entityId) {
if (entityId != this.hightlighted) {
this.hightlighted = entityId;
this.updateHighlight();
}
}
Highlighter.prototype.setSize = function(newSize) {
Overlays.editOverlay(this.highlightCube, {
size: newSize
});
}
Highlighter.prototype.updateHighlight = function() {
if (this.hightlighted) {
var properties = Entities.getEntityProperties(this.hightlighted);
// logDebug("Making highlight " + this.highlightCube + " visible @ " + vec3toStr(properties.position));
Overlays.editOverlay(this.highlightCube, {
position: properties.position,
visible: true
});
} else {
// logDebug("Making highlight invisible");
Overlays.editOverlay(this.highlightCube, {
visible: false
});
}
}

View file

@ -0,0 +1,293 @@
//
// Created by Bradley Austin Davis on 2015/08/29
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var UPDATE_INTERVAL = 0.1;
Script.include("graph.js");
Script.include("edgeSpring.js");
// A collection of balls and edges connecting them.
MagBalls = function() {
Graph.call(this);
this.MAX_ADJUST_ITERATIONS = 100;
this.lastUpdateAge = 0;
this.stable = false;
this.adjustIterations = 0;
this.selectedNodes = {};
this.edgeObjects = {};
this.refresh();
var _this = this;
Script.update.connect(function(deltaTime) {
_this.onUpdate(deltaTime);
});
Script.scriptEnding.connect(function() {
_this.onCleanup();
});
}
MagBalls.prototype = Object.create( Graph.prototype );
MagBalls.prototype.onUpdate = function(deltaTime) {
this.lastUpdateAge += deltaTime;
if (this.lastUpdateAge > UPDATE_INTERVAL) {
this.lastUpdateAge = 0;
if (!this.stable) {
this.adjustIterations += 1;
// logDebug("Update");
var adjusted = false;
var nodeAdjustResults = {};
var fixupEdges = {};
for(var edgeId in this.edges) {
adjusted |= this.edgeObjects[edgeId].adjust(nodeAdjustResults);
}
for (var nodeId in nodeAdjustResults) {
var curPos = this.getNodePosition(nodeId);
var newPos = nodeAdjustResults[nodeId];
var distance = Vec3.distance(curPos, newPos);
for (var edgeId in this.nodes[nodeId]) {
fixupEdges[edgeId] = true;
}
// logDebug("Moving node Id " + nodeId + " " + (distance * 1000).toFixed(3) + " mm");
Entities.editEntity(nodeId, { position: newPos, color: COLORS.RED });
}
for (var edgeId in fixupEdges) {
this.fixupEdge(edgeId);
}
Script.setTimeout(function(){
for (var nodeId in nodeAdjustResults) {
Entities.editEntity(nodeId, { color: BALL_COLOR });
}
}, ((UPDATE_INTERVAL * 1000) / 2));
if (!adjusted || this.adjustIterations > this.MAX_ADJUST_ITERATIONS) {
this.adjustIterations = 0;
this.stable = true;
}
}
}
}
MagBalls.prototype.createNodeEntity = function(customProperies) {
var nodeId = Entities.addEntity(mergeObjects(BALL_PROTOTYPE, customProperies));
return nodeId;
}
MagBalls.prototype.createEdgeEntity = function(nodeIdA, nodeIdB) {
var apos = this.getNodePosition(nodeIdA);
var bpos = this.getNodePosition(nodeIdB);
var edgeId = Entities.addEntity(mergeObjects(EDGE_PROTOTYPE, {
position: apos,
linePoints: [ ZERO_VECTOR, Vec3.subtract(bpos, apos) ],
userData: JSON.stringify({
magBalls: {
start: nodeIdA,
end: nodeIdB,
length: BALL_DISTANCE
}
})
}));
this.edgeObjects[edgeId] = new EdgeSpring(edgeId, this);
return edgeId;
}
MagBalls.prototype.findPotentialEdges = function(nodeId) {
var variances = {};
for (var otherNodeId in this.nodes) {
// can't self connect
if (otherNodeId == nodeId) {
continue;
}
// can't doubly connect
if (this.areConnected(otherNodeId, nodeId)) {
continue;
}
// Check distance to attempt
var distance = this.getNodeDistance(nodeId, otherNodeId);
var variance = this.getVariance(distance);
if (Math.abs(variance) > 0.25) {
continue;
}
variances[otherNodeId] = variance;
}
return variances;
}
MagBalls.prototype.grabBall = function(position, maxDist) {
var selected = this.findNearestNode(position, maxDist);
if (!selected) {
selected = this.createNode({ position: position });
}
if (selected) {
this.stable = true;
this.breakEdges(selected);
this.selectedNodes[selected] = true;
}
return selected;
}
MagBalls.prototype.releaseBall = function(releasedBall) {
delete this.selectedNodes[releasedBall];
logDebug("Released ball: " + releasedBall);
this.stable = false;
var releasePosition = this.getNodePosition(releasedBall);
// iterate through the other balls and ensure we don't intersect with
// any of them. If we do, just delete this ball and return.
// FIXME (play a pop sound)
for (var nodeId in this.nodes) {
if (nodeId == releasedBall) {
continue;
}
var distance = this.getNodeDistance(releasedBall, nodeId);
if (distance < BALL_SIZE) {
this.destroyNode(releasedBall);
return;
}
}
var targets = this.findPotentialEdges(releasedBall);
if (!targets || !Object.keys(targets).length) {
// this.destroyNode(releasedBall);
}
for (var otherBallId in targets) {
this.createEdge(otherBallId, releasedBall);
}
this.validate();
}
MagBalls.prototype.getVariance = function(distance) {
// FIXME different balls or edges might have different ideas of variance...
// let something else handle this
var offset = (BALL_DISTANCE - distance);
var variance = offset / BALL_DISTANCE
return variance;
}
// remove unconnected balls
MagBalls.prototype.clean = function() {
// do nothing unless there are at least 2 balls and one edge
if (Object.keys(this.nodes).length < 2 || !Object.keys(this.edges).length) {
return;
}
var disconnectedNodes = {};
for (var nodeId in this.nodes) {
if (!Object.keys(this.nodes[nodeId]).length) {
disconnectedNodes[nodeId] = true;
}
}
for (var nodeId in disconnectedNodes) {
this.destroyNode(nodeId);
}
}
// remove all balls
MagBalls.prototype.clear = function() {
if (DEBUG_MAGSTICKS) {
this.deleteAll();
var ids = Entities.findEntities(MyAvatar.position, 50);
var result = [];
ids.forEach(function(id) {
var properties = Entities.getEntityProperties(id);
if (properties.name == BALL_NAME || properties.name == EDGE_NAME) {
Entities.deleteEntity(id);
}
}, this);
}
}
MagBalls.prototype.destroyEdge = function(edgeId) {
Graph.prototype.destroyEdge.call(this, edgeId);
delete this.edgeObjects[edgeId];
}
MagBalls.prototype.destroyNode = function(nodeId) {
Graph.prototype.destroyNode.call(this, nodeId);
}
// Scan the entity tree and load all the objects in range
MagBalls.prototype.refresh = function() {
var ids = Entities.findEntities(MyAvatar.position, 50);
for (var i in ids) {
var id = ids[i];
var properties = Entities.getEntityProperties(id);
if (properties.name == BALL_NAME) {
this.nodes[id] = {};
}
}
var deleteEdges = [];
for (var i in ids) {
var id = ids[i];
var properties = Entities.getEntityProperties(id);
if (properties.name == EDGE_NAME) {
var edgeId = id;
this.edges[edgeId] = {};
var magBallData = getMagBallsData(id);
if (!magBallData.start || !magBallData.end) {
logWarn("Edge information is missing for " + id);
continue;
}
if (!this.nodes[magBallData.start] || !this.nodes[magBallData.end]) {
logWarn("Edge " + id + " refers to unknown nodes: " + JSON.stringify(magBallData));
Entities.editEntity(id, { color: COLORS.RED });
deleteEdges.push(id);
continue;
}
this.nodes[magBallData.start][edgeId] = true;
this.nodes[magBallData.end][edgeId] = true;
this.edges[edgeId][magBallData.start] = true;
this.edges[edgeId][magBallData.end] = true;
this.edgeObjects[id] = new EdgeSpring(id, this);
}
}
if (deleteEdges.length) {
Script.setTimeout(function() {
for (var i in deleteEdges) {
var edgeId = deleteEdges[i];
logDebug("deleting invalid edge " + edgeId);
Entities.deleteEntity(edgeId);
}
}, 1000);
}
var edgeCount = Object.keys(this.edges).length;
var nodeCount = Object.keys(this.nodes).length;
logDebug("Found " + nodeCount + " nodes and " + edgeCount + " edges ");
this.validate();
}
MagBalls.prototype.findEdgeParams = function(startBall, endBall) {
var startBallPos = this.getNodePosition(startBall);
var endBallPos = this.getNodePosition(endBall);
var vector = Vec3.subtract(endBallPos, startBallPos);
return {
position: startBallPos,
linePoints: [ ZERO_VECTOR, vector ]
};
}
MagBalls.prototype.fixupEdge = function(edgeId) {
var ballsInEdge = Object.keys(this.edges[edgeId]);
Entities.editEntity(edgeId, this.findEdgeParams(ballsInEdge[0], ballsInEdge[1]));
}

View file

@ -0,0 +1,25 @@
//
// Created by Bradley Austin Davis on 2015/08/25
// Copyright 2015 High Fidelity, Inc.
//
// 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("constants.js");
Script.include("utils.js");
Script.include("magBalls.js");
Script.include("ballController.js");
var magBalls = new MagBalls();
// Clear any previous balls
// magBalls.clear();
MenuController = function(side) {
HandController.call(this, side);
}
// FIXME resolve some of the issues with dual controllers before allowing both controllers active
var handControllers = [new BallController(LEFT_CONTROLLER, magBalls)]; //, new HandController(RIGHT) ];

View file

@ -0,0 +1,66 @@
Script.include("handController.js");
MenuController = function(side, magBalls) {
HandController.call(this, side);
}
MenuController.prototype = Object.create( HandController.prototype );
MenuController.prototype.onUpdate = function(deltaTime) {
HandController.prototype.onUpdate.call(this, deltaTime);
if (!this.selected) {
// Find the highlight target and set it.
var target = this.magBalls.findNearestNode(this.tipPosition, BALL_SELECTION_RADIUS);
this.highlighter.highlight(target);
return;
}
this.highlighter.highlight(null);
Entities.editEntity(this.selected, { position: this.tipPosition });
var targetBalls = this.magBalls.findPotentialEdges(this.selected);
for (var ballId in targetBalls) {
if (!this.ghostEdges[ballId]) {
// create the ovleray
this.ghostEdges[ballId] = Overlays.addOverlay("line3d", {
start: this.magBalls.getNodePosition(ballId),
end: this.tipPosition,
color: COLORS.RED,
alpha: 1,
lineWidth: 5,
visible: true,
});
} else {
Overlays.editOverlay(this.ghostEdges[ballId], {
end: this.tipPosition,
});
}
}
for (var ballId in this.ghostEdges) {
if (!targetBalls[ballId]) {
Overlays.deleteOverlay(this.ghostEdges[ballId]);
delete this.ghostEdges[ballId];
}
}
}
MenuController.prototype.onClick = function() {
this.selected = this.magBalls.grabBall(this.tipPosition, BALL_SELECTION_RADIUS);
this.highlighter.highlight(null);
}
MenuController.prototype.onRelease = function() {
this.clearGhostEdges();
this.magBalls.releaseBall(this.selected);
this.selected = null;
}
MenuController.prototype.clearGhostEdges = function() {
for(var ballId in this.ghostEdges) {
Overlays.deleteOverlay(this.ghostEdges[ballId]);
delete this.ghostEdges[ballId];
}
}
MenuController.prototype.onCleanup = function() {
HandController.prototype.onCleanup.call(this);
this.clearGhostEdges();
}

View file

@ -0,0 +1,106 @@
//
// Created by Bradley Austin Davis on 2015/08/29
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
vec3toStr = function (v, digits) {
if (!digits) { digits = 3; }
return "{ " + v.x.toFixed(digits) + ", " + v.y.toFixed(digits) + ", " + v.z.toFixed(digits)+ " }";
}
scaleLine = function (start, end, scale) {
var v = Vec3.subtract(end, start);
var length = Vec3.length(v);
v = Vec3.multiply(scale, v);
return Vec3.sum(start, v);
}
findAction = function(name) {
var actions = Controller.getAllActions();
for (var i = 0; i < actions.length; i++) {
if (actions[i].actionName == name) {
return i;
}
}
return 0;
}
addLine = function(origin, vector, color) {
if (!color) {
color = COLORS.WHITE
}
return Entities.addEntity(mergeObjects(LINE_PROTOTYPE, {
position: origin,
linePoints: [
ZERO_VECTOR,
vector,
],
color: color
}));
}
// FIXME fetch from a subkey of user data to support non-destructive modifications
setEntityUserData = function(id, data) {
var json = JSON.stringify(data)
Entities.editEntity(id, { userData: json });
}
// FIXME do non-destructive modification of the existing user data
getEntityUserData = function(id) {
var results = null;
var properties = Entities.getEntityProperties(id);
if (properties.userData) {
results = JSON.parse(properties.userData);
}
return results ? results : {};
}
// Non-destructively modify the user data of an entity.
setEntityCustomData = function(customKey, id, data) {
var userData = getEntityUserData(id);
userData[customKey] = data;
setEntityUserData(id, userData);
}
getEntityCustomData = function(customKey, id, defaultValue) {
var userData = getEntityUserData(id);
return userData[customKey] ? userData[customKey] : defaultValue;
}
getMagBallsData = function(id) {
return getEntityCustomData(CUSTOM_DATA_NAME, id, {});
}
setMagBallsData = function(id, value) {
setEntityCustomData(CUSTOM_DATA_NAME, id, value);
}
mergeObjects = function(proto, custom) {
var result = {};
for (var attrname in proto) {
result[attrname] = proto[attrname];
}
for (var attrname in custom) {
result[attrname] = custom[attrname];
}
return result;
}
logWarn = function(str) {
print(str);
}
logError = function(str) {
print(str);
}
logInfo = function(str) {
print(str);
}
logDebug = function(str) {
print(str);
}

View file

@ -1,10 +1,206 @@
var controlHeld = false;
var shiftHeld = false;
Script.include([
"libraries/toolBars.js",
]);
var isActive = false;
var toolIconUrl = "http://headache.hungry.com/~seth/hifi/";
var toolHeight = 50;
var toolWidth = 50;
var addingVoxels = false;
var deletingVoxels = false;
offAlpha = 0.5;
onAlpha = 0.9;
function floorVector(v) {
return {x: Math.floor(v.x), y: Math.floor(v.y), z: Math.floor(v.z)};
}
function vectorToString(v){
return "{" + v.x + ", " + v.x + ", " + v.x + "}";
}
var toolBar = (function () {
var that = {},
toolBar,
activeButton,
addVoxelButton,
deleteVoxelButton,
addTerrainButton;
function initialize() {
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.voxel.toolbar", function (windowDimensions, toolbar) {
return {
x: windowDimensions.x - 8*2 - toolbar.width * 2,
y: (windowDimensions.y - toolbar.height) / 2
};
});
activeButton = toolBar.addTool({
imageURL: "http://s3.amazonaws.com/hifi-public/images/tools/polyvox.svg",
width: toolWidth,
height: toolHeight,
alpha: onAlpha,
visible: true,
});
addVoxelButton = toolBar.addTool({
imageURL: toolIconUrl + "voxel-add.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: toolWidth,
height: toolHeight,
alpha: offAlpha,
visible: false
});
deleteVoxelButton = toolBar.addTool({
imageURL: toolIconUrl + "voxel-delete.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: toolWidth,
height: toolHeight,
alpha: offAlpha,
visible: false
});
addTerrainButton = toolBar.addTool({
imageURL: toolIconUrl + "voxel-terrain.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: toolWidth,
height: toolHeight,
alpha: onAlpha,
visible: false
});
that.setActive(false);
}
that.setActive = function(active) {
if (active != isActive) {
isActive = active;
that.showTools(isActive);
}
toolBar.selectTool(activeButton, isActive);
};
// Sets visibility of tool buttons, excluding the power button
that.showTools = function(doShow) {
toolBar.showTool(addVoxelButton, doShow);
toolBar.showTool(deleteVoxelButton, doShow);
toolBar.showTool(addTerrainButton, doShow);
};
that.mousePressEvent = function (event) {
var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
if (activeButton === toolBar.clicked(clickedOverlay)) {
that.setActive(!isActive);
return true;
}
if (addVoxelButton === toolBar.clicked(clickedOverlay)) {
if (addingVoxels) {
addingVoxels = false;
deletingVoxels = false;
toolBar.setAlpha(offAlpha, addVoxelButton);
toolBar.setAlpha(offAlpha, deleteVoxelButton);
toolBar.selectTool(addVoxelButton, false);
toolBar.selectTool(deleteVoxelButton, false);
} else {
addingVoxels = true;
deletingVoxels = false;
toolBar.setAlpha(onAlpha, addVoxelButton);
toolBar.setAlpha(offAlpha, deleteVoxelButton);
}
return true;
}
if (deleteVoxelButton === toolBar.clicked(clickedOverlay)) {
if (deletingVoxels) {
deletingVoxels = false;
addingVoxels = false;
toolBar.setAlpha(offAlpha, addVoxelButton);
toolBar.setAlpha(offAlpha, deleteVoxelButton);
} else {
deletingVoxels = true;
addingVoxels = false;
toolBar.setAlpha(offAlpha, addVoxelButton);
toolBar.setAlpha(onAlpha, deleteVoxelButton);
}
return true;
}
if (addTerrainButton === toolBar.clicked(clickedOverlay)) {
addTerrainBlock();
return true;
}
}
Window.domainChanged.connect(function() {
that.setActive(false);
});
that.cleanup = function () {
toolBar.cleanup();
// Overlays.deleteOverlay(activeButton);
};
initialize();
return that;
}());
function addTerrainBlock() {
var myPosDiv16 = Vec3.multiply(Vec3.sum(MyAvatar.position, {x:8, x:8, z:8}), 1.0 / 16.0);
var myPosDiv16Floored = floorVector(myPosDiv16);
var baseLocation = Vec3.multiply(myPosDiv16Floored, 16.0);
if (baseLocation.y + 8 > MyAvatar.position.y) {
baseLocation.y -= 16;
}
print("myPosDiv16 is " + vectorToString(myPosDiv16));
print("MyPosDiv16Floored is " + vectorToString(myPosDiv16Floored));
print("baseLocation is " + vectorToString(baseLocation));
alreadyThere = Entities.findEntities(baseLocation, 1.0);
for (var i = 0; i < alreadyThere.length; i++) {
var id = alreadyThere[i];
var properties = Entities.getEntityProperties(id);
if (properties.name == "terrain") {
print("already terrain there");
return;
}
}
var polyVoxId = Entities.addEntity({
type: "PolyVox",
name: "terrain",
position: baseLocation,
dimensions: { x: 16, y: 16, z: 16 },
voxelVolumeSize: {x:16, y:16, z:16},
voxelSurfaceStyle: 2
});
Entities.setAllVoxels(polyVoxId, 255);
for (var y = 8; y < 16; y++) {
for (var x = 0; x < 16; x++) {
for (var z = 0; z < 16; z++) {
Entities.setVoxel(polyVoxId, {x: x, y: y, z: z}, 0);
}
}
}
return true;
}
function attemptVoxelChange(pickRayDir, intersection) {
var properties = Entities.getEntityProperties(intersection.entityID);
@ -12,25 +208,30 @@ function attemptVoxelChange(pickRayDir, intersection) {
return false;
}
if (addingVoxels == false && deletingVoxels == false) {
return false;
}
var voxelPosition = Entities.worldCoordsToVoxelCoords(intersection.entityID, intersection.intersection);
voxelPosition = Vec3.subtract(voxelPosition, {x: 0.5, y: 0.5, z: 0.5});
var pickRayDirInVoxelSpace = Entities.localCoordsToVoxelCoords(intersection.entityID, pickRayDir);
pickRayDirInVoxelSpace = Vec3.normalize(pickRayDirInVoxelSpace);
var doAdd = addingVoxels;
var doDelete = deletingVoxels;
if (controlHeld) {
// hold control to erase a voxel
doAdd = deletingVoxels;
doDelete = addingVoxels;
}
if (doDelete) {
var toErasePosition = Vec3.sum(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1));
return Entities.setVoxel(intersection.entityID, floorVector(toErasePosition), 0);
} else if (shiftHeld) {
// hold shift to set all voxels to 255
return Entities.setAllVoxels(intersection.entityID, 255);
} else {
// no modifier key to add a voxel
}
if (doAdd) {
var toDrawPosition = Vec3.subtract(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1));
return Entities.setVoxel(intersection.entityID, floorVector(toDrawPosition), 255);
}
// Entities.setVoxelSphere(id, intersection.intersection, radius, 0)
}
function mousePressEvent(event) {
@ -38,6 +239,10 @@ function mousePressEvent(event) {
return;
}
if (toolBar.mousePressEvent(event)) {
return;
}
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
@ -76,6 +281,15 @@ function keyReleaseEvent(event) {
}
function cleanup() {
for (var i = 0; i < overlays.length; i++) {
Overlays.deleteOverlay(overlays[i]);
}
toolBar.cleanup();
}
Controller.mousePressEvent.connect(mousePressEvent);
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
Script.scriptEnding.connect(cleanup);

View file

@ -55,46 +55,50 @@ void IceServer::processDatagrams() {
_serverSocket.readDatagram(buffer.get(), packetSizeWithHeader,
sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer());
auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, sendingSockAddr);
PacketType::Value packetType = packet->getType();
if (packetType == PacketType::ICEServerHeartbeat) {
SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*packet);
// so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
peer->activateMatchingOrNewSymmetricSocket(sendingSockAddr);
} else if (packetType == PacketType::ICEServerQuery) {
QDataStream heartbeatStream(packet.get());
// this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer?
QUuid senderUUID;
heartbeatStream >> senderUUID;
// pull the public and private sock addrs for this peer
HifiSockAddr publicSocket, localSocket;
heartbeatStream >> publicSocket >> localSocket;
// check if this node also included a UUID that they would like to connect to
QUuid connectRequestID;
heartbeatStream >> connectRequestID;
// make sure that this packet at least looks like something we can read
if (packetSizeWithHeader >= Packet::localHeaderSize(PacketType::ICEServerHeartbeat)) {
SharedNetworkPeer matchingPeer = _activePeers.value(connectRequestID);
if (matchingPeer) {
auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, sendingSockAddr);
PacketType::Value packetType = packet->getType();
if (packetType == PacketType::ICEServerHeartbeat) {
SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*packet);
qDebug() << "Sending information for peer" << connectRequestID << "to peer" << senderUUID;
// so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
peer->activateMatchingOrNewSymmetricSocket(sendingSockAddr);
} else if (packetType == PacketType::ICEServerQuery) {
QDataStream heartbeatStream(packet.get());
// we have the peer they want to connect to - send them pack the information for that peer
sendPeerInformationPacket(*(matchingPeer.data()), &sendingSockAddr);
// we also need to send them to the active peer they are hoping to connect to
// create a dummy peer object we can pass to sendPeerInformationPacket
NetworkPeer dummyPeer(senderUUID, publicSocket, localSocket);
sendPeerInformationPacket(dummyPeer, matchingPeer->getActiveSocket());
} else {
qDebug() << "Peer" << senderUUID << "asked for" << connectRequestID << "but no matching peer found";
// this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer?
QUuid senderUUID;
heartbeatStream >> senderUUID;
// pull the public and private sock addrs for this peer
HifiSockAddr publicSocket, localSocket;
heartbeatStream >> publicSocket >> localSocket;
// check if this node also included a UUID that they would like to connect to
QUuid connectRequestID;
heartbeatStream >> connectRequestID;
SharedNetworkPeer matchingPeer = _activePeers.value(connectRequestID);
if (matchingPeer) {
qDebug() << "Sending information for peer" << connectRequestID << "to peer" << senderUUID;
// we have the peer they want to connect to - send them pack the information for that peer
sendPeerInformationPacket(*(matchingPeer.data()), &sendingSockAddr);
// we also need to send them to the active peer they are hoping to connect to
// create a dummy peer object we can pass to sendPeerInformationPacket
NetworkPeer dummyPeer(senderUUID, publicSocket, localSocket);
sendPeerInformationPacket(dummyPeer, matchingPeer->getActiveSocket());
} else {
qDebug() << "Peer" << senderUUID << "asked for" << connectRequestID << "but no matching peer found";
}
}
}
}

View file

@ -2804,7 +2804,8 @@ void Application::update(float deltaTime) {
_entities.getTree()->lockForWrite();
_entitySimulation.lock();
_physicsEngine.changeObjects(_entitySimulation.getObjectsToChange());
VectorOfMotionStates stillNeedChange = _physicsEngine.changeObjects(_entitySimulation.getObjectsToChange());
_entitySimulation.setObjectsToChange(stillNeedChange);
_entitySimulation.unlock();
_entities.getTree()->unlock();
@ -5047,5 +5048,7 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index)
void Application::crashApplication() {
QObject* object = nullptr;
bool value = object->isWindowType();
Q_UNUSED(value);
qCDebug(interfaceapp) << "Intentionally crashed Interface";
}

View file

@ -222,7 +222,7 @@ Menu::Menu() {
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
qApp, SLOT(packageModel()));
MenuWrapper* displayMenu = addMenu(DisplayPlugin::MENU_PATH());
addMenu(DisplayPlugin::MENU_PATH());
{
MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu);
QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu);

View file

@ -28,15 +28,20 @@ AvatarMotionState::~AvatarMotionState() {
}
// virtual
uint32_t AvatarMotionState::getAndClearIncomingDirtyFlags() {
uint32_t AvatarMotionState::getIncomingDirtyFlags() {
uint32_t dirtyFlags = 0;
if (_body && _avatar) {
dirtyFlags = _dirtyFlags;
_dirtyFlags = 0;
}
return dirtyFlags;
}
void AvatarMotionState::clearIncomingDirtyFlags() {
if (_body && _avatar) {
_dirtyFlags = 0;
}
}
MotionType AvatarMotionState::computeObjectMotionType() const {
// TODO?: support non-DYNAMIC motion for avatars? (e.g. when sitting)
return MOTION_TYPE_DYNAMIC;

View file

@ -25,7 +25,8 @@ public:
virtual MotionType getMotionType() const { return _motionType; }
virtual uint32_t getAndClearIncomingDirtyFlags();
virtual uint32_t getIncomingDirtyFlags();
virtual void clearIncomingDirtyFlags();
virtual MotionType computeObjectMotionType() const;
@ -65,6 +66,7 @@ public:
friend class AvatarManager;
protected:
virtual bool isReadyToComputeShape() { return true; }
virtual btCollisionShape* computeNewShape();
virtual void clearObjectBackPointer();
Avatar* _avatar;

View file

@ -844,7 +844,6 @@ void MyAvatar::sendKillAvatar() {
DependencyManager::get<NodeList>()->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::AvatarMixer);
}
static int counter = 0;
void MyAvatar::updateLookAtTargetAvatar() {
//
// Look at the avatar whose eyes are closest to the ray in direction of my avatar's head

View file

@ -51,14 +51,14 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
int rightElbowJointIndex = rightHandJointIndex >= 0 ? geometry.joints.at(rightHandJointIndex).parentIndex : -1;
int rightShoulderJointIndex = rightElbowJointIndex >= 0 ? geometry.joints.at(rightElbowJointIndex).parentIndex : -1;
_boundingRadius = _rig->initJointStates(states, parentTransform,
rootJointIndex,
leftHandJointIndex,
leftElbowJointIndex,
leftShoulderJointIndex,
rightHandJointIndex,
rightElbowJointIndex,
rightShoulderJointIndex);
_rig->initJointStates(states, parentTransform,
rootJointIndex,
leftHandJointIndex,
leftElbowJointIndex,
leftShoulderJointIndex,
rightHandJointIndex,
rightElbowJointIndex,
rightShoulderJointIndex);
// Determine the default eye position for avatar scale = 1.0
int headJointIndex = _geometry->getFBXGeometry().headJointIndex;
@ -533,7 +533,6 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition();
_boundingCapsuleLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition;
_boundingRadius = 0.5f * glm::length(diagonal);
}
void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) {

View file

@ -45,6 +45,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid
auto dockWidget = new QDockWidget(title, toolWindow);
dockWidget->setFeatures(QDockWidget::DockWidgetMovable);
connect(dockWidget, &QDockWidget::visibilityChanged, this, &WebWindowClass::visibilityChanged);
_webView = new QWebView(dockWidget);
addEventBridgeToWindowObject();

View file

@ -60,6 +60,7 @@ public slots:
void setTitle(const QString& title);
signals:
void visibilityChanged(bool visible); // Tool window
void moved(glm::vec2 position);
void resized(QSizeF size);
void closed();

View file

@ -206,8 +206,6 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
updateTooltips();
auto deviceSize = qApp->getDeviceSize();
//Handle fading and deactivation/activation of UI
gpu::Batch batch;

View file

@ -185,14 +185,14 @@ void Rig::deleteAnimations() {
_animationHandles.clear();
}
float Rig::initJointStates(QVector<JointState> states, glm::mat4 parentTransform,
int rootJointIndex,
int leftHandJointIndex,
int leftElbowJointIndex,
int leftShoulderJointIndex,
int rightHandJointIndex,
int rightElbowJointIndex,
int rightShoulderJointIndex) {
void Rig::initJointStates(QVector<JointState> states, glm::mat4 parentTransform,
int rootJointIndex,
int leftHandJointIndex,
int leftElbowJointIndex,
int leftShoulderJointIndex,
int rightHandJointIndex,
int rightElbowJointIndex,
int rightShoulderJointIndex) {
_jointStates = states;
_rootJointIndex = rootJointIndex;
@ -206,19 +206,12 @@ float Rig::initJointStates(QVector<JointState> states, glm::mat4 parentTransform
initJointTransforms(parentTransform);
int numStates = _jointStates.size();
float radius = 0.0f;
for (int i = 0; i < numStates; ++i) {
float distance = glm::length(_jointStates[i].getPosition());
if (distance > radius) {
radius = distance;
}
_jointStates[i].buildConstraint();
}
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].slaveVisibleTransform();
}
return radius;
}
// We could build and cache a dictionary, too....

View file

@ -92,14 +92,14 @@ public:
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList(), bool startAutomatically = false);
float initJointStates(QVector<JointState> states, glm::mat4 parentTransform,
int rootJointIndex,
int leftHandJointIndex,
int leftElbowJointIndex,
int leftShoulderJointIndex,
int rightHandJointIndex,
int rightElbowJointIndex,
int rightShoulderJointIndex);
void initJointStates(QVector<JointState> states, glm::mat4 parentTransform,
int rootJointIndex,
int leftHandJointIndex,
int leftElbowJointIndex,
int leftShoulderJointIndex,
int rightHandJointIndex,
int rightElbowJointIndex,
int rightShoulderJointIndex);
bool jointStatesEmpty() { return _jointStates.isEmpty(); };
int getJointStateCount() const { return _jointStates.size(); }
int indexOfJoint(const QString& jointName) ;

View file

@ -80,33 +80,33 @@ void OpenGLDisplayPlugin::deactivate() {
// Pass input events on to the application
bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
switch (event->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
case QEvent::Wheel:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
case QEvent::Wheel:
case QEvent::TouchBegin:
case QEvent::TouchEnd:
case QEvent::TouchUpdate:
case QEvent::TouchBegin:
case QEvent::TouchEnd:
case QEvent::TouchUpdate:
case QEvent::FocusIn:
case QEvent::FocusOut:
case QEvent::FocusIn:
case QEvent::FocusOut:
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::ShortcutOverride:
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::ShortcutOverride:
case QEvent::DragEnter:
case QEvent::Drop:
case QEvent::DragEnter:
case QEvent::Drop:
case QEvent::Resize:
if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) {
return true;
}
break;
default:
break;
case QEvent::Resize:
if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) {
return true;
}
break;
default:
break;
}
return false;
}

View file

@ -51,20 +51,22 @@ protected:
private:
static const QString NAME;
ovrHmd _hmd;
float _ipd{ OVR_DEFAULT_IPD };
unsigned int _frameIndex;
ovrEyeRenderDesc _eyeRenderDescs[2];
ovrPosef _eyePoses[2];
ovrVector3f _eyeOffsets[2];
ovrFovPort _eyeFovs[2];
mat4 _eyeProjections[3];
mat4 _compositeEyeProjections[2];
uvec2 _desiredFramebufferSize;
ovrTrackingState _trackingState;
#if (OVR_MAJOR_VERSION >= 6)
ovrHmd _hmd;
float _ipd{ OVR_DEFAULT_IPD };
unsigned int _frameIndex;
ovrEyeRenderDesc _eyeRenderDescs[2];
ovrVector3f _eyeOffsets[2];
ovrFovPort _eyeFovs[2];
ovrLayerEyeFov& getSceneLayer();
ovrHmdDesc _hmdDesc;
SwapFboPtr _sceneFbo;

View file

@ -34,7 +34,7 @@ const QString & OculusLegacyDisplayPlugin::getName() const {
return NAME;
}
OculusLegacyDisplayPlugin::OculusLegacyDisplayPlugin() : _ipd(OVR_DEFAULT_IPD) {
OculusLegacyDisplayPlugin::OculusLegacyDisplayPlugin() {
}
uvec2 OculusLegacyDisplayPlugin::getRecommendedRenderSize() const {

View file

@ -44,7 +44,6 @@ protected:
private:
static const QString NAME;
float _ipd{ OVR_DEFAULT_IPD };
ovrHmd _hmd;
unsigned int _frameIndex;
ovrTrackingState _trackingState;

View file

@ -78,7 +78,7 @@ void StereoDisplayPlugin::activate() {
}
void StereoDisplayPlugin::updateScreen() {
for (uint32_t i = 0; i < _screenActions.size(); ++i) {
for (int i = 0; i < (int) _screenActions.size(); ++i) {
if (_screenActions[i]->isChecked()) {
CONTAINER->setFullscreen(qApp->screens().at(i));
break;

File diff suppressed because it is too large Load diff

View file

@ -12,13 +12,18 @@
#ifndef hifi_RenderablePolyVoxEntityItem_h
#define hifi_RenderablePolyVoxEntityItem_h
#include <QSemaphore>
#include <atomic>
#include <PolyVoxCore/SimpleVolume.h>
#include <PolyVoxCore/Raycast.h>
#include <TextureCache.h>
#include "PolyVoxEntityItem.h"
#include "RenderableDebugableEntityItem.h"
#include "RenderableEntityItem.h"
#include "gpu/Context.h"
class PolyVoxPayload {
public:
@ -56,19 +61,16 @@ public:
virtual uint8_t getVoxel(int x, int y, int z);
virtual bool setVoxel(int x, int y, int z, uint8_t toValue);
bool updateOnCount(int x, int y, int z, uint8_t new_value);
void render(RenderArgs* args);
virtual bool supportsDetailedRayIntersection() const { return true; }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject, bool precisionPicking) const;
void getModel();
virtual void setVoxelData(QByteArray voxelData);
virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize);
virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle);
glm::vec3 getSurfacePositionAdjustment() const;
glm::mat4 voxelToWorldMatrix() const;
glm::mat4 worldToVoxelMatrix() const;
@ -103,33 +105,48 @@ public:
std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges);
protected:
virtual void updateVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle);
private:
// The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions
// may not match _voxelVolumeSize.
bool setVoxelInternal(int x, int y, int z, uint8_t toValue);
void compressVolumeData();
void decompressVolumeData();
void clearEdges();
PolyVox::SimpleVolume<uint8_t>* _volData = nullptr;
model::Geometry _modelGeometry;
bool _needsModelReload = true;
QVector<QVector<glm::vec3>> _points; // XXX
model::MeshPointer _mesh;
bool _meshDirty; // does collision-shape need to be recomputed?
mutable QReadWriteLock _meshLock{QReadWriteLock::Recursive};
NetworkTexturePointer _xTexture;
NetworkTexturePointer _yTexture;
NetworkTexturePointer _zTexture;
int _onCount = 0; // how many non-zero voxels are in _volData
const int MATERIAL_GPU_SLOT = 3;
render::ItemID _myItem;
static gpu::PipelinePointer _pipeline;
ShapeInfo _shapeInfo;
mutable QReadWriteLock _shapeInfoLock;
PolyVox::SimpleVolume<uint8_t>* _volData = nullptr;
mutable QReadWriteLock _volDataLock{QReadWriteLock::Recursive}; // lock for _volData
bool _volDataDirty = false; // does getMesh need to be called?
int _onCount; // how many non-zero voxels are in _volData
bool inUserBounds(const PolyVox::SimpleVolume<uint8_t>* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle,
int x, int y, int z);
uint8_t getVoxelInternal(int x, int y, int z);
bool setVoxelInternal(int x, int y, int z, uint8_t toValue);
bool updateOnCount(int x, int y, int z, uint8_t toValue);
PolyVox::RaycastResult doRayCast(glm::vec4 originInVoxel, glm::vec4 farInVoxel, glm::vec4& result) const;
// these are run off the main thread
void decompressVolumeData();
void decompressVolumeDataAsync();
void compressVolumeDataAndSendEditPacket();
void compressVolumeDataAndSendEditPacketAsync();
void getMesh();
void getMeshAsync();
void computeShapeInfoWorker();
void computeShapeInfoWorkerAsync();
QSemaphore _threadRunning{1};
};

View file

@ -431,23 +431,10 @@ bool EntityScriptingInterface::setVoxels(QUuid entityID,
return false;
}
auto now = usecTimestampNow();
auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(entity);
_entityTree->lockForWrite();
bool result = actor(*polyVoxEntity);
entity->setLastEdited(now);
entity->setLastBroadcast(now);
_entityTree->unlock();
_entityTree->lockForRead();
EntityItemProperties properties = entity->getProperties();
_entityTree->unlock();
properties.setVoxelDataDirty();
properties.setLastEdited(now);
queueEntityMessage(PacketType::EntityEdit, entityID, properties);
return result;
}

View file

@ -25,7 +25,7 @@ const glm::vec3 PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE = glm::vec3(32, 32,
const float PolyVoxEntityItem::MAX_VOXEL_DIMENSION = 128.0f;
const QByteArray PolyVoxEntityItem::DEFAULT_VOXEL_DATA(PolyVoxEntityItem::makeEmptyVoxelData());
const PolyVoxEntityItem::PolyVoxSurfaceStyle PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE =
PolyVoxEntityItem::SURFACE_MARCHING_CUBES;
PolyVoxEntityItem::SURFACE_EDGED_CUBIC;
const QString PolyVoxEntityItem::DEFAULT_X_TEXTURE_URL = QString("");
const QString PolyVoxEntityItem::DEFAULT_Y_TEXTURE_URL = QString("");
const QString PolyVoxEntityItem::DEFAULT_Z_TEXTURE_URL = QString("");
@ -52,6 +52,7 @@ PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID, const Ent
EntityItem(entityItemID),
_voxelVolumeSize(PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE),
_voxelData(PolyVoxEntityItem::DEFAULT_VOXEL_DATA),
_voxelDataDirty(true),
_voxelSurfaceStyle(PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE),
_xTextureURL(PolyVoxEntityItem::DEFAULT_X_TEXTURE_URL),
_yTextureURL(PolyVoxEntityItem::DEFAULT_Y_TEXTURE_URL),
@ -66,7 +67,7 @@ void PolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) {
assert((int)_voxelVolumeSize.y == _voxelVolumeSize.y);
assert((int)_voxelVolumeSize.z == _voxelVolumeSize.z);
_voxelVolumeSize = voxelVolumeSize;
_voxelVolumeSize = glm::vec3(roundf(voxelVolumeSize.x), roundf(voxelVolumeSize.y), roundf(voxelVolumeSize.z));
if (_voxelVolumeSize.x < 1) {
qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping x of" << _voxelVolumeSize.x << "to 1";
_voxelVolumeSize.x = 1;
@ -185,9 +186,16 @@ void PolyVoxEntityItem::debugDump() const {
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
}
void PolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) {
if (voxelSurfaceStyle == _voxelSurfaceStyle) {
return;
}
updateVoxelSurfaceStyle(voxelSurfaceStyle);
void PolyVoxEntityItem::setVoxelData(QByteArray voxelData) {
_voxelDataLock.lockForWrite();
_voxelData = voxelData;
_voxelDataDirty = true;
_voxelDataLock.unlock();
}
const QByteArray PolyVoxEntityItem::getVoxelData() const {
_voxelDataLock.lockForRead();
auto result = _voxelData;
_voxelDataLock.unlock();
return result;
}

View file

@ -52,8 +52,8 @@ class PolyVoxEntityItem : public EntityItem {
virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize);
virtual const glm::vec3& getVoxelVolumeSize() const { return _voxelVolumeSize; }
virtual void setVoxelData(QByteArray voxelData) { _voxelData = voxelData; }
virtual const QByteArray& getVoxelData() const { return _voxelData; }
virtual void setVoxelData(QByteArray voxelData);
virtual const QByteArray getVoxelData() const;
enum PolyVoxSurfaceStyle {
SURFACE_MARCHING_CUBES,
@ -62,7 +62,7 @@ class PolyVoxEntityItem : public EntityItem {
SURFACE_EDGED_MARCHING_CUBES
};
void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle);
virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { _voxelSurfaceStyle = voxelSurfaceStyle; }
// this other version of setVoxelSurfaceStyle is needed for SET_ENTITY_PROPERTY_FROM_PROPERTIES
void setVoxelSurfaceStyle(uint16_t voxelSurfaceStyle) { setVoxelSurfaceStyle((PolyVoxSurfaceStyle) voxelSurfaceStyle); }
virtual PolyVoxSurfaceStyle getVoxelSurfaceStyle() const { return _voxelSurfaceStyle; }
@ -104,12 +104,12 @@ class PolyVoxEntityItem : public EntityItem {
virtual const QString& getZTextureURL() const { return _zTextureURL; }
protected:
virtual void updateVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) {
_voxelSurfaceStyle = voxelSurfaceStyle;
}
glm::vec3 _voxelVolumeSize; // this is always 3 bytes
mutable QReadWriteLock _voxelDataLock;
QByteArray _voxelData;
bool _voxelDataDirty;
PolyVoxSurfaceStyle _voxelSurfaceStyle;
QString _xTextureURL;

View file

@ -99,7 +99,7 @@ void EntityMotionState::updateServerPhysicsVariables() {
}
// virtual
void EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) {
bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) {
assert(entityTreeIsLocked());
updateServerPhysicsVariables();
ObjectMotionState::handleEasyChanges(flags, engine);
@ -131,13 +131,15 @@ void EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine)
if ((flags & EntityItem::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) {
_body->activate();
}
return true;
}
// virtual
void EntityMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) {
bool EntityMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) {
updateServerPhysicsVariables();
ObjectMotionState::handleHardAndEasyChanges(flags, engine);
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
}
void EntityMotionState::clearObjectBackPointer() {
@ -222,6 +224,15 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
#endif
}
// virtual and protected
bool EntityMotionState::isReadyToComputeShape() {
if (_entity) {
return _entity->isReadyToComputeShape();
}
return false;
}
// virtual and protected
btCollisionShape* EntityMotionState::computeNewShape() {
if (_entity) {
@ -493,12 +504,11 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
_lastStep = step;
}
uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() {
uint32_t EntityMotionState::getIncomingDirtyFlags() {
assert(entityTreeIsLocked());
uint32_t dirtyFlags = 0;
if (_body && _entity) {
dirtyFlags = _entity->getDirtyFlags();
_entity->clearDirtyFlags();
// we add DIRTY_MOTION_TYPE if the body's motion type disagrees with entity velocity settings
int bodyFlags = _body->getCollisionFlags();
bool isMoving = _entity->isMoving();
@ -510,6 +520,13 @@ uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() {
return dirtyFlags;
}
void EntityMotionState::clearIncomingDirtyFlags() {
assert(entityTreeIsLocked());
if (_body && _entity) {
_entity->clearDirtyFlags();
}
}
// virtual
quint8 EntityMotionState::getSimulationPriority() const {
if (_entity) {

View file

@ -29,8 +29,8 @@ public:
virtual ~EntityMotionState();
void updateServerPhysicsVariables();
virtual void handleEasyChanges(uint32_t flags, PhysicsEngine* engine);
virtual void handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine);
virtual bool handleEasyChanges(uint32_t flags, PhysicsEngine* engine);
virtual bool handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine);
/// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem
virtual MotionType computeObjectMotionType() const;
@ -48,7 +48,8 @@ public:
bool shouldSendUpdate(uint32_t simulationStep, const QUuid& sessionID);
void sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step);
virtual uint32_t getAndClearIncomingDirtyFlags();
virtual uint32_t getIncomingDirtyFlags();
virtual void clearIncomingDirtyFlags();
void incrementAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount++; }
void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; }
@ -91,6 +92,7 @@ protected:
bool entityTreeIsLocked() const;
#endif
virtual bool isReadyToComputeShape();
virtual btCollisionShape* computeNewShape();
virtual void clearObjectBackPointer();
virtual void setMotionType(MotionType motionType);

View file

@ -125,7 +125,7 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) {
}
}
void ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) {
bool ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) {
if (flags & EntityItem::DIRTY_POSITION) {
btTransform worldTrans;
if (flags & EntityItem::DIRTY_ROTATION) {
@ -156,11 +156,16 @@ void ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine)
if (flags & EntityItem::DIRTY_MASS) {
updateBodyMassProperties();
}
return true;
}
void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) {
bool ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) {
if (flags & EntityItem::DIRTY_SHAPE) {
// make sure the new shape is valid
if (!isReadyToComputeShape()) {
return false;
}
btCollisionShape* newShape = computeNewShape();
if (!newShape) {
qCDebug(physics) << "Warning: failed to generate new shape!";
@ -172,7 +177,7 @@ void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine*
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
handleEasyChanges(flags, engine);
}
return;
return true;
}
}
getShapeManager()->releaseShape(_shape);
@ -192,6 +197,8 @@ void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine*
if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
engine->reinsertObject(this);
}
return true;
}
void ObjectMotionState::updateBodyMaterialProperties() {

View file

@ -71,8 +71,8 @@ public:
ObjectMotionState(btCollisionShape* shape);
~ObjectMotionState();
virtual void handleEasyChanges(uint32_t flags, PhysicsEngine* engine);
virtual void handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine);
virtual bool handleEasyChanges(uint32_t flags, PhysicsEngine* engine);
virtual bool handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine);
void updateBodyMaterialProperties();
void updateBodyVelocities();
@ -92,7 +92,8 @@ public:
glm::vec3 getBodyAngularVelocity() const;
virtual glm::vec3 getObjectLinearVelocityChange() const;
virtual uint32_t getAndClearIncomingDirtyFlags() = 0;
virtual uint32_t getIncomingDirtyFlags() = 0;
virtual void clearIncomingDirtyFlags() = 0;
virtual MotionType computeObjectMotionType() const = 0;
@ -132,6 +133,7 @@ public:
friend class PhysicsEngine;
protected:
virtual bool isReadyToComputeShape() = 0;
virtual btCollisionShape* computeNewShape() = 0;
void setMotionType(MotionType motionType);

View file

@ -173,6 +173,12 @@ VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToAdd() {
return _tempVector;
}
void PhysicalEntitySimulation::setObjectsToChange(VectorOfMotionStates& objectsToChange) {
for (auto object : objectsToChange) {
_pendingChanges.insert(static_cast<EntityMotionState*>(object));
}
}
VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToChange() {
_tempVector.clear();
for (auto stateItr : _pendingChanges) {

View file

@ -46,11 +46,14 @@ protected: // only called by EntitySimulation
public:
VectorOfMotionStates& getObjectsToDelete();
VectorOfMotionStates& getObjectsToAdd();
void setObjectsToChange(VectorOfMotionStates& objectsToChange);
VectorOfMotionStates& getObjectsToChange();
void handleOutgoingChanges(VectorOfMotionStates& motionStates, const QUuid& sessionID);
void handleCollisionEvents(CollisionEvents& collisionEvents);
EntityEditPacketSender* getPacketSender() { return _entityPacketSender; }
private:
// incoming changes
SetOfEntityMotionStates _pendingRemoves; // EntityMotionStates to be removed from PhysicsEngine (and deleted)

View file

@ -140,7 +140,7 @@ void PhysicsEngine::addObject(ObjectMotionState* motionState) {
int16_t group = motionState->computeCollisionGroup();
_dynamicsWorld->addRigidBody(body, group, getCollisionMask(group));
motionState->getAndClearIncomingDirtyFlags();
motionState->clearIncomingDirtyFlags();
}
void PhysicsEngine::removeObject(ObjectMotionState* object) {
@ -188,15 +188,25 @@ void PhysicsEngine::addObjects(VectorOfMotionStates& objects) {
}
}
void PhysicsEngine::changeObjects(VectorOfMotionStates& objects) {
VectorOfMotionStates PhysicsEngine::changeObjects(VectorOfMotionStates& objects) {
VectorOfMotionStates stillNeedChange;
for (auto object : objects) {
uint32_t flags = object->getAndClearIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS;
uint32_t flags = object->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS;
if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
object->handleHardAndEasyChanges(flags, this);
if (object->handleHardAndEasyChanges(flags, this)) {
object->clearIncomingDirtyFlags();
} else {
stillNeedChange.push_back(object);
}
} else if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
object->handleEasyChanges(flags, this);
if (object->handleEasyChanges(flags, this)) {
object->clearIncomingDirtyFlags();
} else {
stillNeedChange.push_back(object);
}
}
}
return stillNeedChange;
}
void PhysicsEngine::reinsertObject(ObjectMotionState* object) {

View file

@ -60,7 +60,7 @@ public:
void deleteObjects(VectorOfMotionStates& objects);
void deleteObjects(SetOfMotionStates& objects); // only called during teardown
void addObjects(VectorOfMotionStates& objects);
void changeObjects(VectorOfMotionStates& objects);
VectorOfMotionStates changeObjects(VectorOfMotionStates& objects);
void reinsertObject(ObjectMotionState* object);
void stepSimulation();

View file

@ -94,7 +94,6 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
if (numSubShapes == 1) {
shape = createConvexHull(info.getPoints()[0]);
} else {
assert(numSubShapes > 1);
auto compound = new btCompoundShape();
btTransform trans;
trans.setIdentity();

View file

@ -60,6 +60,8 @@ float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
Model::Model(RigPointer rig, QObject* parent) :
QObject(parent),
_translation(0.0f),
_rotation(),
_scale(1.0f, 1.0f, 1.0f),
_scaleToFit(false),
_scaleToFitDimensions(0.0f),
@ -196,6 +198,13 @@ void Model::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, Model:
AbstractViewStateInterface* Model::_viewState = NULL;
void Model::setTranslation(const glm::vec3& translation) {
_translation = translation;
}
void Model::setRotation(const glm::quat& rotation) {
_rotation = rotation;
}
void Model::setScale(const glm::vec3& scale) {
setScaleInternal(scale);
@ -434,14 +443,14 @@ void Model::initJointStates(QVector<JointState> states) {
int rightElbowJointIndex = rightHandJointIndex >= 0 ? geometry.joints.at(rightHandJointIndex).parentIndex : -1;
int rightShoulderJointIndex = rightElbowJointIndex >= 0 ? geometry.joints.at(rightElbowJointIndex).parentIndex : -1;
_boundingRadius = _rig->initJointStates(states, parentTransform,
rootJointIndex,
leftHandJointIndex,
leftElbowJointIndex,
leftShoulderJointIndex,
rightHandJointIndex,
rightElbowJointIndex,
rightShoulderJointIndex);
_rig->initJointStates(states, parentTransform,
rootJointIndex,
leftHandJointIndex,
leftElbowJointIndex,
leftShoulderJointIndex,
rightHandJointIndex,
rightElbowJointIndex,
rightShoulderJointIndex);
}
bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,

View file

@ -27,7 +27,6 @@
#include <gpu/Stream.h>
#include <gpu/Batch.h>
#include <gpu/Pipeline.h>
#include "PhysicsEntity.h"
#include <render/Scene.h>
#include <Transform.h>
@ -54,7 +53,7 @@ inline uint qHash(const std::shared_ptr<MeshPartPayload>& a, uint seed) {
}
/// A generic 3D model displaying geometry loaded from a URL.
class Model : public QObject, public PhysicsEntity {
class Model : public QObject {
Q_OBJECT
public:
@ -173,6 +172,12 @@ public:
/// Returns the extents of the model's mesh
Extents getMeshExtents() const;
void setTranslation(const glm::vec3& translation);
void setRotation(const glm::quat& rotation);
const glm::vec3& getTranslation() const { return _translation; }
const glm::quat& getRotation() const { return _rotation; }
void setScale(const glm::vec3& scale);
const glm::vec3& getScale() const { return _scale; }
@ -233,6 +238,8 @@ protected:
QSharedPointer<NetworkGeometry> _geometry;
void setGeometry(const QSharedPointer<NetworkGeometry>& newGeometry);
glm::vec3 _translation;
glm::quat _rotation;
glm::vec3 _scale;
glm::vec3 _offset;

View file

@ -65,11 +65,11 @@ void compileProgram(ProgramPtr & result, const std::string& vs, const std::strin
.Compile()
);
result->Link();
} catch (ProgramBuildError & err) {
} catch (ProgramBuildError& err) {
Q_UNUSED(err);
qWarning() << err.Log().c_str();
Q_ASSERT_X(false, "compileProgram", "Failed to build shader program");
qFatal((const char*)err.Message);
qFatal("%s", (const char*) err.Message);
result.reset();
}
}

View file

@ -1,34 +0,0 @@
//
// PhysicsEntity.cpp
// libraries/physics/src
//
// Created by Andrew Meadows 2014.06.11
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "PhysicsEntity.h"
PhysicsEntity::PhysicsEntity() :
_translation(0.0f),
_rotation(),
_boundingRadius(0.0f) {
}
PhysicsEntity::~PhysicsEntity() {
}
void PhysicsEntity::setTranslation(const glm::vec3& translation) {
if (_translation != translation) {
_translation = translation;
}
}
void PhysicsEntity::setRotation(const glm::quat& rotation) {
if (_rotation != rotation) {
_rotation = rotation;
}
}

View file

@ -1,42 +0,0 @@
//
// PhysicsEntity.h
// libraries/physics/src
//
// Created by Andrew Meadows 2014.05.30
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_PhysicsEntity_h
#define hifi_PhysicsEntity_h
#include <QVector>
#include <QSet>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
class PhysicsEntity {
public:
PhysicsEntity();
virtual ~PhysicsEntity();
virtual void stepForward(float deltaTime) { }
void setTranslation(const glm::vec3& translation);
void setRotation(const glm::quat& rotation);
const glm::vec3& getTranslation() const { return _translation; }
const glm::quat& getRotation() const { return _rotation; }
float getBoundingRadius() const { return _boundingRadius; }
protected:
glm::vec3 _translation;
glm::quat _rotation;
float _boundingRadius;
};
#endif // hifi_PhysicsEntity_h

View file

@ -16,16 +16,16 @@
#include "WebSocketClass.h"
WebSocketClass::WebSocketClass(QScriptEngine* engine, QString url) :
_engine(engine),
_webSocket(new QWebSocket())
_webSocket(new QWebSocket()),
_engine(engine)
{
initialize();
_webSocket->open(url);
}
WebSocketClass::WebSocketClass(QScriptEngine* engine, QWebSocket* qWebSocket) :
_engine(engine),
_webSocket(qWebSocket)
_webSocket(qWebSocket),
_engine(engine)
{
initialize();
}