Skip to content

Commit 99c1d78

Browse files
committed
character foot placement also works when moving; leaning control; improvements;
1 parent a15a0f6 commit 99c1d78

File tree

7 files changed

+155
-105
lines changed

7 files changed

+155
-105
lines changed

Content/Documentation/ScriptingAPI-Documentation.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1512,9 +1512,11 @@ Implementation of basic character controller features such as movement in the sc
15121512
- Strafe(Vector value) -- Similar to Move, but relative to the facing direction.
15131513
- Jump(float amount) -- Jump upwards by an amount. The jump will be executed in the next scene update, with collisions.
15141514
- Turn(Vector value) -- Turn towards a direction continuously.
1515+
- Lean(float value) -- Lean sideways, negative values mean left, positive values mean right
15151516

1516-
- AddAnimation(Entity entity) -- Adds animation for tracking blending state
1517+
- AddAnimation(Entity entity) -- Adds animation for tracking blending state. The simple animation blending will perform blend-out for each animation except the currenttly active one
15171518
- PlayAnimation(Entity entity) -- Play the animation. This will be blended in as primary animation, others will be belnded out.
1519+
- StopAnimation() -- stops current animation
15181520
- SetAnimationAmount(float value) -- Set target blend amount of current animation
15191521
- GetAnimatioNAmount() : float -- returns target blend amount of current animation
15201522
- IsAnimationEnded() : bool --returns true if the current animation is ended, false otherwise

WickedEngine/wiScene.cpp

Lines changed: 114 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1946,6 +1946,8 @@ namespace wi::scene
19461946
for (size_t animation_index = 0; animation_index < animation_queue.animations.size(); ++animation_index)
19471947
{
19481948
AnimationComponent& animation = *animation_queue.animations[animation_index];
1949+
if (!animation.IsPlaying())
1950+
continue;
19491951
animation.last_update_time = animation.timer;
19501952

19511953
for (const AnimationComponent::AnimationChannel& channel : animation.channels)
@@ -3175,114 +3177,140 @@ namespace wi::scene
31753177
auto range = wi::profiler::BeginRangeCPU("Procedural Animations");
31763178

31773179
// Character IK foot placement, should be after animations and hierarchy update:
3180+
for (size_t i = 0; i < characters.GetCount(); ++i)
3181+
{
3182+
CharacterComponent& character = characters[i];
3183+
if (character.left_foot != INVALID_ENTITY && character.right_foot != INVALID_ENTITY)
3184+
continue;
3185+
HumanoidComponent* humanoid = humanoids.GetComponent(character.humanoidEntity);
3186+
if (humanoid == nullptr)
3187+
continue;
3188+
character.left_foot = humanoid->bones[size_t(HumanoidComponent::HumanoidBone::LeftFoot)];
3189+
{
3190+
InverseKinematicsComponent& ik = inverse_kinematics.Create(character.left_foot);
3191+
ik.use_target_position = true;
3192+
ik.chain_length = 2;
3193+
ik.iteration_count = 10;
3194+
}
3195+
character.right_foot = humanoid->bones[size_t(HumanoidComponent::HumanoidBone::RightFoot)];
3196+
{
3197+
InverseKinematicsComponent& ik = inverse_kinematics.Create(character.right_foot);
3198+
ik.use_target_position = true;
3199+
ik.chain_length = 2;
3200+
ik.iteration_count = 10;
3201+
}
3202+
}
31783203
wi::jobsystem::Dispatch(ctx, (uint32_t)characters.GetCount(), 1, [&](wi::jobsystem::JobArgs args) {
31793204
CharacterComponent& character = characters[args.jobIndex];
3205+
if (character.left_foot == INVALID_ENTITY || character.right_foot == INVALID_ENTITY)
3206+
return;
31803207
if (character.humanoidEntity == INVALID_ENTITY)
31813208
return;
31823209
HumanoidComponent* humanoid = humanoids.GetComponent(character.humanoidEntity);
31833210
if (humanoid == nullptr)
31843211
return;
31853212

31863213
Entity entity = characters.GetEntity(args.jobIndex);
3187-
uint32_t layer = ~0u;
3214+
uint32_t layer = 0;
31883215
LayerComponent* layercomponent = layers.GetComponent(entity);
31893216
if (layercomponent != nullptr)
31903217
{
31913218
layer = layercomponent->GetLayerMask();
31923219
}
31933220

3194-
Entity left_foot = humanoid->bones[size_t(HumanoidComponent::HumanoidBone::LeftFoot)];
3195-
Entity right_foot = humanoid->bones[size_t(HumanoidComponent::HumanoidBone::RightFoot)];
3196-
if (left_foot != INVALID_ENTITY && right_foot != INVALID_ENTITY)
3221+
float base_y = character.position.y;
3222+
Entity ik_foot = INVALID_ENTITY;
3223+
XMFLOAT3 ik_pos = XMFLOAT3(0, 0, 0);
3224+
XMFLOAT3 left_pos = XMFLOAT3(0, 0, 0);
3225+
XMFLOAT3 right_pos = XMFLOAT3(0, 0, 0);
3226+
TransformComponent* left_transform = transforms.GetComponent(character.left_foot);
3227+
TransformComponent* right_transform = transforms.GetComponent(character.right_foot);
3228+
if (left_transform != nullptr && right_transform != nullptr)
31973229
{
3198-
float base_y = character.position.y;
3199-
Entity ik_foot = INVALID_ENTITY;
3200-
XMFLOAT3 ik_pos = XMFLOAT3(0, 0, 0);
3230+
left_pos = left_transform->GetPosition();
3231+
right_pos = right_transform->GetPosition();
3232+
}
32013233

3202-
if (character.IsFootPlacementEnabled() && character.ground_intersect && XMVectorGetX(XMVector3Length(XMVectorSetY(XMLoadFloat3(&character.velocity), 0))) < 0.1f)
3234+
if (character.IsFootPlacementEnabled() && character.ground_intersect)
3235+
{
3236+
// Compute root offset :
3237+
// I determine which foot wants to step on lower ground, that will offset whole root downwards
3238+
// The other foot will be the upper foot which will be later attached an Inverse Kinematics(IK) effector
3239+
Ray left_ray(XMFLOAT3(left_pos.x, left_pos.y + 0.5f, left_pos.z), XMFLOAT3(0, -1, 0), 0, 1);
3240+
Ray right_ray(XMFLOAT3(right_pos.x, right_pos.y + 0.5f, right_pos.z), XMFLOAT3(0, -1, 0), 0, 1);
3241+
RayIntersectionResult left_result = Intersects(left_ray, FILTER_NAVIGATION_MESH | FILTER_COLLIDER, ~layer);
3242+
RayIntersectionResult right_result = Intersects(right_ray, FILTER_NAVIGATION_MESH | FILTER_COLLIDER, ~layer);
3243+
float left_diff = 0;
3244+
float right_diff = 0;
3245+
if (left_result.entity != INVALID_ENTITY)
32033246
{
3204-
TransformComponent* left_transform = transforms.GetComponent(left_foot);
3205-
TransformComponent* right_transform = transforms.GetComponent(right_foot);
3206-
if (left_transform != nullptr && right_transform != nullptr)
3207-
{
3208-
// Compute root offset :
3209-
// I determine which foot wants to step on lower ground, that will offset whole root downwards
3210-
// The other foot will be the upper foot which will be later attached an Inverse Kinematics(IK) effector
3211-
XMFLOAT3 left_pos = left_transform->GetPosition();
3212-
XMFLOAT3 right_pos = right_transform->GetPosition();
3213-
Ray left_ray(XMFLOAT3(left_pos.x, left_pos.y + 1, left_pos.z), XMFLOAT3(0, -1, 0), 0, 1.8f);
3214-
Ray right_ray(XMFLOAT3(right_pos.x, right_pos.y + 1, right_pos.z), XMFLOAT3(0, -1, 0), 0, 1.8f);
3215-
RayIntersectionResult left_result = Intersects(left_ray, FILTER_NAVIGATION_MESH, FILTER_COLLIDER, layer);
3216-
RayIntersectionResult right_result = Intersects(right_ray, FILTER_NAVIGATION_MESH, FILTER_COLLIDER, layer);
3217-
float left_diff = 0;
3218-
float right_diff = 0;
3219-
if (left_result.entity != INVALID_ENTITY)
3220-
{
3221-
left_diff = left_result.position.y - base_y;
3222-
}
3223-
if (right_result.entity != INVALID_ENTITY)
3224-
{
3225-
right_diff = right_result.position.y - base_y;
3226-
}
3227-
float diff = left_diff;
3228-
if (left_result.position.y > right_result.position.y)
3229-
{
3230-
diff = right_diff;
3231-
if (left_result.entity != INVALID_ENTITY)
3232-
{
3233-
ik_foot = left_foot;
3234-
ik_pos = left_result.position;
3235-
}
3236-
}
3237-
else
3238-
{
3239-
if (right_result.entity != INVALID_ENTITY)
3240-
{
3241-
ik_foot = right_foot;
3242-
ik_pos = right_result.position;
3243-
}
3244-
}
3245-
character.root_offset = wi::math::Lerp(character.root_offset, diff, 0.1f);
3246-
}
3247+
left_diff = left_result.position.y - base_y;
32473248
}
3248-
else
3249+
if (right_result.entity != INVALID_ENTITY)
32493250
{
3250-
character.root_offset = wi::math::Lerp(character.root_offset, 0.0f, 0.1f);
3251+
right_diff = right_result.position.y - base_y;
32513252
}
3252-
3253-
TransformComponent* humanoid_transform = transforms.GetComponent(character.humanoidEntity);
3254-
if (humanoid_transform != nullptr)
3253+
float diff = left_diff;
3254+
if (left_result.position.y > right_result.position.y + 0.01f)
32553255
{
3256-
// Offset root transform to lower foot pos:
3257-
humanoid_transform->translation_local.y = character.root_offset;
3258-
humanoid_transform->SetDirty();
3259-
}
3260-
3261-
// Because IK component removals and creates can be performed below, we must lock:
3262-
locker.lock();
3263-
3264-
// Remove IK effectors by default:
3265-
if (inverse_kinematics.Contains(left_foot))
3266-
{
3267-
inverse_kinematics.Remove(left_foot);
3256+
diff = right_diff;
3257+
if (left_result.entity != INVALID_ENTITY)
3258+
{
3259+
ik_foot = character.left_foot;
3260+
ik_pos = left_result.position;
3261+
}
32683262
}
3269-
if (inverse_kinematics.Contains(right_foot))
3263+
else
32703264
{
3271-
inverse_kinematics.Remove(right_foot);
3265+
if (right_result.entity != INVALID_ENTITY)
3266+
{
3267+
ik_foot = character.right_foot;
3268+
ik_pos = right_result.position;
3269+
}
32723270
}
3271+
character.root_offset = wi::math::Lerp(character.root_offset, diff, 0.1f);
3272+
}
3273+
else
3274+
{
3275+
character.root_offset = wi::math::Lerp(character.root_offset, 0.0f, 0.1f);
3276+
}
32733277

3274-
// The upper foot will use IK:
3275-
if (ik_foot != INVALID_ENTITY)
3276-
{
3277-
InverseKinematicsComponent& ik = inverse_kinematics.Create(ik_foot);
3278-
ik.use_target_position = true;
3279-
ik.target_position = ik_pos;
3280-
ik.target_position.y += 0.15f;
3281-
ik.chain_length = 2;
3282-
ik.iteration_count = 10;
3283-
}
3278+
TransformComponent* humanoid_transform = transforms.GetComponent(character.humanoidEntity);
3279+
if (humanoid_transform != nullptr)
3280+
{
3281+
// Offset root transform to lower foot pos:
3282+
humanoid_transform->translation_local.y = character.root_offset;
3283+
humanoid_transform->SetDirty();
3284+
}
32843285

3286+
// Ease out inverse kinematics by default:
3287+
if (inverse_kinematics.Contains(character.left_foot))
3288+
{
3289+
InverseKinematicsComponent& ik = *inverse_kinematics.GetComponent(character.left_foot);
3290+
ik.target_position = wi::math::Lerp(ik.target_position, left_pos, 0.6f);
3291+
}
3292+
if (inverse_kinematics.Contains(character.right_foot))
3293+
{
3294+
InverseKinematicsComponent& ik = *inverse_kinematics.GetComponent(character.right_foot);
3295+
ik.target_position = wi::math::Lerp(ik.target_position, right_pos, 0.6f);
3296+
}
3297+
3298+
// The upper foot will use IK:
3299+
if (ik_foot != INVALID_ENTITY && inverse_kinematics.Contains(ik_foot))
3300+
{
3301+
InverseKinematicsComponent& ik = *inverse_kinematics.GetComponent(ik_foot);
3302+
ik_pos.y += 0.16f;
3303+
ik.target_position = wi::math::Lerp(ik.target_position, ik_pos, 0.6f);
3304+
#if 0
3305+
// Debug draw foot target:
3306+
locker.lock();
3307+
wi::renderer::RenderablePoint point;
3308+
point.position = ik.target_position;
3309+
point.color = XMFLOAT4(1, 1, 0, 1);
3310+
point.size = 0.1f;
3311+
wi::renderer::DrawPoint(point);
32853312
locker.unlock();
3313+
#endif
32863314
}
32873315
});
32883316
wi::jobsystem::Wait(ctx);
@@ -3378,25 +3406,6 @@ namespace wi::scene
33783406
break;
33793407
}
33803408
}
3381-
if (constrain)
3382-
{
3383-
// Constraint swapping fixes for flipped model orientations:
3384-
if (facing < 0)
3385-
{
3386-
// Note: this is a fix for VRM 1.0 and Mixamo model
3387-
std::swap(constraint_min, constraint_max);
3388-
}
3389-
const TransformComponent* bone_transform = transforms.GetComponent(bone);
3390-
if (bone_transform != nullptr)
3391-
{
3392-
if (bone_transform->GetForward().z < 0)
3393-
{
3394-
// Note: this is a fix for FBX Mixamo models
3395-
std::swap(constraint_min, constraint_max);
3396-
}
3397-
}
3398-
break;
3399-
}
34003409
bone_type_idx++;
34013410
}
34023411
}
@@ -5302,13 +5311,14 @@ namespace wi::scene
53025311
{
53035312
static const XMVECTOR up = XMVectorSet(0, 1, 0, 0);
53045313
static const XMMATRIX rotY = XMMatrixRotationY(XM_PI);
5314+
static const int max_substeps = 4;
53055315

53065316
wi::jobsystem::Dispatch(ctx, (uint32_t)characters.GetCount(), 1, [&](wi::jobsystem::JobArgs args) {
53075317
CharacterComponent& character = characters[args.jobIndex];
53085318
if (!character.IsActive())
53095319
return;
53105320
Entity entity = characters.GetEntity(args.jobIndex);
5311-
uint32_t layer = ~0u;
5321+
uint32_t layer = 0;
53125322
LayerComponent* layercomponent = layers.GetComponent(entity);
53135323
if (layercomponent != nullptr)
53145324
{
@@ -5396,8 +5406,10 @@ namespace wi::scene
53965406
}
53975407

53985408
// Fixed timestep logic:
5399-
while (character.accumulator >= timestep)
5409+
int steps = 0;
5410+
while (character.accumulator >= timestep && steps <= max_substeps)
54005411
{
5412+
steps++;
54015413
XMStoreFloat3(&character.position_prev, position);
54025414
character.accumulator -= timestep;
54035415
if (character.swimming)
@@ -5458,6 +5470,7 @@ namespace wi::scene
54585470
character.leaning_next = lerp(character.leaning_next, velocity_leaning, 0.05f);
54595471
character.leaning = lerp(character.leaning, character.leaning_next, 0.05f);
54605472
}
5473+
character.accumulator = clamp(character.accumulator, 0.0f, timestep);
54615474
character.alpha = character.accumulator / timestep;
54625475

54635476
if (platform_velocity_count > 0)

WickedEngine/wiScene_BindLua.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7493,9 +7493,11 @@ Luna<CharacterComponent_BindLua>::FunctionType CharacterComponent_BindLua::metho
74937493
lunamethod(CharacterComponent_BindLua, Strafe),
74947494
lunamethod(CharacterComponent_BindLua, Jump),
74957495
lunamethod(CharacterComponent_BindLua, Turn),
7496+
lunamethod(CharacterComponent_BindLua, Lean),
74967497

74977498
lunamethod(CharacterComponent_BindLua, AddAnimation),
74987499
lunamethod(CharacterComponent_BindLua, PlayAnimation),
7500+
lunamethod(CharacterComponent_BindLua, StopAnimation),
74997501
lunamethod(CharacterComponent_BindLua, SetAnimationAmount),
75007502
lunamethod(CharacterComponent_BindLua, GetAnimationAmount),
75017503
lunamethod(CharacterComponent_BindLua, IsAnimationEnded),
@@ -7606,6 +7608,17 @@ int CharacterComponent_BindLua::Turn(lua_State* L)
76067608
component->Turn(v->GetFloat3());
76077609
return 0;
76087610
}
7611+
int CharacterComponent_BindLua::Lean(lua_State* L)
7612+
{
7613+
int argc = wi::lua::SGetArgCount(L);
7614+
if (argc < 1)
7615+
{
7616+
wi::lua::SError(L, "Lean(float value) not enough arguments!");
7617+
return 0;
7618+
}
7619+
component->Lean(wi::lua::SGetFloat(L, 1));
7620+
return 0;
7621+
}
76097622

76107623
int CharacterComponent_BindLua::AddAnimation(lua_State* L)
76117624
{
@@ -7630,6 +7643,11 @@ int CharacterComponent_BindLua::PlayAnimation(lua_State* L)
76307643
component->PlayAnimation(entity);
76317644
return 0;
76327645
}
7646+
int CharacterComponent_BindLua::StopAnimation(lua_State* L)
7647+
{
7648+
component->StopAnimation();
7649+
return 0;
7650+
}
76337651
int CharacterComponent_BindLua::SetAnimationAmount(lua_State* L)
76347652
{
76357653
int argc = wi::lua::SGetArgCount(L);

WickedEngine/wiScene_BindLua.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,9 +1857,11 @@ namespace wi::lua::scene
18571857
int Strafe(lua_State* L);
18581858
int Jump(lua_State* L);
18591859
int Turn(lua_State* L);
1860+
int Lean(lua_State* L);
18601861

18611862
int AddAnimation(lua_State* L);
18621863
int PlayAnimation(lua_State* L);
1864+
int StopAnimation(lua_State* L);
18631865
int SetAnimationAmount(lua_State* L);
18641866
int GetAnimationAmount(lua_State* L);
18651867
int IsAnimationEnded(lua_State* L);

WickedEngine/wiScene_Components.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2374,18 +2374,26 @@ namespace wi::scene
23742374
}
23752375
facing_next = direction;
23762376
}
2377-
void CharacterComponent::AddAnimation(wi::ecs::Entity entity)
2377+
void CharacterComponent::Lean(float amount)
2378+
{
2379+
leaning_next = amount;
2380+
}
2381+
void CharacterComponent::AddAnimation(Entity entity)
23782382
{
23792383
animations.push_back(entity);
23802384
}
2381-
void CharacterComponent::PlayAnimation(wi::ecs::Entity entity)
2385+
void CharacterComponent::PlayAnimation(Entity entity)
23822386
{
23832387
if (currentAnimation != entity)
23842388
{
23852389
reset_anim = true;
23862390
currentAnimation = entity;
23872391
}
23882392
}
2393+
void CharacterComponent::StopAnimation()
2394+
{
2395+
currentAnimation = INVALID_ENTITY;
2396+
}
23892397
void CharacterComponent::SetAnimationAmount(float amount)
23902398
{
23912399
anim_amount = amount;

0 commit comments

Comments
 (0)