@@ -1946,6 +1946,8 @@ namespace wi::scene
1946
1946
for (size_t animation_index = 0 ; animation_index < animation_queue.animations .size (); ++animation_index)
1947
1947
{
1948
1948
AnimationComponent& animation = *animation_queue.animations [animation_index];
1949
+ if (!animation.IsPlaying ())
1950
+ continue ;
1949
1951
animation.last_update_time = animation.timer ;
1950
1952
1951
1953
for (const AnimationComponent::AnimationChannel& channel : animation.channels )
@@ -3175,114 +3177,140 @@ namespace wi::scene
3175
3177
auto range = wi::profiler::BeginRangeCPU (" Procedural Animations" );
3176
3178
3177
3179
// 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
+ }
3178
3203
wi::jobsystem::Dispatch (ctx, (uint32_t )characters.GetCount (), 1 , [&](wi::jobsystem::JobArgs args) {
3179
3204
CharacterComponent& character = characters[args.jobIndex ];
3205
+ if (character.left_foot == INVALID_ENTITY || character.right_foot == INVALID_ENTITY)
3206
+ return ;
3180
3207
if (character.humanoidEntity == INVALID_ENTITY)
3181
3208
return ;
3182
3209
HumanoidComponent* humanoid = humanoids.GetComponent (character.humanoidEntity );
3183
3210
if (humanoid == nullptr )
3184
3211
return ;
3185
3212
3186
3213
Entity entity = characters.GetEntity (args.jobIndex );
3187
- uint32_t layer = ~ 0u ;
3214
+ uint32_t layer = 0 ;
3188
3215
LayerComponent* layercomponent = layers.GetComponent (entity);
3189
3216
if (layercomponent != nullptr )
3190
3217
{
3191
3218
layer = layercomponent->GetLayerMask ();
3192
3219
}
3193
3220
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 )
3197
3229
{
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
+ }
3201
3233
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)
3203
3246
{
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;
3247
3248
}
3248
- else
3249
+ if (right_result. entity != INVALID_ENTITY)
3249
3250
{
3250
- character. root_offset = wi::math::Lerp (character. root_offset , 0 . 0f , 0 . 1f ) ;
3251
+ right_diff = right_result. position . y - base_y ;
3251
3252
}
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 )
3255
3255
{
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
+ }
3268
3262
}
3269
- if (inverse_kinematics. Contains (right_foot))
3263
+ else
3270
3264
{
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
+ }
3272
3270
}
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
+ }
3273
3277
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
+ }
3284
3285
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);
3285
3312
locker.unlock();
3313
+ #endif
3286
3314
}
3287
3315
});
3288
3316
wi::jobsystem::Wait (ctx);
@@ -3378,25 +3406,6 @@ namespace wi::scene
3378
3406
break ;
3379
3407
}
3380
3408
}
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
- }
3400
3409
bone_type_idx++;
3401
3410
}
3402
3411
}
@@ -5302,13 +5311,14 @@ namespace wi::scene
5302
5311
{
5303
5312
static const XMVECTOR up = XMVectorSet (0 , 1 , 0 , 0 );
5304
5313
static const XMMATRIX rotY = XMMatrixRotationY (XM_PI);
5314
+ static const int max_substeps = 4 ;
5305
5315
5306
5316
wi::jobsystem::Dispatch (ctx, (uint32_t )characters.GetCount (), 1 , [&](wi::jobsystem::JobArgs args) {
5307
5317
CharacterComponent& character = characters[args.jobIndex ];
5308
5318
if (!character.IsActive ())
5309
5319
return ;
5310
5320
Entity entity = characters.GetEntity (args.jobIndex );
5311
- uint32_t layer = ~ 0u ;
5321
+ uint32_t layer = 0 ;
5312
5322
LayerComponent* layercomponent = layers.GetComponent (entity);
5313
5323
if (layercomponent != nullptr )
5314
5324
{
@@ -5396,8 +5406,10 @@ namespace wi::scene
5396
5406
}
5397
5407
5398
5408
// Fixed timestep logic:
5399
- while (character.accumulator >= timestep)
5409
+ int steps = 0 ;
5410
+ while (character.accumulator >= timestep && steps <= max_substeps)
5400
5411
{
5412
+ steps++;
5401
5413
XMStoreFloat3 (&character.position_prev , position);
5402
5414
character.accumulator -= timestep;
5403
5415
if (character.swimming )
@@ -5458,6 +5470,7 @@ namespace wi::scene
5458
5470
character.leaning_next = lerp (character.leaning_next , velocity_leaning, 0 .05f );
5459
5471
character.leaning = lerp (character.leaning , character.leaning_next , 0 .05f );
5460
5472
}
5473
+ character.accumulator = clamp (character.accumulator , 0 .0f , timestep);
5461
5474
character.alpha = character.accumulator / timestep;
5462
5475
5463
5476
if (platform_velocity_count > 0 )
0 commit comments