merge from upstream

This commit is contained in:
Seth Alves 2015-11-03 13:35:17 -08:00
commit dba9eb7046
66 changed files with 1702 additions and 511 deletions

View file

@ -4,10 +4,22 @@ set(EXTERNAL_NAME sixense)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
#set(SIXENSE_URL "https://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_062612.zip")
#set(SIXENSE_URL_MD5 "10cc8dc470d2ac1244a88cf04bc549cc")
#set(SIXENSE_NEW_LAYOUT 0)
#set(SIXENSE_URL "https://public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip")
#set(SIXENSE_URL_MD5 "752a3901f334124e9cffc2ba4136ef7d")
#set(SIXENSE_NEW_LAYOUT 1)
set(SIXENSE_URL "https://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip")
set(SIXENSE_URL_MD5 "93c3a6795cce777a0f472b09532935f1")
set(SIXENSE_NEW_LAYOUT 1)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip
URL_MD5 752a3901f334124e9cffc2ba4136ef7d
URL ${SIXENSE_URL}
URL_MD5 ${SIXENSE_URL_MD5}
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
@ -30,8 +42,18 @@ if (WIN32)
set(ARCH_SUFFIX "")
endif()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2013/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL)
add_paths_to_fixup_libs("${SOURCE_DIR}/bin/${ARCH_DIR}/VS2013/release_dll")
if (${SIXENSE_NEW_LAYOUT})
# for 2015 SDKs (using the 2013 versions may be causing the crash)
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/${ARCH_DIR}/VS2010/release_dll")
set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2010/release_dll")
else()
# for the 2012 SDK
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/${ARCH_DIR}/release_dll")
set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/lib/${ARCH_DIR}/release_dll")
endif()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL)
add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_DLL_PATH}")
elseif(APPLE)

View file

@ -10,5 +10,35 @@
#
include(BundleUtilities)
# replace copy_resolved_item_into_bundle
#
# The official version of copy_resolved_item_into_bundle will print out a "warning:" when
# the resolved item matches the resolved embedded item. This not not really an issue that
# should rise to the level of a "warning" so we replace this message with a "status:"
#
function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item)
if(WIN32)
# ignore case on Windows
string(TOLOWER "${resolved_item}" resolved_item_compare)
string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
else()
set(resolved_item_compare "${resolved_item}")
set(resolved_embedded_item_compare "${resolved_embedded_item}")
endif()
if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
# this is our only change from the original version
message(STATUS "status: resolved_item == resolved_embedded_item - not copying...")
else()
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
if(UNIX AND NOT APPLE)
file(RPATH_REMOVE FILE "${resolved_embedded_item}")
endif()
endif()
endfunction()
message(STATUS "FIXUP_LIBS for fixup_bundle called for bundle ${BUNDLE_EXECUTABLE} are @FIXUP_LIBS@")
fixup_bundle("${BUNDLE_EXECUTABLE}" "" "@FIXUP_LIBS@")

View file

