mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:44:02 +02:00
Merge branch 'master' into tony/anim-sync-blend
This commit is contained in:
commit
2236573a9d
12 changed files with 542 additions and 146 deletions
4
cmake/externals/sixense/CMakeLists.txt
vendored
4
cmake/externals/sixense/CMakeLists.txt
vendored
|
@ -6,8 +6,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip
|
||||
URL_MD5 752a3901f334124e9cffc2ba4136ef7d
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip
|
||||
URL_MD5 93c3a6795cce777a0f472b09532935f1
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
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)
|
|
@ -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" },
|
||||
|
@ -81,6 +81,7 @@
|
|||
|
||||
{ "from": "Keyboard.Space", "to": "Actions.SHIFT" },
|
||||
{ "from": "Keyboard.R", "to": "Actions.ACTION1" },
|
||||
{ "from": "Keyboard.T", "to": "Actions.ACTION2" }
|
||||
{ "from": "Keyboard.T", "to": "Actions.ACTION2" },
|
||||
{ "from": "Keyboard.RightMouseClicked", "to": "Actions.ContextMenu" }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -157,6 +157,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);
|
||||
|
@ -248,7 +250,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) {
|
||||
|
@ -332,25 +334,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.
|
||||
|
@ -359,102 +342,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.
|
||||
|
@ -1356,17 +1296,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 {
|
||||
|
@ -1468,7 +1459,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);
|
||||
}
|
||||
|
@ -1947,7 +1938,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);
|
||||
|
@ -316,9 +315,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;
|
||||
|
||||
|
@ -395,6 +395,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;
|
||||
|
@ -415,9 +418,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 };
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#define hifi_GlWindow_h
|
||||
|
||||
#include <mutex>
|
||||
#include <QWindow>
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
class QOpenGLContext;
|
||||
class QOpenGLDebugLogger;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue