Skip to content

Commit

Permalink
Grass Collision: Performance and visual improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
doodlum committed Mar 27, 2023
1 parent 0c69d7a commit 011ba45
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[Info]
Version = 1-0-0
Version = 1-0-1
26 changes: 18 additions & 8 deletions features/Grass Collision/Shaders/RunGrass/GrassCollision.hlsli
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

cbuffer GrassCollisionPerFrame : register(b4)
{
float3 boundCentre;
float boundRadius;
bool EnableGrassCollision;
float RadiusMultiplier;
float DisplacementMultiplier;
Expand All @@ -16,28 +18,36 @@ StructuredBuffer<StructuredCollision> collisions : register(t0);

float3 GetDisplacedPosition(float3 position, float alpha)
{
float3 worldPosition = mul(World, float4(position, 1)).xyz;
float3 displacement = 0;


// Player bound culling
{
float dist = distance(boundCentre, worldPosition);
if (dist > boundRadius)
{
return 0;
}
}

if (EnableGrassCollision){
uint counter = 0;
uint collision_count, dummy;
collisions.GetDimensions(collision_count, dummy);
for (uint collision_index = 0; collision_index < collision_count; collision_index++)
{
float3 worldPosition = mul(World, float4(position, 1)).xyz;

StructuredCollision collision = collisions[collision_index];
collision.radius *= RadiusMultiplier;

float dist = distance(collision.centre, worldPosition);
float power = smoothstep(collision.radius, 0.0, dist);
float3 direction = worldPosition - collision.centre;
direction.y = 0; // stops expanding/stretching
direction = normalize(direction);
float3 shift = direction * power * alpha;
shift.z = -abs(shift.z);
displacement += shift.xyz * DisplacementMultiplier;
float3 shift = power * direction;
shift.z -= power; // bias downwards
displacement += shift.xyz;
}
}

return displacement;
return displacement * saturate(alpha * alpha * 5) * DisplacementMultiplier;
}
29 changes: 22 additions & 7 deletions src/Features/GrassCollision.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@ void GrassCollision::DrawSettings()
if (ImGui::BeginTabItem("Grass Collision")) {
if (ImGui::TreeNodeEx("Grass Collision", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Text("Allows player collision to modify grass position.");

ImGui::Checkbox("Enable Grass Collision", (bool*)&settings.EnableGrassCollision);
ImGui::Text("Distance from collision centres to apply collision");
ImGui::SliderFloat("Radius Multiplier", &settings.RadiusMultiplier, 0.0f, 8.0f);

ImGui::Text("Strength of each collision on grass position.");
ImGui::SliderFloat("Displacement Multiplier", &settings.DisplacementMultiplier, 0.0f, 16.0f);

ImGui::TreePop();
}
ImGui::EndTabItem();

ImGui::EndTabItem();
}
}

Expand All @@ -49,7 +52,7 @@ static bool GetShapeBound(RE::NiAVObject* a_node, RE::NiPoint3& centerPos, float
if (shape) {
float upExtent = shape->GetMaximumProjection(RE::hkVector4{ 0.0f, 0.0f, 1.0f, 0.0f }) * RE::bhkWorld::GetWorldScaleInverse();
float downExtent = shape->GetMaximumProjection(RE::hkVector4{ 0.0f, 0.0f, -1.0f, 0.0f }) * RE::bhkWorld::GetWorldScaleInverse();
auto z_extent = upExtent + downExtent;
auto z_extent = (upExtent + downExtent) / 2.0f;

float forwardExtent = shape->GetMaximumProjection(RE::hkVector4{ 0.0f, 1.0f, 0.0f, 0.0f }) * RE::bhkWorld::GetWorldScaleInverse();
float backwardExtent = shape->GetMaximumProjection(RE::hkVector4{ 0.0f, -1.0f, 0.0f, 0.0f }) * RE::bhkWorld::GetWorldScaleInverse();
Expand Down Expand Up @@ -86,7 +89,7 @@ static bool GetShapeBound(RE::bhkNiCollisionObject* Colliedobj, RE::NiPoint3& ce
if (shape) {
float upExtent = shape->GetMaximumProjection(RE::hkVector4{ 0.0f, 0.0f, 1.0f, 0.0f }) * RE::bhkWorld::GetWorldScaleInverse();
float downExtent = shape->GetMaximumProjection(RE::hkVector4{ 0.0f, 0.0f, -1.0f, 0.0f }) * RE::bhkWorld::GetWorldScaleInverse();
auto z_extent = upExtent + downExtent;
auto z_extent = (upExtent + downExtent) / 2.0f;

float forwardExtent = shape->GetMaximumProjection(RE::hkVector4{ 0.0f, 1.0f, 0.0f, 0.0f }) * RE::bhkWorld::GetWorldScaleInverse();
float backwardExtent = shape->GetMaximumProjection(RE::hkVector4{ 0.0f, -1.0f, 0.0f, 0.0f }) * RE::bhkWorld::GetWorldScaleInverse();
Expand Down Expand Up @@ -114,12 +117,13 @@ void GrassCollision::UpdateCollisions()
std::vector<CollisionSData> collisionsData{};

if (auto player = RE::PlayerCharacter::GetSingleton()) {
using namespace RE;
if (auto root = player->Get3D(false)) {
BSVisit::TraverseScenegraphCollision(root, [&](bhkNiCollisionObject* a_object) -> BSVisit::BSVisitControl {
auto position = player->GetPosition();
RE::BSVisit::TraverseScenegraphCollision(root, [&](RE::bhkNiCollisionObject* a_object) -> RE::BSVisit::BSVisitControl {
RE::NiPoint3 centerPos;
float radius;
if (GetShapeBound(a_object, centerPos, radius)) {
radius *= settings.RadiusMultiplier;
CollisionSData data{};
data.centre.x = centerPos.x - state->m_PosAdjust.x;
data.centre.y = centerPos.y - state->m_PosAdjust.y;
Expand All @@ -128,7 +132,7 @@ void GrassCollision::UpdateCollisions()
currentCollisionCount++;
collisionsData.push_back(data);
}
return BSVisit::BSVisitControl::kContinue;
return RE::BSVisit::BSVisitControl::kContinue;
});
}
}
Expand Down Expand Up @@ -174,11 +178,22 @@ void GrassCollision::UpdateCollisions()
void GrassCollision::ModifyGrass(const RE::BSShader*, const uint32_t)
{
if (updatePerFrame) {
UpdateCollisions();
if (settings.EnableGrassCollision) {
UpdateCollisions();
}

PerFrame perFrameData{};
ZeroMemory(&perFrameData, sizeof(perFrameData));

auto state = BSGraphics::RendererShadowState::QInstance();
auto shaderState = BSGraphics::ShaderState::QInstance();

auto bound = shaderState->kCachedPlayerBound;
perFrameData.boundCentre.x = bound.center.x - state->m_PosAdjust.x;
perFrameData.boundCentre.y = bound.center.y - state->m_PosAdjust.y;
perFrameData.boundCentre.z = bound.center.z - state->m_PosAdjust.z;
perFrameData.boundRadius = bound.radius * settings.RadiusMultiplier;

perFrameData.Settings = settings;

perFrame->Update(perFrameData);
Expand Down
2 changes: 2 additions & 0 deletions src/Features/GrassCollision.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class GrassCollision

struct alignas(16) PerFrame
{
DirectX::XMFLOAT3 boundCentre;
float boundRadius;
Settings Settings;
float pad0;
};
Expand Down

0 comments on commit 011ba45

Please sign in to comment.