// ------------------------------------------------------------------------------------ /// <summary> /// Solve feet positions. /// </summary> /// <param name="enabled">Feet resolving is enabled - this is a parameter because of blending over time.</param> /// <param name="characterBones">Character bones storage.</param> /// <param name="allowMovingWithBody">If feet cannot reach, move the body</param> public void SolveFeet(bool enabled, MyCharacterBone[] characterBones, bool allowMovingWithBody) { m_currentFeetIkInfluence = MathHelper.Clamp(m_currentFeetIkInfluence + (enabled ? 0.1f : -0.1f), 0.0f, 1.0f); if (m_currentFeetIkInfluence <= 0 || TerrainHeightProvider == null || characterBones == null || characterBones.Length == 0 || m_feet.Count == 0) { return; } RecreateIgnoredBonesTableIfNeeded(characterBones); BackupIgnoredBones(characterBones); if (allowMovingWithBody) { MoveTheBodyDown(characterBones); // move the body down! } RestoreIgnoredBones(characterBones); float refTerrainHeight = TerrainHeightProvider.GetReferenceTerrainHeight(); float maximumNegativeOffsetValue = m_characterDirDownOffsetMax; bool foundAnyOffset = false; foreach (var foot in m_feet) { if (foot.BoneIndex == -1) { foreach (var bone in characterBones) { if (bone.Name == foot.BoneName) { foot.BoneIndex = bone.Index; break; } } if (foot.BoneIndex == -1) // alright alright, skip this bone { continue; } } var footBone = characterBones[foot.BoneIndex]; var firstBone = footBone; var secondBone = footBone; for (int i = 0; i < foot.ChainLength; i++) { secondBone = firstBone; firstBone = firstBone.Parent; } firstBone.ComputeAbsoluteTransform(); // will propagate the transform to children // obtain terrain height Vector3 bonePos = footBone.AbsoluteTransform.Translation; float terrainHeight; Vector3 terrainNormal; if (TerrainHeightProvider.GetTerrainHeight(bonePos, out terrainHeight, out terrainNormal)) { terrainNormal = Vector3.Lerp(foot.LastTerrainNormal, terrainNormal, 0.2f); } else { terrainHeight = foot.LastTerrainHeight; terrainNormal = Vector3.Lerp(foot.LastTerrainNormal, Vector3.Up, 0.1f); foot.LastTerrainHeight *= 0.9f; } foot.LastTerrainHeight = terrainHeight; foot.LastTerrainNormal = terrainNormal; float terrainHeightDelta = terrainHeight - refTerrainHeight; // compute IK! { float originalBoneY = bonePos.Y; float unclampedNewBoneY = terrainHeightDelta + (bonePos.Y - m_filteredOffsetValue) / terrainNormal.Y; // we expect the normal to be normalized bonePos.Y = Math.Min(secondBone.AbsoluteTransform.Translation.Y, unclampedNewBoneY); // do not allow the feet to be above ankle bonePos.Y = MathHelper.Lerp(originalBoneY, bonePos.Y, m_currentFeetIkInfluence); maximumNegativeOffsetValue = MathHelper.Clamp(terrainHeightDelta, m_characterDirDownOffsetMin, maximumNegativeOffsetValue); // store worst negative offset foundAnyOffset = true; if (originalBoneY > bonePos.Y) { bonePos.Y = originalBoneY; } SolveIkTwoBones(characterBones, foot, ref bonePos, ref terrainNormal, fromBindPose: false); } } if (!foundAnyOffset) { maximumNegativeOffsetValue = 0; } m_characterDirDownOffset = MathHelper.Lerp(maximumNegativeOffsetValue, m_characterDirDownOffset, m_characterDirDownOffsetSmoothness * m_offsetFiltering.Count / m_offsetFilteringSampleCount); }
public ResourceCeilingProvider(TerrainHeightProvider terrainHeightProvider) { m_terrainHeightProvider = terrainHeightProvider; }