mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge branch 'master' of https://github.com/highfidelity/hifi into decouple-avatar-updates
This commit is contained in:
commit
8cf71f285d
81 changed files with 3402 additions and 982 deletions
|
@ -295,7 +295,7 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
avatarPacketList.startSegment();
|
||||
|
||||
numAvatarDataBytes += avatarPacketList.write(otherNode->getUUID().toRfc4122());
|
||||
numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray());
|
||||
numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray(false));
|
||||
|
||||
avatarPacketList.endSegment();
|
||||
|
||||
|
|
|
@ -77,10 +77,7 @@ void ScriptableAvatar::update(float deltatime) {
|
|||
int mapping = animationJoints.indexOf(modelJoints[i]);
|
||||
if (mapping != -1 && !_maskedJoints.contains(modelJoints[i])) {
|
||||
JointData& data = _jointData[i];
|
||||
data.valid = true;
|
||||
data.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
|
||||
} else {
|
||||
_jointData[i].valid = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
4
cmake/externals/sixense/CMakeLists.txt
vendored
4
cmake/externals/sixense/CMakeLists.txt
vendored
|
@ -47,13 +47,13 @@ if (WIN32)
|
|||
elseif(APPLE)
|
||||
|
||||
# FIXME need to account for different architectures
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx32/libopenvr_api.dylib CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL)
|
||||
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/osx32)
|
||||
|
||||
elseif(NOT ANDROID)
|
||||
|
||||
# FIXME need to account for different architectures
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux32/libopenvr_api.so CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL)
|
||||
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/linux32)
|
||||
|
||||
endif()
|
||||
|
|
582
examples/controllers/breakdanceToy.js
Normal file
582
examples/controllers/breakdanceToy.js
Normal 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);
|
||||
});
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
elRefresh = document.getElementById("refresh");
|
||||
elDelete = document.getElementById("delete");
|
||||
elTeleport = document.getElementById("teleport");
|
||||
elRadius = document.getElementById("radius");
|
||||
elNoEntitiesMessage = document.getElementById("no-entities");
|
||||
elNoEntitiesRadius = document.getElementById("no-entities-radius");
|
||||
|
||||
document.getElementById("entity-name").onclick = function() {
|
||||
setSortColumn('name');
|
||||
|
@ -186,6 +188,13 @@
|
|||
}
|
||||
}, false);
|
||||
|
||||
elRadius.onchange = function () {
|
||||
elRadius.value = Math.max(elRadius.value, 0);
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elRadius.value }));
|
||||
refreshEntities();
|
||||
elNoEntitiesRadius.firstChild.nodeValue = elRadius.value;
|
||||
}
|
||||
|
||||
if (window.EventBridge !== undefined) {
|
||||
EventBridge.scriptEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
|
@ -218,14 +227,15 @@
|
|||
</head>
|
||||
<body onload='loaded();'>
|
||||
<div id="entity-list-header">
|
||||
<input type="button" id="refresh" value="Refresh"></button>
|
||||
<input type="button" id="teleport" value="Teleport"></button>
|
||||
<input type="button" id="delete" style="background-color: rgb(244, 64, 64); float: right" value="Delete"></button>
|
||||
<input type="button" id="refresh" value="Refresh" />
|
||||
<input type="button" id="teleport" value="Teleport" />
|
||||
<input type="button" id="delete" style="background-color: rgb(244, 64, 64); float: right" value="Delete" />
|
||||
</div>
|
||||
|
||||
<div id="entity-list">
|
||||
<div id="search-area">
|
||||
<input type="text" class="search" id="filter" placeholder="Filter" />
|
||||
<span id="radius-and-unit"><input type="number" id="radius" value="100" /> m</span>
|
||||
</div>
|
||||
<table id="entity-table">
|
||||
<thead>
|
||||
|
@ -246,7 +256,7 @@
|
|||
</table>
|
||||
</div>
|
||||
<div id="no-entities">
|
||||
No entities found within 50 meter radius. Try moving to a different location and refreshing.
|
||||
No entities found within a <span id="no-entities-radius">100</span> meter radius. Try moving to a different location and refreshing.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -102,13 +102,23 @@ input[type=button] {
|
|||
}
|
||||
|
||||
#search-area {
|
||||
width: 100%;
|
||||
padding: 0.5em;
|
||||
box-sizing: border-box;
|
||||
padding-right: 6em;
|
||||
}
|
||||
|
||||
#search-area input {
|
||||
width: 100%;
|
||||
#filter {
|
||||
width: 99%;
|
||||
}
|
||||
|
||||
#radius-and-unit {
|
||||
width: 6em;
|
||||
float: right;
|
||||
margin-right: -6em;
|
||||
}
|
||||
|
||||
#radius {
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
textarea, input {
|
||||
|
|
|
@ -4,6 +4,8 @@ EntityListTool = function(opts) {
|
|||
var url = Script.resolvePath('html/entityList.html');
|
||||
var webView = new WebWindow('Entities', url, 200, 280, true);
|
||||
|
||||
var searchRadius = 100;
|
||||
|
||||
var visible = false;
|
||||
|
||||
webView.setVisible(visible);
|
||||
|
@ -33,7 +35,7 @@ EntityListTool = function(opts) {
|
|||
|
||||
that.sendUpdate = function() {
|
||||
var entities = [];
|
||||
var ids = Entities.findEntities(MyAvatar.position, 100);
|
||||
var ids = Entities.findEntities(MyAvatar.position, searchRadius);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var id = ids[i];
|
||||
var properties = Entities.getEntityProperties(id);
|
||||
|
@ -80,6 +82,15 @@ EntityListTool = function(opts) {
|
|||
}
|
||||
} else if (data.type == "delete") {
|
||||
deleteSelectedEntities();
|
||||
} else if (data.type === "radius") {
|
||||
searchRadius = data.radius;
|
||||
that.sendUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
webView.visibilityChanged.connect(function (visible) {
|
||||
if (visible) {
|
||||
that.sendUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
103
examples/toys/magBalls/ballController.js
Normal file
103
examples/toys/magBalls/ballController.js
Normal 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() {
|
||||
}
|
140
examples/toys/magBalls/constants.js
Normal file
140
examples/toys/magBalls/constants.js
Normal 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
|
||||
// }
|
||||
|
||||
|
95
examples/toys/magBalls/debugUtils.js
Normal file
95
examples/toys/magBalls/debugUtils.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
45
examples/toys/magBalls/edgeSpring.js
Normal file
45
examples/toys/magBalls/edgeSpring.js
Normal 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;
|
||||
}
|
281
examples/toys/magBalls/graph.js
Normal file
281
examples/toys/magBalls/graph.js
Normal 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;
|
||||
}
|
127
examples/toys/magBalls/handController.js
Normal file
127
examples/toys/magBalls/handController.js
Normal 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");
|
||||
}
|
||||
|
||||
|
70
examples/toys/magBalls/highlighter.js
Normal file
70
examples/toys/magBalls/highlighter.js
Normal 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
|
||||
});
|
||||
}
|
||||
}
|
293
examples/toys/magBalls/magBalls.js
Normal file
293
examples/toys/magBalls/magBalls.js
Normal 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]));
|
||||
}
|
||||
|
25
examples/toys/magBalls/magBallsMain.js
Normal file
25
examples/toys/magBalls/magBallsMain.js
Normal 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) ];
|
66
examples/toys/magBalls/menuController.js
Normal file
66
examples/toys/magBalls/menuController.js
Normal 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();
|
||||
}
|
106
examples/toys/magBalls/utils.js
Normal file
106
examples/toys/magBalls/utils.js
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2818,7 +2818,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();
|
||||
|
||||
|
@ -5056,5 +5057,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";
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -431,6 +431,8 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableRigAnimations, 0, false,
|
||||
avatar, SLOT(setEnableRigAnimations(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu,
|
||||
MenuOption::Connexion,
|
||||
|
@ -439,8 +441,6 @@ Menu::Menu() {
|
|||
SLOT(toggleConnexion(bool)));
|
||||
|
||||
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandMouseInput, 0, true);
|
||||
|
|
|
@ -131,8 +131,6 @@ namespace MenuOption {
|
|||
const QString AboutApp = "About Interface";
|
||||
const QString AddRemoveFriends = "Add/Remove Friends...";
|
||||
const QString AddressBar = "Show Address Bar";
|
||||
const QString AlignForearmsWithWrists = "Align Forearms with Wrists";
|
||||
const QString AlternateIK = "Alternate IK";
|
||||
const QString Animations = "Animations...";
|
||||
const QString Atmosphere = "Atmosphere";
|
||||
const QString Attachments = "Attachments...";
|
||||
|
@ -188,6 +186,7 @@ namespace MenuOption {
|
|||
const QString EditEntitiesHelp = "Edit Entities Help...";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString EnableCharacterController = "Enable avatar collisions";
|
||||
const QString EnableRigAnimations = "Enable Rig Animations";
|
||||
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
|
||||
const QString ExpandMyAvatarTiming = "Expand /myAvatar";
|
||||
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
|
||||
|
|
|
@ -200,11 +200,9 @@ void Avatar::simulate(float deltaTime) {
|
|||
if (!_shouldRenderBillboard && inViewFrustum) {
|
||||
{
|
||||
PerformanceTimer perfTimer("skeleton");
|
||||
if (_hasNewJointRotations) {
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
const JointData& data = _jointData.at(i);
|
||||
_skeletonModel.setJointState(i, data.valid, data.rotation);
|
||||
}
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
const JointData& data = _jointData.at(i);
|
||||
_skeletonModel.setJointState(i, true, data.rotation);
|
||||
}
|
||||
_skeletonModel.simulate(deltaTime, _hasNewJointRotations);
|
||||
simulateAttachments(deltaTime);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -123,18 +123,18 @@ MyAvatar::~MyAvatar() {
|
|||
_lookAtTargetAvatar.reset();
|
||||
}
|
||||
|
||||
QByteArray MyAvatar::toByteArray() {
|
||||
QByteArray MyAvatar::toByteArray(bool cullSmallChanges) {
|
||||
CameraMode mode = Application::getInstance()->getCamera()->getMode();
|
||||
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
|
||||
// fake the avatar position that is sent up to the AvatarMixer
|
||||
glm::vec3 oldPosition = _position;
|
||||
_position = getSkeletonPosition();
|
||||
QByteArray array = AvatarData::toByteArray();
|
||||
QByteArray array = AvatarData::toByteArray(cullSmallChanges);
|
||||
// copy the correct position back
|
||||
_position = oldPosition;
|
||||
return array;
|
||||
}
|
||||
return AvatarData::toByteArray();
|
||||
return AvatarData::toByteArray(cullSmallChanges);
|
||||
}
|
||||
|
||||
void MyAvatar::reset() {
|
||||
|
@ -220,7 +220,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
_jointData.resize(_rig->getJointStateCount());
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
JointData& data = _jointData[i];
|
||||
data.valid = _rig->getJointStateRotation(i, data.rotation);
|
||||
_rig->getJointStateRotation(i, data.rotation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -705,9 +705,10 @@ float loadSetting(QSettings& settings, const char* name, float defaultValue) {
|
|||
}
|
||||
|
||||
void MyAvatar::setEnableRigAnimations(bool isEnabled) {
|
||||
Settings settings;
|
||||
settings.setValue("enableRig", isEnabled);
|
||||
_rig->setEnableRig(isEnabled);
|
||||
if (!isEnabled) {
|
||||
_rig->deleteAnimations();
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::loadData() {
|
||||
|
@ -769,7 +770,7 @@ void MyAvatar::loadData() {
|
|||
setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString());
|
||||
|
||||
settings.endGroup();
|
||||
_rig->setEnableRig(settings.value("enableRig").toBool());
|
||||
_rig->setEnableRig(Menu::getInstance()->isOptionChecked(MenuOption::EnableRigAnimations));
|
||||
}
|
||||
|
||||
void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const {
|
||||
|
@ -843,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
|
||||
|
|
|
@ -87,7 +87,6 @@ public:
|
|||
Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role);
|
||||
Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url);
|
||||
void clearJointAnimationPriorities();
|
||||
Q_INVOKABLE void setEnableRigAnimations(bool isEnabled);
|
||||
|
||||
// get/set avatar data
|
||||
void saveData();
|
||||
|
@ -191,6 +190,7 @@ public slots:
|
|||
void loadLastRecording();
|
||||
|
||||
virtual void rebuildSkeletonBody();
|
||||
void setEnableRigAnimations(bool isEnabled);
|
||||
|
||||
signals:
|
||||
void transformChanged();
|
||||
|
@ -201,7 +201,7 @@ private:
|
|||
|
||||
glm::vec3 getWorldBodyPosition() const;
|
||||
glm::quat getWorldBodyOrientation() const;
|
||||
QByteArray toByteArray();
|
||||
QByteArray toByteArray(bool cullSmallChanges);
|
||||
void simulate(float deltaTime);
|
||||
void updateFromTrackers(float deltaTime);
|
||||
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
|
||||
|
|
|
@ -48,7 +48,7 @@ void SkeletonModel::avatarLockReleaseIfApplicable() {
|
|||
|
||||
void SkeletonModel::initJointStates(QVector<JointState> states) {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
glm::mat4 rootTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
|
||||
int rootJointIndex = geometry.rootJointIndex;
|
||||
int leftHandJointIndex = geometry.leftHandJointIndex;
|
||||
|
@ -58,14 +58,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, rootTransform,
|
||||
rootJointIndex,
|
||||
leftHandJointIndex,
|
||||
leftElbowJointIndex,
|
||||
leftShoulderJointIndex,
|
||||
rightHandJointIndex,
|
||||
rightElbowJointIndex,
|
||||
rightShoulderJointIndex);
|
||||
|
||||
// Determine the default eye position for avatar scale = 1.0
|
||||
int headJointIndex = _geometry->getFBXGeometry().headJointIndex;
|
||||
|
@ -90,7 +90,7 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
|
|||
// of its root joint and we need that done before we try to build shapes hence we
|
||||
// recompute all joint transforms at this time.
|
||||
for (int i = 0; i < _rig->getJointStateCount(); i++) {
|
||||
_rig->updateJointState(i, parentTransform);
|
||||
_rig->updateJointState(i, rootTransform);
|
||||
}
|
||||
|
||||
buildShapes();
|
||||
|
@ -164,11 +164,11 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
|
||||
|
||||
Model::simulate(deltaTime, fullUpdate);
|
||||
|
||||
|
||||
if (!isActive() || !_owningAvatar->isMyAvatar()) {
|
||||
return; // only simulate for own avatar
|
||||
}
|
||||
|
||||
|
||||
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
|
||||
if (myAvatar->isPlaying()) {
|
||||
// Don't take inputs if playing back a recording.
|
||||
|
@ -255,40 +255,24 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
|
|||
rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector),
|
||||
true, PALM_PRIORITY);
|
||||
}
|
||||
|
||||
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
||||
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
|
||||
return;
|
||||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
|
||||
int parentJointIndex = geometry.joints.at(jointIndex).parentIndex;
|
||||
if (parentJointIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// rotate palm to align with its normal (normal points out of hand's palm)
|
||||
|
||||
// the palm's position must be transformed into the model-frame
|
||||
glm::quat inverseRotation = glm::inverse(_rotation);
|
||||
glm::vec3 palmPosition = inverseRotation * (palm.getPosition() - _translation);
|
||||
glm::vec3 palmNormal = inverseRotation * palm.getNormal();
|
||||
glm::vec3 fingerDirection = inverseRotation * palm.getFingerDirection();
|
||||
|
||||
glm::quat palmRotation = rotationBetween(geometry.palmDirection, palmNormal);
|
||||
palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), fingerDirection) * palmRotation;
|
||||
// the palm's "raw" rotation is already in the model-frame
|
||||
glm::quat palmRotation = palm.getRawRotation();
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK)) {
|
||||
_rig->setHandPosition(jointIndex, palmPosition, palmRotation, extractUniformScale(_scale), PALM_PRIORITY);
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
||||
float forearmLength = geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale);
|
||||
glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f);
|
||||
setJointPosition(parentJointIndex, palmPosition + forearm,
|
||||
glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
||||
_rig->setJointRotationInBindFrame(parentJointIndex, palmRotation, PALM_PRIORITY);
|
||||
// lock hand to forearm by slamming its rotation (in parent-frame) to identity
|
||||
_rig->setJointRotationInConstrainedFrame(jointIndex, glm::quat(), PALM_PRIORITY);
|
||||
} else {
|
||||
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
||||
}
|
||||
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
||||
}
|
||||
|
||||
void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
|
||||
|
@ -308,13 +292,13 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
|
|||
_rotation :
|
||||
_rotation * _rig->getJointState(joint.parentIndex).getRotation();
|
||||
float fanScale = directionSize * 0.75f;
|
||||
|
||||
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(position);
|
||||
transform.setRotation(parentRotation);
|
||||
transform.setScale(fanScale);
|
||||
batch.setModelTransform(transform);
|
||||
|
||||
|
||||
const int AXIS_COUNT = 3;
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
@ -325,7 +309,7 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
|
|||
}
|
||||
glm::vec3 axis;
|
||||
axis[i] = 1.0f;
|
||||
|
||||
|
||||
glm::vec3 otherAxis;
|
||||
if (i == 0) {
|
||||
otherAxis.y = 1.0f;
|
||||
|
@ -346,18 +330,18 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
|
|||
// better if the skeleton model cached these buffers for each of the joints they are rendering
|
||||
geometryCache->updateVertices(_triangleFanID, points, color);
|
||||
geometryCache->renderVertices(batch, gpu::TRIANGLE_FAN, _triangleFanID);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
renderOrientationDirections(batch, jointIndex, position, _rotation * jointState.getRotation(), directionSize);
|
||||
jointIndex = joint.parentIndex;
|
||||
|
||||
|
||||
} while (jointIndex != -1 && geometry.joints.at(jointIndex).isFree);
|
||||
}
|
||||
|
||||
void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointIndex,
|
||||
void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointIndex,
|
||||
glm::vec3 position, const glm::quat& orientation, float size) {
|
||||
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
if (!_jointOrientationLines.contains(jointIndex)) {
|
||||
|
@ -493,7 +477,7 @@ void SkeletonModel::buildShapes() {
|
|||
if (_geometry == NULL || _rig->jointStatesEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (geometry.joints.isEmpty() || geometry.rootJointIndex == -1) {
|
||||
// rootJointIndex == -1 if the avatar model has no skeleton
|
||||
|
@ -540,7 +524,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) {
|
||||
|
@ -558,7 +541,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha
|
|||
geometryCache->renderSphere(batch, _boundingCapsuleRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
|
||||
glm::vec4(0.6f, 0.6f, 0.8f, alpha));
|
||||
|
||||
// draw a yellow sphere at the capsule bottom point
|
||||
// draw a yellow sphere at the capsule bottom point
|
||||
glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, -_boundingCapsuleHeight, 0.0f);
|
||||
glm::vec3 axis = topPoint - bottomPoint;
|
||||
transform.setTranslation(bottomPoint);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -206,8 +206,6 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
|||
|
||||
updateTooltips();
|
||||
|
||||
auto deviceSize = qApp->getDeviceSize();
|
||||
|
||||
//Handle fading and deactivation/activation of UI
|
||||
gpu::Batch batch;
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "AvatarRig.h"
|
||||
|
||||
/// Updates the state of the joint at the specified index.
|
||||
void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) {
|
||||
void AvatarRig::updateJointState(int index, glm::mat4 rootTransform) {
|
||||
if (index < 0 && index >= _jointStates.size()) {
|
||||
return; // bail
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) {
|
|||
// compute model transforms
|
||||
if (index == _rootJointIndex) {
|
||||
// we always zero-out the translation part of an avatar's root join-transform.
|
||||
state.computeTransform(parentTransform);
|
||||
state.computeTransform(rootTransform);
|
||||
clearJointTransformTranslation(index);
|
||||
} else {
|
||||
// guard against out-of-bounds access to _jointStates
|
||||
|
|
|
@ -21,7 +21,7 @@ class AvatarRig : public Rig {
|
|||
|
||||
public:
|
||||
~AvatarRig() {}
|
||||
virtual void updateJointState(int index, glm::mat4 parentTransform);
|
||||
virtual void updateJointState(int index, glm::mat4 rootTransform);
|
||||
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
|
||||
float scale, float priority);
|
||||
};
|
||||
|
|
|
@ -12,13 +12,13 @@
|
|||
#include "EntityRig.h"
|
||||
|
||||
/// Updates the state of the joint at the specified index.
|
||||
void EntityRig::updateJointState(int index, glm::mat4 parentTransform) {
|
||||
void EntityRig::updateJointState(int index, glm::mat4 rootTransform) {
|
||||
JointState& state = _jointStates[index];
|
||||
|
||||
// compute model transforms
|
||||
int parentIndex = state.getParentIndex();
|
||||
if (parentIndex == -1) {
|
||||
state.computeTransform(parentTransform);
|
||||
state.computeTransform(rootTransform);
|
||||
} else {
|
||||
// guard against out-of-bounds access to _jointStates
|
||||
if (parentIndex >= 0 && parentIndex < _jointStates.size()) {
|
||||
|
|
|
@ -21,7 +21,7 @@ class EntityRig : public Rig {
|
|||
|
||||
public:
|
||||
~EntityRig() {}
|
||||
virtual void updateJointState(int index, glm::mat4 parentTransform);
|
||||
virtual void updateJointState(int index, glm::mat4 rootTransform);
|
||||
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
|
||||
float scale, float priority) {}
|
||||
};
|
||||
|
|
|
@ -153,6 +153,24 @@ void JointState::setRotationInBindFrame(const glm::quat& rotation, float priorit
|
|||
}
|
||||
}
|
||||
|
||||
void JointState::setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority, bool constrain) {
|
||||
// rotation is from bind- to model-frame
|
||||
if (priority >= _animationPriority) {
|
||||
glm::quat parentRotation = computeParentRotation();
|
||||
|
||||
// R = Rp * Rpre * r * Rpost
|
||||
// R' = Rp * Rpre * r' * Rpost
|
||||
// r' = (Rp * Rpre)^ * R' * Rpost^
|
||||
glm::quat targetRotation = glm::inverse(parentRotation * _preRotation) * rotationInModelFrame * glm::inverse(_postRotation);
|
||||
if (constrain && _constraint) {
|
||||
_constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
|
||||
}
|
||||
_rotationInConstrainedFrame = glm::normalize(targetRotation);
|
||||
_transformChanged = true;
|
||||
_animationPriority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::clearTransformTranslation() {
|
||||
_transform[3][0] = 0.0f;
|
||||
_transform[3][1] = 0.0f;
|
||||
|
|
|
@ -82,6 +82,11 @@ public:
|
|||
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
|
||||
void setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain = false);
|
||||
|
||||
/// \param rotationInModelRame is in model-frame
|
||||
/// computes and sets new _rotationInConstrainedFrame to match rotationInModelFrame
|
||||
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
|
||||
void setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority, bool constrain);
|
||||
|
||||
void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false, float mix = 1.0f);
|
||||
void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation);
|
||||
const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; }
|
||||
|
|
|
@ -100,24 +100,21 @@ AnimationHandlePointer Rig::addAnimationByRole(const QString& role, const QStrin
|
|||
AnimationHandlePointer handle = createAnimationHandle();
|
||||
QString standard = "";
|
||||
if (url.isEmpty()) { // Default animations for fight club
|
||||
const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/";
|
||||
const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/";
|
||||
if (role == "walk") {
|
||||
standard = base + "support/FightClubBotTest1/Animations/standard_walk.fbx";
|
||||
lastFrame = 60;
|
||||
standard = base + "walk_fwd.fbx";
|
||||
} else if (role == "backup") {
|
||||
standard = base + "walk_bwd.fbx";
|
||||
} else if (role == "leftTurn") {
|
||||
standard = base + "support/FightClubBotTest1/Animations/left_turn_noHipRotation.fbx";
|
||||
lastFrame = 29;
|
||||
standard = base + "turn_left.fbx";
|
||||
} else if (role == "rightTurn") {
|
||||
standard = base + "support/FightClubBotTest1/Animations/right_turn_noHipRotation.fbx";
|
||||
lastFrame = 31;
|
||||
standard = base + "turn_right.fbx";
|
||||
} else if (role == "leftStrafe") {
|
||||
standard = base + "animations/fightclub_bot_anims/side_step_left_inPlace.fbx";
|
||||
lastFrame = 31;
|
||||
standard = base + "strafe_left.fbx";
|
||||
} else if (role == "rightStrafe") {
|
||||
standard = base + "animations/fightclub_bot_anims/side_step_right_inPlace.fbx";
|
||||
lastFrame = 31;
|
||||
standard = base + "strafe_right.fbx";
|
||||
} else if (role == "idle") {
|
||||
standard = base + "support/FightClubBotTest1/Animations/standard_idle.fbx";
|
||||
standard = base + "idle.fbx";
|
||||
fps = 25.0f;
|
||||
}
|
||||
if (!standard.isEmpty()) {
|
||||
|
@ -188,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 rootTransform,
|
||||
int rootJointIndex,
|
||||
int leftHandJointIndex,
|
||||
int leftElbowJointIndex,
|
||||
int leftShoulderJointIndex,
|
||||
int rightHandJointIndex,
|
||||
int rightElbowJointIndex,
|
||||
int rightShoulderJointIndex) {
|
||||
_jointStates = states;
|
||||
|
||||
_rootJointIndex = rootJointIndex;
|
||||
|
@ -206,22 +203,15 @@ float Rig::initJointStates(QVector<JointState> states, glm::mat4 parentTransform
|
|||
_rightElbowJointIndex = rightElbowJointIndex;
|
||||
_rightShoulderJointIndex = rightShoulderJointIndex;
|
||||
|
||||
initJointTransforms(parentTransform);
|
||||
initJointTransforms(rootTransform);
|
||||
|
||||
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....
|
||||
|
@ -236,14 +226,14 @@ int Rig::indexOfJoint(const QString& jointName) {
|
|||
}
|
||||
|
||||
|
||||
void Rig::initJointTransforms(glm::mat4 parentTransform) {
|
||||
void Rig::initJointTransforms(glm::mat4 rootTransform) {
|
||||
// compute model transforms
|
||||
int numStates = _jointStates.size();
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
JointState& state = _jointStates[i];
|
||||
int parentIndex = state.getParentIndex();
|
||||
if (parentIndex == -1) {
|
||||
state.initTransform(parentTransform);
|
||||
state.initTransform(rootTransform);
|
||||
} else {
|
||||
const JointState& parentState = _jointStates.at(parentIndex);
|
||||
state.initTransform(parentState.getTransform());
|
||||
|
@ -438,11 +428,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
|||
}
|
||||
}
|
||||
};
|
||||
updateRole("walk", std::abs(forwardSpeed) > 0.01f);
|
||||
updateRole("walk", forwardSpeed > 0.01f);
|
||||
updateRole("backup", forwardSpeed < -0.01f);
|
||||
bool isTurning = std::abs(rightTurningSpeed) > 0.5f;
|
||||
updateRole("rightTurn", isTurning && (rightTurningSpeed > 0));
|
||||
updateRole("leftTurn", isTurning && (rightTurningSpeed < 0));
|
||||
bool isStrafing = std::abs(rightLateralSpeed) > 0.01f;
|
||||
bool isStrafing = !isTurning && (std::abs(rightLateralSpeed) > 0.01f);
|
||||
updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f));
|
||||
updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f));
|
||||
updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus.
|
||||
|
@ -450,7 +441,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
|||
_lastPosition = worldPosition;
|
||||
}
|
||||
|
||||
void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
|
||||
void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
|
||||
|
||||
// First normalize the fades so that they sum to 1.0.
|
||||
// update the fade data in each animation (not normalized as they are an independent propert of animation)
|
||||
|
@ -496,7 +487,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
|
|||
}
|
||||
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
updateJointState(i, parentTransform);
|
||||
updateJointState(i, rootTransform);
|
||||
}
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].resetTransformChanged();
|
||||
|
@ -505,7 +496,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
|
|||
|
||||
bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
|
||||
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority,
|
||||
const QVector<int>& freeLineage, glm::mat4 parentTransform) {
|
||||
const QVector<int>& freeLineage, glm::mat4 rootTransform) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -557,7 +548,7 @@ bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm:
|
|||
glm::vec3 positionSum;
|
||||
for (int k = j - 1; k > 0; k--) {
|
||||
int index = freeLineage.at(k);
|
||||
updateJointState(index, parentTransform);
|
||||
updateJointState(index, rootTransform);
|
||||
positionSum += extractTranslation(_jointStates.at(index).getTransform());
|
||||
}
|
||||
glm::vec3 projectedCenterOfMass = glm::cross(jointVector,
|
||||
|
@ -580,15 +571,15 @@ bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm:
|
|||
|
||||
// now update the joint states from the top
|
||||
for (int j = freeLineage.size() - 1; j >= 0; j--) {
|
||||
updateJointState(freeLineage.at(j), parentTransform);
|
||||
updateJointState(freeLineage.at(j), rootTransform);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
|
||||
const QVector<int>& freeLineage, glm::mat4 parentTransform) {
|
||||
// NOTE: targetRotation is from bind- to model-frame
|
||||
const QVector<int>& freeLineage, glm::mat4 rootTransform) {
|
||||
// NOTE: targetRotation is from in model-frame
|
||||
|
||||
if (endIndex == -1 || _jointStates.isEmpty()) {
|
||||
return;
|
||||
|
@ -606,12 +597,27 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
|
|||
const JointState& state = _jointStates.at(index);
|
||||
int parentIndex = state.getParentIndex();
|
||||
if (parentIndex == -1) {
|
||||
topParentTransform = parentTransform;
|
||||
topParentTransform = rootTransform;
|
||||
} else {
|
||||
topParentTransform = _jointStates[parentIndex].getTransform();
|
||||
}
|
||||
}
|
||||
|
||||
// relax toward default rotation
|
||||
// NOTE: ideally this should use dt and a relaxation timescale to compute how much to relax
|
||||
for (int j = 0; j < numFree; j++) {
|
||||
int nextIndex = freeLineage.at(j);
|
||||
JointState& nextState = _jointStates[nextIndex];
|
||||
if (! nextState.getIsFree()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply the zero rotationDelta, but use mixRotationDelta() which blends a bit of the default pose
|
||||
// in the process. This provides stability to the IK solution for most models.
|
||||
float mixFactor = 0.08f;
|
||||
nextState.mixRotationDelta(glm::quat(), mixFactor, priority);
|
||||
}
|
||||
|
||||
// this is a cyclic coordinate descent algorithm: see
|
||||
// http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
|
||||
|
||||
|
@ -620,7 +626,7 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
|
|||
glm::vec3 endPosition = endState.getPosition();
|
||||
float distanceToGo = glm::distance(targetPosition, endPosition);
|
||||
|
||||
const int MAX_ITERATION_COUNT = 2;
|
||||
const int MAX_ITERATION_COUNT = 3;
|
||||
const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm
|
||||
int numIterations = 0;
|
||||
do {
|
||||
|
@ -658,7 +664,7 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
|
|||
|
||||
float gravityAngle = glm::angle(gravityDelta);
|
||||
const float MIN_GRAVITY_ANGLE = 0.1f;
|
||||
float mixFactor = 0.5f;
|
||||
float mixFactor = 0.1f;
|
||||
if (gravityAngle < MIN_GRAVITY_ANGLE) {
|
||||
// the final rotation is a mix of the two
|
||||
mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE;
|
||||
|
@ -666,11 +672,10 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
|
|||
deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor);
|
||||
}
|
||||
|
||||
// Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose
|
||||
// in the process. This provides stability to the IK solution for most models.
|
||||
// Apply the rotation delta.
|
||||
glm::quat oldNextRotation = nextState.getRotation();
|
||||
float mixFactor = 0.03f;
|
||||
nextState.mixRotationDelta(deltaRotation, mixFactor, priority);
|
||||
float mixFactor = 0.05f;
|
||||
nextState.applyRotationDelta(deltaRotation, mixFactor, priority);
|
||||
|
||||
// measure the result of the rotation which may have been modified by
|
||||
// blending and constraints
|
||||
|
@ -689,10 +694,10 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
|
|||
// measure our success
|
||||
endPosition = endState.getPosition();
|
||||
distanceToGo = glm::distance(targetPosition, endPosition);
|
||||
} while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR);
|
||||
} while (numIterations < MAX_ITERATION_COUNT && distanceToGo > ACCEPTABLE_IK_ERROR);
|
||||
|
||||
// set final rotation of the end joint
|
||||
endState.setRotationInBindFrame(targetRotation, priority, true);
|
||||
endState.setRotationInModelFrame(targetRotation, priority, true);
|
||||
}
|
||||
|
||||
bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, const QVector<int>& freeLineage) {
|
||||
|
|
|
@ -92,19 +92,19 @@ 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 rootTransform,
|
||||
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) ;
|
||||
|
||||
void initJointTransforms(glm::mat4 parentTransform);
|
||||
void initJointTransforms(glm::mat4 rootTransform);
|
||||
void clearJointTransformTranslation(int jointIndex);
|
||||
void reset(const QVector<FBXJoint>& fbxJoints);
|
||||
bool getJointStateRotation(int index, glm::quat& rotation) const;
|
||||
|
@ -135,12 +135,12 @@ public:
|
|||
// Start or stop animations as needed.
|
||||
void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation);
|
||||
// Regardless of who started the animations or how many, update the joints.
|
||||
void updateAnimations(float deltaTime, glm::mat4 parentTransform);
|
||||
void updateAnimations(float deltaTime, glm::mat4 rootTransform);
|
||||
bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
|
||||
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority,
|
||||
const QVector<int>& freeLineage, glm::mat4 parentTransform);
|
||||
const QVector<int>& freeLineage, glm::mat4 rootTransform);
|
||||
void inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
|
||||
const QVector<int>& freeLineage, glm::mat4 parentTransform);
|
||||
const QVector<int>& freeLineage, glm::mat4 rootTransform);
|
||||
bool restoreJointPosition(int jointIndex, float fraction, float priority, const QVector<int>& freeLineage);
|
||||
float getLimbLength(int jointIndex, const QVector<int>& freeLineage,
|
||||
const glm::vec3 scale, const QVector<FBXJoint>& fbxJoints) const;
|
||||
|
@ -152,7 +152,7 @@ public:
|
|||
glm::quat getJointDefaultRotationInParentFrame(int jointIndex);
|
||||
void updateVisibleJointStates();
|
||||
|
||||
virtual void updateJointState(int index, glm::mat4 parentTransform) = 0;
|
||||
virtual void updateJointState(int index, glm::mat4 rootTransform) = 0;
|
||||
|
||||
void setEnableRig(bool isEnabled) { _enableRig = isEnabled; }
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@
|
|||
|
||||
quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND;
|
||||
|
||||
// this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer
|
||||
const float MIN_ROTATION_DOT = 0.9999999f;
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
const glm::vec3 DEFAULT_LOCAL_AABOX_CORNER(-0.5f);
|
||||
|
@ -141,7 +145,7 @@ void AvatarData::setHandPosition(const glm::vec3& handPosition) {
|
|||
_handPosition = glm::inverse(getOrientation()) * (handPosition - _position);
|
||||
}
|
||||
|
||||
QByteArray AvatarData::toByteArray() {
|
||||
QByteArray AvatarData::toByteArray(bool cullSmallChanges) {
|
||||
// TODO: DRY this up to a shared method
|
||||
// that can pack any type given the number of bytes
|
||||
// and return the number of bytes to push the pointer
|
||||
|
@ -234,11 +238,19 @@ QByteArray AvatarData::toByteArray() {
|
|||
|
||||
// joint data
|
||||
*destinationBuffer++ = _jointData.size();
|
||||
unsigned char* validityPosition = destinationBuffer;
|
||||
unsigned char validity = 0;
|
||||
int validityBit = 0;
|
||||
foreach (const JointData& data, _jointData) {
|
||||
if (data.valid) {
|
||||
validity |= (1 << validityBit);
|
||||
|
||||
_lastSentJointData.resize(_jointData.size());
|
||||
|
||||
// foreach (const JointData& data, _jointData) {
|
||||
for (int i=0; i < _jointData.size(); i++) {
|
||||
const JointData& data = _jointData.at(i);
|
||||
if (_lastSentJointData[i].rotation != data.rotation) {
|
||||
if (!cullSmallChanges || fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= MIN_ROTATION_DOT) {
|
||||
validity |= (1 << validityBit);
|
||||
}
|
||||
}
|
||||
if (++validityBit == BITS_IN_BYTE) {
|
||||
*destinationBuffer++ = validity;
|
||||
|
@ -248,9 +260,18 @@ QByteArray AvatarData::toByteArray() {
|
|||
if (validityBit != 0) {
|
||||
*destinationBuffer++ = validity;
|
||||
}
|
||||
foreach (const JointData& data, _jointData) {
|
||||
if (data.valid) {
|
||||
|
||||
validityBit = 0;
|
||||
validity = *validityPosition++;
|
||||
for (int i = 0; i < _jointData.size(); i ++) {
|
||||
const JointData& data = _jointData[ i ];
|
||||
if (validity & (1 << validityBit)) {
|
||||
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, data.rotation);
|
||||
_lastSentJointData[i].rotation = data.rotation;
|
||||
}
|
||||
if (++validityBit == BITS_IN_BYTE) {
|
||||
validityBit = 0;
|
||||
validity = *validityPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -494,6 +515,10 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
}
|
||||
int numValidJoints = 0;
|
||||
_jointData.resize(numJoints);
|
||||
|
||||
QVector<bool> valids;
|
||||
valids.resize(numJoints);
|
||||
|
||||
{ // validity bits
|
||||
unsigned char validity = 0;
|
||||
int validityBit = 0;
|
||||
|
@ -505,7 +530,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
if (valid) {
|
||||
++numValidJoints;
|
||||
}
|
||||
_jointData[i].valid = valid;
|
||||
valids[i] = valid;
|
||||
validityBit = (validityBit + 1) % BITS_IN_BYTE;
|
||||
}
|
||||
}
|
||||
|
@ -527,7 +552,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
{ // joint data
|
||||
for (int i = 0; i < numJoints; i++) {
|
||||
JointData& data = _jointData[i];
|
||||
if (data.valid) {
|
||||
if (valids[i]) {
|
||||
_hasNewJointRotations = true;
|
||||
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation);
|
||||
}
|
||||
|
@ -731,7 +756,6 @@ void AvatarData::setJointData(int index, const glm::quat& rotation) {
|
|||
_jointData.resize(index + 1);
|
||||
}
|
||||
JointData& data = _jointData[index];
|
||||
data.valid = true;
|
||||
data.rotation = rotation;
|
||||
}
|
||||
|
||||
|
@ -746,7 +770,6 @@ void AvatarData::clearJointData(int index) {
|
|||
if (_jointData.size() <= index) {
|
||||
_jointData.resize(index + 1);
|
||||
}
|
||||
_jointData[index].valid = false;
|
||||
}
|
||||
|
||||
bool AvatarData::isJointDataValid(int index) const {
|
||||
|
@ -759,7 +782,7 @@ bool AvatarData::isJointDataValid(int index) const {
|
|||
Q_RETURN_ARG(bool, result), Q_ARG(int, index));
|
||||
return result;
|
||||
}
|
||||
return index < _jointData.size() && _jointData.at(index).valid;
|
||||
return index < _jointData.size();
|
||||
}
|
||||
|
||||
glm::quat AvatarData::getJointRotation(int index) const {
|
||||
|
@ -1060,7 +1083,7 @@ void AvatarData::setJointMappingsFromNetworkReply() {
|
|||
void AvatarData::sendAvatarDataPacket() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
QByteArray avatarByteArray = toByteArray();
|
||||
QByteArray avatarByteArray = toByteArray(true);
|
||||
|
||||
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size());
|
||||
avatarPacket->write(avatarByteArray);
|
||||
|
|
|
@ -171,7 +171,7 @@ public:
|
|||
glm::vec3 getHandPosition() const;
|
||||
void setHandPosition(const glm::vec3& handPosition);
|
||||
|
||||
virtual QByteArray toByteArray();
|
||||
virtual QByteArray toByteArray(bool cullSmallChanges);
|
||||
|
||||
/// \return true if an error should be logged
|
||||
bool shouldLogError(const quint64& now);
|
||||
|
@ -359,6 +359,7 @@ protected:
|
|||
char _handState;
|
||||
|
||||
QVector<JointData> _jointData; ///< the state of the skeleton joints
|
||||
QVector<JointData> _lastSentJointData; ///< the state of the skeleton joints last time we transmitted
|
||||
|
||||
// key state
|
||||
KeyState _keyState;
|
||||
|
@ -410,7 +411,6 @@ Q_DECLARE_METATYPE(AvatarData*)
|
|||
|
||||
class JointData {
|
||||
public:
|
||||
bool valid;
|
||||
glm::quat rotation;
|
||||
};
|
||||
|
||||
|
|
|
@ -80,31 +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;
|
||||
case QEvent::Resize:
|
||||
if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -141,4 +143,4 @@ bool OpenGLDisplayPlugin::isVsyncEnabled() {
|
|||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -34,7 +34,7 @@ const QString & OculusLegacyDisplayPlugin::getName() const {
|
|||
return NAME;
|
||||
}
|
||||
|
||||
OculusLegacyDisplayPlugin::OculusLegacyDisplayPlugin() : _ipd(OVR_DEFAULT_IPD) {
|
||||
OculusLegacyDisplayPlugin::OculusLegacyDisplayPlugin() {
|
||||
}
|
||||
|
||||
uvec2 OculusLegacyDisplayPlugin::getRecommendedRenderSize() const {
|
||||
|
|
|
@ -44,7 +44,6 @@ protected:
|
|||
private:
|
||||
static const QString NAME;
|
||||
|
||||
float _ipd{ OVR_DEFAULT_IPD };
|
||||
ovrHmd _hmd;
|
||||
unsigned int _frameIndex;
|
||||
ovrTrackingState _trackingState;
|
||||
|
|
|
@ -78,7 +78,7 @@ void StereoDisplayPlugin::activate() {
|
|||
}
|
||||
|
||||
void StereoDisplayPlugin::updateScreen() {
|
||||
for (int 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
|
@ -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};
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -131,20 +131,20 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() {
|
|||
|
||||
float maxVelocityX = fabsf(_velocity.x) + _velocitySpread.x;
|
||||
float maxAccelerationX = fabsf(_acceleration.x) + _accelerationSpread.x;
|
||||
float maxXDistance = (maxVelocityX * time) + (0.5 * maxAccelerationX * time * time);
|
||||
float maxXDistance = (maxVelocityX * time) + (0.5f * maxAccelerationX * time * time);
|
||||
|
||||
float maxVelocityY = fabs(_velocity.y) + _velocitySpread.y;
|
||||
float maxVelocityY = fabsf(_velocity.y) + _velocitySpread.y;
|
||||
float maxAccelerationY = fabsf(_acceleration.y) + _accelerationSpread.y;
|
||||
float maxYDistance = (maxVelocityY * time) + (0.5 * maxAccelerationY * time * time);
|
||||
float maxYDistance = (maxVelocityY * time) + (0.5f * maxAccelerationY * time * time);
|
||||
|
||||
float maxVelocityZ = fabsf(_velocity.z) + _velocitySpread.z;
|
||||
float maxAccelerationZ = fabsf(_acceleration.z) + _accelerationSpread.z;
|
||||
float maxZDistance = (maxVelocityZ * time) + (0.5 * maxAccelerationZ * time * time);
|
||||
float maxZDistance = (maxVelocityZ * time) + (0.5f * maxAccelerationZ * time * time);
|
||||
|
||||
float maxDistance = std::max(maxXDistance, std::max(maxYDistance, maxZDistance));
|
||||
|
||||
//times 2 because dimensions are diameters not radii
|
||||
glm::vec3 dims(2.0 * maxDistance);
|
||||
glm::vec3 dims(2.0f * maxDistance);
|
||||
EntityItem::setDimensions(dims);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -41,7 +41,6 @@ void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) {
|
|||
if (_stereo._pass) {
|
||||
vp.x += vp.z;
|
||||
}
|
||||
int i = 0;
|
||||
}
|
||||
|
||||
glViewport(vp.x, vp.y, vp.z, vp.w);
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
#include "SixenseManager.h"
|
||||
#include "UserActivityLogger.h"
|
||||
|
||||
#ifdef HAVE_SIXENSE
|
||||
#include "sixense.h"
|
||||
#endif
|
||||
|
||||
// TODO: This should not be here
|
||||
#include <QLoggingCategory>
|
||||
Q_DECLARE_LOGGING_CATEGORY(inputplugins)
|
||||
|
@ -30,8 +34,6 @@ Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")
|
|||
const unsigned int LEFT_MASK = 0;
|
||||
const unsigned int RIGHT_MASK = 1U << 1;
|
||||
|
||||
#ifdef HAVE_SIXENSE
|
||||
|
||||
const int CALIBRATION_STATE_IDLE = 0;
|
||||
const int CALIBRATION_STATE_X = 1;
|
||||
const int CALIBRATION_STATE_Y = 2;
|
||||
|
@ -51,8 +53,6 @@ typedef int (*SixenseTakeIntFunction)(int);
|
|||
typedef int (*SixenseTakeIntAndSixenseControllerData)(int, sixenseControllerData*);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
const QString SixenseManager::NAME = "Sixense";
|
||||
|
||||
const QString MENU_PARENT = "Avatar";
|
||||
|
@ -66,8 +66,8 @@ SixenseManager& SixenseManager::getInstance() {
|
|||
}
|
||||
|
||||
SixenseManager::SixenseManager() :
|
||||
InputDevice("Hydra"),
|
||||
#if defined(HAVE_SIXENSE) && defined(__APPLE__)
|
||||
InputDevice("Hydra"),
|
||||
#ifdef __APPLE__
|
||||
_sixenseLibrary(NULL),
|
||||
#endif
|
||||
_hydrasConnected(false)
|
||||
|
@ -213,18 +213,16 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
|
|||
// NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
|
||||
glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
|
||||
position *= METERS_PER_MILLIMETER;
|
||||
|
||||
|
||||
// Check to see if this hand/controller is on the base
|
||||
const float CONTROLLER_AT_BASE_DISTANCE = 0.075f;
|
||||
if (glm::length(position) >= CONTROLLER_AT_BASE_DISTANCE) {
|
||||
handleButtonEvent(data->buttons, numActiveControllers - 1);
|
||||
handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1);
|
||||
|
||||
// Rotation of Palm
|
||||
glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]);
|
||||
rotation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)) * _orbRotation * rotation;
|
||||
|
||||
if (!jointsCaptured) {
|
||||
// Rotation of Palm
|
||||
glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]);
|
||||
handlePoseEvent(position, rotation, numActiveControllers - 1);
|
||||
} else {
|
||||
_poseStateMap.clear();
|
||||
|
@ -232,7 +230,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
|
|||
} else {
|
||||
_poseStateMap[(numActiveControllers - 1) == 0 ? LEFT_HAND : RIGHT_HAND] = UserInputMapper::PoseValue();
|
||||
}
|
||||
|
||||
|
||||
// // Read controller buttons and joystick into the hand
|
||||
// palm->setControllerButtons(data->buttons);
|
||||
// palm->setTrigger(data->trigger);
|
||||
|
@ -242,7 +240,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
|
|||
if (numActiveControllers == 2) {
|
||||
updateCalibration(controllers);
|
||||
}
|
||||
|
||||
|
||||
for (auto axisState : _axisStateMap) {
|
||||
if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) {
|
||||
_axisStateMap[axisState.first] = 0.0f;
|
||||
|
@ -436,16 +434,66 @@ void SixenseManager::handleButtonEvent(unsigned int buttons, int index) {
|
|||
|
||||
void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int index) {
|
||||
#ifdef HAVE_SIXENSE
|
||||
// From ABOVE the sixense coordinate frame looks like this:
|
||||
//
|
||||
// |
|
||||
// USB cables
|
||||
// |
|
||||
// .-. user
|
||||
// (Orb) --neckX---- forward
|
||||
// '-' |
|
||||
// | | user
|
||||
// neckZ y +---- right
|
||||
// | (o)-----x
|
||||
// |
|
||||
// |
|
||||
// z
|
||||
|
||||
// Transform the measured position into body frame.
|
||||
glm::vec3 neck = _neckBase;
|
||||
// Set y component of the "neck" to raise the measured position a little bit.
|
||||
neck.y = 0.5f;
|
||||
position = _orbRotation * (position - neck);
|
||||
|
||||
// adjustment for hydra controllers fit into hands
|
||||
float sign = (index == 0) ? -1.0f : 1.0f;
|
||||
rotation *= glm::angleAxis(sign * PI/4.0f, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
|
||||
// From ABOVE the hand canonical axes looks like this:
|
||||
//
|
||||
// | | | | y | | | |
|
||||
// | | | | | | | | |
|
||||
// | | | | |
|
||||
// |left | / x----(+) \ |right|
|
||||
// | _/ z \_ |
|
||||
// | | | |
|
||||
// | | | |
|
||||
//
|
||||
|
||||
// To convert sixense's delta-rotation into the hand's frame we will have to transform it like so:
|
||||
//
|
||||
// deltaHand = Qsh^ * deltaSixense * Qsh
|
||||
//
|
||||
// where Qsh = transform from sixense axes to hand axes. By inspection we can determine Qsh:
|
||||
//
|
||||
// Qsh = angleAxis(PI, zAxis) * angleAxis(-PI/2, xAxis)
|
||||
//
|
||||
const glm::vec3 xAxis = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
const glm::vec3 zAxis = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
const glm::quat sixenseToHand = glm::angleAxis(PI, zAxis) * glm::angleAxis(-PI/2.0f, xAxis);
|
||||
|
||||
// In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers
|
||||
// and how they fit into the hand in their relaxed state. This offset is a quarter turn about
|
||||
// the sixense's z-axis, with its direction different for the two hands:
|
||||
float sign = (index == 0) ? 1.0f : -1.0f;
|
||||
const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, zAxis);
|
||||
|
||||
// Finally, there is a post-offset (same for both hands) to get the hand's rest orientation
|
||||
// (fingers forward, palm down) aligned properly in the avatar's model-frame.
|
||||
const glm::quat postOffset = glm::angleAxis(PI / 2.0f, xAxis);
|
||||
|
||||
// The total rotation of the hand uses the formula:
|
||||
//
|
||||
// rotation = postOffset * Qsh^ * (measuredRotation * preOffset) * Qsh
|
||||
//
|
||||
rotation = postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
|
||||
|
||||
_poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation);
|
||||
#endif // HAVE_SIXENSE
|
||||
}
|
||||
|
@ -453,7 +501,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int
|
|||
void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) {
|
||||
// Grab the current free device ID
|
||||
_deviceID = mapper.getFreeDeviceID();
|
||||
|
||||
|
||||
auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name);
|
||||
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
|
||||
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
|
||||
|
@ -465,25 +513,25 @@ void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) {
|
|||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 0), "Left Button 2"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 0), "Left Button 3"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 0), "Left Button 4"));
|
||||
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 0), "L1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "L2"));
|
||||
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Stick Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Stick Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Stick Right"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Stick Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 0), "Left Trigger Press"));
|
||||
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 1), "Right Start"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 1), "Right Button 1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 1), "Right Button 2"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 1), "Right Button 3"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 1), "Right Button 4"));
|
||||
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 1), "R1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "R2"));
|
||||
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Stick Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Stick Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Stick Right"));
|
||||
|
@ -506,35 +554,35 @@ void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
const float JOYSTICK_PITCH_SPEED = 0.25f;
|
||||
const float BUTTON_MOVE_SPEED = 1.0f;
|
||||
const float BOOM_SPEED = 0.1f;
|
||||
|
||||
|
||||
// Left Joystick: Movement, strafing
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), JOYSTICK_MOVE_SPEED);
|
||||
|
||||
|
||||
// Right Joystick: Camera orientation
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(AXIS_X_POS, 1), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(AXIS_X_NEG, 1), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(AXIS_Y_POS, 1), JOYSTICK_PITCH_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(AXIS_Y_NEG, 1), JOYSTICK_PITCH_SPEED);
|
||||
|
||||
|
||||
// Buttons
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_3, 0), BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_1, 0), BOOM_SPEED);
|
||||
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED);
|
||||
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 0));
|
||||
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 1));
|
||||
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(BUTTON_4, 0));
|
||||
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(BUTTON_4, 1));
|
||||
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND));
|
||||
mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));
|
||||
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0));
|
||||
mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1));
|
||||
|
||||
|
|
|
@ -230,7 +230,7 @@ void UserInputMapper::update(float deltaTime) {
|
|||
for (auto i = 0; i < NUM_ACTIONS; i++) {
|
||||
_actionStates[i] *= _actionScales[i];
|
||||
// Emit only on change, and emit when moving back to 0
|
||||
if (fabs(_actionStates[i] - _lastActionStates[i]) > EPSILON) {
|
||||
if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) {
|
||||
_lastActionStates[i] = _actionStates[i];
|
||||
emit actionEvent(i, _actionStates[i]);
|
||||
}
|
||||
|
@ -319,4 +319,4 @@ void UserInputMapper::createActionNames() {
|
|||
_actionNames[SHIFT] = "SHIFT";
|
||||
_actionNames[ACTION1] = "ACTION1";
|
||||
_actionNames[ACTION2] = "ACTION2";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -324,9 +324,64 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) {
|
|||
glm::vec3 position = extractTranslation(mat);
|
||||
glm::quat rotation = glm::quat_cast(mat);
|
||||
|
||||
// Flip the rotation appropriately for each hand
|
||||
int sign = index == LEFT_HAND ? 1 : -1;
|
||||
rotation = rotation * glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::angleAxis(sign * PI_OVER_TWO, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
// When the sensor-to-world rotation is identity the coordinate axes look like this:
|
||||
//
|
||||
// user
|
||||
// forward
|
||||
// z
|
||||
// |
|
||||
// y| user
|
||||
// y o----x right
|
||||
// o-----x user
|
||||
// | up
|
||||
// |
|
||||
// z
|
||||
//
|
||||
// Vive
|
||||
//
|
||||
|
||||
// From ABOVE the hand canonical axes looks like this:
|
||||
//
|
||||
// | | | | y | | | |
|
||||
// | | | | | | | | |
|
||||
// | | | | |
|
||||
// |left | / x---- + \ |right|
|
||||
// | _/ z \_ |
|
||||
// | | | |
|
||||
// | | | |
|
||||
//
|
||||
|
||||
// So when the user is standing in Vive space facing the -zAxis with hands outstretched and palms down
|
||||
// the rotation to align the Vive axes with those of the hands is:
|
||||
//
|
||||
// QviveToHand = halfTurnAboutY * quaterTurnAboutX
|
||||
|
||||
// Due to how the Vive controllers fit into the palm there is an offset that is different for each hand.
|
||||
// You can think of this offset as the inverse of the measured rotation when the hands are posed, such that
|
||||
// the combination (measurement * offset) is identity at this orientation.
|
||||
//
|
||||
// Qoffset = glm::inverse(deltaRotation when hand is posed fingers forward, palm down)
|
||||
//
|
||||
// An approximate offset for the Vive can be obtained by inpection:
|
||||
//
|
||||
// Qoffset = glm::inverse(glm::angleAxis(sign * PI/4.0f, zAxis) * glm::angleAxis(PI/2.0f, xAxis))
|
||||
//
|
||||
|
||||
// Finally there is another flip around the yAxis to re-align from model to Vive space, so the full equation is:
|
||||
//
|
||||
// Q = yFlip * combinedMeasurement * viveToHand
|
||||
//
|
||||
// Q = yFlip * (deltaQ * QOffset) * (yFlip * quarterTurnAboutX)
|
||||
//
|
||||
// Q = yFlip * (deltaQ * inverse(deltaQForAlignedHand)) * (yFlip * quarterTurnAboutX)
|
||||
|
||||
const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
const glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
float sign = (index == LEFT_HAND) ? -1.0f : 1.0f;
|
||||
const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
const glm::quat offset = glm::inverse(signedQuaterZ * eighthX);
|
||||
rotation = yFlip * rotation * offset * yFlip * quarterX;
|
||||
|
||||
position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET);
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ PacketVersion versionForPacketType(PacketType::Value packetType) {
|
|||
case EntityData:
|
||||
return VERSION_ENTITIES_PARTICLE_MODIFICATIONS;
|
||||
case AvatarData:
|
||||
return 12;
|
||||
return 13;
|
||||
default:
|
||||
return 11;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -243,7 +243,7 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons
|
|||
batch._glUniform2f(_depthTexCoordScaleLoc, depthTexCoordScaleS, depthTexCoordScaleT);
|
||||
|
||||
batch._glUniform2f(_renderTargetResLoc, fbWidth, fbHeight);
|
||||
batch._glUniform2f(_renderTargetResInvLoc, 1.0/fbWidth, 1.0/fbHeight);
|
||||
batch._glUniform2f(_renderTargetResInvLoc, 1.0f / fbWidth, 1.0f / fbHeight);
|
||||
|
||||
glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glm::vec2 bottomLeft(-1.0f, -1.0f);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
@ -176,6 +175,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; }
|
||||
|
||||
|
@ -236,6 +241,8 @@ protected:
|
|||
QSharedPointer<NetworkGeometry> _geometry;
|
||||
void setGeometry(const QSharedPointer<NetworkGeometry>& newGeometry);
|
||||
|
||||
glm::vec3 _translation;
|
||||
glm::quat _rotation;
|
||||
glm::vec3 _scale;
|
||||
glm::vec3 _offset;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -599,7 +599,7 @@ void ScriptEngine::run() {
|
|||
/ (1000 * 1000)) + 0.5);
|
||||
const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t);
|
||||
|
||||
QByteArray avatarByteArray = _avatarData->toByteArray();
|
||||
QByteArray avatarByteArray = _avatarData->toByteArray(true);
|
||||
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size());
|
||||
|
||||
avatarPacket->write(avatarByteArray);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
#include "WebSocketServerClass.h"
|
||||
|
||||
WebSocketServerClass::WebSocketServerClass(QScriptEngine* engine, const QString& serverName, const quint16 port) :
|
||||
_engine(engine),
|
||||
_webSocketServer(serverName, QWebSocketServer::SslMode::NonSecureMode)
|
||||
_webSocketServer(serverName, QWebSocketServer::SslMode::NonSecureMode),
|
||||
_engine(engine)
|
||||
{
|
||||
if (_webSocketServer.listen(QHostAddress::Any, port)) {
|
||||
connect(&_webSocketServer, &QWebSocketServer::newConnection, this, &WebSocketServerClass::onNewConnection);
|
||||
|
|
|
@ -372,41 +372,37 @@ QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) {
|
|||
// create matrix from orientation and position
|
||||
glm::mat4 createMatFromQuatAndPos(const glm::quat& q, const glm::vec3& p) {
|
||||
glm::mat4 m = glm::mat4_cast(q);
|
||||
m[3] = glm::vec4(p, 1);
|
||||
m[3] = glm::vec4(p, 1.0f);
|
||||
return m;
|
||||
}
|
||||
|
||||
// cancel out roll and pitch
|
||||
glm::quat cancelOutRollAndPitch(const glm::quat& q) {
|
||||
glm::vec3 xAxis = q * glm::vec3(1, 0, 0);
|
||||
glm::vec3 yAxis = q * glm::vec3(0, 1, 0);
|
||||
glm::vec3 zAxis = q * glm::vec3(0, 0, 1);
|
||||
glm::vec3 zAxis = q * glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
|
||||
// cancel out the roll and pitch
|
||||
glm::vec3 newZ = (zAxis.x == 0 && zAxis.z == 0) ? vec3(1, 0, 0) : glm::normalize(vec3(zAxis.x, 0, zAxis.z));
|
||||
glm::vec3 newX = glm::cross(vec3(0, 1, 0), newZ);
|
||||
glm::vec3 newZ = (zAxis.x == 0 && zAxis.z == 0.0f) ? vec3(1.0f, 0.0f, 0.0f) : glm::normalize(vec3(zAxis.x, 0.0f, zAxis.z));
|
||||
glm::vec3 newX = glm::cross(vec3(0.0f, 1.0f, 0.0f), newZ);
|
||||
glm::vec3 newY = glm::cross(newZ, newX);
|
||||
|
||||
glm::mat4 temp(glm::vec4(newX, 0), glm::vec4(newY, 0), glm::vec4(newZ, 0), glm::vec4(0, 0, 0, 1));
|
||||
glm::mat4 temp(glm::vec4(newX, 0.0f), glm::vec4(newY, 0.0f), glm::vec4(newZ, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
return glm::quat_cast(temp);
|
||||
}
|
||||
|
||||
// cancel out roll and pitch
|
||||
glm::mat4 cancelOutRollAndPitch(const glm::mat4& m) {
|
||||
glm::vec3 xAxis = glm::vec3(m[0]);
|
||||
glm::vec3 yAxis = glm::vec3(m[1]);
|
||||
glm::vec3 zAxis = glm::vec3(m[2]);
|
||||
|
||||
// cancel out the roll and pitch
|
||||
glm::vec3 newZ = (zAxis.x == 0 && zAxis.z == 0) ? vec3(1, 0, 0) : glm::normalize(vec3(zAxis.x, 0, zAxis.z));
|
||||
glm::vec3 newX = glm::cross(vec3(0, 1, 0), newZ);
|
||||
glm::vec3 newZ = (zAxis.x == 0.0f && zAxis.z == 0.0f) ? vec3(1.0f, 0.0f, 0.0f) : glm::normalize(vec3(zAxis.x, 0.0f, zAxis.z));
|
||||
glm::vec3 newX = glm::cross(vec3(0.0f, 1.0f, 0.0f), newZ);
|
||||
glm::vec3 newY = glm::cross(newZ, newX);
|
||||
|
||||
glm::mat4 temp(glm::vec4(newX, 0), glm::vec4(newY, 0), glm::vec4(newZ, 0), m[3]);
|
||||
glm::mat4 temp(glm::vec4(newX, 0.0f), glm::vec4(newY, 0.0f), glm::vec4(newZ, 0.0f), m[3]);
|
||||
return temp;
|
||||
}
|
||||
|
||||
glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& p) {
|
||||
glm::vec4 temp = m * glm::vec4(p, 1);
|
||||
glm::vec4 temp = m * glm::vec4(p, 1.0f);
|
||||
return glm::vec3(temp.x / temp.w, temp.y / temp.w, temp.z / temp.w);
|
||||
}
|
||||
|
|
|
@ -93,8 +93,8 @@ template <typename T>
|
|||
void testByteCountCoded() {
|
||||
testByteCountCodedStable<T>(0);
|
||||
testByteCountCodedStable<T>(1);
|
||||
testByteCountCodedStable<T>(1 << 16);
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max() >> 16);
|
||||
testByteCountCodedStable<T>(1 << 8*sizeof(T));
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max() >> 8*sizeof(T));
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max() >> 8);
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max() >> 1);
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max());
|
||||
|
|
|
@ -342,8 +342,8 @@ public:
|
|||
glm::vec3 unitscale { 1.0f };
|
||||
glm::vec3 up { 0.0f, 1.0f, 0.0f };
|
||||
|
||||
glm::vec3 cam_pos { 1.5f * sin(t), 0.0f, 2.0f };
|
||||
// glm::vec3 camera_focus { 5.0f * cos(t * 0.1f), 0.0f, 0.0f };
|
||||
glm::vec3 cam_pos { 1.5f * sinf(t), 0.0f, 2.0f };
|
||||
// glm::vec3 camera_focus { 5.0f * cosf(t * 0.1f), 0.0f, 0.0f };
|
||||
glm::vec3 camera_focus { 0.0f, 0.0f, 0.0f };
|
||||
glm::quat cam_rotation;
|
||||
// glm::quat cam_rotation = glm::quat_cast(glm::lookAt(cam_pos, camera_focus, up));
|
||||
|
|
Loading…
Reference in a new issue