mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 19:41:20 +02:00
Merge pull request #7032 from hyperlogic/tony/standing-jump
MyAvatar: Standing Takeoff and In-Air Animations
This commit is contained in:
commit
09bdccb18c
9 changed files with 302 additions and 87 deletions
|
@ -248,8 +248,10 @@
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" },
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
{ "var": "isFlying", "state": "fly" },
|
{ "var": "isFlying", "state": "fly" },
|
||||||
{ "var": "isTakeoff", "state": "takeoff" },
|
{ "var": "isTakeoffStand", "state": "takeoffStand" },
|
||||||
{ "var": "isInAir", "state": "inAir" }
|
{ "var": "isTakeoffRun", "state": "takeoffRun" },
|
||||||
|
{ "var": "isInAirStand", "state": "inAirStand" },
|
||||||
|
{ "var": "isInAirRun", "state": "inAirRun" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -266,8 +268,10 @@
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" },
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
{ "var": "isFlying", "state": "fly" },
|
{ "var": "isFlying", "state": "fly" },
|
||||||
{ "var": "isTakeoff", "state": "takeoff" },
|
{ "var": "isTakeoffStand", "state": "takeoffStand" },
|
||||||
{ "var": "isInAir", "state": "inAir" }
|
{ "var": "isTakeoffRun", "state": "takeoffRun" },
|
||||||
|
{ "var": "isInAirStand", "state": "inAirStand" },
|
||||||
|
{ "var": "isInAirRun", "state": "inAirRun" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -283,8 +287,10 @@
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" },
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
{ "var": "isFlying", "state": "fly" },
|
{ "var": "isFlying", "state": "fly" },
|
||||||
{ "var": "isTakeoff", "state": "takeoff" },
|
{ "var": "isTakeoffStand", "state": "takeoffStand" },
|
||||||
{ "var": "isInAir", "state": "inAir" }
|
{ "var": "isTakeoffRun", "state": "takeoffRun" },
|
||||||
|
{ "var": "isInAirStand", "state": "inAirStand" },
|
||||||
|
{ "var": "isInAirRun", "state": "inAirRun" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -300,8 +306,10 @@
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" },
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
{ "var": "isFlying", "state": "fly" },
|
{ "var": "isFlying", "state": "fly" },
|
||||||
{ "var": "isTakeoff", "state": "takeoff" },
|
{ "var": "isTakeoffStand", "state": "takeoffStand" },
|
||||||
{ "var": "isInAir", "state": "inAir" }
|
{ "var": "isTakeoffRun", "state": "takeoffRun" },
|
||||||
|
{ "var": "isInAirStand", "state": "inAirStand" },
|
||||||
|
{ "var": "isInAirRun", "state": "inAirRun" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -317,8 +325,10 @@
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" },
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
{ "var": "isFlying", "state": "fly" },
|
{ "var": "isFlying", "state": "fly" },
|
||||||
{ "var": "isTakeoff", "state": "takeoff" },
|
{ "var": "isTakeoffStand", "state": "takeoffStand" },
|
||||||
{ "var": "isInAir", "state": "inAir" }
|
{ "var": "isTakeoffRun", "state": "takeoffRun" },
|
||||||
|
{ "var": "isInAirStand", "state": "inAirStand" },
|
||||||
|
{ "var": "isInAirRun", "state": "inAirRun" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -334,8 +344,10 @@
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" },
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
{ "var": "isFlying", "state": "fly" },
|
{ "var": "isFlying", "state": "fly" },
|
||||||
{ "var": "isTakeoff", "state": "takeoff" },
|
{ "var": "isTakeoffStand", "state": "takeoffStand" },
|
||||||
{ "var": "isInAir", "state": "inAir" }
|
{ "var": "isTakeoffRun", "state": "takeoffRun" },
|
||||||
|
{ "var": "isInAirStand", "state": "inAirStand" },
|
||||||
|
{ "var": "isInAirRun", "state": "inAirRun" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -351,8 +363,10 @@
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" },
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
{ "var": "isFlying", "state": "fly" },
|
{ "var": "isFlying", "state": "fly" },
|
||||||
{ "var": "isTakeoff", "state": "takeoff" },
|
{ "var": "isTakeoffStand", "state": "takeoffStand" },
|
||||||
{ "var": "isInAir", "state": "inAir" }
|
{ "var": "isTakeoffRun", "state": "takeoffRun" },
|
||||||
|
{ "var": "isInAirStand", "state": "inAirStand" },
|
||||||
|
{ "var": "isInAirRun", "state": "inAirRun" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -368,8 +382,10 @@
|
||||||
{ "var": "isTurningRight", "state": "turnRight" },
|
{ "var": "isTurningRight", "state": "turnRight" },
|
||||||
{ "var": "isAway", "state": "awayIntro" },
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
{ "var": "isFlying", "state": "fly" },
|
{ "var": "isFlying", "state": "fly" },
|
||||||
{ "var": "isTakeoff", "state": "takeoff" },
|
{ "var": "isTakeoffStand", "state": "takeoffStand" },
|
||||||
{ "var": "isInAir", "state": "inAir" }
|
{ "var": "isTakeoffRun", "state": "takeoffRun" },
|
||||||
|
{ "var": "isInAirStand", "state": "inAirStand" },
|
||||||
|
{ "var": "isInAirRun", "state": "inAirRun" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -407,18 +423,38 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "takeoff",
|
"id": "takeoffStand",
|
||||||
"interpTarget": 0,
|
"interpTarget": 0,
|
||||||
"interpDuration": 6,
|
"interpDuration": 6,
|
||||||
"transitions": [
|
"transitions": [
|
||||||
{ "var": "isAway", "state": "awayIntro" },
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
{ "var": "isNotTakeoff", "state": "inAir" }
|
{ "var": "isNotTakeoff", "state": "inAirStand" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "inAir",
|
"id": "takeoffRun",
|
||||||
"interpTarget": 0,
|
"interpTarget": 0,
|
||||||
"interpDuration": 6,
|
"interpDuration": 6,
|
||||||
|
"transitions": [
|
||||||
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
|
{ "var": "isNotTakeoff", "state": "inAirRun" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "inAirStand",
|
||||||
|
"interpTarget": 0,
|
||||||
|
"interpDuration": 6,
|
||||||
|
"interpType": "snapshotPrev",
|
||||||
|
"transitions": [
|
||||||
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
|
{ "var": "isNotInAir", "state": "idle" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "inAirRun",
|
||||||
|
"interpTarget": 0,
|
||||||
|
"interpDuration": 6,
|
||||||
|
"interpType": "snapshotPrev",
|
||||||
"transitions": [
|
"transitions": [
|
||||||
{ "var": "isAway", "state": "awayIntro" },
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
{ "var": "isNotInAir", "state": "idle" }
|
{ "var": "isNotInAir", "state": "idle" }
|
||||||
|
@ -723,19 +759,31 @@
|
||||||
"children": []
|
"children": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "takeoff",
|
"id": "takeoffStand",
|
||||||
"type": "clip",
|
"type": "clip",
|
||||||
"data": {
|
"data": {
|
||||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_takeoff.fbx",
|
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_standing_takeoff.fbx",
|
||||||
"startFrame": 1.0,
|
"startFrame": 17.0,
|
||||||
"endFrame": 2.5,
|
"endFrame": 25.0,
|
||||||
"timeScale": 1.0,
|
"timeScale": 1.0,
|
||||||
"loopFlag": false
|
"loopFlag": false
|
||||||
},
|
},
|
||||||
"children": []
|
"children": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "inAir",
|
"id": "takeoffRun",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_takeoff.fbx",
|
||||||
|
"startFrame": 1.0,
|
||||||
|
"endFrame": 2.5,
|
||||||
|
"timeScale": 0.01,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "inAirStand",
|
||||||
"type": "blendLinear",
|
"type": "blendLinear",
|
||||||
"data": {
|
"data": {
|
||||||
"alpha": 0.0,
|
"alpha": 0.0,
|
||||||
|
@ -743,10 +791,10 @@
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": "inAirPreApex",
|
"id": "inAirStandPreApex",
|
||||||
"type": "clip",
|
"type": "clip",
|
||||||
"data": {
|
"data": {
|
||||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx",
|
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_standing_apex.fbx",
|
||||||
"startFrame": 0.0,
|
"startFrame": 0.0,
|
||||||
"endFrame": 0.0,
|
"endFrame": 0.0,
|
||||||
"timeScale": 0.0,
|
"timeScale": 0.0,
|
||||||
|
@ -755,10 +803,56 @@
|
||||||
"children": []
|
"children": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "inAirApex",
|
"id": "inAirStandApex",
|
||||||
"type": "clip",
|
"type": "clip",
|
||||||
"data": {
|
"data": {
|
||||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx",
|
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_standing_apex.fbx",
|
||||||
|
"startFrame": 1.0,
|
||||||
|
"endFrame": 1.0,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "inAirStandPostApex",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_standing_apex.fbx",
|
||||||
|
"startFrame": 2.0,
|
||||||
|
"endFrame": 2.0,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "inAirRun",
|
||||||
|
"type": "blendLinear",
|
||||||
|
"data": {
|
||||||
|
"alpha": 0.0,
|
||||||
|
"alphaVar": "inAirAlpha"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "inAirRunPreApex",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx",
|
||||||
|
"startFrame": 0.0,
|
||||||
|
"endFrame": 0.0,
|
||||||
|
"timeScale": 0.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "inAirRunApex",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx",
|
||||||
"startFrame": 6.0,
|
"startFrame": 6.0,
|
||||||
"endFrame": 6.0,
|
"endFrame": 6.0,
|
||||||
"timeScale": 1.0,
|
"timeScale": 1.0,
|
||||||
|
@ -767,10 +861,10 @@
|
||||||
"children": []
|
"children": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "inAirPostApex",
|
"id": "inAirRunPostApex",
|
||||||
"type": "clip",
|
"type": "clip",
|
||||||
"data": {
|
"data": {
|
||||||
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx",
|
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx",
|
||||||
"startFrame": 11.0,
|
"startFrame": 11.0,
|
||||||
"endFrame": 11.0,
|
"endFrame": 11.0,
|
||||||
"timeScale": 1.0,
|
"timeScale": 1.0,
|
||||||
|
|
|
@ -590,17 +590,24 @@ void AnimInverseKinematics::initConstraints() {
|
||||||
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
|
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
|
||||||
stConstraint->setTwistLimits(-PI / 4.0f, PI / 4.0f);
|
stConstraint->setTwistLimits(-PI / 4.0f, PI / 4.0f);
|
||||||
|
|
||||||
// these directions are approximate swing limits in root-frame
|
|
||||||
// NOTE: they don't need to be normalized
|
|
||||||
std::vector<glm::vec3> swungDirections;
|
std::vector<glm::vec3> swungDirections;
|
||||||
swungDirections.push_back(glm::vec3(mirror * 0.25f, 0.0f, 1.0f));
|
float deltaTheta = PI / 4.0f;
|
||||||
swungDirections.push_back(glm::vec3(mirror * -0.5f, 0.0f, 1.0f));
|
float theta = 0.0f;
|
||||||
swungDirections.push_back(glm::vec3(mirror * -1.0f, 0.0f, 1.0f));
|
swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.25f, sinf(theta)));
|
||||||
swungDirections.push_back(glm::vec3(mirror * -1.0f, 0.0f, 0.0f));
|
theta += deltaTheta;
|
||||||
swungDirections.push_back(glm::vec3(mirror * -0.5f, -0.5f, -1.0f));
|
swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.0f, sinf(theta)));
|
||||||
swungDirections.push_back(glm::vec3(mirror * 0.0f, -0.75f, -1.0f));
|
theta += deltaTheta;
|
||||||
swungDirections.push_back(glm::vec3(mirror * 0.25f, -1.0f, 0.0f));
|
swungDirections.push_back(glm::vec3(mirror * cosf(theta), -0.25f, sinf(theta))); // posterior
|
||||||
swungDirections.push_back(glm::vec3(mirror * 0.25f, -1.0f, 1.0f));
|
theta += deltaTheta;
|
||||||
|
swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.0f, sinf(theta)));
|
||||||
|
theta += deltaTheta;
|
||||||
|
swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.25f, sinf(theta)));
|
||||||
|
theta += deltaTheta;
|
||||||
|
swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.5f, sinf(theta)));
|
||||||
|
theta += deltaTheta;
|
||||||
|
swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.5f, sinf(theta))); // anterior
|
||||||
|
theta += deltaTheta;
|
||||||
|
swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.5f, sinf(theta)));
|
||||||
|
|
||||||
// rotate directions into joint-frame
|
// rotate directions into joint-frame
|
||||||
glm::quat invAbsoluteRotation = glm::inverse(absolutePoses[i].rot);
|
glm::quat invAbsoluteRotation = glm::inverse(absolutePoses[i].rot);
|
||||||
|
@ -755,7 +762,7 @@ void AnimInverseKinematics::initConstraints() {
|
||||||
// we determine the max/min angles by rotating the swing limit lines from parent- to child-frame
|
// we determine the max/min angles by rotating the swing limit lines from parent- to child-frame
|
||||||
// then measure the angles to swing the yAxis into alignment
|
// then measure the angles to swing the yAxis into alignment
|
||||||
const float MIN_KNEE_ANGLE = 0.0f;
|
const float MIN_KNEE_ANGLE = 0.0f;
|
||||||
const float MAX_KNEE_ANGLE = 3.0f * PI / 4.0f;
|
const float MAX_KNEE_ANGLE = 7.0f * PI / 8.0f;
|
||||||
glm::quat invReferenceRotation = glm::inverse(referenceRotation);
|
glm::quat invReferenceRotation = glm::inverse(referenceRotation);
|
||||||
glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y;
|
glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y;
|
||||||
glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y;
|
glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y;
|
||||||
|
|
|
@ -67,6 +67,16 @@ static AnimNode::Type stringToAnimNodeType(const QString& str) {
|
||||||
return AnimNode::Type::NumTypes;
|
return AnimNode::Type::NumTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AnimStateMachine::InterpType stringToInterpType(const QString& str) {
|
||||||
|
if (str == "snapshotBoth") {
|
||||||
|
return AnimStateMachine::InterpType::SnapshotBoth;
|
||||||
|
} else if (str == "snapshotPrev") {
|
||||||
|
return AnimStateMachine::InterpType::SnapshotPrev;
|
||||||
|
} else {
|
||||||
|
return AnimStateMachine::InterpType::NumTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) {
|
static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AnimManipulator::JointVar::Type::AbsoluteRotation: return "absoluteRotation";
|
case AnimManipulator::JointVar::Type::AbsoluteRotation: return "absoluteRotation";
|
||||||
|
@ -465,9 +475,11 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
|
||||||
READ_STRING(id, stateObj, nodeId, jsonUrl, false);
|
READ_STRING(id, stateObj, nodeId, jsonUrl, false);
|
||||||
READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl, false);
|
READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl, false);
|
||||||
READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl, false);
|
READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl, false);
|
||||||
|
READ_OPTIONAL_STRING(interpType, stateObj);
|
||||||
|
|
||||||
READ_OPTIONAL_STRING(interpTargetVar, stateObj);
|
READ_OPTIONAL_STRING(interpTargetVar, stateObj);
|
||||||
READ_OPTIONAL_STRING(interpDurationVar, stateObj);
|
READ_OPTIONAL_STRING(interpDurationVar, stateObj);
|
||||||
|
READ_OPTIONAL_STRING(interpTypeVar, stateObj);
|
||||||
|
|
||||||
auto iter = childMap.find(id);
|
auto iter = childMap.find(id);
|
||||||
if (iter == childMap.end()) {
|
if (iter == childMap.end()) {
|
||||||
|
@ -475,7 +487,16 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto statePtr = std::make_shared<AnimStateMachine::State>(id, iter->second, interpTarget, interpDuration);
|
AnimStateMachine::InterpType interpTypeEnum = AnimStateMachine::InterpType::SnapshotBoth; // default value
|
||||||
|
if (!interpType.isEmpty()) {
|
||||||
|
interpTypeEnum = stringToInterpType(interpType);
|
||||||
|
if (interpTypeEnum == AnimStateMachine::InterpType::NumTypes) {
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, bad interpType on stateMachine state, nodeId = " << nodeId << "stateId =" << id << "url = " << jsonUrl.toDisplayString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto statePtr = std::make_shared<AnimStateMachine::State>(id, iter->second, interpTarget, interpDuration, interpTypeEnum);
|
||||||
assert(statePtr);
|
assert(statePtr);
|
||||||
|
|
||||||
if (!interpTargetVar.isEmpty()) {
|
if (!interpTargetVar.isEmpty()) {
|
||||||
|
@ -484,6 +505,9 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
|
||||||
if (!interpDurationVar.isEmpty()) {
|
if (!interpDurationVar.isEmpty()) {
|
||||||
statePtr->setInterpDurationVar(interpDurationVar);
|
statePtr->setInterpDurationVar(interpDurationVar);
|
||||||
}
|
}
|
||||||
|
if (!interpTypeVar.isEmpty()) {
|
||||||
|
statePtr->setInterpTypeVar(interpTypeVar);
|
||||||
|
}
|
||||||
|
|
||||||
smNode->addState(statePtr);
|
smNode->addState(statePtr);
|
||||||
stateMap.insert(StateMap::value_type(statePtr->getID(), statePtr));
|
stateMap.insert(StateMap::value_type(statePtr->getID(), statePtr));
|
||||||
|
|
|
@ -52,8 +52,25 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl
|
||||||
if (_duringInterp) {
|
if (_duringInterp) {
|
||||||
_alpha += _alphaVel * dt;
|
_alpha += _alphaVel * dt;
|
||||||
if (_alpha < 1.0f) {
|
if (_alpha < 1.0f) {
|
||||||
if (_poses.size() > 0 && _nextPoses.size() > 0 && _prevPoses.size() > 0) {
|
AnimPoseVec* nextPoses = nullptr;
|
||||||
::blend(_poses.size(), &_prevPoses[0], &_nextPoses[0], _alpha, &_poses[0]);
|
AnimPoseVec* prevPoses = nullptr;
|
||||||
|
AnimPoseVec localNextPoses;
|
||||||
|
if (_interpType == InterpType::SnapshotBoth) {
|
||||||
|
// interp between both snapshots
|
||||||
|
prevPoses = &_prevPoses;
|
||||||
|
nextPoses = &_nextPoses;
|
||||||
|
} else if (_interpType == InterpType::SnapshotPrev) {
|
||||||
|
// interp between the prev snapshot and evaluated next target.
|
||||||
|
// this is useful for interping into a blend
|
||||||
|
localNextPoses = currentStateNode->evaluate(animVars, dt, triggersOut);
|
||||||
|
prevPoses = &_prevPoses;
|
||||||
|
nextPoses = &localNextPoses;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) {
|
||||||
|
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), _alpha, &_poses[0]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_duringInterp = false;
|
_duringInterp = false;
|
||||||
|
@ -86,16 +103,32 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointe
|
||||||
_alpha = 0.0f;
|
_alpha = 0.0f;
|
||||||
float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration));
|
float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration));
|
||||||
_alphaVel = FRAMES_PER_SECOND / duration;
|
_alphaVel = FRAMES_PER_SECOND / duration;
|
||||||
_prevPoses = _poses;
|
_interpType = (InterpType)animVars.lookup(desiredState->_interpTypeVar, (int)desiredState->_interpType);
|
||||||
nextStateNode->setCurrentFrame(desiredState->_interpTarget);
|
|
||||||
|
|
||||||
// because dt is 0, we should not encounter any triggers
|
// because dt is 0, we should not encounter any triggers
|
||||||
const float dt = 0.0f;
|
const float dt = 0.0f;
|
||||||
Triggers triggers;
|
Triggers triggers;
|
||||||
_nextPoses = nextStateNode->evaluate(animVars, dt, triggers);
|
|
||||||
|
if (_interpType == InterpType::SnapshotBoth) {
|
||||||
|
// snapshot previous pose.
|
||||||
|
_prevPoses = _poses;
|
||||||
|
// snapshot next pose at the target frame.
|
||||||
|
nextStateNode->setCurrentFrame(desiredState->_interpTarget);
|
||||||
|
_nextPoses = nextStateNode->evaluate(animVars, dt, triggers);
|
||||||
|
} else if (_interpType == InterpType::SnapshotPrev) {
|
||||||
|
// snapshot previoius pose
|
||||||
|
_prevPoses = _poses;
|
||||||
|
// no need to evaluate _nextPoses we will do it dynamically during the interp,
|
||||||
|
// however we need to set the current frame.
|
||||||
|
nextStateNode->setCurrentFrame(desiredState->_interpTarget - duration);
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
#if WANT_DEBUG
|
#if WANT_DEBUG
|
||||||
qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget;
|
qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget << "interpType = " << (int)_interpType;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_currentState = desiredState;
|
_currentState = desiredState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,13 +31,27 @@
|
||||||
// visible after interpolation is complete.
|
// visible after interpolation is complete.
|
||||||
// * interpDuration - (frames) The total length of time it will take to interp between the current pose and the
|
// * interpDuration - (frames) The total length of time it will take to interp between the current pose and the
|
||||||
// interpTarget frame.
|
// interpTarget frame.
|
||||||
|
// * interpType - How the interpolation is performed.
|
||||||
|
// * SnapshotBoth: Stores two snapshots, the previous animation before interpolation begins and the target state at the
|
||||||
|
// interTarget frame. Then during the interpolation period the two snapshots are interpolated to produce smooth motion between them.
|
||||||
|
// * SnapshotPrev: Stores a snapshot of the previous animation before interpolation begins. However the target state is
|
||||||
|
// evaluated dynamically. During the interpolation period the previous snapshot is interpolated with the target pose
|
||||||
|
// to produce smooth motion between them. This mode is useful for interping into a blended animation where the actual
|
||||||
|
// blend factor is not known at the start of the interp or is might change dramatically during the interp.
|
||||||
|
|
||||||
class AnimStateMachine : public AnimNode {
|
class AnimStateMachine : public AnimNode {
|
||||||
public:
|
public:
|
||||||
friend class AnimNodeLoader;
|
friend class AnimNodeLoader;
|
||||||
friend bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl);
|
friend bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl);
|
||||||
|
|
||||||
|
enum class InterpType {
|
||||||
|
SnapshotBoth = 0,
|
||||||
|
SnapshotPrev,
|
||||||
|
NumTypes
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
class State {
|
class State {
|
||||||
public:
|
public:
|
||||||
friend AnimStateMachine;
|
friend AnimStateMachine;
|
||||||
|
@ -55,14 +69,16 @@ protected:
|
||||||
State::Pointer _state;
|
State::Pointer _state;
|
||||||
};
|
};
|
||||||
|
|
||||||
State(const QString& id, int childIndex, float interpTarget, float interpDuration) :
|
State(const QString& id, int childIndex, float interpTarget, float interpDuration, InterpType interpType) :
|
||||||
_id(id),
|
_id(id),
|
||||||
_childIndex(childIndex),
|
_childIndex(childIndex),
|
||||||
_interpTarget(interpTarget),
|
_interpTarget(interpTarget),
|
||||||
_interpDuration(interpDuration) {}
|
_interpDuration(interpDuration),
|
||||||
|
_interpType(interpType) {}
|
||||||
|
|
||||||
void setInterpTargetVar(const QString& interpTargetVar) { _interpTargetVar = interpTargetVar; }
|
void setInterpTargetVar(const QString& interpTargetVar) { _interpTargetVar = interpTargetVar; }
|
||||||
void setInterpDurationVar(const QString& interpDurationVar) { _interpDurationVar = interpDurationVar; }
|
void setInterpDurationVar(const QString& interpDurationVar) { _interpDurationVar = interpDurationVar; }
|
||||||
|
void setInterpTypeVar(const QString& interpTypeVar) { _interpTypeVar = interpTypeVar; }
|
||||||
|
|
||||||
int getChildIndex() const { return _childIndex; }
|
int getChildIndex() const { return _childIndex; }
|
||||||
const QString& getID() const { return _id; }
|
const QString& getID() const { return _id; }
|
||||||
|
@ -78,9 +94,11 @@ protected:
|
||||||
int _childIndex;
|
int _childIndex;
|
||||||
float _interpTarget; // frames
|
float _interpTarget; // frames
|
||||||
float _interpDuration; // frames
|
float _interpDuration; // frames
|
||||||
|
InterpType _interpType;
|
||||||
|
|
||||||
QString _interpTargetVar;
|
QString _interpTargetVar;
|
||||||
QString _interpDurationVar;
|
QString _interpDurationVar;
|
||||||
|
QString _interpTypeVar;
|
||||||
|
|
||||||
std::vector<Transition> _transitions;
|
std::vector<Transition> _transitions;
|
||||||
|
|
||||||
|
@ -115,6 +133,7 @@ protected:
|
||||||
|
|
||||||
// interpolation state
|
// interpolation state
|
||||||
bool _duringInterp = false;
|
bool _duringInterp = false;
|
||||||
|
InterpType _interpType { InterpType::SnapshotBoth };
|
||||||
float _alphaVel = 0.0f;
|
float _alphaVel = 0.0f;
|
||||||
float _alpha = 0.0f;
|
float _alpha = 0.0f;
|
||||||
AnimPoseVec _prevPoses;
|
AnimPoseVec _prevPoses;
|
||||||
|
|
|
@ -627,6 +627,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
// Skip hystersis timer for jump transitions.
|
// Skip hystersis timer for jump transitions.
|
||||||
if (_desiredState == RigRole::Takeoff) {
|
if (_desiredState == RigRole::Takeoff) {
|
||||||
_desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER;
|
_desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER;
|
||||||
|
} else if (_state == RigRole::Takeoff && _desiredState == RigRole::InAir) {
|
||||||
|
_desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER;
|
||||||
} else if (_state == RigRole::InAir && _desiredState != RigRole::InAir) {
|
} else if (_state == RigRole::InAir && _desiredState != RigRole::InAir) {
|
||||||
_desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER;
|
_desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER;
|
||||||
}
|
}
|
||||||
|
@ -679,9 +681,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
_animVars.set("isNotTurning", true);
|
_animVars.set("isNotTurning", true);
|
||||||
_animVars.set("isFlying", false);
|
_animVars.set("isFlying", false);
|
||||||
_animVars.set("isNotFlying", true);
|
_animVars.set("isNotFlying", true);
|
||||||
_animVars.set("isTakeoff", false);
|
_animVars.set("isTakeoffStand", false);
|
||||||
|
_animVars.set("isTakeoffRun", false);
|
||||||
_animVars.set("isNotTakeoff", true);
|
_animVars.set("isNotTakeoff", true);
|
||||||
_animVars.set("isInAir", false);
|
_animVars.set("isInAirStand", false);
|
||||||
|
_animVars.set("isInAirRun", false);
|
||||||
_animVars.set("isNotInAir", true);
|
_animVars.set("isNotInAir", true);
|
||||||
}
|
}
|
||||||
} else if (_state == RigRole::Turn) {
|
} else if (_state == RigRole::Turn) {
|
||||||
|
@ -703,9 +707,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
_animVars.set("isNotMoving", true);
|
_animVars.set("isNotMoving", true);
|
||||||
_animVars.set("isFlying", false);
|
_animVars.set("isFlying", false);
|
||||||
_animVars.set("isNotFlying", true);
|
_animVars.set("isNotFlying", true);
|
||||||
_animVars.set("isTakeoff", false);
|
_animVars.set("isTakeoffStand", false);
|
||||||
|
_animVars.set("isTakeoffRun", false);
|
||||||
_animVars.set("isNotTakeoff", true);
|
_animVars.set("isNotTakeoff", true);
|
||||||
_animVars.set("isInAir", false);
|
_animVars.set("isInAirStand", false);
|
||||||
|
_animVars.set("isInAirRun", false);
|
||||||
_animVars.set("isNotInAir", true);
|
_animVars.set("isNotInAir", true);
|
||||||
|
|
||||||
} else if (_state == RigRole::Idle ) {
|
} else if (_state == RigRole::Idle ) {
|
||||||
|
@ -720,9 +726,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
_animVars.set("isNotTurning", true);
|
_animVars.set("isNotTurning", true);
|
||||||
_animVars.set("isFlying", false);
|
_animVars.set("isFlying", false);
|
||||||
_animVars.set("isNotFlying", true);
|
_animVars.set("isNotFlying", true);
|
||||||
_animVars.set("isTakeoff", false);
|
_animVars.set("isTakeoffStand", false);
|
||||||
|
_animVars.set("isTakeoffRun", false);
|
||||||
_animVars.set("isNotTakeoff", true);
|
_animVars.set("isNotTakeoff", true);
|
||||||
_animVars.set("isInAir", false);
|
_animVars.set("isInAirStand", false);
|
||||||
|
_animVars.set("isInAirRun", false);
|
||||||
_animVars.set("isNotInAir", true);
|
_animVars.set("isNotInAir", true);
|
||||||
|
|
||||||
} else if (_state == RigRole::Hover) {
|
} else if (_state == RigRole::Hover) {
|
||||||
|
@ -737,9 +745,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
_animVars.set("isNotTurning", true);
|
_animVars.set("isNotTurning", true);
|
||||||
_animVars.set("isFlying", true);
|
_animVars.set("isFlying", true);
|
||||||
_animVars.set("isNotFlying", false);
|
_animVars.set("isNotFlying", false);
|
||||||
_animVars.set("isTakeoff", false);
|
_animVars.set("isTakeoffStand", false);
|
||||||
|
_animVars.set("isTakeoffRun", false);
|
||||||
_animVars.set("isNotTakeoff", true);
|
_animVars.set("isNotTakeoff", true);
|
||||||
_animVars.set("isInAir", false);
|
_animVars.set("isInAirStand", false);
|
||||||
|
_animVars.set("isInAirRun", false);
|
||||||
_animVars.set("isNotInAir", true);
|
_animVars.set("isNotInAir", true);
|
||||||
|
|
||||||
} else if (_state == RigRole::Takeoff) {
|
} else if (_state == RigRole::Takeoff) {
|
||||||
|
@ -754,9 +764,19 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
_animVars.set("isNotTurning", true);
|
_animVars.set("isNotTurning", true);
|
||||||
_animVars.set("isFlying", false);
|
_animVars.set("isFlying", false);
|
||||||
_animVars.set("isNotFlying", true);
|
_animVars.set("isNotFlying", true);
|
||||||
_animVars.set("isTakeoff", true);
|
|
||||||
|
bool takeOffRun = forwardSpeed > 0.1f;
|
||||||
|
if (takeOffRun) {
|
||||||
|
_animVars.set("isTakeoffStand", false);
|
||||||
|
_animVars.set("isTakeoffRun", true);
|
||||||
|
} else {
|
||||||
|
_animVars.set("isTakeoffStand", true);
|
||||||
|
_animVars.set("isTakeoffRun", false);
|
||||||
|
}
|
||||||
|
|
||||||
_animVars.set("isNotTakeoff", false);
|
_animVars.set("isNotTakeoff", false);
|
||||||
_animVars.set("isInAir", true);
|
_animVars.set("isInAirStand", false);
|
||||||
|
_animVars.set("isInAirRun", false);
|
||||||
_animVars.set("isNotInAir", false);
|
_animVars.set("isNotInAir", false);
|
||||||
|
|
||||||
} else if (_state == RigRole::InAir) {
|
} else if (_state == RigRole::InAir) {
|
||||||
|
@ -771,9 +791,18 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
_animVars.set("isNotTurning", true);
|
_animVars.set("isNotTurning", true);
|
||||||
_animVars.set("isFlying", false);
|
_animVars.set("isFlying", false);
|
||||||
_animVars.set("isNotFlying", true);
|
_animVars.set("isNotFlying", true);
|
||||||
_animVars.set("isTakeoff", false);
|
_animVars.set("isTakeoffStand", false);
|
||||||
|
_animVars.set("isTakeoffRun", false);
|
||||||
_animVars.set("isNotTakeoff", true);
|
_animVars.set("isNotTakeoff", true);
|
||||||
_animVars.set("isInAir", true);
|
|
||||||
|
bool inAirRun = forwardSpeed > 0.1f;
|
||||||
|
if (inAirRun) {
|
||||||
|
_animVars.set("isInAirStand", false);
|
||||||
|
_animVars.set("isInAirRun", true);
|
||||||
|
} else {
|
||||||
|
_animVars.set("isInAirStand", true);
|
||||||
|
_animVars.set("isInAirRun", false);
|
||||||
|
}
|
||||||
_animVars.set("isNotInAir", false);
|
_animVars.set("isNotInAir", false);
|
||||||
|
|
||||||
// compute blend based on velocity
|
// compute blend based on velocity
|
||||||
|
|
|
@ -301,7 +301,7 @@ public:
|
||||||
std::map<QString, AnimNode::Pointer> _origRoleAnimations;
|
std::map<QString, AnimNode::Pointer> _origRoleAnimations;
|
||||||
std::vector<AnimNode::Pointer> _prefetchedAnimations;
|
std::vector<AnimNode::Pointer> _prefetchedAnimations;
|
||||||
|
|
||||||
bool _lastEnableInverseKinematics { false };
|
bool _lastEnableInverseKinematics { true };
|
||||||
bool _enableInverseKinematics { true };
|
bool _enableInverseKinematics { true };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -60,9 +60,10 @@ CharacterController::CharacterController() {
|
||||||
_jumpSpeed = JUMP_SPEED;
|
_jumpSpeed = JUMP_SPEED;
|
||||||
_state = State::Hover;
|
_state = State::Hover;
|
||||||
_isPushingUp = false;
|
_isPushingUp = false;
|
||||||
_jumpButtonDownStart = 0;
|
_rayHitStartTime = 0;
|
||||||
|
_takeoffToInAirStartTime = 0;
|
||||||
|
_jumpButtonDownStartTime = 0;
|
||||||
_jumpButtonDownCount = 0;
|
_jumpButtonDownCount = 0;
|
||||||
_takeoffToInAirStart = 0;
|
|
||||||
_followTime = 0.0f;
|
_followTime = 0.0f;
|
||||||
_followLinearDisplacement = btVector3(0, 0, 0);
|
_followLinearDisplacement = btVector3(0, 0, 0);
|
||||||
_followAngularDisplacement = btQuaternion::getIdentity();
|
_followAngularDisplacement = btQuaternion::getIdentity();
|
||||||
|
@ -417,6 +418,8 @@ glm::vec3 CharacterController::getLinearVelocity() const {
|
||||||
|
|
||||||
void CharacterController::preSimulation() {
|
void CharacterController::preSimulation() {
|
||||||
if (_enabled && _dynamicsWorld) {
|
if (_enabled && _dynamicsWorld) {
|
||||||
|
quint64 now = usecTimestampNow();
|
||||||
|
|
||||||
// slam body to where it is supposed to be
|
// slam body to where it is supposed to be
|
||||||
_rigidBody->setWorldTransform(_characterBodyTransform);
|
_rigidBody->setWorldTransform(_characterBodyTransform);
|
||||||
btVector3 velocity = _rigidBody->getLinearVelocity();
|
btVector3 velocity = _rigidBody->getLinearVelocity();
|
||||||
|
@ -432,27 +435,30 @@ void CharacterController::preSimulation() {
|
||||||
btScalar rayLength = _radius + MAX_FALL_HEIGHT;
|
btScalar rayLength = _radius + MAX_FALL_HEIGHT;
|
||||||
btVector3 rayEnd = rayStart - rayLength * _currentUp;
|
btVector3 rayEnd = rayStart - rayLength * _currentUp;
|
||||||
|
|
||||||
|
const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius;
|
||||||
|
const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND;
|
||||||
|
const btScalar MIN_HOVER_HEIGHT = 2.5f;
|
||||||
|
const quint64 JUMP_TO_HOVER_PERIOD = 1100 * MSECS_PER_SECOND;
|
||||||
|
const btScalar MAX_WALKING_SPEED = 2.5f;
|
||||||
|
const quint64 RAY_HIT_START_PERIOD = 500 * MSECS_PER_SECOND;
|
||||||
|
|
||||||
ClosestNotMe rayCallback(_rigidBody);
|
ClosestNotMe rayCallback(_rigidBody);
|
||||||
rayCallback.m_closestHitFraction = 1.0f;
|
rayCallback.m_closestHitFraction = 1.0f;
|
||||||
_dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback);
|
_dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback);
|
||||||
if (rayCallback.hasHit()) {
|
bool rayHasHit = rayCallback.hasHit();
|
||||||
|
if (rayHasHit) {
|
||||||
|
_rayHitStartTime = now;
|
||||||
_floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius;
|
_floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius;
|
||||||
|
} else if ((now - _rayHitStartTime) < RAY_HIT_START_PERIOD) {
|
||||||
|
rayHasHit = true;
|
||||||
} else {
|
} else {
|
||||||
_floorDistance = FLT_MAX;
|
_floorDistance = FLT_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius;
|
|
||||||
const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 200 * MSECS_PER_SECOND;
|
|
||||||
const btScalar MIN_HOVER_HEIGHT = 2.5f;
|
|
||||||
const quint64 JUMP_TO_HOVER_PERIOD = 750 * MSECS_PER_SECOND;
|
|
||||||
const btScalar MAX_WALKING_SPEED = 2.5f;
|
|
||||||
|
|
||||||
quint64 now = usecTimestampNow();
|
|
||||||
|
|
||||||
// record a time stamp when the jump button was first pressed.
|
// record a time stamp when the jump button was first pressed.
|
||||||
if ((_previousFlags & PENDING_FLAG_JUMP) != (_pendingFlags & PENDING_FLAG_JUMP)) {
|
if ((_previousFlags & PENDING_FLAG_JUMP) != (_pendingFlags & PENDING_FLAG_JUMP)) {
|
||||||
if (_pendingFlags & PENDING_FLAG_JUMP) {
|
if (_pendingFlags & PENDING_FLAG_JUMP) {
|
||||||
_jumpButtonDownStart = now;
|
_jumpButtonDownStartTime = now;
|
||||||
_jumpButtonDownCount++;
|
_jumpButtonDownCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -462,19 +468,21 @@ void CharacterController::preSimulation() {
|
||||||
|
|
||||||
switch (_state) {
|
switch (_state) {
|
||||||
case State::Ground:
|
case State::Ground:
|
||||||
if (!rayCallback.hasHit() && !_hasSupport) {
|
if (!rayHasHit && !_hasSupport) {
|
||||||
SET_STATE(State::Hover, "no ground");
|
SET_STATE(State::Hover, "no ground detected");
|
||||||
} else if (_pendingFlags & PENDING_FLAG_JUMP) {
|
} else if (_pendingFlags & PENDING_FLAG_JUMP && _jumpButtonDownCount != _takeoffJumpButtonID) {
|
||||||
_takeOffJumpButtonID = _jumpButtonDownCount;
|
_takeoffJumpButtonID = _jumpButtonDownCount;
|
||||||
|
_takeoffToInAirStartTime = now;
|
||||||
SET_STATE(State::Takeoff, "jump pressed");
|
SET_STATE(State::Takeoff, "jump pressed");
|
||||||
|
} else if (rayHasHit && !_hasSupport && _floorDistance > JUMP_PROXIMITY_THRESHOLD) {
|
||||||
|
SET_STATE(State::InAir, "falling");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case State::Takeoff:
|
case State::Takeoff:
|
||||||
if (!rayCallback.hasHit() && !_hasSupport) {
|
if (!rayHasHit && !_hasSupport) {
|
||||||
SET_STATE(State::Hover, "no ground");
|
SET_STATE(State::Hover, "no ground");
|
||||||
} else if ((now - _takeoffToInAirStart) > TAKE_OFF_TO_IN_AIR_PERIOD) {
|
} else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) {
|
||||||
SET_STATE(State::InAir, "takeoff done");
|
SET_STATE(State::InAir, "takeoff done");
|
||||||
_takeoffToInAirStart = now + USECS_PER_SECOND * 86500.0f;
|
|
||||||
velocity += _jumpSpeed * _currentUp;
|
velocity += _jumpSpeed * _currentUp;
|
||||||
_rigidBody->setLinearVelocity(velocity);
|
_rigidBody->setLinearVelocity(velocity);
|
||||||
}
|
}
|
||||||
|
@ -482,9 +490,9 @@ void CharacterController::preSimulation() {
|
||||||
case State::InAir: {
|
case State::InAir: {
|
||||||
if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) {
|
if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) {
|
||||||
SET_STATE(State::Ground, "hit ground");
|
SET_STATE(State::Ground, "hit ground");
|
||||||
} else if (jumpButtonHeld && (_takeOffJumpButtonID != _jumpButtonDownCount)) {
|
} else if (jumpButtonHeld && (_takeoffJumpButtonID != _jumpButtonDownCount)) {
|
||||||
SET_STATE(State::Hover, "double jump button");
|
SET_STATE(State::Hover, "double jump button");
|
||||||
} else if (jumpButtonHeld && (now - _jumpButtonDownStart) > JUMP_TO_HOVER_PERIOD) {
|
} else if (jumpButtonHeld && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) {
|
||||||
SET_STATE(State::Hover, "jump button held");
|
SET_STATE(State::Hover, "jump button held");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -114,10 +114,11 @@ protected:
|
||||||
|
|
||||||
glm::vec3 _boxScale; // used to compute capsule shape
|
glm::vec3 _boxScale; // used to compute capsule shape
|
||||||
|
|
||||||
quint64 _takeoffToInAirStart;
|
quint64 _rayHitStartTime;
|
||||||
quint64 _jumpButtonDownStart;
|
quint64 _takeoffToInAirStartTime;
|
||||||
|
quint64 _jumpButtonDownStartTime;
|
||||||
quint32 _jumpButtonDownCount;
|
quint32 _jumpButtonDownCount;
|
||||||
quint32 _takeOffJumpButtonID;
|
quint32 _takeoffJumpButtonID;
|
||||||
|
|
||||||
btScalar _halfHeight;
|
btScalar _halfHeight;
|
||||||
btScalar _radius;
|
btScalar _radius;
|
||||||
|
|
Loading…
Reference in a new issue