载入中...
搜索中...
未找到
VolumeFluidCoupling.cpp
浏览该文件的文档.
2#include <algorithm>
3#include <cmath>
4#include <glm/gtc/quaternion.hpp>
6#include "physics/Body3D.h"
8#include "physics/World3D.h"
9
10namespace eve::fluids {
16 std::vector<Binding> bindings;
17 std::vector<physics::Body3D*> bodies;
18 std::vector<VolumeFluidCollider> samples;
19 std::vector<glm::vec3> centers, linear, angular;
20 std::vector<std::pair<unsigned, size_t>> labels;
22 unsigned clampedBodyCount = 0;
23};
27 if (impl_->bindings.size() >= 1024 || std::any_of(impl_->bindings.begin(), impl_->bindings.end(),
28 [&](const auto& b) { return b.shape.label == localShape.label; }))
30 "Duplicate coupling label or binding limit exceeded",
31 "fluids.volume.coupling"));
33 if (!link) return Result<void>::failure(link.status());
34 auto validator = VolumeFluid::create(VolumeFluidSettings{.capacity = 1});
35 if (!validator) return Result<void>::failure(validator.status());
36 auto valid = validator.value()->setColliders(std::span(&localShape, 1));
37 if (!valid) return valid;
38 impl_->bindings.push_back({link.value(), localShape});
39 return Result<void>::success();
40}
42 std::erase_if(impl_->bindings, [label](const auto& b) { return b.shape.label == label; });
43}
44Result<void> VolumeFluidCoupling::setImpulseLimits(float maximumLinearDelta, float maximumAngularDelta) {
45 if (!std::isfinite(maximumLinearDelta) || maximumLinearDelta < .01f || maximumLinearDelta > 1000.f ||
46 !std::isfinite(maximumAngularDelta) || maximumAngularDelta < .01f || maximumAngularDelta > 10000.f)
48 DiagnosticCode::InvalidArgument, "Invalid rigid impulse velocity-change limits", "fluids.volume.coupling"));
49 impl_->maximumLinearDelta = maximumLinearDelta;
50 impl_->maximumAngularDelta = maximumAngularDelta;
51 return Result<void>::success();
52}
53unsigned VolumeFluidCoupling::lastClampedBodyCount() const { return impl_->clampedBodyCount; }
55 std::span<const VolumeFluidThermalRule> rules) {
56 if (!world.isValid())
58 Diagnostic::error(DiagnosticCode::StaleHandle, "Rigid world is no longer valid", "fluids.volume.coupling"));
59 auto& bodies = impl_->bodies;
60 auto& samples = impl_->samples;
61 auto& centers = impl_->centers;
62 auto& labels = impl_->labels;
63 bodies.clear();
64 samples.clear();
65 centers.clear();
66 labels.clear();
67 bodies.reserve(impl_->bindings.size());
68 samples.reserve(impl_->bindings.size());
69 centers.reserve(impl_->bindings.size());
70 labels.reserve(impl_->bindings.size());
71 for (const auto& binding : impl_->bindings) {
72 auto resolved = binding.link.resolve(world);
73 if (!resolved) return Result<unsigned>::failure(resolved.status());
74 auto* body = resolved.value();
75 const glm::quat orientation(body->getRotW(), body->getRotX(), body->getRotY(), body->getRotZ());
76 auto sample = binding.shape;
77 const auto center = body->localToWorldPointOwned(sample.center.x, sample.center.y, sample.center.z);
78 if (!center) return Result<unsigned>::failure(center.status());
79 const auto velocity = body->getLocalPointVelocityOwned(sample.center.x, sample.center.y, sample.center.z);
80 if (!velocity) return Result<unsigned>::failure(velocity.status());
81 sample.center = {center.value().x, center.value().y, center.value().z};
82 sample.velocity = {velocity.value().x, velocity.value().y, velocity.value().z};
83 sample.angularVelocity = {body->getAngularVelocityX(), body->getAngularVelocityY(),
84 body->getAngularVelocityZ()};
85 const auto q =
86 orientation * glm::quat(sample.rotation.w, sample.rotation.x, sample.rotation.y, sample.rotation.z);
87 sample.rotation = {q.x, q.y, q.z, q.w};
88 labels.emplace_back(sample.label, bodies.size());
89 bodies.push_back(body);
90 samples.push_back(sample);
91 centers.push_back({body->getWorldCenterX(), body->getWorldCenterY(), body->getWorldCenterZ()});
92 }
93 std::sort(labels.begin(), labels.end());
94 // Allocate all output aggregation before advancing either domain.
95 auto& linear = impl_->linear;
96 auto& angular = impl_->angular;
97 linear.assign(bodies.size(), glm::vec3(0.f));
98 angular.assign(bodies.size(), glm::vec3(0.f));
99 auto advanced = fluid.stepWithColliders(dt, substeps, samples, rules);
100 if (!advanced) return Result<unsigned>::failure(advanced.status());
101 const auto contacts = fluid.contacts();
102 for (const auto& contact : contacts) {
103 const auto at =
104 std::lower_bound(labels.begin(), labels.end(), std::pair<unsigned, size_t>{contact.colliderLabel, 0});
105 if (at == labels.end() || at->first != contact.colliderLabel) continue;
106 const size_t i = at->second;
107 linear[i] += contact.impulse;
108 angular[i] += glm::cross(contact.point - centers[i], contact.impulse);
109 }
110 const auto reactions = fluid.attachmentReactions();
111 for (const auto& reaction : reactions) {
112 const auto at =
113 std::lower_bound(labels.begin(), labels.end(), std::pair<unsigned, size_t>{reaction.colliderLabel, 0});
114 if (at == labels.end() || at->first != reaction.colliderLabel) continue;
115 const size_t i = at->second;
116 linear[i] += reaction.impulse;
117 angular[i] += glm::cross(reaction.point - centers[i], reaction.impulse);
118 angular[i] += reaction.angularImpulse;
119 }
120 unsigned clampedBodyCount = 0;
121 for (size_t i = 0; i < bodies.size(); ++i) {
122 bool clamped = false;
123 const float mass = bodies[i]->getMass();
124 const float linearLength = glm::length(linear[i]);
125 if (mass > 0.f && linearLength > mass * impl_->maximumLinearDelta) {
126 linear[i] *= mass * impl_->maximumLinearDelta / linearLength;
127 clamped = true;
128 }
129 if (mass > 0.f && glm::dot(angular[i], angular[i]) > 0.f) {
130 const glm::mat3 localInertia(
131 bodies[i]->getInertiaXX(), bodies[i]->getInertiaXY(), bodies[i]->getInertiaXZ(),
132 bodies[i]->getInertiaXY(), bodies[i]->getInertiaYY(), bodies[i]->getInertiaYZ(),
133 bodies[i]->getInertiaXZ(), bodies[i]->getInertiaYZ(), bodies[i]->getInertiaZZ());
134 const float inertiaScale =
135 std::max({std::abs(localInertia[0][0]), std::abs(localInertia[0][1]), std::abs(localInertia[0][2]),
136 std::abs(localInertia[1][0]), std::abs(localInertia[1][1]), std::abs(localInertia[1][2]),
137 std::abs(localInertia[2][0]), std::abs(localInertia[2][1]), std::abs(localInertia[2][2])});
138 if (std::isfinite(inertiaScale) && inertiaScale > 0.f) {
139 const auto normalizedInertia = localInertia / inertiaScale;
140 const float determinant = glm::determinant(normalizedInertia);
141 if (std::isfinite(determinant) && std::abs(determinant) > 1e-6f) {
142 const glm::quat q(bodies[i]->getRotW(), bodies[i]->getRotX(), bodies[i]->getRotY(),
143 bodies[i]->getRotZ());
144 const auto rotation = glm::mat3_cast(q);
145 const auto angularDelta = rotation * (glm::inverse(normalizedInertia) / inertiaScale) *
146 glm::transpose(rotation) * angular[i];
147 const float angularLength = glm::length(angularDelta);
148 if (angularLength > impl_->maximumAngularDelta) {
149 angular[i] *= impl_->maximumAngularDelta / angularLength;
150 clamped = true;
151 }
152 }
153 }
154 }
155 if (glm::dot(linear[i], linear[i]) > 0.f) bodies[i]->applyLinearImpulse(linear[i].x, linear[i].y, linear[i].z);
156 if (glm::dot(angular[i], angular[i]) > 0.f)
157 bodies[i]->applyAngularImpulse(angular[i].x, angular[i].y, angular[i].z);
158 if (clamped) ++clampedBodyCount;
159 }
160 impl_->clampedBodyCount = clampedBodyCount;
161 return Result<unsigned>::success(unsigned(contacts.size() + reactions.size()));
162}
163} // namespace eve::fluids
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
std::string label
std::array< double, 10 > q
std::array< float, 4 > rotation
bool valid
MeleePoint3 b
Definition MeleeHit.cpp:41
eve::action::ActionVfxBinding binding
World3D * world
Battle::Reactions reactions
std::string body
std::size_t at
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
void detach(unsigned label)
Removes a label; absent labels are a successful no-op.
Result< unsigned > step(physics::World3D &world, VolumeFluid &fluid, float dt, unsigned substeps=4, std::span< const VolumeFluidThermalRule > rules={})
Resolves all links, samples poses, advances fluid and applies opposite impulses once.
unsigned lastClampedBodyCount() const
Returns how many linked bodies had a linear or angular impulse scaled in the last successful step.
Result< void > setImpulseLimits(float maximumLinearDelta, float maximumAngularDelta)
Sets per-step rigid velocity-change limits used after impulse aggregation.
VolumeFluidCoupling()
Volume fluid coupling.
Result< void > attach(physics::Body3D &body, const VolumeFluidCollider &localShape)
Registers a live body and local-space shape; labels must be unique, maximum 1024.
~VolumeFluidCoupling()
Volume fluid coupling.
CPU position-based free-volume fluid with a bounded spatial grid. @ownership Owns all particle state;...
static Result< std::unique_ptr< VolumeFluid > > create(const VolumeFluidSettings &settings)
Validates settings before allocating an owning solver; errors publish no state.
std::span< const VolumeFluidAttachmentReaction > attachmentReactions() const
Borrows attachment-constraint reactions from the last completed step.
Result< void > stepWithColliders(float seconds, unsigned substeps, std::span< const VolumeFluidCollider > colliders, std::span< const VolumeFluidThermalRule > rules={})
Atomically updates collider samples and advances; failed steps restore prior samples and particles.
std::vector< VolumeFluidContact > contacts() const
Returns owning events from the last completed step; no borrowed world pointers are stored.
3D rigid body (Box3D) in meter-space coordinates (+Y up by convention). Owned by a World3D; create pr...
Definition Body3D.h:24
Box3D rigid-body world. Script coordinates are meters (Box3D native), unlike 2D World which uses pixe...
Definition World3D.h:63
GLSL compute kernels for the GPU surface-flow solver.
Definition FluidTarget.h:12
Owned obstacle sample; the caller updates moving obstacles before stepping.
std::vector< VolumeFluidCollider > samples
std::vector< std::pair< unsigned, size_t > > labels
std::vector< physics::Body3D * > bodies
Meter-space (+Y up) volume-fluid material and solver policy.
Definition VolumeFluid.h:82
unsigned capacity
Maximum live particles; admission fails atomically at capacity.
Definition VolumeFluid.h:84