mirror of
https://github.com/JulianGro/overte.git
synced 2025-08-04 06:44:53 +02:00
merge from upstream
This commit is contained in:
commit
dba9eb7046
66 changed files with 1702 additions and 511 deletions
30
cmake/externals/sixense/CMakeLists.txt
vendored
30
cmake/externals/sixense/CMakeLists.txt
vendored
|
@ -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)
|
||||
|
||||
|
|
|
@ -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@")
|
|
@ -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);
|
||||
|
||||
|
|
10
examples/controllers/rightClickExample.js
Normal file
10
examples/controllers/rightClickExample.js
Normal 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
341
examples/libraries/fjs.js
Normal 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;
|
||||
}
|
9
examples/libraries/fjsExample.js
Normal file
9
examples/libraries/fjsExample.js
Normal 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)
|
|
@ -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")
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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" }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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" },
|
||||
|
||||
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
126
libraries/animation/src/AnimBlendLinearMove.cpp
Normal file
126
libraries/animation/src/AnimBlendLinearMove.cpp
Normal 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);
|
||||
}
|
77
libraries/animation/src/AnimBlendLinearMove.h
Normal file
77
libraries/animation/src/AnimBlendLinearMove.h
Normal 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
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
56
libraries/animation/src/AnimPose.cpp
Normal file
56
libraries/animation/src/AnimPose.cpp
Normal 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));
|
||||
}
|
47
libraries/animation/src/AnimPose.h
Normal file
47
libraries/animation/src/AnimPose.h
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#define hifi_GlWindow_h
|
||||
|
||||
#include <mutex>
|
||||
#include <QWindow>
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
class QOpenGLContext;
|
||||
class QOpenGLDebugLogger;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
Loading…
Reference in a new issue