@ -352,14 +352,14 @@ function MyController(hand) {
var intersection = Entities.findRayIntersection(pickRayBacked, true);
if (intersection.intersects && intersection.properties.locked === 0) {
if (intersection.intersects && !intersection.properties.locked) {
// the ray is intersecting something we can move.
var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection);
this.grabbedEntity = intersection.entityID;
//this code will disabled the beam for the opposite hand of the one that grabbed it if the entity says so
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA);
if (grabbableData["turnOffOppositeBeam"] === true) {
if (grabbableData["turnOffOppositeBeam"]) {
if (this.hand === RIGHT_HAND) {
disabledHand = LEFT_HAND;
} else {
@ -369,7 +369,7 @@ function MyController(hand) {
disabledHand = 'none';
}
if (grabbableData.grabbable === false) {
if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) {
this.grabbedEntity = null;
continue;
}
@ -379,7 +379,6 @@ function MyController(hand) {
}
if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) {
// the hand is very close to the intersected object. go into close-grabbing mode.
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
if (grabbableData.wantsTrigger) {
this.setState(STATE_NEAR_GRABBING_NON_COLLIDING);
} else {
@ -391,7 +390,7 @@ function MyController(hand) {
this.grabbedEntity = null;
} else {
// the hand is far from the intersected object. go into distance-holding mode
if (intersection.properties.collisionsWillMove === 1) {
if (intersection.properties.collisionsWillMove) {
this.setState(STATE_DISTANCE_HOLDING);
} else {
this.setState(STATE_FAR_GRABBING_NON_COLLIDING);
@ -409,7 +408,7 @@ function MyController(hand) {
for (i = 0; i < nearbyEntities.length; i++) {
var grabbableDataForCandidate =
getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA);
if (grabbableDataForCandidate.grabbable === false) {
if (!grabbableDataForCandidate.grabbable) {
continue;
}
var propsForCandidate =
@ -427,7 +426,7 @@ function MyController(hand) {
}
if (grabbableData.wantsTrigger) {
this.setState(STATE_NEAR_GRABBING_NON_COLLIDING);
} else if (props.locked === 0) {
} else if (!props.locked) {
this.setState(STATE_NEAR_GRABBING);
}
}
@ -446,7 +445,7 @@ function MyController(hand) {
this.currentObjectPosition = grabbedProperties.position;
this.currentObjectRotation = grabbedProperties.rotation;
this.currentObjectTime = now;
this.handPreviousPosition = handControllerPosition;
this.handRelativePreviousPosition = Vec3.subtract(handControllerPosition, MyAvatar.position);
this.handPreviousRotation = handRotation;
this.actionID = NULL_ACTION_ID;
@ -523,11 +522,10 @@ function MyController(hand) {
this.currentAvatarOrientation = currentOrientation;
// how far did hand move this timestep?
var handMoved = Vec3.subtract(handControllerPosition, this.handPreviousPosition);
this.handPreviousPosition = handControllerPosition;
var handMoved = Vec3.subtract(handToAvatar, this.handRelativePreviousPosition);
this.handRelativePreviousPosition = handToAvatar;
// magnify the hand movement but not the change from avatar movement & rotation
handMoved = Vec3.subtract(handMoved, avatarDeltaPosition);
handMoved = Vec3.subtract(handMoved, handMovementFromTurning);
var superHandMoved = Vec3.multiply(handMoved, radius);
@ -570,7 +568,7 @@ function MyController(hand) {
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
var turnOffOtherHand = grabbableData["turnOffOtherHand"];
if (turnOffOtherHand === true) {
if (turnOffOtherHand) {
//don't activate the second hand grab because the script is handling the second hand logic
return;
}
@ -783,11 +781,11 @@ function MyController(hand) {
// we haven't been touched before, but either right or left is touching us now
_this.allTouchedIDs[id] = true;
_this.startTouch(id);
} else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === true) {
} else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) {
// we have been touched before and are still being touched
// continue touch
_this.continueTouch(id);
} else if (_this.allTouchedIDs[id] === true) {
} else if (_this.allTouchedIDs[id]) {
delete _this.allTouchedIDs[id];
_this.stopTouch(id);

View file

@ -0,0 +1,10 @@
var MAPPING_NAME = "com.highfidelity.rightClickExample";
var mapping = Controller.newMapping(MAPPING_NAME);
mapping.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) {
print("Keyboard.RightMouseClicked");
});
Controller.enableMapping(MAPPING_NAME);
Script.scriptEnding.connect(function () {
Controller.disableMapping(MAPPING_NAME);
});

341
examples/libraries/fjs.js Normal file
View file

@ -0,0 +1,341 @@
/*
http://functionaljs.com/
https://github.com/leecrossley/functional-js/
The MIT License (MIT)
Copyright © 2015 Lee Crossley <leee@hotmail.co.uk>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the Software), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
loadFJS = function(){
return fjs();
}
var fjs = function() {
"use strict";
var fjs = {},
hardReturn = "hardReturn;";
var lambda = function(exp) {
if (!fjs.isString(exp)) {
return;
}
var parts = exp.match(/(.*)\s*[=-]>\s*(.*)/);
parts.shift();
var params = parts.shift()
.replace(/^\s*|\s(?=\s)|\s*$|,/g, "").split(" ");
var body = parts.shift();
parts = ((!/\s*return\s+/.test(body)) ? "return " : "") + body;
params.push(parts);
return Function.apply({}, params);
};
var sliceArgs = function(args) {
return args.length > 0 ? [].slice.call(args, 0) : [];
};
fjs.isFunction = function(obj) {
return !!(obj && obj.constructor && obj.call && obj.apply);
};
fjs.isObject = function(obj) {
return fjs.isFunction(obj) || (!!obj && typeof(obj) === "object");
};
fjs.isArray = function(obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
};
var checkFunction = function(func) {
if (!fjs.isFunction(func)) {
func = lambda(func);
if (!fjs.isFunction(func)) {
throw "fjs Error: Invalid function";
}
}
return func;
};
fjs.curry = function(func) {
func = checkFunction(func);
return function inner() {
var _args = sliceArgs(arguments);
if (_args.length === func.length) {
return func.apply(null, _args);
} else if (_args.length > func.length) {
var initial = func.apply(null, _args);
return fjs.fold(func, initial, _args.slice(func.length));
} else {
return function() {
var args = sliceArgs(arguments);
return inner.apply(null, _args.concat(args));
};
}
};
};
fjs.each = fjs.curry(function(iterator, items) {
iterator = checkFunction(iterator);
if (!fjs.exists(items) || !fjs.isArray(items)) {
return;
}
for (var i = 0; i < items.length; i += 1) {
if (iterator.call(null, items[i], i) === hardReturn) {
return;
}
}
});
fjs.map = fjs.curry(function(iterator, items) {
iterator = checkFunction(iterator);
var mapped = [];
fjs.each(function() {
mapped.push(iterator.apply(null, arguments));
}, items);
return mapped;
});
fjs.fold = fjs.foldl = fjs.curry(function(iterator, cumulate, items) {
iterator = checkFunction(iterator);
fjs.each(function(item, i) {
cumulate = iterator.call(null, cumulate, item, i);
}, items);
return cumulate;
});
fjs.reduce = fjs.reducel = fjs.foldll = fjs.curry(function(iterator, items) {
iterator = checkFunction(iterator);
var cumulate = items[0];
items.shift();
return fjs.fold(iterator, cumulate, items);
});
fjs.clone = function(items) {
var clone = [];
fjs.each(function(item) {
clone.push(item);
}, items);
return clone;
};
fjs.first = fjs.head = fjs.take = fjs.curry(function(iterator, items) {
iterator = checkFunction(iterator);
var first;
fjs.each(function(item) {
if (iterator.call(null, item)) {
first = item;
return hardReturn;
}
}, items);
return first;
});
fjs.rest = fjs.tail = fjs.drop = fjs.curry(function(iterator, items) {
var result = fjs.select(iterator, items);
result.shift();
return result;
});
fjs.last = fjs.curry(function(iterator, items) {
var itemsClone = fjs.clone(items);
return fjs.first(iterator, itemsClone.reverse());
});
fjs.every = fjs.all = fjs.curry(function(iterator, items) {
iterator = checkFunction(iterator);
var isEvery = true;
fjs.each(function(item) {
if (!iterator.call(null, item)) {
isEvery = false;
return hardReturn;
}
}, items);
return isEvery;
});
fjs.any = fjs.contains = fjs.curry(function(iterator, items) {
iterator = checkFunction(iterator);
var isAny = false;
fjs.each(function(item) {
if (iterator.call(null, item)) {
isAny = true;
return hardReturn;
}
}, items);
return isAny;
});
fjs.select = fjs.filter = fjs.curry(function(iterator, items) {
iterator = checkFunction(iterator);
var filtered = [];
fjs.each(function(item) {
if (iterator.call(null, item)) {
filtered.push(item);
}
}, items);
return filtered;
});
fjs.best = fjs.curry(function(iterator, items) {
iterator = checkFunction(iterator);
var compare = function(arg1, arg2) {
return iterator.call(this, arg1, arg2) ?
arg1 : arg2;
};
return fjs.reduce(compare, items);
});
fjs._while = fjs.curry(function(iterator, items) {
iterator = checkFunction(iterator);
var result = [];
fjs.each(function(item) {
if (iterator.call(null, item)) {
result.push(item);
} else {
return hardReturn;
}
}, items);
return result;
});
fjs.compose = function(funcs) {
var anyInvalid = fjs.any(function(func) {
return !fjs.isFunction(func);
});
funcs = sliceArgs(arguments).reverse();
if (anyInvalid(funcs)) {
throw "fjs Error: Invalid function to compose";
}
return function() {
var args = arguments;
var applyEach = fjs.each(function(func) {
args = [func.apply(null, args)];
});
applyEach(funcs);
return args[0];
};
};
fjs.partition = fjs.curry(function(iterator, items) {
iterator = checkFunction(iterator);
var truthy = [],
falsy = [];
fjs.each(function(item) {
(iterator.call(null, item) ? truthy : falsy).push(item);
}, items);
return [truthy, falsy];
});
fjs.group = fjs.curry(function(iterator, items) {
iterator = checkFunction(iterator);
var result = {};
var group;
fjs.each(function(item) {
group = iterator.call(null, item);
result[group] = result[group] || [];
result[group].push(item);
}, items);
return result;
});
fjs.shuffle = function(items) {
var j, t;
fjs.each(function(item, i) {
j = Math.floor(Math.random() * (i + 1));
t = items[i];
items[i] = items[j];
items[j] = t;
}, items);
return items;
};
fjs.toArray = function(obj) {
return fjs.map(function(key) {
return [key, obj[key]];
}, Object.keys(obj));
};
fjs.apply = fjs.curry(function(func, items) {
var args = [];
if (fjs.isArray(func)) {
args = [].slice.call(func, 1);
func = func[0];
}
return fjs.map(function(item) {
return item[func].apply(item, args);
}, items);
});
fjs.assign = fjs.extend = fjs.curry(function(obj1, obj2) {
fjs.each(function(key) {
obj2[key] = obj1[key];
}, Object.keys(obj1));
return obj2;
});
fjs.prop = function(prop) {
return function(obj) {
return obj[prop];
};
};
fjs.pluck = fjs.curry(function(prop, items) {
return fjs.map(fjs.prop(prop), items);
});
fjs.nub = fjs.unique = fjs.distinct = fjs.curry(function(comparator, items) {
var unique = items.length > 0 ? [items[0]] : [];
fjs.each(function(item) {
if (!fjs.any(fjs.curry(comparator)(item), unique)) {
unique[unique.length] = item;
}
}, items);
return unique;
});
fjs.exists = function(obj) {
return obj != null; // jshint ignore:line
};
fjs.truthy = function(obj) {
return fjs.exists(obj) && obj !== false;
};
fjs.falsy = function(obj) {
return !fjs.truthy(obj);
};
fjs.each(function(type) {
fjs["is" + type] = function(obj) {
return Object.prototype.toString.call(obj) === "[object " + type + "]";
};
}, ["Arguments", "Date", "Number", "RegExp", "String"]);
return fjs;
}

View file

@ -0,0 +1,9 @@
Script.include('fjs.js');
var fjs = loadFJS();
var concatenate = fjs.curry(function(word1, word2) {
return word1 + " " + word2;
});
var concatenateHello = concatenate("Hello");
var hi = concatenateHello("World");
print('anyone listening? ' + hi)

View file

@ -2,7 +2,7 @@ set(TARGET_NAME interface)
project(${TARGET_NAME})
# set a default root dir for each of our optional externals if it was not passed
set(OPTIONAL_EXTERNALS "LeapMotion" "RtMidi" "RSSDK" "iViewHMD")
set(OPTIONAL_EXTERNALS "LeapMotion" "RtMidi" "RSSDK")
if(WIN32)
list(APPEND OPTIONAL_EXTERNALS "3DConnexionClient")

View file

@ -13,17 +13,12 @@
{ "from": "Hydra.RB", "to": "Standard.RB" },
{ "from": "Hydra.RS", "to": "Standard.RS" },
{ "from": "Hydra.L0", "to": "Standard.Back" },
{ "from": "Hydra.L1", "to": "Standard.DL" },
{ "from": "Hydra.L2", "to": "Standard.DD" },
{ "from": "Hydra.L3", "to": "Standard.DR" },
{ "from": "Hydra.L4", "to": "Standard.DU" },
{ "from": [ "Hydra.L3", "Hydra.L4" ], "to": "Standard.LeftPrimaryThumb" },
{ "from": [ "Hydra.L1", "Hydra.L2" ], "to": "Standard.LeftSecondaryThumb" },
{ "from": [ "Hydra.R3", "Hydra.R4" ], "to": "Standard.RightPrimaryThumb" },
{ "from": [ "Hydra.R1", "Hydra.R2" ], "to": "Standard.RightSecondaryThumb" },
{ "from": "Hydra.R0", "to": "Standard.Start" },
{ "from": "Hydra.R1", "to": "Standard.X" },
{ "from": "Hydra.R2", "to": "Standard.A" },
{ "from": "Hydra.R3", "to": "Standard.B" },
{ "from": "Hydra.R4", "to": "Standard.Y" },
{ "from": "Hydra.LeftHand", "to": "Standard.LeftHand" },
{ "from": "Hydra.RightHand", "to": "Standard.RightHand" }

View file

@ -4,8 +4,8 @@
{ "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.A", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.D", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.A", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.D", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.E", "when": "Keyboard.Shift", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.05 } ] },
{ "from": "Keyboard.C", "when": "Keyboard.Shift", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.05 } ] },
{ "from": "Keyboard.S", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" },
@ -13,7 +13,7 @@
{ "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] },
"when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseClick" ],
"when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseButton" ],
"to": "Actions.StepYaw",
"filters":
[
@ -46,7 +46,7 @@
},
{ "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] },
"when": "Keyboard.RightMouseClick",
"when": "Keyboard.RightMouseButton",
"to": "Actions.Yaw"
},
@ -55,8 +55,8 @@
{ "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" },
{ "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" },
{ "from": "Keyboard.Left", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.Right", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.Left", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.Right", "when": "Keyboard.RightMouseButton", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.Down", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" },
@ -68,8 +68,8 @@
{ "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" },
{ "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" },
{ "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_UP" },
{ "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_DOWN" },
{ "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_UP" },
{ "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_DOWN" },
{ "from": "Keyboard.TouchpadDown", "to": "Actions.PITCH_DOWN" },
{ "from": "Keyboard.TouchpadUp", "to": "Actions.PITCH_UP" },
@ -82,6 +82,6 @@
{ "from": "Keyboard.Space", "to": "Actions.SHIFT" },
{ "from": "Keyboard.R", "to": "Actions.ACTION1" },
{ "from": "Keyboard.T", "to": "Actions.ACTION2" },
{ "from": "Keyboard.RightMouseClick", "to": "Actions.ContextMenu" }
{ "from": "Keyboard.RightMouseClicked", "to": "Actions.ContextMenu" }
]
}

View file

@ -2,9 +2,8 @@
"name": "Standard to Action",
"channels": [
{ "from": "Standard.LY", "to": "Actions.TranslateZ" },
{ "from": "Standard.LX", "to": "Actions.TranslateX" },
{ "from": "Standard.RX",
{ "from": "Standard.LX",
"when": [ "Application.InHMD", "Application.ComfortMode" ],
"to": "Actions.StepYaw",
"filters":
@ -14,8 +13,9 @@
]
},
{ "from": "Standard.LX", "to": "Actions.Yaw" },
{ "from": "Standard.RX", "to": "Actions.Yaw" },
{ "from": "Standard.RX", "to": "Actions.TranslateX" },
{ "from": "Standard.RY", "filters": "invert", "to": "Actions.TranslateY" },

View file

@ -1,15 +1,15 @@
{
"name": "Vive to Standard",
"channels": [
{ "from": "Vive.LY", "filters": [ "invert", { "type": "deadZone", "min": 0.7 } ], "to": "Standard.LY" },
{ "from": "Vive.LX", "filters": { "type": "deadZone", "min": 0.7 }, "to": "Standard.LX" },
{ "from": "Vive.LY", "when": "Vive.LS", "filters": "invert", "to": "Standard.LY" },
{ "from": "Vive.LX", "when": "Vive.LS", "to": "Standard.LX" },
{ "from": "Vive.LT", "to": "Standard.LT" },
{ "from": "Vive.LB", "to": "Standard.LB" },
{ "from": "Vive.LS", "to": "Standard.LS" },
{ "from": "Vive.RY", "filters": "invert", "to": "Standard.RY" },
{ "from": "Vive.RX", "to": "Standard.RX" },
{ "from": "Vive.RY", "when": "Vive.RS", "filters": "invert", "to": "Standard.RY" },
{ "from": "Vive.RX", "when": "Vive.RS", "to": "Standard.RX" },
{ "from": "Vive.RT", "to": "Standard.RT" },
{ "from": "Vive.RB", "to": "Standard.RB" },

View file

@ -523,29 +523,89 @@
},
{
"id": "walkFwd",
"type": "clip",
"type": "blendLinearMove",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx",
"startFrame": 0.0,
"endFrame": 35.0,
"timeScale": 1.0,
"loopFlag": true,
"timeScaleVar": "walkTimeScale"
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0.5, 1.4, 4.5],
"alphaVar": "moveForwardAlpha",
"desiredSpeedVar": "moveForwardSpeed"
},
"children": []
"children": [
{
"id": "walkFwdShort",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_fwd.fbx",
"startFrame": 0.0,
"endFrame": 39.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "walkFwdNormal",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx",
"startFrame": 0.0,
"endFrame": 35.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "walkFwdRun",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/run_fwd.fbx",
"startFrame": 0.0,
"endFrame": 21.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
}
]
},
{
"id": "walkBwd",
"type": "clip",
"type": "blendLinearMove",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx",
"startFrame": 0.0,
"endFrame": 37.0,
"timeScale": 1.0,
"loopFlag": true,
"timeScaleVar": "walkTimeScale"
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0.6, 1.45],
"alphaVar": "moveBackwardAlpha",
"desiredSpeedVar": "moveBackwardSpeed"
},
"children": []
"children": [
{
"id": "walkBwdShort",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_bwd.fbx",
"startFrame": 0.0,
"endFrame": 38.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "walkBwdNormal",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx",
"startFrame": 0.0,
"endFrame": 36.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
}
]
},
{
"id": "turnLeft",
@ -573,27 +633,77 @@
},
{
"id": "strafeLeft",
"type": "clip",
"type": "blendLinearMove",
"data": {
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_left.fbx",
"startFrame": 0.0,
"endFrame": 31.0,
"timeScale": 1.0,
"loopFlag": true
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0.2, 0.65],
"alphaVar": "moveLateralAlpha",
"desiredSpeedVar": "moveLateralSpeed"
},
"children": []
"children": [
{
"id": "strafeLeftShort",
"type": "clip",
"data": {
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_left.fbx",
"startFrame": 0.0,
"endFrame": 28.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "strafeLeftNormal",
"type": "clip",
"data": {
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_left.fbx",
"startFrame": 0.0,
"endFrame": 30.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
}
]
},
{
"id": "strafeRight",
"type": "clip",
"type": "blendLinearMove",
"data": {
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_right.fbx",
"startFrame": 0.0,
"endFrame": 31.0,
"timeScale": 1.0,
"loopFlag": true
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0.2, 0.65],
"alphaVar": "moveLateralAlpha",
"desiredSpeedVar": "moveLateralSpeed"
},
"children": []
"children": [
{
"id": "strafeRightShort",
"type": "clip",
"data": {
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_right.fbx",
"startFrame": 0.0,
"endFrame": 28.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "strafeRightNormal",
"type": "clip",
"data": {
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_right.fbx",
"startFrame": 0.0,
"endFrame": 30.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
}
]
}
]
}

View file

@ -72,6 +72,7 @@ namespace render {
avatarPtr->setDisplayingLookatTarget(renderLookAtTarget);
if (avatarPtr->isInitialized() && args) {
PROFILE_RANGE_BATCH(*args->_batch, "renderAvatarPayload");
avatarPtr->render(args, qApp->getCamera()->getPosition());
}
}
@ -304,6 +305,7 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::S
void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
auto& batch = *renderArgs->_batch;
PROFILE_RANGE_BATCH(batch, __FUNCTION__);
if (glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), getPosition()) < 10.0f) {
auto geometryCache = DependencyManager::get<GeometryCache>();
@ -330,6 +332,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
}
if (havePosition && haveRotation) {
PROFILE_RANGE_BATCH(batch, __FUNCTION__":leftHandPointer");
Transform pointerTransform;
pointerTransform.setTranslation(position);
pointerTransform.setRotation(rotation);
@ -353,6 +356,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
}
if (havePosition && haveRotation) {
PROFILE_RANGE_BATCH(batch, __FUNCTION__":rightHandPointer");
Transform pointerTransform;
pointerTransform.setTranslation(position);
pointerTransform.setRotation(rotation);
@ -424,6 +428,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel.isRenderable()) {
PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes");
_skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f);
}
@ -432,7 +437,9 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
static const float INDICATOR_OFFSET = 0.22f;
static const float INDICATOR_RADIUS = 0.03f;
static const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f };
glm::vec3 position = glm::vec3(getPosition().x, getDisplayNamePosition().y + INDICATOR_OFFSET, getPosition().z);
glm::vec3 avatarPosition = getPosition();
glm::vec3 position = glm::vec3(avatarPosition.x, getDisplayNamePosition().y + INDICATOR_OFFSET, avatarPosition.z);
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderFocusIndicator");
Transform transform;
transform.setTranslation(position);
transform.postScale(INDICATOR_RADIUS);
@ -441,6 +448,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
// If the avatar is looking at me, indicate that they are
if (getHead()->isLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) {
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderLookingAtMe");
const glm::vec3 LOOKING_AT_ME_COLOR = { 1.0f, 1.0f, 1.0f };
const float LOOKING_AT_ME_ALPHA_START = 0.8f;
const float LOOKING_AT_ME_DURATION = 0.5f; // seconds
@ -486,6 +494,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
const float MIN_VOICE_SPHERE_DISTANCE = 12.0f;
if (Menu::getInstance()->isOptionChecked(MenuOption::BlueSpeechSphere)
&& distanceToTarget > MIN_VOICE_SPHERE_DISTANCE) {
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderVoiceSphere");
// render voice intensity sphere for avatars that are farther away
const float MAX_SPHERE_ANGLE = 10.0f * RADIANS_PER_DEGREE;
@ -621,6 +630,9 @@ void Avatar::updateJointMappings() {
}
void Avatar::renderBillboard(RenderArgs* renderArgs) {
// FIXME disabling the billboard because it doesn't appear to work reliably
// the billboard is ending up with a random texture and position.
return;
if (_billboard.isEmpty()) {
return;
}
@ -652,6 +664,7 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) {
glm::vec2 texCoordBottomRight(1.0f, 1.0f);
gpu::Batch& batch = *renderArgs->_batch;
PROFILE_RANGE_BATCH(batch, __FUNCTION__);
batch.setResourceTexture(0, _billboardTexture->getGPUTexture());
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
@ -734,6 +747,8 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, cons
}
void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, const glm::vec3& textPosition) const {
PROFILE_RANGE_BATCH(batch, __FUNCTION__);
bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats() && !isMyAvatar();
// If we have nothing to draw, or it's totally transparent, or it's too close or behind the camera, return
@ -784,17 +799,24 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co
// Test on extent above insures abs(height) > 0.0f
textTransform.postScale(1.0f / height);
batch.setModelTransform(textTransform);
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, true, true, true);
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
bevelDistance, backgroundColor);
{
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect");
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, true, true, true);
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
bevelDistance, backgroundColor);
}
// Render actual name
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
// Render text slightly in front to avoid z-fighting
textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * renderer->getFontSize()));
batch.setModelTransform(textTransform);
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
{
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText");
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
}
}
}
@ -1059,6 +1081,7 @@ void Avatar::renderJointConnectingCone(gpu::Batch& batch, glm::vec3 position1, g
points << p1a << p1b << p2a << p1b << p2a << p2b;
}
PROFILE_RANGE_BATCH(batch, __FUNCTION__);
// TODO: this is really inefficient constantly recreating these vertices buffers. It would be
// better if the avatars cached these buffers for each of the joints they are rendering
geometryCache->updateVertices(_jointConesID, points, color);

View file

@ -156,6 +156,8 @@ void MyAvatar::reset(bool andReload) {
// Reset dynamic state.
_wasPushing = _isPushing = _isBraking = _billboardValid = false;
_isFollowingHMD = false;
_hmdFollowVelocity = Vectors::ZERO;
_hmdFollowSpeed = 0.0f;
_skeletonModel.reset();
getHead()->reset();
_targetVelocity = glm::vec3(0.0f);
@ -243,7 +245,7 @@ void MyAvatar::simulate(float deltaTime) {
{
PerformanceTimer perfTimer("transform");
bool stepAction = false;
// When there are no step values, we zero out the last step pulse.
// When there are no step values, we zero out the last step pulse.
// This allows a user to do faster snapping by tapping a control
for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) {
if (_driveKeys[i] != 0.0f) {
@ -327,25 +329,6 @@ glm::mat4 MyAvatar::getSensorToWorldMatrix() const {
return _sensorToWorldMatrix;
}
// returns true if pos is OUTSIDE of the vertical capsule
// where the middle cylinder length is defined by capsuleLen and the radius by capsuleRad.
static bool pointIsOutsideCapsule(const glm::vec3& pos, float capsuleLen, float capsuleRad) {
const float halfCapsuleLen = capsuleLen / 2.0f;
if (fabs(pos.y) <= halfCapsuleLen) {
// cylinder check for middle capsule
glm::vec2 horizPos(pos.x, pos.z);
return glm::length(horizPos) > capsuleRad;
} else if (pos.y > halfCapsuleLen) {
glm::vec3 center(0.0f, halfCapsuleLen, 0.0f);
return glm::length(center - pos) > capsuleRad;
} else if (pos.y < halfCapsuleLen) {
glm::vec3 center(0.0f, -halfCapsuleLen, 0.0f);
return glm::length(center - pos) > capsuleRad;
} else {
return false;
}
}
// Pass a recent sample of the HMD to the avatar.
// This can also update the avatar's position to follow the HMD
// as it moves through the world.
@ -354,102 +337,59 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
_hmdSensorMatrix = hmdSensorMatrix;
_hmdSensorPosition = extractTranslation(hmdSensorMatrix);
_hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix);
}
// calc deltaTime
auto now = usecTimestampNow();
auto deltaUsecs = now - _lastUpdateFromHMDTime;
_lastUpdateFromHMDTime = now;
double actualDeltaTime = (double)deltaUsecs / (double)USECS_PER_SECOND;
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
float deltaTime = glm::clamp((float)actualDeltaTime, 0.0f, BIGGEST_DELTA_TIME_SECS);
bool hmdIsAtRest = _hmdAtRestDetector.update(deltaTime, _hmdSensorPosition, _hmdSensorOrientation);
// It can be more accurate/smooth to use velocity rather than position,
// but some modes (e.g., hmd standing) update position without updating velocity.
// So, let's create our own workingVelocity from the worldPosition...
glm::vec3 positionDelta = getPosition() - _lastPosition;
glm::vec3 workingVelocity = positionDelta / deltaTime;
_lastPosition = getPosition();
const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec
const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec
void MyAvatar::updateHMDFollowVelocity() {
bool isMoving;
if (_lastIsMoving) {
isMoving = glm::length(workingVelocity) >= MOVE_EXIT_SPEED_THRESHOLD;
const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec
isMoving = glm::length(_velocity) >= MOVE_EXIT_SPEED_THRESHOLD;
} else {
isMoving = glm::length(workingVelocity) > MOVE_ENTER_SPEED_THRESHOLD;
const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec
isMoving = glm::length(_velocity) > MOVE_ENTER_SPEED_THRESHOLD;
}
bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving;
_lastIsMoving = isMoving;
if (shouldFollowHMD() || hmdIsAtRest || justStartedMoving) {
beginFollowingHMD();
}
followHMD(deltaTime);
}
glm::vec3 MyAvatar::getHMDCorrectionVelocity() const {
// TODO: impelement this
return Vectors::ZERO;
}
void MyAvatar::beginFollowingHMD() {
// begin homing toward derived body position.
if (!_isFollowingHMD) {
bool hmdIsAtRest = _hmdAtRestDetector.update(_hmdSensorPosition, _hmdSensorOrientation);
if (hmdIsAtRest || justStartedMoving) {
_isFollowingHMD = true;
_followHMDAlpha = 0.0f;
}
}
bool MyAvatar::shouldFollowHMD() const {
if (!_isFollowingHMD) {
// define a vertical capsule
const float FOLLOW_HMD_CAPSULE_RADIUS = 0.2f; // meters
const float FOLLOW_HMD_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters.
// detect if the derived body position is outside of a capsule around the _bodySensorMatrix
auto newBodySensorMatrix = deriveBodyFromHMDSensor();
glm::vec3 localPoint = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix);
return pointIsOutsideCapsule(localPoint, FOLLOW_HMD_CAPSULE_LENGTH, FOLLOW_HMD_CAPSULE_RADIUS);
// compute offset to body's target position (in sensor-frame)
auto sensorBodyMatrix = deriveBodyFromHMDSensor();
_hmdFollowOffset = extractTranslation(sensorBodyMatrix) - extractTranslation(_bodySensorMatrix);
glm::vec3 truncatedOffset = _hmdFollowOffset;
if (truncatedOffset.y < 0.0f) {
// don't pull the body DOWN to match the target (allow animation system to squat)
truncatedOffset.y = 0.0f;
}
return false;
}
void MyAvatar::followHMD(float deltaTime) {
if (_isFollowingHMD) {
const float FOLLOW_HMD_DURATION = 0.5f; // seconds
auto newBodySensorMatrix = deriveBodyFromHMDSensor();
auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix;
glm::vec3 worldBodyPos = extractTranslation(worldBodyMatrix);
glm::quat worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix));
_followHMDAlpha += (1.0f / FOLLOW_HMD_DURATION) * deltaTime;
if (_followHMDAlpha >= 1.0f) {
_isFollowingHMD = false;
nextAttitude(worldBodyPos, worldBodyRot);
_bodySensorMatrix = newBodySensorMatrix;
} else {
// interp position toward the desired pos
glm::vec3 pos = lerp(getPosition(), worldBodyPos, _followHMDAlpha);
glm::quat rot = glm::normalize(safeMix(getOrientation(), worldBodyRot, _followHMDAlpha));
nextAttitude(pos, rot);
// interp sensor matrix toward desired
glm::vec3 nextBodyPos = extractTranslation(newBodySensorMatrix);
glm::quat nextBodyRot = glm::normalize(glm::quat_cast(newBodySensorMatrix));
glm::vec3 prevBodyPos = extractTranslation(_bodySensorMatrix);
glm::quat prevBodyRot = glm::normalize(glm::quat_cast(_bodySensorMatrix));
pos = lerp(prevBodyPos, nextBodyPos, _followHMDAlpha);
rot = glm::normalize(safeMix(prevBodyRot, nextBodyRot, _followHMDAlpha));
_bodySensorMatrix = createMatFromQuatAndPos(rot, pos);
bool needNewFollowSpeed = (_isFollowingHMD && _hmdFollowSpeed == 0.0f);
if (!needNewFollowSpeed) {
// check to see if offset has exceeded its threshold
float distance = glm::length(truncatedOffset);
const float MAX_HMD_HIP_SHIFT = 0.2f;
if (distance > MAX_HMD_HIP_SHIFT) {
_isFollowingHMD = true;
needNewFollowSpeed = true;
}
}
if (_isFollowingHMD) {
// only bother to rotate into world frame if we're following
glm::quat sensorToWorldRotation = extractRotation(_sensorToWorldMatrix);
_hmdFollowOffset = sensorToWorldRotation * _hmdFollowOffset;
}
if (needNewFollowSpeed) {
// compute new velocity that will be used to resolve offset of hips from body
const float FOLLOW_HMD_DURATION = 0.5f; // seconds
_hmdFollowVelocity = (_hmdFollowOffset / FOLLOW_HMD_DURATION);
_hmdFollowSpeed = glm::length(_hmdFollowVelocity);
} else if (_isFollowingHMD) {
// compute new velocity (but not new speed)
_hmdFollowVelocity = _hmdFollowSpeed * glm::normalize(_hmdFollowOffset);
}
}
// best called at end of main loop, just before rendering.
@ -1326,17 +1266,68 @@ void MyAvatar::prepareForPhysicsSimulation() {
relayDriveKeysToCharacterController();
_characterController.setTargetVelocity(getTargetVelocity());
_characterController.setAvatarPositionAndOrientation(getPosition(), getOrientation());
_characterController.setHMDVelocity(getHMDCorrectionVelocity());
}
if (qApp->isHMDMode()) {
updateHMDFollowVelocity();
_characterController.setHMDVelocity(_hmdFollowVelocity);
} else {
_characterController.setHMDVelocity(Vectors::ZERO);
_isFollowingHMD = false;
}
}
void MyAvatar::harvestResultsFromPhysicsSimulation() {
glm::vec3 position = getPosition();
glm::quat orientation = getOrientation();
_characterController.getAvatarPositionAndOrientation(position, orientation);
nextAttitude(position, orientation);
setVelocity(_characterController.getLinearVelocity());
// TODO: harvest HMD shift here
//glm::vec3 hmdShift = _characterController.getHMDShift();
if (_isFollowingHMD) {
setVelocity(_characterController.getLinearVelocity() + _hmdFollowVelocity);
glm::vec3 hmdShift = _characterController.getHMDShift();
adjustSensorTransform(hmdShift);
} else {
setVelocity(_characterController.getLinearVelocity());
}
}
void MyAvatar::adjustSensorTransform(glm::vec3 hmdShift) {
// compute blendFactor of latest hmdShift
// which we'll use to blend the rotation part
float blendFactor = 1.0f;
float shiftLength = glm::length(hmdShift);
if (shiftLength > 1.0e-5f) {
float offsetLength = glm::length(_hmdFollowOffset);
if (offsetLength > shiftLength) {
blendFactor = shiftLength / offsetLength;
}
}
auto newBodySensorMatrix = deriveBodyFromHMDSensor();
auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix;
glm::quat finalBodyRotation = glm::normalize(glm::quat_cast(worldBodyMatrix));
if (blendFactor >= 0.99f) {
// the "adjustment" is more or less complete so stop following
_isFollowingHMD = false;
_hmdFollowSpeed = 0.0f;
// and slam the body's transform anyway to eliminate any slight errors
glm::vec3 finalBodyPosition = extractTranslation(worldBodyMatrix);
nextAttitude(finalBodyPosition, finalBodyRotation);
_bodySensorMatrix = newBodySensorMatrix;
} else {
// physics already did the positional blending for us
glm::vec3 newBodyPosition = getPosition();
// but the rotational part must be done manually
glm::quat newBodyRotation = glm::normalize(safeMix(getOrientation(), finalBodyRotation, blendFactor));
nextAttitude(newBodyPosition, newBodyRotation);
// interp sensor matrix toward the desired
glm::vec3 prevPosition = extractTranslation(_bodySensorMatrix);
glm::quat prevRotation = glm::normalize(glm::quat_cast(_bodySensorMatrix));
glm::vec3 nextPosition = extractTranslation(newBodySensorMatrix);
glm::quat nextRotation = glm::normalize(glm::quat_cast(newBodySensorMatrix));
_bodySensorMatrix = createMatFromQuatAndPos(
glm::normalize(safeMix(prevRotation, nextRotation, blendFactor)),
lerp(prevPosition, nextPosition, blendFactor));
}
}
QString MyAvatar::getScriptedMotorFrame() const {
@ -1438,7 +1429,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl
getHead()->renderLookAts(renderArgs);
}
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE &&
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE &&
Menu::getInstance()->isOptionChecked(MenuOption::DisplayHandTargets)) {
getHand()->renderHandTargets(renderArgs, true);
}
@ -1917,7 +1908,7 @@ void MyAvatar::updateMotionBehaviorFromMenu() {
QMetaObject::invokeMethod(this, "updateMotionBehaviorFromMenu");
return;
}
Menu* menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::KeyboardMotorControl)) {
_motionBehaviors |= AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED;

View file

@ -101,8 +101,6 @@ public:
// as it moves through the world.
void updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix);
glm::vec3 getHMDCorrectionVelocity() const;
// best called at end of main loop, just before rendering.
// update sensor to world matrix from current body position and hmd sensor.
// This is so the correct camera can be used for rendering.
@ -211,6 +209,7 @@ public:
void prepareForPhysicsSimulation();
void harvestResultsFromPhysicsSimulation();
void adjustSensorTransform(glm::vec3 hmdShift);
const QString& getCollisionSoundURL() { return _collisionSoundURL; }
void setCollisionSoundURL(const QString& url);
@ -312,9 +311,10 @@ private:
const RecorderPointer getRecorder() const { return _recorder; }
const PlayerPointer getPlayer() const { return _player; }
void beginFollowingHMD();
bool shouldFollowHMD() const;
void followHMD(float deltaTime);
//void beginFollowingHMD();
//bool shouldFollowHMD() const;
//void followHMD(float deltaTime);
void updateHMDFollowVelocity();
bool cameraInsideHead() const;
@ -391,6 +391,9 @@ private:
// used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers.
glm::mat4 _sensorToWorldMatrix;
glm::vec3 _hmdFollowOffset { Vectors::ZERO };
glm::vec3 _hmdFollowVelocity { Vectors::ZERO };
float _hmdFollowSpeed { 0.0f };
bool _goToPending;
glm::vec3 _goToPosition;
@ -411,9 +414,7 @@ private:
bool _isFollowingHMD { false };
float _followHMDAlpha { 0.0f };
quint64 _lastUpdateFromHMDTime { usecTimestampNow() };
AtRestDetector _hmdAtRestDetector;
glm::vec3 _lastPosition;
bool _lastIsMoving { false };
quint64 _lastStepPulse { 0 };
bool _pulseUpdate { false };

View file

@ -118,18 +118,6 @@ void EyeTracker::init() {
qCWarning(interfaceapp) << "Eye Tracker: Already initialized";
return;
}
#ifdef HAVE_IVIEWHMD
int result = smi_setCallback(eyeTrackerCallback);
if (result != SMI_RET_SUCCESS) {
qCWarning(interfaceapp) << "Eye Tracker: Error setting callback:" << smiReturnValueToString(result);
QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result));
} else {
_isInitialized = true;
}
connect(&_startStreamingWatcher, SIGNAL(finished()), this, SLOT(onStreamStarted()));
#endif
}
#ifdef HAVE_IVIEWHMD
@ -140,6 +128,10 @@ int EyeTracker::startStreaming(bool simulate) {
#ifdef HAVE_IVIEWHMD
void EyeTracker::onStreamStarted() {
if (!_isInitialized) {
return;
}
int result = _startStreamingWatcher.result();
_isStreaming = (result == SMI_RET_SUCCESS);
@ -171,6 +163,20 @@ void EyeTracker::onStreamStarted() {
#endif
void EyeTracker::setEnabled(bool enabled, bool simulate) {
if (enabled && !_isInitialized) {
#ifdef HAVE_IVIEWHMD
int result = smi_setCallback(eyeTrackerCallback);
if (result != SMI_RET_SUCCESS) {
qCWarning(interfaceapp) << "Eye Tracker: Error setting callback:" << smiReturnValueToString(result);
QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result));
} else {
_isInitialized = true;
}
connect(&_startStreamingWatcher, SIGNAL(finished()), this, SLOT(onStreamStarted()));
#endif
}
if (!_isInitialized) {
return;
}

View file

@ -12,6 +12,7 @@
#include "GLMHelpers.h"
#include "AnimationLogging.h"
#include "AnimUtil.h"
#include "AnimClip.h"
AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha) :
AnimNode(AnimNode::Type::BlendLinear, id),
@ -34,24 +35,13 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo
} else if (_children.size() == 1) {
_poses = _children[0]->evaluate(animVars, dt, triggersOut);
} else {
float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
size_t prevPoseIndex = glm::floor(clampedAlpha);
size_t nextPoseIndex = glm::ceil(clampedAlpha);
float alpha = glm::fract(clampedAlpha);
if (prevPoseIndex == nextPoseIndex) {
// this can happen if alpha is on an integer boundary
_poses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut);
} else {
// need to eval and blend between two children.
auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut);
auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt, triggersOut);
if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) {
_poses.resize(prevPoses.size());
::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]);
}
}
evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt);
}
return _poses;
}
@ -60,3 +50,21 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo
const AnimPoseVec& AnimBlendLinear::getPosesInternal() const {
return _poses;
}
void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha,
size_t prevPoseIndex, size_t nextPoseIndex, float dt) {
if (prevPoseIndex == nextPoseIndex) {
// this can happen if alpha is on an integer boundary
_poses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut);
} else {
// need to eval and blend between two children.
auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut);
auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt, triggersOut);
if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) {
_poses.resize(prevPoses.size());
::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]);
}
}
}

View file

@ -38,6 +38,9 @@ protected:
// for AnimDebugDraw rendering
virtual const AnimPoseVec& getPosesInternal() const override;
void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha,
size_t prevPoseIndex, size_t nextPoseIndex, float dt);
AnimPoseVec _poses;
float _alpha;

View file

@ -0,0 +1,126 @@
//
// AnimBlendLinearMove.cpp
//
// Created by Anthony J. Thibault on 10/22/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AnimBlendLinearMove.h"
#include <GLMHelpers.h>
#include "AnimationLogging.h"
#include "AnimUtil.h"
#include "AnimClip.h"
AnimBlendLinearMove::AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector<float>& characteristicSpeeds) :
AnimNode(AnimNode::Type::BlendLinearMove, id),
_alpha(alpha),
_desiredSpeed(desiredSpeed),
_characteristicSpeeds(characteristicSpeeds) {
}
AnimBlendLinearMove::~AnimBlendLinearMove() {
}
const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) {
assert(_children.size() == _characteristicSpeeds.size());
_alpha = animVars.lookup(_alphaVar, _alpha);
_desiredSpeed = animVars.lookup(_desiredSpeedVar, _desiredSpeed);
if (_children.size() == 0) {
for (auto&& pose : _poses) {
pose = AnimPose::identity;
}
} else if (_children.size() == 1) {
const float alpha = 0.0f;
const int prevPoseIndex = 0;
const int nextPoseIndex = 0;
float prevDeltaTime, nextDeltaTime;
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
} else {
float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1));
size_t prevPoseIndex = glm::floor(clampedAlpha);
size_t nextPoseIndex = glm::ceil(clampedAlpha);
float alpha = glm::fract(clampedAlpha);
float prevDeltaTime, nextDeltaTime;
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
}
return _poses;
}
// for AnimDebugDraw rendering
const AnimPoseVec& AnimBlendLinearMove::getPosesInternal() const {
return _poses;
}
void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha,
size_t prevPoseIndex, size_t nextPoseIndex,
float prevDeltaTime, float nextDeltaTime) {
if (prevPoseIndex == nextPoseIndex) {
// this can happen if alpha is on an integer boundary
_poses = _children[prevPoseIndex]->evaluate(animVars, prevDeltaTime, triggersOut);
} else {
// need to eval and blend between two children.
auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, prevDeltaTime, triggersOut);
auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, nextDeltaTime, triggersOut);
if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) {
_poses.resize(prevPoses.size());
::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]);
}
}
}
void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex,
float* prevDeltaTimeOut, float* nextDeltaTimeOut, Triggers& triggersOut) {
const float FRAMES_PER_SECOND = 30.0f;
auto prevClipNode = std::dynamic_pointer_cast<AnimClip>(_children[prevPoseIndex]);
assert(prevClipNode);
auto nextClipNode = std::dynamic_pointer_cast<AnimClip>(_children[nextPoseIndex]);
assert(nextClipNode);
float v0 = _characteristicSpeeds[prevPoseIndex];
float n0 = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f;
float v1 = _characteristicSpeeds[nextPoseIndex];
float n1 = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) + 1.0f;
// rate of change in phase space, necessary to achive desired speed.
float omega = (_desiredSpeed * FRAMES_PER_SECOND) / ((1.0f - alpha) * v0 * n0 + alpha * v1 * n1);
float f0 = prevClipNode->getStartFrame() + _phase * n0;
prevClipNode->setCurrentFrame(f0);
float f1 = nextClipNode->getStartFrame() + _phase * n1;
nextClipNode->setCurrentFrame(f1);
// integrate phase forward in time.
_phase += omega * dt;
// detect loop trigger events
if (_phase >= 1.0f) {
triggersOut.push_back(_id + "Loop");
_phase = glm::fract(_phase);
}
*prevDeltaTimeOut = omega * dt * (n0 / FRAMES_PER_SECOND);
*nextDeltaTimeOut = omega * dt * (n1 / FRAMES_PER_SECOND);
}
void AnimBlendLinearMove::setCurrentFrameInternal(float frame) {
assert(_children.size() > 0);
auto clipNode = std::dynamic_pointer_cast<AnimClip>(_children.front());
assert(clipNode);
const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f;
_phase = fmodf(frame, NUM_FRAMES);
}

View file

@ -0,0 +1,77 @@
//
// AnimBlendLinearMove.h
//
// Created by Anthony J. Thibault on 10/22/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
//
// 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_AnimBlendLinearMove_h
#define hifi_AnimBlendLinearMove_h
#include "AnimNode.h"
// Synced linear blend between two AnimNodes, where the playback speed of
// the animation is timeScaled to match movement speed.
//
// Each child animation is associated with a chracteristic speed.
// This defines the speed of that animation when played at the normal playback rate, 30 frames per second.
//
// The user also specifies a desired speed. This desired speed is used to timescale
// the animation to achive the desired movement velocity.
//
// Blending is determined by the alpha parameter.
// If the number of children is 2, then the alpha parameters should be between
// 0 and 1. The first animation will have a (1 - alpha) factor, and the second
// will have factor of alpha.
//
// This node supports more then 2 children. In this case the alpha should be
// between 0 and n - 1. This alpha can be used to linearly interpolate between
// the closest two children poses. This can be used to sweep through a series
// of animation poses.
class AnimBlendLinearMove : public AnimNode {
public:
friend class AnimTests;
AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector<float>& characteristicSpeeds);
virtual ~AnimBlendLinearMove() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
void setDesiredSpeedVar(const QString& desiredSpeedVar) { _desiredSpeedVar = desiredSpeedVar; }
protected:
// for AnimDebugDraw rendering
virtual const AnimPoseVec& getPosesInternal() const override;
void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha,
size_t prevPoseIndex, size_t nextPoseIndex,
float prevDeltaTime, float nextDeltaTime);
void setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex,
float* prevDeltaTimeOut, float* nextDeltaTimeOut, Triggers& triggersOut);
virtual void setCurrentFrameInternal(float frame) override;
AnimPoseVec _poses;
float _alpha;
float _desiredSpeed;
float _phase = 0.0f;
QString _alphaVar;
QString _desiredSpeedVar;
std::vector<float> _characteristicSpeeds;
// no copies
AnimBlendLinearMove(const AnimBlendLinearMove&) = delete;
AnimBlendLinearMove& operator=(const AnimBlendLinearMove&) = delete;
};
#endif // hifi_AnimBlendLinearMove_h

View file

@ -35,7 +35,9 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt,
_endFrame = animVars.lookup(_endFrameVar, _endFrame);
_timeScale = animVars.lookup(_timeScaleVar, _timeScale);
_loopFlag = animVars.lookup(_loopFlagVar, _loopFlag);
_frame = accumulateTime(animVars.lookup(_frameVar, _frame), dt, triggersOut);
float frame = animVars.lookup(_frameVar, _frame);
_frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggersOut);
// poll network anim to see if it's finished loading yet.
if (_networkAnim && _networkAnim->isLoaded() && _skeleton) {
@ -45,16 +47,17 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt,
}
if (_anim.size()) {
int frameCount = _anim.size();
int prevIndex = (int)glm::floor(_frame);
int nextIndex = (int)glm::ceil(_frame);
if (_loopFlag && nextIndex >= frameCount) {
nextIndex = 0;
int nextIndex;
if (_loopFlag && _frame >= _endFrame) {
nextIndex = (int)glm::ceil(_startFrame);
} else {
nextIndex = (int)glm::ceil(_frame);
}
// It can be quite possible for the user to set _startFrame and _endFrame to
// values before or past valid ranges. We clamp the frames here.
int frameCount = _anim.size();
prevIndex = std::min(std::max(0, prevIndex), frameCount - 1);
nextIndex = std::min(std::max(0, nextIndex), frameCount - 1);
@ -78,39 +81,7 @@ void AnimClip::setCurrentFrameInternal(float frame) {
// because dt is 0, we should not encounter any triggers
const float dt = 0.0f;
Triggers triggers;
_frame = accumulateTime(frame * _timeScale, dt, triggers);
}
float AnimClip::accumulateTime(float frame, float dt, Triggers& triggersOut) const {
const float startFrame = std::min(_startFrame, _endFrame);
if (startFrame == _endFrame) {
// when startFrame >= endFrame
frame = _endFrame;
} else if (_timeScale > 0.0f) {
// accumulate time, keeping track of loops and end of animation events.
const float FRAMES_PER_SECOND = 30.0f;
float framesRemaining = (dt * _timeScale) * FRAMES_PER_SECOND;
while (framesRemaining > 0.0f) {
float framesTillEnd = _endFrame - _frame;
if (framesRemaining >= framesTillEnd) {
if (_loopFlag) {
// anim loop
triggersOut.push_back(_id + "OnLoop");
framesRemaining -= framesTillEnd;
frame = startFrame;
} else {
// anim end
triggersOut.push_back(_id + "OnDone");
frame = _endFrame;
framesRemaining = 0.0f;
}
} else {
frame += framesRemaining;
framesRemaining = 0.0f;
}
}
}
return frame;
_frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggers);
}
void AnimClip::copyFromNetworkAnim() {

View file

@ -36,12 +36,17 @@ public:
void setLoopFlagVar(const QString& loopFlagVar) { _loopFlagVar = loopFlagVar; }
void setFrameVar(const QString& frameVar) { _frameVar = frameVar; }
float getStartFrame() const { return _startFrame; }
float getEndFrame() const { return _endFrame; }
void setTimeScale(float timeScale) { _timeScale = timeScale; }
float getTimeScale() const { return _timeScale; }
protected:
void loadURL(const QString& url);
virtual void setCurrentFrameInternal(float frame) override;
float accumulateTime(float frame, float dt, Triggers& triggersOut) const;
void copyFromNetworkAnim();
// for AnimDebugDraw rendering

View file

@ -38,6 +38,7 @@ public:
enum class Type {
Clip = 0,
BlendLinear,
BlendLinearMove,
Overlay,
StateMachine,
Manipulator,
@ -75,10 +76,10 @@ public:
return evaluate(animVars, dt, triggersOut);
}
protected:
void setCurrentFrame(float frame);
protected:
virtual void setCurrentFrameInternal(float frame) {}
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) { _skeleton = skeleton; }

View file

@ -16,6 +16,7 @@
#include "AnimNode.h"
#include "AnimClip.h"
#include "AnimBlendLinear.h"
#include "AnimBlendLinearMove.h"
#include "AnimationLogging.h"
#include "AnimOverlay.h"
#include "AnimNodeLoader.h"
@ -29,6 +30,7 @@ using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& json
// factory functions
static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadBlendLinearMoveNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
@ -36,17 +38,14 @@ static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, c
// called after children have been loaded
// returns node on success, nullptr on failure.
static bool processClipNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
static bool processBlendLinearNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
static bool processOverlayNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
static bool processDoNothing(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static bool processManipulatorNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
static bool processInverseKinematicsNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
static const char* animNodeTypeToString(AnimNode::Type type) {
switch (type) {
case AnimNode::Type::Clip: return "clip";
case AnimNode::Type::BlendLinear: return "blendLinear";
case AnimNode::Type::BlendLinearMove: return "blendLinearMove";
case AnimNode::Type::Overlay: return "overlay";
case AnimNode::Type::StateMachine: return "stateMachine";
case AnimNode::Type::Manipulator: return "manipulator";
@ -60,6 +59,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
switch (type) {
case AnimNode::Type::Clip: return loadClipNode;
case AnimNode::Type::BlendLinear: return loadBlendLinearNode;
case AnimNode::Type::BlendLinearMove: return loadBlendLinearMoveNode;
case AnimNode::Type::Overlay: return loadOverlayNode;
case AnimNode::Type::StateMachine: return loadStateMachineNode;
case AnimNode::Type::Manipulator: return loadManipulatorNode;
@ -71,12 +71,13 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
switch (type) {
case AnimNode::Type::Clip: return processClipNode;
case AnimNode::Type::BlendLinear: return processBlendLinearNode;
case AnimNode::Type::Overlay: return processOverlayNode;
case AnimNode::Type::Clip: return processDoNothing;
case AnimNode::Type::BlendLinear: return processDoNothing;
case AnimNode::Type::BlendLinearMove: return processDoNothing;
case AnimNode::Type::Overlay: return processDoNothing;
case AnimNode::Type::StateMachine: return processStateMachineNode;
case AnimNode::Type::Manipulator: return processManipulatorNode;
case AnimNode::Type::InverseKinematics: return processInverseKinematicsNode;
case AnimNode::Type::Manipulator: return processDoNothing;
case AnimNode::Type::InverseKinematics: return processDoNothing;
case AnimNode::Type::NumTypes: return nullptr;
};
return nullptr;
@ -160,6 +161,9 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr
assert((int)type >= 0 && type < AnimNode::Type::NumTypes);
auto node = (animNodeTypeToLoaderFunc(type))(dataObj, id, jsonUrl);
if (!node) {
return nullptr;
}
auto childrenValue = jsonObj.value("children");
if (!childrenValue.isArray()) {
@ -233,6 +237,45 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q
return node;
}
static AnimNode::Pointer loadBlendLinearMoveNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);
READ_FLOAT(desiredSpeed, jsonObj, id, jsonUrl, nullptr);
std::vector<float> characteristicSpeeds;
auto speedsValue = jsonObj.value("characteristicSpeeds");
if (!speedsValue.isArray()) {
qCCritical(animation) << "AnimNodeLoader, bad array \"characteristicSpeeds\" in blendLinearMove node, id =" << id << ", url =" << jsonUrl.toDisplayString();
return nullptr;
}
auto speedsArray = speedsValue.toArray();
for (const auto& speedValue : speedsArray) {
if (!speedValue.isDouble()) {
qCCritical(animation) << "AnimNodeLoader, bad number in \"characteristicSpeeds\", id =" << id << ", url =" << jsonUrl.toDisplayString();
return nullptr;
}
float speedVal = (float)speedValue.toDouble();
characteristicSpeeds.push_back(speedVal);
};
READ_OPTIONAL_STRING(alphaVar, jsonObj);
READ_OPTIONAL_STRING(desiredSpeedVar, jsonObj);
auto node = std::make_shared<AnimBlendLinearMove>(id, alpha, desiredSpeed, characteristicSpeeds);
if (!alphaVar.isEmpty()) {
node->setAlphaVar(alphaVar);
}
if (!desiredSpeedVar.isEmpty()) {
node->setDesiredSpeedVar(desiredSpeedVar);
}
return node;
}
static const char* boneSetStrings[AnimOverlay::NumBoneSets] = {
"fullBody",
"upperBody",

View file

@ -0,0 +1,56 @@
//
// AnimPose.cpp
//
// Created by Anthony J. Thibault on 10/14/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AnimPose.h"
#include "GLMHelpers.h"
const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
glm::quat(),
glm::vec3(0.0f));
AnimPose::AnimPose(const glm::mat4& mat) {
scale = extractScale(mat);
rot = glm::normalize(glm::quat_cast(mat));
trans = extractTranslation(mat);
}
glm::vec3 AnimPose::operator*(const glm::vec3& rhs) const {
return trans + (rot * (scale * rhs));
}
glm::vec3 AnimPose::xformPoint(const glm::vec3& rhs) const {
return *this * rhs;
}
// really slow
glm::vec3 AnimPose::xformVector(const glm::vec3& rhs) const {
glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f);
glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f);
glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z);
glm::mat3 mat(xAxis, yAxis, zAxis);
glm::mat3 transInvMat = glm::inverse(glm::transpose(mat));
return transInvMat * rhs;
}
AnimPose AnimPose::operator*(const AnimPose& rhs) const {
return AnimPose(static_cast<glm::mat4>(*this) * static_cast<glm::mat4>(rhs));
}
AnimPose AnimPose::inverse() const {
return AnimPose(glm::inverse(static_cast<glm::mat4>(*this)));
}
AnimPose::operator glm::mat4() const {
glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f);
glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f);
glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z);
return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f),
glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f));
}

View file

@ -0,0 +1,47 @@
//
// AnimPose.h
//
// Created by Anthony J. Thibault on 10/14/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
//
// 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_AnimPose
#define hifi_AnimPose
#include <QtGlobal>
#include <QDebug>
#include <vector>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
struct AnimPose {
AnimPose() {}
explicit AnimPose(const glm::mat4& mat);
AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : scale(scaleIn), rot(rotIn), trans(transIn) {}
static const AnimPose identity;
glm::vec3 xformPoint(const glm::vec3& rhs) const;
glm::vec3 xformVector(const glm::vec3& rhs) const; // really slow
glm::vec3 operator*(const glm::vec3& rhs) const; // same as xformPoint
AnimPose operator*(const AnimPose& rhs) const;
AnimPose inverse() const;
operator glm::mat4() const;
glm::vec3 scale;
glm::quat rot;
glm::vec3 trans;
};
inline QDebug operator<<(QDebug debug, const AnimPose& pose) {
debug << "AnimPose, trans = (" << pose.trans.x << pose.trans.y << pose.trans.z << "), rot = (" << pose.rot.x << pose.rot.y << pose.rot.z << pose.rot.w << "), scale = (" << pose.scale.x << pose.scale.y << pose.scale.z << ")";
return debug;
}
using AnimPoseVec = std::vector<AnimPose>;
#endif

View file

@ -16,50 +16,6 @@
#include "AnimationLogging.h"
const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
glm::quat(),
glm::vec3(0.0f));
AnimPose::AnimPose(const glm::mat4& mat) {
scale = extractScale(mat);
rot = glm::normalize(glm::quat_cast(mat));
trans = extractTranslation(mat);
}
glm::vec3 AnimPose::operator*(const glm::vec3& rhs) const {
return trans + (rot * (scale * rhs));
}
glm::vec3 AnimPose::xformPoint(const glm::vec3& rhs) const {
return *this * rhs;
}
// really slow
glm::vec3 AnimPose::xformVector(const glm::vec3& rhs) const {
glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f);
glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f);
glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z);
glm::mat3 mat(xAxis, yAxis, zAxis);
glm::mat3 transInvMat = glm::inverse(glm::transpose(mat));
return transInvMat * rhs;
}
AnimPose AnimPose::operator*(const AnimPose& rhs) const {
return AnimPose(static_cast<glm::mat4>(*this) * static_cast<glm::mat4>(rhs));
}
AnimPose AnimPose::inverse() const {
return AnimPose(glm::inverse(static_cast<glm::mat4>(*this)));
}
AnimPose::operator glm::mat4() const {
glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f);
glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f);
glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z);
return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f),
glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f));
}
AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) {
// convert to std::vector of joints
std::vector<FBXJoint> joints;

View file

@ -16,33 +16,7 @@
#include <glm/gtc/quaternion.hpp>
#include <FBXReader.h>
struct AnimPose {
AnimPose() {}
explicit AnimPose(const glm::mat4& mat);
AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : scale(scaleIn), rot(rotIn), trans(transIn) {}
static const AnimPose identity;
glm::vec3 xformPoint(const glm::vec3& rhs) const;
glm::vec3 xformVector(const glm::vec3& rhs) const; // really slow
glm::vec3 operator*(const glm::vec3& rhs) const; // same as xformPoint
AnimPose operator*(const AnimPose& rhs) const;
AnimPose inverse() const;
operator glm::mat4() const;
glm::vec3 scale;
glm::quat rot;
glm::vec3 trans;
};
inline QDebug operator<<(QDebug debug, const AnimPose& pose) {
debug << "AnimPose, trans = (" << pose.trans.x << pose.trans.y << pose.trans.z << "), rot = (" << pose.rot.x << pose.rot.y << pose.rot.z << pose.rot.w << "), scale = (" << pose.scale.x << pose.scale.y << pose.scale.z << ")";
return debug;
}
using AnimPoseVec = std::vector<AnimPose>;
#include "AnimPose.h"
class AnimSkeleton {
public:

View file

@ -11,6 +11,9 @@
#include "AnimUtil.h"
#include "GLMHelpers.h"
// TODO: use restrict keyword
// TODO: excellent candidate for simd vectorization.
void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) {
for (size_t i = 0; i < numPoses; i++) {
const AnimPose& aPose = a[i];
@ -20,3 +23,42 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A
result[i].trans = lerp(aPose.trans, bPose.trans, alpha);
}
}
float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag,
const QString& id, AnimNode::Triggers& triggersOut) {
float frame = currentFrame;
const float clampedStartFrame = std::min(startFrame, endFrame);
if (fabsf(clampedStartFrame - endFrame) < 1.0f) {
frame = endFrame;
} else if (timeScale > 0.0f) {
// accumulate time, keeping track of loops and end of animation events.
const float FRAMES_PER_SECOND = 30.0f;
float framesRemaining = (dt * timeScale) * FRAMES_PER_SECOND;
while (framesRemaining > 0.0f) {
float framesTillEnd = endFrame - frame;
// when looping, add one frame between start and end.
if (loopFlag) {
framesTillEnd += 1.0f;
}
if (framesRemaining >= framesTillEnd) {
if (loopFlag) {
// anim loop
triggersOut.push_back(id + "OnLoop");
framesRemaining -= framesTillEnd;
frame = clampedStartFrame;
} else {
// anim end
triggersOut.push_back(id + "OnDone");
frame = endFrame;
framesRemaining = 0.0f;
}
} else {
frame += framesRemaining;
framesRemaining = 0.0f;
}
}
}
return frame;
}

View file

@ -13,12 +13,12 @@
#include "AnimNode.h"
// TODO: use restrict keyword
// TODO: excellent candidate for simd vectorization.
// this is where the magic happens
void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result);
float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag,
const QString& id, AnimNode::Triggers& triggersOut);
#endif

View file

@ -381,6 +381,33 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const {
return _jointStates[jointIndex].getTransform();
}
void Rig::calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut) const {
assert(referenceSpeeds.size() > 0);
// calculate alpha from linear combination of referenceSpeeds.
float alpha = 0.0f;
if (speed <= referenceSpeeds.front()) {
alpha = 0.0f;
} else if (speed > referenceSpeeds.back()) {
alpha = (float)(referenceSpeeds.size() - 1);
} else {
for (size_t i = 0; i < referenceSpeeds.size() - 1; i++) {
if (referenceSpeeds[i] < speed && speed < referenceSpeeds[i + 1]) {
alpha = (float)i + ((speed - referenceSpeeds[i]) / (referenceSpeeds[i + 1] - referenceSpeeds[i]));
break;
}
}
}
*alphaOut = alpha;
}
// animation reference speeds.
static const std::vector<float> FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s
static const std::vector<float> BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s
static const std::vector<float> LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) {
glm::vec3 front = worldRotation * IDENTITY_FRONT;
@ -389,8 +416,16 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
// but some modes (e.g., hmd standing) update position without updating velocity.
// It's very hard to debug hmd standing. (Look down at yourself, or have a second person observe. HMD third person is a bit undefined...)
// So, let's create our own workingVelocity from the worldPosition...
glm::vec3 workingVelocity = _lastVelocity;
glm::vec3 positionDelta = worldPosition - _lastPosition;
glm::vec3 workingVelocity = positionDelta / deltaTime;
// Don't trust position delta if deltaTime is 'small'.
// NOTE: This is mostly just a work around for an issue in oculus 0.7 runtime, where
// Application::idle() is being called more frequently and with smaller dt's then expected.
const float SMALL_DELTA_TIME = 0.006f; // 6 ms
if (deltaTime > SMALL_DELTA_TIME) {
workingVelocity = positionDelta / deltaTime;
}
#if !WANT_DEBUG
// But for smoothest (non-hmd standing) results, go ahead and use velocity:
@ -399,19 +434,43 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
}
#endif
if (deltaTime > SMALL_DELTA_TIME) {
_lastVelocity = workingVelocity;
}
if (_enableAnimGraph) {
glm::vec3 localVel = glm::inverse(worldRotation) * workingVelocity;
float forwardSpeed = glm::dot(localVel, IDENTITY_FRONT);
float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT);
float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime;
// filter speeds using a simple moving average.
_averageForwardSpeed.updateAverage(forwardSpeed);
_averageLateralSpeed.updateAverage(lateralSpeed);
// sine wave LFO var for testing.
static float t = 0.0f;
_animVars.set("sine", static_cast<float>(0.5 * sin(t) + 0.5));
_animVars.set("sine", 2.0f * static_cast<float>(0.5 * sin(t) + 0.5));
const float ANIM_WALK_SPEED = 1.4f; // m/s
_animVars.set("walkTimeScale", glm::clamp(0.5f, 2.0f, glm::length(localVel) / ANIM_WALK_SPEED));
float moveForwardAlpha = 0.0f;
float moveBackwardAlpha = 0.0f;
float moveLateralAlpha = 0.0f;
// calcuate the animation alpha and timeScale values based on current speeds and animation reference speeds.
calcAnimAlpha(_averageForwardSpeed.getAverage(), FORWARD_SPEEDS, &moveForwardAlpha);
calcAnimAlpha(-_averageForwardSpeed.getAverage(), BACKWARD_SPEEDS, &moveBackwardAlpha);
calcAnimAlpha(fabsf(_averageLateralSpeed.getAverage()), LATERAL_SPEEDS, &moveLateralAlpha);
_animVars.set("moveForwardSpeed", _averageForwardSpeed.getAverage());
_animVars.set("moveForwardAlpha", moveForwardAlpha);
_animVars.set("moveBackwardSpeed", -_averageForwardSpeed.getAverage());
_animVars.set("moveBackwardAlpha", moveBackwardAlpha);
_animVars.set("moveLateralSpeed", fabsf(_averageLateralSpeed.getAverage()));
_animVars.set("moveLateralAlpha", moveLateralAlpha);
const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec
const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec

View file

@ -44,6 +44,7 @@
#include "AnimNode.h"
#include "AnimNodeLoader.h"
#include "SimpleMovingAverage.h"
class AnimationHandle;
typedef std::shared_ptr<AnimationHandle> AnimationHandlePointer;
@ -219,6 +220,7 @@ public:
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
void updateNeckJoint(int index, const HeadParameters& params);
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade);
void calcAnimAlpha(float speed, const std::vector<float>& referenceSpeeds, float* alphaOut) const;
QVector<JointState> _jointStates;
int _rootJointIndex = -1;
@ -238,6 +240,7 @@ public:
bool _enableAnimGraph = false;
glm::vec3 _lastFront;
glm::vec3 _lastPosition;
glm::vec3 _lastVelocity;
std::shared_ptr<AnimNode> _animNode;
std::shared_ptr<AnimSkeleton> _animSkeleton;
@ -254,6 +257,9 @@ public:
float _leftHandOverlayAlpha = 0.0f;
float _rightHandOverlayAlpha = 0.0f;
SimpleMovingAverage _averageForwardSpeed{ 10 };
SimpleMovingAverage _averageLateralSpeed{ 10 };
private:
QMap<int, StateHandler> _stateHandlers;
int _nextStateHandlerId {0};

View file

@ -7,8 +7,11 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "DeviceProxy.h"
namespace controller {
}
// NOTE: we don't need to include this header unless/until we add additional symbols.
// By removing this header we prevent these warnings on windows:
//
// warning LNK4221: This object file does not define any previously undefined public symbols,
// so it will not be used by any link operation that consumes this library
//
//#include "DeviceProxy.h"

View file

@ -55,6 +55,9 @@ Input::NamedVector StandardController::getAvailableInputs() const {
makePair(LS, "LS"),
makePair(RS, "RS"),
makePair(LS_TOUCH, "LSTouch"),
makePair(RS_TOUCH, "RSTouch"),
// Center buttons
makePair(START, "Start"),
makePair(BACK, "Back"),
@ -69,26 +72,41 @@ Input::NamedVector StandardController::getAvailableInputs() const {
makePair(LT, "LT"),
makePair(RT, "RT"),
// Finger abstractions
makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"),
makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"),
makePair(LEFT_THUMB_UP, "LeftThumbUp"),
makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"),
makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"),
makePair(RIGHT_THUMB_UP, "RightThumbUp"),
makePair(LEFT_PRIMARY_THUMB_TOUCH, "LeftPrimaryThumbTouch"),
makePair(LEFT_SECONDARY_THUMB_TOUCH, "LeftSecondaryThumbTouch"),
makePair(RIGHT_PRIMARY_THUMB_TOUCH, "RightPrimaryThumbTouch"),
makePair(RIGHT_SECONDARY_THUMB_TOUCH, "RightSecondaryThumbTouch"),
makePair(LEFT_INDEX_POINT, "LeftIndexPoint"),
makePair(RIGHT_INDEX_POINT, "RightIndexPoint"),
makePair(LEFT_PRIMARY_INDEX, "LeftPrimaryIndex"),
makePair(LEFT_SECONDARY_INDEX, "LeftSecondaryIndex"),
makePair(RIGHT_PRIMARY_INDEX, "RightPrimaryIndex"),
makePair(RIGHT_SECONDARY_INDEX, "RightSecondaryIndex"),
makePair(LEFT_PRIMARY_INDEX_TOUCH, "LeftPrimaryIndexTouch"),
makePair(LEFT_SECONDARY_INDEX_TOUCH, "LeftSecondaryIndexTouch"),
makePair(RIGHT_PRIMARY_INDEX_TOUCH, "RightPrimaryIndexTouch"),
makePair(RIGHT_SECONDARY_INDEX_TOUCH, "RightSecondaryIndexTouch"),
makePair(LEFT_GRIP, "LeftGrip"),
makePair(LEFT_GRIP_TOUCH, "LeftGripTouch"),
makePair(RIGHT_GRIP, "RightGrip"),
makePair(RIGHT_GRIP_TOUCH, "RightGripTouch"),
// Poses
makePair(LEFT_HAND, "LeftHand"),
makePair(RIGHT_HAND, "RightHand"),
// Aliases, PlayStation style names
makePair(LB, "L1"),
makePair(RB, "R1"),

View file

@ -39,16 +39,33 @@ namespace controller {
// These don't map to SDL types
LEFT_PRIMARY_THUMB,
LEFT_SECONDARY_THUMB,
LEFT_PRIMARY_THUMB_TOUCH,
LEFT_SECONDARY_THUMB_TOUCH,
LS_TOUCH,
LEFT_THUMB_UP,
RIGHT_PRIMARY_THUMB,
RIGHT_SECONDARY_THUMB,
RIGHT_PRIMARY_THUMB_TOUCH,
RIGHT_SECONDARY_THUMB_TOUCH,
RS_TOUCH,
RIGHT_THUMB_UP,
LEFT_PRIMARY_INDEX,
LEFT_SECONDARY_INDEX,
LEFT_PRIMARY_INDEX_TOUCH,
LEFT_SECONDARY_INDEX_TOUCH,
LEFT_INDEX_POINT,
RIGHT_PRIMARY_INDEX,
RIGHT_SECONDARY_INDEX,
RIGHT_PRIMARY_INDEX_TOUCH,
RIGHT_SECONDARY_INDEX_TOUCH,
RIGHT_INDEX_POINT,
LEFT_GRIP,
LEFT_GRIP_TOUCH,
RIGHT_GRIP,
RIGHT_GRIP_TOUCH,
NUM_STANDARD_BUTTONS
};

View file

@ -26,6 +26,7 @@
#include "Logging.h"
#include "impl/conditionals/AndConditional.h"
#include "impl/conditionals/NotConditional.h"
#include "impl/conditionals/EndpointConditional.h"
#include "impl/conditionals/ScriptConditional.h"
@ -676,7 +677,7 @@ Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) {
void UserInputMapper::enableMapping(const QString& mappingName, bool enable) {
Locker locker(_lock);
qCDebug(controllers) << "Attempting to enable mapping " << mappingName;
qCDebug(controllers) << "Attempting to " << (enable ? "enable" : "disable") << " mapping " << mappingName;
auto iterator = _mappingsByName.find(mappingName);
if (_mappingsByName.end() == iterator) {
qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName;
@ -826,13 +827,31 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value)
return std::make_shared<AndConditional>(children);
} else if (value.isString()) {
// Support "when" : "GamePad.RB"
auto input = findDeviceInput(value.toString());
auto conditionalToken = value.toString();
// Detect for modifier case (Not...)
QString conditionalModifier;
const QString JSON_CONDITIONAL_MODIFIER_NOT("!");
if (conditionalToken.startsWith(JSON_CONDITIONAL_MODIFIER_NOT)) {
conditionalModifier = JSON_CONDITIONAL_MODIFIER_NOT;
conditionalToken = conditionalToken.right(conditionalToken.size() - conditionalModifier.size());
}
auto input = findDeviceInput(conditionalToken);
auto endpoint = endpointFor(input);
if (!endpoint) {
return Conditional::Pointer();
}
auto conditional = std::make_shared<EndpointConditional>(endpoint);
return std::make_shared<EndpointConditional>(endpoint);
if (!conditionalModifier.isEmpty()) {
if (conditionalModifier == JSON_CONDITIONAL_MODIFIER_NOT) {
return std::make_shared<NotConditional>(conditional);
}
}
// Default and conditional behavior
return conditional;
}
return Conditional::parse(value);

View file

@ -6,9 +6,11 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Endpoint.h"
namespace controller {
}
// NOTE: we don't need to include this header unless/until we add additional symbols.
// By removing this header we prevent these warnings on windows:
//
// warning LNK4221: This object file does not define any previously undefined public symbols,
// so it will not be used by any link operation that consumes this library
//
//#include "Endpoint.h"

View file

@ -5,4 +5,11 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Mapping.h"
// NOTE: we don't need to include this header unless/until we add additional symbols.
// By removing this header we prevent these warnings on windows:
//
// warning LNK4221: This object file does not define any previously undefined public symbols,
// so it will not be used by any link operation that consumes this library
//
//#include "Mapping.h"

View file

@ -6,4 +6,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Route.h"
// NOTE: we don't need to include this header unless/until we add additional symbols.
// By removing this header we prevent these warnings on windows:
//
// warning LNK4221: This object file does not define any previously undefined public symbols,
// so it will not be used by any link operation that consumes this library
//
//#include "Route.h"

View file

@ -6,4 +6,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EndpointConditional.h"
// NOTE: we don't need to include this header unless/until we add additional symbols.
// By removing this header we prevent these warnings on windows:
//
// warning LNK4221: This object file does not define any previously undefined public symbols,
// so it will not be used by any link operation that consumes this library
//
//#include "EndpointConditional.h"

View file

@ -6,4 +6,16 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "NotConditional.h"
using namespace controller;
bool NotConditional::satisfied() {
if (_operand) {
return (!_operand->satisfied());
} else {
return false;
}
}

View file

@ -12,5 +12,19 @@
#include "../Conditional.h"
namespace controller {
class NotConditional : public Conditional {
public:
using Pointer = std::shared_ptr<NotConditional>;
NotConditional(Conditional::Pointer operand) : _operand(operand) { }
virtual bool satisfied() override;
private:
Conditional::Pointer _operand;
};
}
#endif

View file

@ -6,4 +6,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ArrayEndpoint.h"
// NOTE: we don't need to include this header unless/until we add additional symbols.
// By removing this header we prevent these warnings on windows:
//
// warning LNK4221: This object file does not define any previously undefined public symbols,
// so it will not be used by any link operation that consumes this library
//
//#include "ArrayEndpoint.h"

View file

@ -6,4 +6,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "JSEndpoint.h"
// NOTE: we don't need to include this header unless/until we add additional symbols.
// By removing this header we prevent these warnings on windows:
//
// warning LNK4221: This object file does not define any previously undefined public symbols,
// so it will not be used by any link operation that consumes this library
//
//#include "JSEndpoint.h"

View file

@ -6,4 +6,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "StandardEndpoint.h"
// NOTE: we don't need to include this header unless/until we add additional symbols.
// By removing this header we prevent these warnings on windows:
//
// warning LNK4221: This object file does not define any previously undefined public symbols,
// so it will not be used by any link operation that consumes this library
//
//#include "StandardEndpoint.h"

View file

@ -6,4 +6,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ConstrainToIntegerFilter.h"
// NOTE: we don't need to include this header unless/until we add additional symbols.
// By removing this header we prevent these warnings on windows:
//
// warning LNK4221: This object file does not define any previously undefined public symbols,
// so it will not be used by any link operation that consumes this library
//
//#include "ConstrainToIntegerFilter.h"

View file

@ -6,4 +6,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ConstrainToPositiveIntegerFilter.h"
// NOTE: we don't need to include this header unless/until we add additional symbols.
// By removing this header we prevent these warnings on windows:
//
// warning LNK4221: This object file does not define any previously undefined public symbols,
// so it will not be used by any link operation that consumes this library
//
//#include "ConstrainToPositiveIntegerFilter.h"

View file

@ -6,4 +6,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "InvertFilter.h"
// NOTE: we don't need to include this header unless/until we add additional symbols.
// By removing this header we prevent these warnings on windows:
//
// warning LNK4221: This object file does not define any previously undefined public symbols,
// so it will not be used by any link operation that consumes this library
//
//#include "InvertFilter.h"

View file

@ -11,7 +11,7 @@
#define hifi_GlWindow_h
#include <mutex>
#include <QWindow>
#include <QtGui/QWindow>
class QOpenGLContext;
class QOpenGLDebugLogger;

View file

@ -22,6 +22,14 @@ ProfileRange::ProfileRange(const char *name) {
ProfileRange::~ProfileRange() {
nvtxRangePop();
}
ProfileRangeBatch::ProfileRangeBatch(gpu::Batch& batch, const char *name) : _batch(batch) {
_batch.pushProfileRange(name);
}
ProfileRangeBatch::~ProfileRangeBatch() {
_batch.popProfileRange();
}
#endif
#define ADD_COMMAND(call) _commands.push_back(COMMAND_##call); _commandOffsets.push_back(_params.size());
@ -391,3 +399,17 @@ QDebug& operator<<(QDebug& debug, const Batch::CacheState& cacheState) {
return debug;
}
// Debugging
void Batch::pushProfileRange(const char* name) {
#if defined(NSIGHT_FOUND)
ADD_COMMAND(pushProfileRange);
_params.push_back(_profileRanges.cache(name));
#endif
}
void Batch::popProfileRange() {
#if defined(NSIGHT_FOUND)
ADD_COMMAND(popProfileRange);
#endif
}

View file

@ -229,6 +229,10 @@ public:
// Reset the stage caches and states
void resetStages();
// Debugging
void pushProfileRange(const char* name);
void popProfileRange();
// TODO: As long as we have gl calls explicitely issued from interface
// code, we need to be able to record and batch these calls. THe long
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
@ -324,6 +328,9 @@ public:
COMMAND_glColor4f,
COMMAND_pushProfileRange,
COMMAND_popProfileRange,
NUM_COMMANDS,
};
typedef std::vector<Command> Commands;
@ -389,6 +396,7 @@ public:
typedef Cache<PipelinePointer>::Vector PipelineCaches;
typedef Cache<FramebufferPointer>::Vector FramebufferCaches;
typedef Cache<QueryPointer>::Vector QueryCaches;
typedef Cache<std::string>::Vector ProfileRangeCaches;
typedef Cache<std::function<void()>>::Vector LambdaCache;
// Cache Data in a byte array if too big to fit in Param
@ -416,6 +424,7 @@ public:
FramebufferCaches _framebuffers;
QueryCaches _queries;
LambdaCache _lambdas;
ProfileRangeCaches _profileRanges;
NamedBatchDataMap _namedData;
@ -429,6 +438,25 @@ protected:
}
#if defined(NSIGHT_FOUND)
class ProfileRangeBatch {
public:
ProfileRangeBatch(gpu::Batch& batch, const char *name);
~ProfileRangeBatch();
private:
gpu::Batch& _batch;
};
#define PROFILE_RANGE_BATCH(batch, name) ProfileRangeBatch profileRangeThis(batch, name);
#else
#define PROFILE_RANGE_BATCH(batch, name)
#endif
QDebug& operator<<(QDebug& debug, const gpu::Batch::CacheState& cacheState);
#endif

View file

@ -15,6 +15,11 @@
#include <list>
#include <glm/gtc/type_ptr.hpp>
#if defined(NSIGHT_FOUND)
#include "nvToolsExt.h"
#endif
using namespace gpu;
GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
@ -69,6 +74,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::GLBackend::do_glUniformMatrix4fv),
(&::gpu::GLBackend::do_glColor4f),
(&::gpu::GLBackend::do_pushProfileRange),
(&::gpu::GLBackend::do_popProfileRange),
};
void GLBackend::init() {
@ -710,3 +718,17 @@ void GLBackend::do_glColor4f(Batch& batch, uint32 paramOffset) {
}
(void) CHECK_GL_ERROR();
}
void GLBackend::do_pushProfileRange(Batch& batch, uint32 paramOffset) {
#if defined(NSIGHT_FOUND)
auto name = batch._profileRanges.get(batch._params[paramOffset]._uint);
nvtxRangePush(name.c_str());
#endif
}
void GLBackend::do_popProfileRange(Batch& batch, uint32 paramOffset) {
#if defined(NSIGHT_FOUND)
nvtxRangePop();
#endif
}

View file

@ -479,6 +479,9 @@ protected:
void do_glColor4f(Batch& batch, uint32 paramOffset);
void do_pushProfileRange(Batch& batch, uint32 paramOffset);
void do_popProfileRange(Batch& batch, uint32 paramOffset);
typedef void (GLBackend::*CommandCall)(Batch&, uint32);
static CommandCall _commandCalls[Batch::NUM_COMMANDS];
};

View file

@ -59,11 +59,27 @@ void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int devic
// key pressed again ? without catching the release event ?
}
_lastCursor = event->pos();
_mousePressAt = event->pos();
eraseMouseClicked();
}
void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
auto input = makeInput((Qt::MouseButton) event->button());
_buttonPressedMap.erase(input.getChannel());
// if we pressed and released at the same location, then create a "_CLICKED" input for this button
// we might want to add some small tolerance to this so if you do a small drag it still counts as
// a clicked.
if (_mousePressAt == event->pos()) {
_buttonPressedMap.insert(makeInput((Qt::MouseButton) event->button(), true).getChannel());
}
}
void KeyboardMouseDevice::eraseMouseClicked() {
_buttonPressedMap.erase(makeInput(Qt::LeftButton, true).getChannel());
_buttonPressedMap.erase(makeInput(Qt::MiddleButton, true).getChannel());
_buttonPressedMap.erase(makeInput(Qt::RightButton, true).getChannel());
}
void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
@ -77,6 +93,8 @@ void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int device
_axisStateMap[makeInput(MOUSE_AXIS_Y_NEG).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f);
_lastCursor = currentPos;
eraseMouseClicked();
}
void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) {
@ -138,14 +156,17 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const {
return controller::Input(_deviceID, shortCode, controller::ChannelType::BUTTON);
}
controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) const {
controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool clicked) const {
switch (code) {
case Qt::LeftButton:
return controller::Input(_deviceID, MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON);
return controller::Input(_deviceID, clicked ? MOUSE_BUTTON_LEFT_CLICKED :
MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON);
case Qt::RightButton:
return controller::Input(_deviceID, MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON);
return controller::Input(_deviceID, clicked ? MOUSE_BUTTON_RIGHT_CLICKED :
MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON);
case Qt::MiddleButton:
return controller::Input(_deviceID, MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON);
return controller::Input(_deviceID, clicked ? MOUSE_BUTTON_MIDDLE_CLICKED :
MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON);
default:
return controller::Input();
};
@ -182,9 +203,13 @@ controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const {
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString()));
availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString()));
availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseClick"));
availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseClick"));
availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "RightMouseClick"));
availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton"));
availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton"));
availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "RightMouseButton"));
availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton, true), "LeftMouseClicked"));
availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton, true), "MiddleMouseClicked"));
availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton, true), "RightMouseClicked"));
availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_POS), "MouseMoveRight"));
availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_NEG), "MouseMoveLeft"));

View file

@ -37,6 +37,9 @@ public:
MOUSE_BUTTON_LEFT = KEYBOARD_LAST + 1,
MOUSE_BUTTON_RIGHT,
MOUSE_BUTTON_MIDDLE,
MOUSE_BUTTON_LEFT_CLICKED,
MOUSE_BUTTON_RIGHT_CLICKED,
MOUSE_BUTTON_MIDDLE_CLICKED,
};
enum MouseAxisChannel {
@ -83,6 +86,7 @@ public:
void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0);
void mousePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0);
void eraseMouseClicked();
void touchBeginEvent(const QTouchEvent* event);
void touchEndEvent(const QTouchEvent* event);
@ -92,7 +96,7 @@ public:
// Let's make it easy for Qt because we assume we love Qt forever
controller::Input makeInput(Qt::Key code) const;
controller::Input makeInput(Qt::MouseButton code) const;
controller::Input makeInput(Qt::MouseButton code, bool clicked = false) const;
controller::Input makeInput(MouseAxisChannel axis) const;
controller::Input makeInput(TouchAxisChannel axis) const;
controller::Input makeInput(TouchButtonChannel button) const;
@ -101,6 +105,7 @@ public:
protected:
QPoint _lastCursor;
QPoint _mousePressAt;
glm::vec2 _lastTouch;
bool _isTouching = false;

View file

@ -20,27 +20,27 @@
#ifdef HAVE_SDL2
static_assert(
controller::A == SDL_CONTROLLER_BUTTON_A &&
controller::B == SDL_CONTROLLER_BUTTON_B &&
controller::X == SDL_CONTROLLER_BUTTON_X &&
controller::Y == SDL_CONTROLLER_BUTTON_Y &&
controller::BACK == SDL_CONTROLLER_BUTTON_BACK &&
controller::GUIDE == SDL_CONTROLLER_BUTTON_GUIDE &&
controller::START == SDL_CONTROLLER_BUTTON_START &&
controller::LS == SDL_CONTROLLER_BUTTON_LEFTSTICK &&
controller::RS == SDL_CONTROLLER_BUTTON_RIGHTSTICK &&
controller::LB == SDL_CONTROLLER_BUTTON_LEFTSHOULDER &&
controller::RB == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER &&
controller::DU == SDL_CONTROLLER_BUTTON_DPAD_UP &&
controller::DD == SDL_CONTROLLER_BUTTON_DPAD_DOWN &&
controller::DL == SDL_CONTROLLER_BUTTON_DPAD_LEFT &&
controller::DR == SDL_CONTROLLER_BUTTON_DPAD_RIGHT &&
controller::LX == SDL_CONTROLLER_AXIS_LEFTX &&
controller::LY == SDL_CONTROLLER_AXIS_LEFTY &&
controller::RX == SDL_CONTROLLER_AXIS_RIGHTX &&
controller::RY == SDL_CONTROLLER_AXIS_RIGHTY &&
controller::LT == SDL_CONTROLLER_AXIS_TRIGGERLEFT &&
controller::RT == SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
(int)controller::A == (int)SDL_CONTROLLER_BUTTON_A &&
(int)controller::B == (int)SDL_CONTROLLER_BUTTON_B &&
(int)controller::X == (int)SDL_CONTROLLER_BUTTON_X &&
(int)controller::Y == (int)SDL_CONTROLLER_BUTTON_Y &&
(int)controller::BACK == (int)SDL_CONTROLLER_BUTTON_BACK &&
(int)controller::GUIDE == (int)SDL_CONTROLLER_BUTTON_GUIDE &&
(int)controller::START == (int)SDL_CONTROLLER_BUTTON_START &&
(int)controller::LS == (int)SDL_CONTROLLER_BUTTON_LEFTSTICK &&
(int)controller::RS == (int)SDL_CONTROLLER_BUTTON_RIGHTSTICK &&
(int)controller::LB == (int)SDL_CONTROLLER_BUTTON_LEFTSHOULDER &&
(int)controller::RB == (int)SDL_CONTROLLER_BUTTON_RIGHTSHOULDER &&
(int)controller::DU == (int)SDL_CONTROLLER_BUTTON_DPAD_UP &&
(int)controller::DD == (int)SDL_CONTROLLER_BUTTON_DPAD_DOWN &&
(int)controller::DL == (int)SDL_CONTROLLER_BUTTON_DPAD_LEFT &&
(int)controller::DR == (int)SDL_CONTROLLER_BUTTON_DPAD_RIGHT &&
(int)controller::LX == (int)SDL_CONTROLLER_AXIS_LEFTX &&
(int)controller::LY == (int)SDL_CONTROLLER_AXIS_LEFTY &&
(int)controller::RX == (int)SDL_CONTROLLER_AXIS_RIGHTX &&
(int)controller::RY == (int)SDL_CONTROLLER_AXIS_RIGHTY &&
(int)controller::LT == (int)SDL_CONTROLLER_AXIS_TRIGGERLEFT &&
(int)controller::RT == (int)SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
"SDL2 equvalence: Enums and values from StandardControls.h are assumed to match enums from SDL_gamecontroller.h");
#endif

View file

@ -84,7 +84,7 @@ private:
const glm::vec3& TWO() { return Vectors::TWO; }
const glm::vec3& HALF() { return Vectors::HALF; }
const glm::vec3& RIGHT() { return Vectors::RIGHT; }
const glm::vec3& UP() { return Vectors::UNIT_X; }
const glm::vec3& UP() { return Vectors::UP; }
const glm::vec3& FRONT() { return Vectors::FRONT; }
};

View file

@ -1,5 +1,19 @@
//
// AtRestDetector.cpp
// libraries/shared/src
//
// Created by Anthony Thibault on 10/6/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
//
#include "AtRestDetector.h"
#include "NumericalConstants.h"
#include "SharedLogging.h"
#include "SharedUtil.h"
AtRestDetector::AtRestDetector(const glm::vec3& startPosition, const glm::quat& startRotation) {
reset(startPosition, startRotation);
@ -12,9 +26,14 @@ void AtRestDetector::reset(const glm::vec3& startPosition, const glm::quat& star
glm::quat ql = glm::log(startRotation);
_quatLogAverage = glm::vec3(ql.x, ql.y, ql.z);
_quatLogVariance = 0.0f;
_lastUpdateTime = usecTimestampNow();
_isAtRest = false;
}
bool AtRestDetector::update(float dt, const glm::vec3& position, const glm::quat& rotation) {
bool AtRestDetector::update(const glm::vec3& position, const glm::quat& rotation) {
uint64_t now = usecTimestampNow();
float dt = (float)(_lastUpdateTime - now) / (float)USECS_PER_SECOND;
_lastUpdateTime = now;
const float TAU = 1.0f;
float delta = glm::min(dt / TAU, 1.0f);
@ -37,5 +56,6 @@ bool AtRestDetector::update(float dt, const glm::vec3& position, const glm::quat
const float POSITION_VARIANCE_THRESHOLD = 0.001f;
const float QUAT_LOG_VARIANCE_THRESHOLD = 0.00002f;
return _positionVariance < POSITION_VARIANCE_THRESHOLD && _quatLogVariance < QUAT_LOG_VARIANCE_THRESHOLD;
_isAtRest = _positionVariance < POSITION_VARIANCE_THRESHOLD && _quatLogVariance < QUAT_LOG_VARIANCE_THRESHOLD;
return _isAtRest;
}

View file

@ -21,14 +21,17 @@ public:
void reset(const glm::vec3& startPosition, const glm::quat& startRotation);
// returns true if object is at rest, dt in assumed to be seconds.
bool update(float dt, const glm::vec3& position, const glm::quat& startRotation);
bool update(const glm::vec3& position, const glm::quat& startRotation);
bool isAtRest() const { return _isAtRest; }
protected:
glm::vec3 _positionAverage;
float _positionVariance;
glm::vec3 _quatLogAverage;
float _quatLogVariance;
uint64_t _lastUpdateTime { 0 };
float _positionVariance { 0.0f };
float _quatLogVariance { 0.0f };
bool _isAtRest { false };
};
#endif

View file

@ -183,6 +183,11 @@ T toNormalizedDeviceScale(const T& value, const T& size) {
#define PITCH(euler) euler.x
#define ROLL(euler) euler.z
// float - linear interpolate
inline float lerp(float x, float y, float a) {
return x * (1.0f - a) + (y * a);
}
// vec2 lerp - linear interpolate
template<typename T, glm::precision P>
glm::detail::tvec2<T, P> lerp(const glm::detail::tvec2<T, P>& x, const glm::detail::tvec2<T, P>& y, T a) {

View file

@ -8,6 +8,9 @@
if (WIN32)
# we're using static GLEW, so define GLEW_STATIC
add_definitions(-DGLEW_STATIC)
set(TARGET_NAME oculus)
setup_hifi_plugin()
link_hifi_libraries(shared gl plugins display-plugins)

View file

@ -9,15 +9,15 @@
#include <memory>
#include <QMainWindow>
#include <QGLWidget>
#include <QtWidgets/QMainWindow>
#include <QtOpenGL/QGLWidget>
#include <GLMHelpers.h>
#include <gl/GlWindow.h>
#include <QEvent>
#include <QResizeEvent>
#include <QOpenGLContext>
#include <QGuiApplication>
#include <QScreen>
#include <QtGui/QResizeEvent>
#include <QtGui/QOpenGLContext>
#include <QtGui/QGuiApplication>
#include <QtGui/QScreen>
#include <PerfStat.h>
#include <gl/OglplusHelpers.h>
@ -193,7 +193,7 @@ void OculusLegacyDisplayPlugin::deactivate() {
// DLL based display plugins MUST initialize GLEW inside the DLL code.
void OculusLegacyDisplayPlugin::customizeContext() {
glewExperimental = true;
GLenum err = glewInit();
glewInit();
glGetError();
WindowOpenGLDisplayPlugin::customizeContext();
}

View file

@ -13,6 +13,7 @@
#include "AnimBlendLinear.h"
#include "AnimationLogging.h"
#include "AnimVariant.h"
#include "AnimUtil.h"
#include <../QTestExtensions.h>
@ -30,8 +31,8 @@ void AnimTests::cleanupTestCase() {
}
void AnimTests::testClipInternalState() {
std::string id = "my anim clip";
std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx";
QString id = "my anim clip";
QString url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx";
float startFrame = 2.0f;
float endFrame = 20.0f;
float timeScale = 1.1f;
@ -55,8 +56,8 @@ static float framesToSec(float secs) {
}
void AnimTests::testClipEvaulate() {
std::string id = "myClipNode";
std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx";
QString id = "myClipNode";
QString url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx";
float startFrame = 2.0f;
float endFrame = 22.0f;
float timeScale = 1.0f;
@ -73,8 +74,8 @@ void AnimTests::testClipEvaulate() {
// does it loop?
triggers.clear();
clip.evaluate(vars, framesToSec(11.0f), triggers);
QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, EPSILON);
clip.evaluate(vars, framesToSec(12.0f), triggers);
QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, EPSILON); // Note: frame 3 and not 4, because extra frame between start and end.
// did we receive a loop trigger?
QVERIFY(std::find(triggers.begin(), triggers.end(), "myClipNodeOnLoop") != triggers.end());
@ -90,8 +91,8 @@ void AnimTests::testClipEvaulate() {
}
void AnimTests::testClipEvaulateWithVars() {
std::string id = "myClipNode";
std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx";
QString id = "myClipNode";
QString url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx";
float startFrame = 2.0f;
float endFrame = 22.0f;
float timeScale = 1.0f;
@ -126,9 +127,9 @@ void AnimTests::testClipEvaulateWithVars() {
}
void AnimTests::testLoader() {
auto url = QUrl("https://gist.githubusercontent.com/hyperlogic/857129fe04567cbe670f/raw/8ba57a8f0a76f88b39a11f77f8d9df04af9cec95/test.json");
auto url = QUrl("https://gist.githubusercontent.com/hyperlogic/857129fe04567cbe670f/raw/0c54500f480fd7314a5aeb147c45a8a707edcc2e/test.json");
// NOTE: This will warn about missing "test01.fbx", "test02.fbx", etc. if the resource loading code doesn't handle relative pathnames!
// However, the test will proceed.
// However, the test will proceed.
AnimNodeLoader loader(url);
const int timeout = 1000;
@ -238,3 +239,87 @@ void AnimTests::testVariant() {
QVERIFY(m[1].z == -7.0f);
QVERIFY(m[3].w == 16.0f);
}
void AnimTests::testAccumulateTime() {
float startFrame = 0.0f;
float endFrame = 10.0f;
float timeScale = 1.0f;
testAccumulateTimeWithParameters(startFrame, endFrame, timeScale);
startFrame = 5.0f;
endFrame = 15.0f;
timeScale = 1.0f;
testAccumulateTimeWithParameters(startFrame, endFrame, timeScale);
startFrame = 0.0f;
endFrame = 10.0f;
timeScale = 0.5f;
testAccumulateTimeWithParameters(startFrame, endFrame, timeScale);
startFrame = 5.0f;
endFrame = 15.0f;
timeScale = 2.0f;
testAccumulateTimeWithParameters(startFrame, endFrame, timeScale);
}
void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFrame, float timeScale) const {
float dt = (1.0f / 30.0f) / timeScale; // sec
QString id = "testNode";
AnimNode::Triggers triggers;
bool loopFlag = false;
float resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == startFrame + 1.0f);
QVERIFY(triggers.empty());
triggers.clear();
resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == startFrame + 2.0f);
QVERIFY(triggers.empty());
triggers.clear();
resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == startFrame + 3.0f);
QVERIFY(triggers.empty());
triggers.clear();
// test onDone trigger and frame clamping.
resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == endFrame);
QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnDone");
triggers.clear();
resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == endFrame);
QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnDone");
triggers.clear();
// test onLoop trigger and looping frame logic
loopFlag = true;
// should NOT trigger loop even though we stop at last frame, because there is an extra frame between end and start frames.
resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == endFrame);
QVERIFY(triggers.empty());
triggers.clear();
// now we should hit loop trigger
resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == startFrame);
QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop");
triggers.clear();
// should NOT trigger loop, even though we move past the end frame, because of extra frame between end and start.
resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == endFrame + 0.5f);
QVERIFY(triggers.empty());
triggers.clear();
// now we should hit loop trigger
resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers);
QVERIFY(resultFrame == startFrame + 0.5f);
QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop");
triggers.clear();
}

View file

@ -15,6 +15,8 @@
class AnimTests : public QObject {
Q_OBJECT
public:
void testAccumulateTimeWithParameters(float startFrame, float endFrame, float timeScale) const;
private slots:
void initTestCase();
void cleanupTestCase();
@ -23,6 +25,7 @@ private slots:
void testClipEvaulateWithVars();
void testLoader();
void testVariant();
void testAccumulateTime();
};
#endif // hifi_AnimTests_h

View file

@ -114,7 +114,6 @@
createTargets();
createTargetResetter();
createBasketballHoop();
createBasketballRack();
createBasketballResetter();
@ -130,14 +129,11 @@
z: 503.49
});
createSprayCan({
x: 549.7,
y: 495.6,
z: 503.91
});
}
function deleteAllToys() {
@ -930,45 +926,6 @@
});
}
function createBasketballHoop() {
var position = {
x: 539.23,
y: 496.13,
z: 475.89
};
var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0);
var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx";
var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj";
var hoop = Entities.addEntity({
type: "Model",
modelURL: hoopURL,
position: position,
rotation: rotation,
shapeType: 'compound',
gravity: {
x: 0,
y: -9.8,
z: 0
},
dimensions: {
x: 1.89,
y: 3.99,
z: 3.79
},
compoundShapeURL: hoopCollisionHullURL,
userData: JSON.stringify({
resetMe: {
resetMe: true
},
grabbableKey: {
grabbable: false
}
})
});
}
function createWand(position) {
var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx';
var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj';

View file

@ -10,7 +10,7 @@
//per script
/*global deleteAllToys, createAllToys, createGates, createPingPongBallGun, createFire, createPottedPlant, createCombinedArmChair, createBasketballHoop, createBasketBall, createSprayCan, createDoll, createWand, createDice, createCat, deleteAllToys, createFlashlight, createBlocks, createMagballs, createLights */
/*global deleteAllToys, createAllToys, createGates, createPingPongBallGun, createFire, createPottedPlant, createCombinedArmChair, createBasketBall, createSprayCan, createDoll, createWand, createDice, createCat, deleteAllToys, createFlashlight, createBlocks, createMagballs, createLights */
var utilitiesScript = Script.resolvePath("../examples/libraries/utils.js");
Script.include(utilitiesScript);
@ -87,7 +87,6 @@ MasterReset = function() {
createTargets();
createTargetResetter();
createBasketballHoop();
createBasketballRack();
createBasketballResetter();
@ -908,45 +907,6 @@ MasterReset = function() {
});
}
function createBasketballHoop() {
var position = {
x: 539.23,
y: 496.13,
z: 475.89
};
var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0);
var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx";
var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj";
var hoop = Entities.addEntity({
type: "Model",
modelURL: hoopURL,
position: position,
rotation: rotation,
shapeType: 'compound',
gravity: {
x: 0,
y: -9.8,
z: 0
},
dimensions: {
x: 1.89,
y: 3.99,
z: 3.79
},
compoundShapeURL: hoopCollisionHullURL,
userData: JSON.stringify({
resetMe: {
resetMe: true
},
grabbableKey: {
grabbable: false
}
})
});
}
function createWand(position) {
var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx';
var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj';