载入中...
搜索中...
未找到
SurfaceDropletSimulation.cpp
浏览该文件的文档.
2
3#include <glm/gtx/norm.hpp>
4
5#include <algorithm>
6#include <cmath>
7
8namespace eve::fluids {
9
14
16 const float theta = glm::radians(std::clamp(params_.contactAngleDegrees, 5.f, 175.f));
17 const float cosine = std::cos(theta);
18 const float sine = std::max(1e-3f, std::sin(theta));
19 constexpr float pi = 3.14159265358979323846f;
20 const float factor = pi * (2.f - 3.f * cosine + cosine * cosine * cosine) /
21 (3.f * sine * sine * sine);
22 return std::cbrt(std::max(0.f, volume) / std::max(1e-6f, factor));
23}
24
26 const glm::vec3& relativeVelocity) {
27 if (!binding_ || !binding_->isValid() || location.triangle >= uint32_t(binding_->triangleCount()) ||
28 volume <= 0.f)
29 return false;
30 SurfaceDroplet droplet;
31 droplet.id = nextDropletId_++;
32 droplet.location = location;
33 droplet.volume = volume;
34 const SurfaceSample sample = binding_->evaluate(location, 0.f);
35 droplet.relativeVelocity = relativeVelocity - sample.normal * glm::dot(relativeVelocity, sample.normal);
36 droplets_.push_back(droplet);
37 return true;
38}
39
41 detached_.clear();
42 if (!binding_ || !binding_->isValid() || dt <= 0.f || (droplets_.empty() && airborne_.empty())) return;
43 float remaining = dt;
44 while (remaining > 1e-7f) {
45 const float substep = std::min(remaining, 0.05f);
46 stepSubstep(substep, dt);
47 remaining -= substep;
48 }
49}
50
51void SurfaceDropletSimulation::stepSubstep(float dt, float poseDt) {
52
53 std::vector<SurfaceDroplet> attached;
54 attached.reserve(droplets_.size());
55 for (SurfaceDroplet droplet : droplets_) {
56 const SurfaceLocation previousLocation = droplet.location;
57 const SurfaceSample previousSample = binding_->evaluate(previousLocation, poseDt);
58 const SurfaceSample sample = binding_->evaluate(droplet.location, poseDt);
59 glm::vec3 surfaceAcceleration(0.f);
60 if (droplet.hasPreviousSurfaceVelocity)
61 surfaceAcceleration = (sample.velocity - droplet.previousSurfaceVelocity) / poseDt;
62
63 const glm::vec3 relativeAcceleration = params_.gravity - surfaceAcceleration;
64 const float outwardAcceleration = glm::dot(relativeAcceleration, sample.normal);
65 if (outwardAcceleration > std::max(0.f, params_.adhesionAcceleration)) {
66 detached_.push_back(
67 {sample.position, sample.velocity + droplet.relativeVelocity, droplet.volume});
68 airborne_.push_back(
69 {sample.position, sample.velocity + droplet.relativeVelocity, droplet.volume});
70 continue;
71 }
72
73 glm::vec3 tangentAcceleration =
74 relativeAcceleration - sample.normal * glm::dot(relativeAcceleration, sample.normal);
75 droplet.relativeVelocity += tangentAcceleration * dt;
76 droplet.relativeVelocity *= std::exp(-std::max(0.f, params_.friction) * dt);
77 const float speed2 = glm::length2(droplet.relativeVelocity);
78 const float maxSpeed = std::max(0.f, params_.maxSpeed);
79 if (maxSpeed > 0.f && speed2 > maxSpeed * maxSpeed)
80 droplet.relativeVelocity *= maxSpeed / std::sqrt(speed2);
81
82 const SurfaceWalkResult walk = binding_->walkAcrossSurface(
83 droplet.location, droplet.relativeVelocity * dt, std::max(0, params_.maxCrossings));
84 if (!walk.valid) continue;
85 droplet.location = walk.location;
86 if (walk.reachedBoundary) {
87 const SurfaceSample edge = binding_->evaluate(walk.location, poseDt);
88 detached_.push_back({edge.position, edge.velocity + droplet.relativeVelocity, droplet.volume});
89 airborne_.push_back({edge.position, edge.velocity + droplet.relativeVelocity, droplet.volume, 0.f});
90 continue;
91 }
92
93 const SurfaceSample end = binding_->evaluate(droplet.location, poseDt);
94 droplet.relativeVelocity -= end.normal * glm::dot(droplet.relativeVelocity, end.normal);
95 droplet.previousSurfaceVelocity = end.velocity;
96 droplet.hasPreviousSurfaceVelocity = true;
97 if (wetness_) {
98 const float distance = glm::distance(previousSample.position, end.position);
99 wetness_->deposit(previousLocation, distance * params_.trailDeposition * droplet.volume);
100 wetness_->deposit(droplet.location, distance * params_.trailDeposition * droplet.volume);
101 }
102 attached.push_back(droplet);
103 }
104 droplets_.swap(attached);
105
106 // Merge overlapping spherical caps without depending on mesh UV seams.
107 for (size_t i = 0; i < droplets_.size(); ++i) {
108 const SurfaceSample a = binding_->evaluate(droplets_[i].location, poseDt);
109 for (size_t j = i + 1u; j < droplets_.size();) {
110 const SurfaceSample b = binding_->evaluate(droplets_[j].location, poseDt);
111 const float mergeDistance = params_.mergeRadiusScale *
112 (dropletRadius(droplets_[i].volume) + dropletRadius(droplets_[j].volume));
113 if (glm::distance2(a.position, b.position) > mergeDistance * mergeDistance) {
114 ++j;
115 continue;
116 }
117 const float combined = droplets_[i].volume + droplets_[j].volume;
118 droplets_[i].relativeVelocity =
119 (droplets_[i].relativeVelocity * droplets_[i].volume +
120 droplets_[j].relativeVelocity * droplets_[j].volume) / combined;
121 droplets_[i].volume = combined;
122 droplets_.erase(droplets_.begin() + std::ptrdiff_t(j));
123 }
124 }
125
126 std::vector<AirborneDroplet> flying;
127 flying.reserve(airborne_.size());
128 for (AirborneDroplet drop : airborne_) {
129 drop.age += dt;
130 drop.velocity += params_.gravity * dt;
131 drop.velocity *= std::exp(-std::max(0.f, params_.airDrag) * dt);
132 drop.position += drop.velocity * dt;
133 SurfaceLocation hit;
134 if (drop.age > 0.08f &&
135 binding_->project(drop.position, std::max(0.f, params_.reattachDistance), hit)) {
136 const SurfaceSample surface = binding_->evaluate(hit, poseDt);
137 if (glm::dot(drop.velocity - surface.velocity, surface.normal) <= 0.f) {
138 addDroplet(hit, drop.volume, drop.velocity - surface.velocity);
139 if (wetness_) wetness_->deposit(hit, drop.volume * 0.15f);
140 continue;
141 }
142 }
143 flying.push_back(drop);
144 }
145 airborne_.swap(flying);
146}
147
149 droplets_.clear();
150 detached_.clear();
151 airborne_.clear();
152 nextDropletId_ = 1;
153}
154
155} // namespace eve::fluids
double volume
float wetness
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float distance
eve::action::ActionVfxBinding binding
bool hit
const RoadEdge * edge
Dynamic triangle surface used to bind films and droplets to deforming meshes.
SurfaceSample evaluate(const SurfaceLocation &location, float dt) const
Evaluate a surface address in the current and previous poses.
bool isValid() const
True when valid.
int triangleCount() const
Triangle count.
SurfaceWalkResult walkAcrossSurface(const SurfaceLocation &start, const glm::vec3 &worldDisplacement, int maxCrossings=16) const
Move a location by a world-space displacement, crossing triangle edges.
bool project(const glm::vec3 &worldPosition, float maxDistance, SurfaceLocation &outLocation) const
Find the closest material point on the current surface.
void step(float dt)
Advance all bound droplets and rebuild the detached event list.
void clear()
Remove attached droplets and pending detach events.
bool addDroplet(const SurfaceLocation &location, float volume=1.f, const glm::vec3 &relativeVelocity=glm::vec3(0.f))
Add one bound droplet. Invalid locations are rejected.
float dropletRadius(float volume) const
Droplet radius.
SurfaceDropletSimulation(FluidSurfaceBinding *binding, const SurfaceDropletParams &params={}, SurfaceWetnessField *wetness=nullptr)
Surface droplet simulation.
Lightweight material-space wet-film field stored at mesh vertices.
void deposit(const SurfaceLocation &location, float amount)
Deposit a wet trace at a material-space location.
std::vector< ParamSpec > params
GLSL compute kernels for the GPU surface-flow solver.
Definition FluidTarget.h:12
double sample(const Heightmap &map, double u, double v)
Sample.
Material and integration parameters for surface-bound droplets.
float reattachDistance
Maximum distance at which an airborne droplet can reattach.
int maxCrossings
Maximum number of triangle edges crossed by one step.
float mergeRadiusScale
Multiplier for the sum of cap radii used by droplet merging.
float adhesionAcceleration
Maximum outward acceleration retained by adhesion.
float airDrag
Air drag applied after a droplet leaves the surface.
float trailDeposition
Wet-film amount deposited per world-space unit travelled.
float contactAngleDegrees
Water contact angle in degrees, used to derive a visible cap radius.
glm::vec3 gravity
World-space gravity in units per second squared.
float friction
Exponential damping of velocity relative to the surface.
float maxSpeed
Maximum relative droplet speed.
One droplet addressed in material space on a dynamic triangle surface.
Stable material-space address of a point on a triangle surface.
Evaluated world-space frame and motion at a surface location.