载入中...
搜索中...
未找到
VolumeFluidBindings.cpp
浏览该文件的文档.
1#include "fluids/VolumeFluidBindingInternal.inc"
2
3namespace eve::fluids {
4void exposeVolumeFluidType(ssq::Table& table) {
5 const auto vm = table.getHandle();
6 auto contactTracker = table.addClass<VolumeFluidContactTracker>(
7 "VolumeFluidContactTracker",
8 std::function<VolumeFluidContactTracker*()>([] { return new VolumeFluidContactTracker(); }), true);
9 contactTracker.addFunc("advance", [vm](VolumeFluidContactTracker* self, VolumeFluid* fluid, float threshold) {
10 if (!fluid)
13 "fluids.volume.contacts")));
14 auto events = self->advance(fluid->contacts(), threshold);
15 if (!events) return script::projectStatusResult(vm, events.status());
17 });
18 contactTracker.addFunc("reset", [](VolumeFluidContactTracker* self) { self->reset(); });
19 auto foam = table.addClass<VolumeFluidFoam>(
20 "VolumeFluidFoam", std::function<VolumeFluidFoam*()>([] { return new VolumeFluidFoam(); }), true);
21 foam.addFunc("snapshot", [vm](VolumeFluidFoam* self) {
23 });
24 foam.addFunc("restore", [vm](VolumeFluidFoam* self, ssq::Object object) {
25 auto value = script::valueFromSquirrel(object);
26 if (!value) return script::projectStatusResult(vm, value.status());
27 auto decoded = decodeVolumeFluidFoam(value.value());
28 if (!decoded) return script::projectStatusResult(vm, decoded.status());
29 return script::projectResult(vm, self->restore(decoded.value()));
30 });
31 foam.addFunc("advance", [vm](VolumeFluidFoam* self, VolumeFluid* fluid, VolumeFluidDiffuse* pool, float dt) {
32 if (!fluid || !pool)
35 "fluids.volume.foam")));
36 auto emitted = self->advance(*fluid, *pool, dt);
37 if (!emitted) return script::projectStatusResult(vm, emitted.status());
38 return script::projectStatusResult(vm, Status::success(), Value(int64_t(emitted.value())));
39 });
40 foam.addFunc("advanceFromActor", [vm](VolumeFluidFoam* self, VolumeFluid* fluid, VolumeFluidDiffuse* pool, float dt,
41 int actorGroup) {
42 if (!fluid || !pool || actorGroup < 0)
46 actorGroup < 0 ? "Actor group must be nonnegative" : "Missing foam source or pool",
47 "fluids.volume.foam")));
48 auto emitted = self->advanceFromActor(*fluid, *pool, dt, unsigned(actorGroup));
49 if (!emitted) return script::projectStatusResult(vm, emitted.status());
50 return script::projectStatusResult(vm, Status::success(), Value(int64_t(emitted.value())));
51 });
52 foam.addFunc("advanceFromActorInterpolated", [vm](VolumeFluidFoam* self, VolumeFluid* fluid,
53 VolumeFluidDiffuse* pool, float dt, int actorGroup, float alpha) {
54 if (!fluid || !pool || actorGroup < 0)
58 actorGroup < 0 ? "Actor group must be nonnegative" : "Missing foam source or pool",
59 "fluids.volume.foam")));
60 auto emitted = self->advanceFromActorInterpolated(*fluid, *pool, dt, unsigned(actorGroup), alpha);
61 if (!emitted) return script::projectStatusResult(vm, emitted.status());
62 return script::projectStatusResult(vm, Status::success(), Value(int64_t(emitted.value())));
63 });
64 auto diffuse = table.addClass<VolumeFluidDiffuse>(
65 "VolumeFluidDiffuse", std::function<VolumeFluidDiffuse*()>([] { return new VolumeFluidDiffuse(); }), true);
66 diffuse.addFunc("snapshot", [vm](VolumeFluidDiffuse* self) {
68 });
69 diffuse.addFunc("restore", [vm](VolumeFluidDiffuse* self, ssq::Object object) {
70 auto value = script::valueFromSquirrel(object);
71 if (!value) return script::projectStatusResult(vm, value.status());
72 auto decoded = decodeVolumeFluidDiffuse(value.value());
73 if (!decoded) return script::projectStatusResult(vm, decoded.status());
74 return script::projectResult(vm, self->restore(decoded.value()));
75 });
76 diffuse.addFunc("emit", [vm](VolumeFluidDiffuse* self, ssq::Object object) {
77 auto value = script::valueFromSquirrel(object);
78 if (!value) return script::projectStatusResult(vm, value.status());
79 auto decoded = decodeVolumeFluidDiffuseParticles(value.value());
80 if (!decoded) return script::projectStatusResult(vm, decoded.status());
81 return script::projectResult(vm, self->emit(decoded.value()));
82 });
83 diffuse.addFunc("advance", [vm](VolumeFluidDiffuse* self, VolumeFluid* fluid, float dt, int minimumNeighbors) {
84 if (!fluid)
87 "fluids.volume.diffuse")));
88 return script::projectResult(vm, self->advance(*fluid, dt, unsigned(minimumNeighbors)));
89 });
90 diffuse.addFunc("getParticleCount", [](VolumeFluidDiffuse* self) { return int(self->particleCount()); });
91 diffuse.addFunc("getAvailableCapacity", [](VolumeFluidDiffuse* self) { return int(self->availableCapacity()); });
92 diffuse.addFunc("clear", [](VolumeFluidDiffuse* self) { self->clear(); });
93 auto coupling = table.addClass<VolumeFluidCoupling>(
94 "VolumeFluidCoupling", std::function<VolumeFluidCoupling*()>([] { return new VolumeFluidCoupling(); }), true);
95 coupling.addFunc("attach", [vm](VolumeFluidCoupling* self, physics::Body3D* body, ssq::Object object) {
96 if (!body)
99 "fluids.volume.coupling")));
100 auto value = script::valueFromSquirrel(object);
101 if (!value) return script::projectStatusResult(vm, value.status());
102 Value::Array batch;
103 batch.push_back(std::move(value).takeValue());
104 auto decoded = decodeVolumeFluidColliders(Value(std::move(batch)));
105 if (!decoded) return script::projectStatusResult(vm, decoded.status());
106 return script::projectResult(vm, self->attach(*body, decoded.value()[0]));
107 });
108 coupling.addFunc("detach", [](VolumeFluidCoupling* self, int label) { self->detach(unsigned(label)); });
109 coupling.addFunc("setImpulseLimits", [vm](VolumeFluidCoupling* self, float linear, float angular) {
110 return script::projectResult(vm, self->setImpulseLimits(linear, angular));
111 });
112 coupling.addFunc("lastClampedBodyCount", &VolumeFluidCoupling::lastClampedBodyCount);
113 coupling.addFunc(
114 "step", [vm](VolumeFluidCoupling* self, physics::World3D* world, VolumeFluid* fluid, float dt, int substeps) {
115 if (!world || !fluid)
118 "Missing rigid world or fluid", "fluids.volume.coupling")));
119 auto result = self->step(*world, *fluid, dt, unsigned(substeps));
120 if (!result) return script::projectStatusResult(vm, result.status());
121 return script::projectStatusResult(vm, Status::success(), Value(int64_t(result.value())));
122 });
123 coupling.addFunc("stepWithThermalContacts", [vm](VolumeFluidCoupling* self, physics::World3D* world,
124 VolumeFluid* fluid, float dt, int substeps, ssq::Object object) {
125 const auto failure = [&](const Status& status) { return script::projectStatusResult(vm, status); };
126 if (!world || !fluid)
127 return failure(Status::failure(Diagnostic::error(
128 DiagnosticCode::InvalidArgument, "Missing rigid world or fluid", "fluids.volume.coupling")));
129 auto value = script::valueFromSquirrel(object);
130 if (!value) return failure(value.status());
131 auto rules = decodeVolumeFluidThermalRules(value.value());
132 if (!rules) return failure(rules.status());
133 auto result = self->step(*world, *fluid, dt, unsigned(substeps), rules.value());
134 if (!result) return failure(result.status());
135 return script::projectStatusResult(vm, Status::success(), Value(int64_t(result.value())));
136 });
137 auto jet = table.addClass<VolumeFluidJetEmitter>(
138 "VolumeFluidJetEmitter", std::function<VolumeFluidJetEmitter*()>([] { return new VolumeFluidJetEmitter(); }),
139 true);
140 exposeEmitterState<VolumeFluidJetEmitter>(jet, vm);
141 jet.addFunc(
142 "advanceMoving", [vm](VolumeFluidJetEmitter* self, VolumeFluid* solver, ssq::Object object, ssq::Object begin,
143 ssq::Object end, float dt, int limit, float threshold, float inheritVelocity) {
144 const auto failure = [&](const Status& status) { return script::projectStatusResult(vm, status); };
145 if (!solver)
146 return failure(Status::failure(
147 Diagnostic::error(DiagnosticCode::InvalidArgument, "Missing volume solver", "fluids.volume.jet")));
148 auto value = script::valueFromSquirrel(object);
149 if (!value) return failure(value.status());
151 if (!first) return failure(first.status());
152 auto last = script::valueFromSquirrel(end);
153 if (!last) return failure(last.status());
154 auto emission = decodeVolumeFluidEmission(value.value());
155 if (!emission) return failure(emission.status());
156 auto firstPose = decodeVolumeFluidNozzlePose(first.value());
157 if (!firstPose) return failure(firstPose.status());
158 auto lastPose = decodeVolumeFluidNozzlePose(last.value());
159 if (!lastPose) return failure(lastPose.status());
160 auto emitted = self->advanceMoving(*solver, emission.value(), firstPose.value(), lastPose.value(), dt,
161 unsigned(limit), threshold, inheritVelocity);
162 if (!emitted) return failure(emitted.status());
163 return script::projectStatusResult(vm, Status::success(), Value(int64_t(emitted.value())));
164 });
165 jet.addFunc("advance", [vm](VolumeFluidJetEmitter* self, VolumeFluid* solver, ssq::Object object, float dt,
166 int limit, float threshold) {
167 if (!solver)
170 "fluids.volume.jet")));
171 auto value = script::valueFromSquirrel(object);
172 if (!value) return script::projectStatusResult(vm, value.status());
173 auto decoded = decodeVolumeFluidEmission(value.value());
174 if (!decoded) return script::projectStatusResult(vm, decoded.status());
175 auto emitted = self->advance(*solver, decoded.value(), dt, unsigned(limit), threshold);
176 if (!emitted) return script::projectStatusResult(vm, emitted.status());
177 return script::projectStatusResult(vm, Status::success(), Value(int64_t(emitted.value())));
178 });
179 auto emitter = table.addClass<VolumeFluidEmitter>(
180 "VolumeFluidEmitter", std::function<VolumeFluidEmitter*()>([] { return new VolumeFluidEmitter(); }), true);
181 exposeEmitterState<VolumeFluidEmitter>(emitter, vm);
182 emitter.addFunc("emitParticle",
183 [vm](VolumeFluidEmitter* self, VolumeFluid* solver, ssq::Object object, float offset, float dt) {
184 if (!solver)
186 vm,
188 "Missing volume solver", "fluids.volume.emitter")));
189 auto value = script::valueFromSquirrel(object);
190 if (!value) return script::projectStatusResult(vm, value.status());
191 auto decoded = decodeVolumeFluidEmission(value.value());
192 if (!decoded) return script::projectStatusResult(vm, decoded.status());
193 return script::projectResult(vm, self->emitParticle(*solver, decoded.value(), offset, dt),
194 [](unsigned count) { return Value(int64_t(count)); });
195 });
196 emitter.addFunc("advance", [vm](VolumeFluidEmitter* self, VolumeFluid* solver, ssq::Object object, float dt,
197 float rate, int limit, float threshold) {
198 if (!solver)
201 "fluids.volume.emitter")));
202 auto value = script::valueFromSquirrel(object);
203 if (!value) return script::projectStatusResult(vm, value.status());
204 auto decoded = decodeVolumeFluidEmission(value.value());
205 if (!decoded) return script::projectStatusResult(vm, decoded.status());
206 auto emitted = self->advance(*solver, decoded.value(), dt, rate, unsigned(limit), threshold);
207 if (!emitted) return script::projectStatusResult(vm, emitted.status());
208 return script::projectStatusResult(vm, Status::success(), Value(int64_t(emitted.value())));
209 });
210 emitter.addFunc("advanceBurst", [vm](VolumeFluidEmitter* self, VolumeFluid* solver, ssq::Object object, int count,
211 float threshold) {
212 if (!solver || count <= 0)
215 solver ? "Burst count must be positive" : "Missing volume solver",
216 "fluids.volume.emitter")));
217 auto value = script::valueFromSquirrel(object);
218 if (!value) return script::projectStatusResult(vm, value.status());
219 auto decoded = decodeVolumeFluidEmission(value.value());
220 if (!decoded) return script::projectStatusResult(vm, decoded.status());
221 auto emitted = self->advanceBurst(*solver, decoded.value(), unsigned(count), threshold);
222 if (!emitted) return script::projectStatusResult(vm, emitted.status());
223 return script::projectStatusResult(vm, Status::success(), Value(int64_t(emitted.value())));
224 });
225 auto cls = table.addClass<VolumeFluid>(
226 "VolumeFluid", std::function<VolumeFluid*()>([]() -> VolumeFluid* { return nullptr; }), true);
227 cls.addFunc("step", [vm](VolumeFluid* self, float dt, int substeps) {
228 return script::projectResult(vm, self->step(dt, unsigned(substeps)));
229 });
230 cls.addFunc("setGravity", [vm](VolumeFluid* self, float x, float y, float z) {
231 return script::projectResult(vm, self->setGravity({x, y, z}));
232 });
233 cls.addFunc("setParticleWinds", [vm](VolumeFluid* self, ssq::Object object) {
234 auto value = script::valueFromSquirrel(object);
235 if (!value) return script::projectStatusResult(vm, value.status());
236 auto winds = decodeVolumeFluidFieldPositions(value.value());
237 if (!winds) return script::projectStatusResult(vm, winds.status());
238 return script::projectResult(vm, self->setParticleWinds(winds.value()));
239 });
240 cls.addFunc("setParticleExternalForces", [vm](VolumeFluid* self, ssq::Object object) {
241 auto value = script::valueFromSquirrel(object);
242 if (!value) return script::projectStatusResult(vm, value.status());
243 auto forces = decodeVolumeFluidFieldPositions(value.value());
244 if (!forces) return script::projectStatusResult(vm, forces.status());
245 return script::projectResult(vm, self->setParticleExternalForces(forces.value()));
246 });
247 cls.addFunc("accumulateWindZones", [vm](VolumeFluid* self, ssq::Object object, float fixedTimeSeconds) {
248 auto value = script::valueFromSquirrel(object);
249 if (!value) return script::projectStatusResult(vm, value.status());
250 auto zones = decodeVolumeFluidWindZones(value.value());
251 if (!zones) return script::projectStatusResult(vm, zones.status());
252 return script::projectResult(vm, self->accumulateWindZones(zones.value(), fixedTimeSeconds));
253 });
254 cls.addFunc("accumulateExternalForceZones", [vm](VolumeFluid* self, ssq::Object object, float fixedTimeSeconds) {
255 auto value = script::valueFromSquirrel(object);
256 if (!value) return script::projectStatusResult(vm, value.status());
257 auto zones = decodeVolumeFluidWindZones(value.value());
258 if (!zones) return script::projectStatusResult(vm, zones.status());
259 return script::projectResult(vm, self->accumulateExternalForceZones(zones.value(), fixedTimeSeconds));
260 });
261 cls.addFunc("stepWithThermalContacts", [vm](VolumeFluid* self, float dt, int substeps, ssq::Object object) {
262 auto value = script::valueFromSquirrel(object);
263 if (!value) return script::projectStatusResult(vm, value.status());
264 auto rules = decodeVolumeFluidThermalRules(value.value());
265 if (!rules) return script::projectStatusResult(vm, rules.status());
266 return script::projectResult(vm, self->stepWithThermalContacts(dt, unsigned(substeps), rules.value()));
267 });
268 cls.addFunc("snapshot", [vm](VolumeFluid* self) {
270 });
271 cls.addFunc("restore", [vm](VolumeFluid* self, ssq::Object object) {
272 auto decoded = read(object);
273 if (!decoded) return script::projectStatusResult(vm, decoded.status());
274 return script::projectResult(vm, self->restore(decoded.value()));
275 });
276 cls.addFunc("emit", [vm](VolumeFluid* self, ssq::Object object) {
277 auto value = script::valueFromSquirrel(object);
278 if (!value) return script::projectStatusResult(vm, value.status());
279 auto decoded = decodeVolumeFluidParticles(value.value());
280 if (!decoded) return script::projectStatusResult(vm, decoded.status());
281 return script::projectResult(vm, self->emit(decoded.value()));
282 });
283 cls.addFunc("configureParticleEvents", [vm](VolumeFluid* self, int capacity) {
284 if (capacity < 0)
287 "Particle event capacity must be nonnegative",
288 "fluids.volume.particleEvents")));
289 return script::projectResult(vm, self->configureParticleEvents(unsigned(capacity)));
290 });
291 cls.addFunc("drainParticleEvents", [vm](VolumeFluid* self) {
293 encodeVolumeFluidParticleEvents(self->drainParticleEvents()));
294 });
295 cls.addFunc("clear", [](VolumeFluid* self) { self->clear(); });
296 cls.addFunc("killParticle", [vm](VolumeFluid* self, int particleIndex) {
297 if (particleIndex < 0)
299 vm,
300 Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "Particle index must be nonnegative",
301 "fluids.volume.killParticle")));
302 return script::projectResult(vm, self->killParticle(unsigned(particleIndex)));
303 });
304 cls.addFunc("bindStaticParticles",
305 [vm](VolumeFluid* self, ssq::Object object, int colliderLabel, bool constrainOrientation) {
306 if (colliderLabel < 0)
309 "Attachment collider label must be nonnegative",
310 "fluids.volume.attachment")));
311 auto indices = readParticleIndices(object);
312 if (!indices) return script::projectStatusResult(vm, indices.status());
314 vm, self->bindStaticParticles(indices.value(), unsigned(colliderLabel), constrainOrientation),
315 [](unsigned count) { return Value(int64_t(count)); });
316 });
317 cls.addFunc("bindDynamicParticles", [vm](VolumeFluid* self, ssq::Object object, int colliderLabel, float compliance,
318 float breakThreshold, bool constrainOrientation) {
319 if (colliderLabel < 0)
322 "Attachment collider label must be nonnegative",
323 "fluids.volume.attachment")));
324 auto indices = readParticleIndices(object);
325 if (!indices) return script::projectStatusResult(vm, indices.status());
327 self->bindDynamicParticles(indices.value(), unsigned(colliderLabel), compliance,
328 breakThreshold, constrainOrientation),
329 [](unsigned count) { return Value(int64_t(count)); });
330 });
331 cls.addFunc("unbindStaticParticles", [vm](VolumeFluid* self, ssq::Object object) {
332 auto indices = readParticleIndices(object);
333 if (!indices) return script::projectStatusResult(vm, indices.status());
334 return script::projectResult(vm, self->unbindStaticParticles(indices.value()),
335 [](unsigned count) { return Value(int64_t(count)); });
336 });
337 cls.addFunc("setStitches", [vm](VolumeFluid* self, ssq::Object object) {
338 auto value = script::valueFromSquirrel(object);
339 if (!value) return script::projectStatusResult(vm, value.status());
340 auto stitches = decodeVolumeFluidStitches(value.value());
341 if (!stitches) return script::projectStatusResult(vm, stitches.status());
342 return script::projectResult(vm, self->setStitches(stitches.value()));
343 });
344 cls.addFunc("sampleField", [vm](VolumeFluid* self, ssq::Object object) {
345 auto value = script::valueFromSquirrel(object);
346 if (!value) return script::projectStatusResult(vm, value.status());
348 if (!positions) return script::projectStatusResult(vm, positions.status());
349 auto samples = self->sampleField(positions.value());
350 if (!samples) return script::projectStatusResult(vm, samples.status());
352 });
353 cls.addFunc("debugParticleGrid", [vm](VolumeFluid* self, int maxCells) {
354 if (maxCells < 0)
357 "Particle-grid cell budget must be nonnegative",
358 "fluids.volume.gridDebug")));
359 auto cells = self->debugParticleGrid(unsigned(maxCells));
360 if (!cells) return script::projectStatusResult(vm, cells.status());
361 Value::Array encoded;
362 encoded.reserve(cells.value().size());
363 for (const auto& cell : cells.value())
364 encoded.push_back(Value::object({{"center", Value::array({cell.center.x, cell.center.y, cell.center.z})},
365 {"size", Value::array({cell.size.x, cell.size.y, cell.size.z})},
366 {"particleCount", int64_t(cell.particleCount)}}));
367 return script::projectStatusResult(vm, Status::success(), Value(std::move(encoded)));
368 });
369 cls.addFunc("debugParticleFrames", [vm](VolumeFluid* self, int actorGroup, float size, int maxParticles) {
370 if (actorGroup < 0 || maxParticles < 0)
371 return script::projectStatusResult(
372 vm, Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,
373 "Particle-frame arguments must be nonnegative",
374 "fluids.volume.frameDebug")));
375 auto frames = self->debugParticleFrames(unsigned(actorGroup), size, unsigned(maxParticles));
376 if (!frames) return script::projectStatusResult(vm, frames.status());
377 Value::Array encoded;
378 encoded.reserve(frames.value().size());
379 for (const auto& frame : frames.value())
380 encoded.push_back(Value::object({{"particleIndex", int64_t(frame.particleIndex)},
381 {"origin", Value::array({frame.origin.x, frame.origin.y, frame.origin.z})},
382 {"x", Value::array({frame.x.x, frame.x.y, frame.x.z})},
383 {"y", Value::array({frame.y.x, frame.y.y, frame.y.z})},
384 {"z", Value::array({frame.z.x, frame.z.y, frame.z.z})}}));
385 return script::projectStatusResult(vm, Status::success(), Value(std::move(encoded)));
386 });
387 cls.addFunc("particleInstances", [vm](VolumeFluid* self, int actorGroup, float sx, float sy, float sz, float alpha,
388 int maxInstances) {
389 if (actorGroup < 0 || maxInstances < 0)
390 return script::projectStatusResult(
391 vm, Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,
392 "Particle-instance group and budget must be nonnegative",
393 "fluids.volume.particleInstances")));
394 auto copied = self->particleInstances(unsigned(actorGroup), {sx, sy, sz}, alpha, unsigned(maxInstances));
395 if (!copied) return script::projectStatusResult(vm, copied.status());
396 Value::Array encoded;
397 encoded.reserve(copied.value().size());
398 for (const auto& instance : copied.value())
399 encoded.push_back(Value::object(
400 {{"particleIndex", int64_t(instance.particleIndex)},
401 {"position", Value::array({instance.position.x, instance.position.y, instance.position.z})},
402 {"orientation", Value::array({instance.orientation.x, instance.orientation.y, instance.orientation.z,
403 instance.orientation.w})},
404 {"scale", Value::array({instance.scale.x, instance.scale.y, instance.scale.z})},
405 {"color", Value::array({instance.color.x, instance.color.y, instance.color.z, instance.color.w})}}));
406 return script::projectStatusResult(vm, Status::success(), Value(std::move(encoded)));
407 });
408 cls.addFunc("particleImpostors", [vm](VolumeFluid* self, int actorGroup, float radiusScale, float r, float g,
409 float b, float a, float alpha, int maxInstances) {
410 if (actorGroup < 0 || maxInstances < 0)
411 return script::projectStatusResult(
412 vm, Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,
413 "Particle-impostor group and budget must be nonnegative",
414 "fluids.volume.particleImpostors")));
415 auto copied =
416 self->particleImpostors(unsigned(actorGroup), radiusScale, {r, g, b, a}, alpha, unsigned(maxInstances));
417 if (!copied) return script::projectStatusResult(vm, copied.status());
418 Value::Array encoded;
419 encoded.reserve(copied.value().size());
420 for (const auto& instance : copied.value())
421 encoded.push_back(Value::object(
422 {{"particleIndex", int64_t(instance.particleIndex)},
423 {"position", Value::array({instance.position.x, instance.position.y, instance.position.z})},
424 {"orientation", Value::array({instance.orientation.x, instance.orientation.y, instance.orientation.z,
425 instance.orientation.w})},
426 {"scale", Value::array({instance.scale.x, instance.scale.y, instance.scale.z})},
427 {"color", Value::array({instance.color.x, instance.color.y, instance.color.z, instance.color.w})}}));
428 return script::projectStatusResult(vm, Status::success(), Value(std::move(encoded)));
429 });
430 cls.addFunc("debugSdfSlice",
431 [vm](VolumeFluid* self, int colliderLabel, int axis, float slice, float maxDistance, int maxSamples) {
432 if (colliderLabel < 0 || maxSamples < 0)
433 return script::projectStatusResult(
434 vm, Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,
435 "SDF slice label and budget must be nonnegative",
436 "fluids.volume.sdfSliceDebug")));
437 auto sampled = self->debugSdfSlice(unsigned(colliderLabel), VolumeFluidSdfSliceAxis(axis), slice,
438 maxDistance, unsigned(maxSamples));
439 if (!sampled) return script::projectStatusResult(vm, sampled.status());
440 const auto& result = sampled.value();
441 Value::Array values;
442 values.reserve(result.values.size());
443 for (float value : result.values) values.emplace_back(value);
444 return script::projectStatusResult(
445 vm, Status::success(),
446 Value::object({{"width", int64_t(result.width)},
447 {"height", int64_t(result.height)},
448 {"origin", Value::array({result.origin.x, result.origin.y, result.origin.z})},
449 {"stepX", Value::array({result.stepX.x, result.stepX.y, result.stepX.z})},
450 {"stepY", Value::array({result.stepY.x, result.stepY.y, result.stepY.z})},
451 {"values", Value(std::move(values))}}));
452 });
453 cls.addFunc("applySolidColor", [vm](VolumeFluid* self, float r, float g, float b, float a) {
454 return script::projectResult(vm, self->applySolidColor({r, g, b, a}));
455 });
456 cls.addFunc("applyVelocityColors", [vm](VolumeFluid* self, float sensibility) {
457 return script::projectResult(vm, self->applyVelocityColors(sensibility));
458 });
459 cls.addFunc("applyActorGroupColors",
460 [vm](VolumeFluid* self) { return script::projectResult(vm, self->applyActorGroupColors()); });
461 cls.addFunc("applyDataColors", [vm](VolumeFluid* self, int channel, ssq::Object object) {
462 if (channel < 0)
463 return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,
464 "Invalid data color channel",
465 "fluids.volume.applyDataColors")));
466 auto value = script::valueFromSquirrel(object);
467 if (!value) return script::projectStatusResult(vm, value.status());
468 auto gradient = decodeVolumeFluidColorGradient(value.value());
469 if (!gradient) return script::projectStatusResult(vm, gradient.status());
470 return script::projectResult(vm, self->applyDataColors(unsigned(channel), gradient.value()));
471 });
472 cls.addFunc("applyRandomColors", [vm](VolumeFluid* self, ssq::Object object, int seed) {
473 if (seed < 0)
474 return script::projectResult(vm, Result<void>::failure(Diagnostic::error(
475 DiagnosticCode::InvalidArgument, "Invalid random color seed",
476 "fluids.volume.applyRandomColors")));
477 auto value = script::valueFromSquirrel(object);
478 if (!value) return script::projectStatusResult(vm, value.status());
479 auto gradient = decodeVolumeFluidColorGradient(value.value());
480 if (!gradient) return script::projectStatusResult(vm, gradient.status());
481 return script::projectResult(vm, self->applyRandomColors(gradient.value(), uint32_t(seed)));
482 });
483 cls.addFunc("addRandomVelocity", [vm](VolumeFluid* self, float intensity, int seed) {
484 if (seed < 0)
485 return script::projectResult(vm, Result<void>::failure(Diagnostic::error(
486 DiagnosticCode::InvalidArgument, "Invalid random velocity seed",
487 "fluids.volume.addRandomVelocity")));
488 return script::projectResult(vm, self->addRandomVelocity(intensity, uint32_t(seed)));
489 });
490 cls.addFunc("teleportActor", [vm](VolumeFluid* self, ssq::Object currentObject, ssq::Object targetObject) {
491 auto currentValue = script::valueFromSquirrel(currentObject);
492 if (!currentValue) return script::projectStatusResult(vm, currentValue.status());
493 auto targetValue = script::valueFromSquirrel(targetObject);
494 if (!targetValue) return script::projectStatusResult(vm, targetValue.status());
495 auto current = decodeVolumeFluidNozzlePose(currentValue.value());
496 if (!current) return script::projectStatusResult(vm, current.status());
497 auto target = decodeVolumeFluidNozzlePose(targetValue.value());
498 if (!target) return script::projectStatusResult(vm, target.status());
499 return script::projectResult(vm, self->teleportActor(current.value().position, current.value().rotation,
500 target.value().position, target.value().rotation));
501 });
502 cls.addFunc("actorMassProperties", [vm](VolumeFluid* self, int actorGroup) {
503 if (actorGroup < 0)
504 return script::projectStatusResult(
505 vm, Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,
506 "Actor group must be nonnegative", "fluids.volume.actorMass")));
507 auto properties = self->actorMassProperties(unsigned(actorGroup));
508 if (!properties) return script::projectStatusResult(vm, properties.status());
509 const auto& p = properties.value();
510 return script::projectStatusResult(
511 vm, Status::success(),
512 Value::object({{"mass", p.mass},
513 {"centerOfMass", Value::array({p.centerOfMass.x, p.centerOfMass.y, p.centerOfMass.z})},
514 {"particleCount", int64_t(p.particleCount)}}));
515 });
516 cls.addFunc("setActorFilterCategory", [vm](VolumeFluid* self, int actorGroup, int category) {
517 if (actorGroup < 0 || category < 0)
518 return script::projectStatusResult(
519 vm, Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,
520 "Actor group and category must be nonnegative",
521 "fluids.volume.filter")));
522 return script::projectResult(vm, self->setActorFilterCategory(unsigned(actorGroup), unsigned(category)));
523 });
524 cls.addFunc("setActorCollisionFilter", [vm](VolumeFluid* self, int actorGroup, int64_t collisionFilter) {
525 if (actorGroup < 0 || collisionFilter < 0 || collisionFilter > int64_t(0xffffffffu))
526 return script::projectStatusResult(
527 vm, Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,
528 "Actor group and packed collision filter are out of range",
529 "fluids.volume.filter")));
530 return script::projectResult(vm,
531 self->setActorCollisionFilter(unsigned(actorGroup), unsigned(collisionFilter)));
532 });
533 cls.addFunc("updateActorMaterial", [vm](VolumeFluid* self, int actorGroup, ssq::Object emissionObject) {
534 if (actorGroup < 0)
535 return script::projectStatusResult(vm, Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,
536 "Actor group must be nonnegative",
537 "fluids.volume.actorMaterial")));
538 auto value = script::valueFromSquirrel(emissionObject);
539 if (!value) return script::projectStatusResult(vm, value.status());
540 auto emission = decodeVolumeFluidEmission(value.value());
541 if (!emission) return script::projectStatusResult(vm, emission.status());
542 return script::projectResult(vm, self->updateActorMaterial(unsigned(actorGroup), emission.value().prototype));
543 });
544 cls.addFunc("setActorSelfCollisions", [vm](VolumeFluid* self, int actorGroup, bool enabled) {
545 if (actorGroup < 0)
546 return script::projectStatusResult(vm, Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,
547 "Actor group must be nonnegative",
548 "fluids.volume.selfCollisions")));
549 return script::projectResult(vm, self->setActorSelfCollisions(unsigned(actorGroup), enabled));
550 });
551 cls.addFunc("killActorParticles", [vm](VolumeFluid* self, int actorGroup) {
552 if (actorGroup < 0)
553 return script::projectStatusResult(
554 vm, Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,
555 "Actor group must be nonnegative", "fluids.volume.killActor")));
556 return script::projectResult(vm, self->killActorParticles(unsigned(actorGroup)),
557 [](unsigned count) { return Value(int64_t(count)); });
558 });
559 cls.addFunc("applyParticleDrag", [vm](VolumeFluid* self, int particleIndex, float x, float y, float z,
560 float stiffness, float damping, float seconds) {
561 if (particleIndex < 0)
562 return script::projectStatusResult(
563 vm, Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,
564 "Particle index must be nonnegative", "fluids.volume.drag")));
565 return script::projectResult(
566 vm, self->applyParticleDrag(unsigned(particleIndex), {x, y, z}, stiffness, damping, seconds));
567 });
568 cls.addFunc("grabContactParticles", [vm](VolumeFluid* self, int colliderLabel, float x, float y, float z, float qx,
569 float qy, float qz, float qw, float distanceThreshold) {
570 if (colliderLabel < 0)
571 return script::projectStatusResult(
572 vm,
573 Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "Collider label must be nonnegative",
574 "fluids.volume.contactGrabber")));
575 return script::projectResult(
576 vm, self->grabContactParticles(unsigned(colliderLabel), {x, y, z}, {qx, qy, qz, qw}, distanceThreshold),
577 [](unsigned count) { return Value(int64_t(count)); });
578 });
579 cls.addFunc("updateGrabbedParticles", [vm](VolumeFluid* self, int colliderLabel, float x, float y, float z,
580 float qx, float qy, float qz, float qw) {
581 if (colliderLabel < 0)
582 return script::projectStatusResult(
583 vm,
584 Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "Collider label must be nonnegative",
585 "fluids.volume.contactGrabber")));
586 return script::projectResult(vm,
587 self->updateGrabbedParticles(unsigned(colliderLabel), {x, y, z}, {qx, qy, qz, qw}),
588 [](unsigned count) { return Value(int64_t(count)); });
589 });
590 cls.addFunc("releaseGrabbedParticles", [vm](VolumeFluid* self, int colliderLabel) {
591 if (colliderLabel < 0)
592 return script::projectStatusResult(
593 vm,
594 Status::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "Collider label must be nonnegative",
595 "fluids.volume.contactGrabber")));
596 return script::projectResult(vm, self->releaseGrabbedParticles(unsigned(colliderLabel)),
597 [](unsigned count) { return Value(int64_t(count)); });
598 });
599 cls.addFunc("applyMaterialChannels",
600 [vm](VolumeFluid* self) { return script::projectResult(vm, self->applyMaterialChannels()); });
601 cls.addFunc("applyMaterialChannelsWithColors", [vm](VolumeFluid* self, ssq::Object object) {
602 auto value = script::valueFromSquirrel(object);
603 if (!value) return script::projectStatusResult(vm, value.status());
604 auto keys = decodeVolumeFluidViscosityColors(value.value());
605 if (!keys) return script::projectStatusResult(vm, keys.status());
606 return script::projectResult(vm, self->applyMaterialChannels(keys.value()));
607 });
608 cls.addFunc("overlapSphere", [vm](VolumeFluid* self, float x, float y, float z, float radius) {
609 auto hits = self->overlap({x, y, z}, radius);
610 if (!hits) return script::projectStatusResult(vm, hits.status());
611 return script::projectStatusResult(vm, Status::success(), encodeVolumeFluidParticles(hits.value()));
612 });
613 cls.addFunc("paintSphere", [vm](VolumeFluid* self, float x, float y, float z, float radius, ssq::Object material) {
614 auto value = script::valueFromSquirrel(material);
615 if (!value) return script::projectStatusResult(vm, value.status());
616 auto decoded = decodeVolumeFluidMaterial(value.value());
617 if (!decoded) return script::projectStatusResult(vm, decoded.status());
618 return script::projectResult(vm, self->paint({x, y, z}, radius, decoded.value()));
619 });
620 cls.addFunc("overlapBox", [vm](VolumeFluid* self, float x, float y, float z, float hx, float hy, float hz, float qx,
621 float qy, float qz, float qw) {
622 auto hits = self->overlapBox({x, y, z}, {hx, hy, hz}, {qx, qy, qz, qw});
623 if (!hits) return script::projectStatusResult(vm, hits.status());
624 return script::projectStatusResult(vm, Status::success(), encodeVolumeFluidParticles(hits.value()));
625 });
626 cls.addFunc("raycast", [vm](VolumeFluid* self, float ox, float oy, float oz, float dx, float dy, float dz,
627 float maxDistance, int maxHits, int phaseMask) {
628 auto hits = self->raycast({ox, oy, oz}, {dx, dy, dz}, maxDistance, unsigned(maxHits), unsigned(phaseMask));
629 if (!hits) return script::projectStatusResult(vm, hits.status());
630 return script::projectStatusResult(vm, Status::success(), encodeVolumeFluidRayHits(hits.value()));
631 });
632 cls.addFunc("querySphere", [vm](VolumeFluid* self, float x, float y, float z, float radius, float contactOffset,
633 float maxDistance, int maxHits, int phaseMask) {
634 auto hits =
635 self->querySphere({x, y, z}, radius, contactOffset, maxDistance, unsigned(maxHits), unsigned(phaseMask));
636 if (!hits) return script::projectStatusResult(vm, hits.status());
637 return script::projectStatusResult(vm, Status::success(), encodeVolumeFluidDistanceHits(hits.value()));
638 });
639 cls.addFunc("queryBox",
640 [vm](VolumeFluid* self, float x, float y, float z, float hx, float hy, float hz, float qx, float qy,
641 float qz, float qw, float contactOffset, float maxDistance, int maxHits, int phaseMask) {
642 auto hits = self->queryBox({x, y, z}, {hx, hy, hz}, {qx, qy, qz, qw}, contactOffset, maxDistance,
643 unsigned(maxHits), unsigned(phaseMask));
644 if (!hits) return script::projectStatusResult(vm, hits.status());
645 return script::projectStatusResult(vm, Status::success(),
646 encodeVolumeFluidDistanceHits(hits.value()));
647 });
648 cls.addFunc("queryBatch", [vm](VolumeFluid* self, ssq::Object object, int maxHitsPerQuery) {
649 auto value = script::valueFromSquirrel(object);
650 if (!value) return script::projectStatusResult(vm, value.status());
651 auto queries = decodeVolumeFluidQueries(value.value());
652 if (!queries) return script::projectStatusResult(vm, queries.status());
653 auto hits = self->queryBatch(queries.value(), unsigned(maxHitsPerQuery));
654 if (!hits) return script::projectStatusResult(vm, hits.status());
655 return script::projectStatusResult(vm, Status::success(), encodeVolumeFluidQueryHits(hits.value()));
656 });
657 cls.addFunc("applyQueryColors", [vm](VolumeFluid* self, ssq::Object queryObject, ssq::Object colorObject, float r,
658 float g, float b, float a, int maxHitsPerQuery) {
659 auto queryValue = script::valueFromSquirrel(queryObject);
660 if (!queryValue) return script::projectStatusResult(vm, queryValue.status());
661 auto colorValue = script::valueFromSquirrel(colorObject);
662 if (!colorValue) return script::projectStatusResult(vm, colorValue.status());
663 auto queries = decodeVolumeFluidQueries(queryValue.value());
664 if (!queries) return script::projectStatusResult(vm, queries.status());
665 auto colors = decodeVolumeFluidColors(colorValue.value());
666 if (!colors) return script::projectStatusResult(vm, colors.status());
667 auto counts = self->applyQueryColors(queries.value(), colors.value(), {r, g, b, a}, unsigned(maxHitsPerQuery));
668 if (!counts) return script::projectStatusResult(vm, counts.status());
669 Value::Array encoded;
670 encoded.reserve(counts.value().size());
671 for (unsigned count : counts.value()) encoded.emplace_back(int64_t(count));
672 return script::projectStatusResult(vm, Status::success(), Value(std::move(encoded)));
673 });
674 cls.addFunc("applyQueryColorsPreservingOutside",
675 [vm](VolumeFluid* self, ssq::Object queryObject, ssq::Object colorObject, int maxHitsPerQuery) {
676 auto queryValue = script::valueFromSquirrel(queryObject);
677 if (!queryValue) return script::projectStatusResult(vm, queryValue.status());
678 auto colorValue = script::valueFromSquirrel(colorObject);
679 if (!colorValue) return script::projectStatusResult(vm, colorValue.status());
680 auto queries = decodeVolumeFluidQueries(queryValue.value());
681 if (!queries) return script::projectStatusResult(vm, queries.status());
682 auto colors = decodeVolumeFluidColors(colorValue.value());
683 if (!colors) return script::projectStatusResult(vm, colors.status());
684 auto counts = self->applyQueryColorsPreservingOutside(queries.value(), colors.value(),
685 unsigned(maxHitsPerQuery));
686 if (!counts) return script::projectStatusResult(vm, counts.status());
687 Value::Array encoded;
688 encoded.reserve(counts.value().size());
689 for (unsigned count : counts.value()) encoded.emplace_back(int64_t(count));
690 return script::projectStatusResult(vm, Status::success(), Value(std::move(encoded)));
691 });
692 cls.addFunc("setSimplexes", [vm](VolumeFluid* self, ssq::Object object) {
693 auto value = script::valueFromSquirrel(object);
694 if (!value) return script::projectStatusResult(vm, value.status());
695 auto simplexes = decodeVolumeFluidSimplexes(value.value());
696 if (!simplexes) return script::projectStatusResult(vm, simplexes.status());
697 return script::projectResult(vm, self->setSimplexes(simplexes.value()));
698 });
699 cls.addFunc("querySimplexes", [vm](VolumeFluid* self, ssq::Object object, int maxHitsPerQuery) {
700 auto value = script::valueFromSquirrel(object);
701 if (!value) return script::projectStatusResult(vm, value.status());
702 auto queries = decodeVolumeFluidQueries(value.value());
703 if (!queries) return script::projectStatusResult(vm, queries.status());
704 auto hits = self->querySimplexes(queries.value(), unsigned(maxHitsPerQuery));
705 if (!hits) return script::projectStatusResult(vm, hits.status());
706 return script::projectStatusResult(vm, Status::success(), encodeVolumeFluidSimplexHits(hits.value()));
707 });
708 cls.addFunc("setColliders", [vm](VolumeFluid* self, ssq::Object object) {
709 auto value = script::valueFromSquirrel(object);
710 if (!value) return script::projectStatusResult(vm, value.status());
711 auto decoded = decodeVolumeFluidColliders(value.value());
712 if (!decoded) return script::projectStatusResult(vm, decoded.status());
713 return script::projectResult(vm, self->setColliders(decoded.value()));
714 });
715 cls.addFunc("setSdfColliders", [vm](VolumeFluid* self, ssq::Object object) {
716 auto value = script::valueFromSquirrel(object);
717 if (!value) return script::projectStatusResult(vm, value.status());
718 auto decoded = decodeVolumeFluidSdfColliders(value.value());
719 if (!decoded) return script::projectStatusResult(vm, decoded.status());
720 return script::projectResult(vm, self->setSdfColliders(decoded.value()));
721 });
722 cls.addFunc("setHeightFieldColliders", [vm](VolumeFluid* self, ssq::Object object) {
723 auto value = script::valueFromSquirrel(object);
724 if (!value) return script::projectStatusResult(vm, value.status());
725 auto decoded = decodeVolumeFluidHeightFieldColliders(value.value());
726 if (!decoded) return script::projectStatusResult(vm, decoded.status());
727 return script::projectResult(vm, self->setHeightFieldColliders(decoded.value()));
728 });
729 cls.addFunc("updateSdfColliderPoses", [vm](VolumeFluid* self, ssq::Object object) {
730 auto value = script::valueFromSquirrel(object);
731 if (!value) return script::projectStatusResult(vm, value.status());
732 auto decoded = decodeVolumeFluidSdfPoses(value.value());
733 if (!decoded) return script::projectStatusResult(vm, decoded.status());
734 return script::projectResult(vm, self->updateSdfColliderPoses(decoded.value()));
735 });
736 cls.addFunc("contacts", [vm](VolumeFluid* self) {
737 return script::projectStatusResult(vm, Status::success(), encodeVolumeFluidContacts(self->contacts()));
738 });
739 cls.addFunc("emitBurst", [vm](VolumeFluid* self, ssq::Object object, int count) {
740 auto value = script::valueFromSquirrel(object);
741 if (!value) return script::projectStatusResult(vm, value.status());
742 auto decoded = decodeVolumeFluidEmission(value.value());
743 if (!decoded) return script::projectStatusResult(vm, decoded.status());
744 auto emitted = emitVolumeFluidBurst(*self, decoded.value(), unsigned(count));
745 if (!emitted) return script::projectStatusResult(vm, emitted.status());
746 return script::projectStatusResult(vm, Status::success(), Value(int64_t(emitted.value())));
747 });
748 cls.addFunc("getParticleCount", [](VolumeFluid* self) { return int(self->particleCount()); });
749}
750
751} // namespace eve::fluids
LogicalId target
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
glm::vec4 p[6]
std::string label
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
std::map< std::string, Var > values
tensor::Graph g
Definition GpuGraph.cpp:7
std::uint32_t capacity
wgpu::PopErrorScopeStatus status
double r
std::vector< std::uint32_t > indices
std::vector< float > positions
std::int32_t first
size_t offset
std::map< std::string, PropertyRule > properties
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
World3D * world
std::array< PixelCell, kPixelChunkSize *kPixelChunkSize > cells
float radius
std::uint32_t seed
Definition PointSet.cpp:807
float begin
Material * material
std::vector< float > colors
double current
float dz
float dy
float dx
std::uint32_t count
std::unique_ptr< TrailEmitter > emitter
Cell cell
Battle::Events events
Json object
int limit
Definition TreeMesh.cpp:164
float size
Definition TreeMesh.cpp:156
const char * category
std::string body
double oy
double ox
float qy
float qx
float qw
float qz
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
Structured status and zero or more diagnostics for an operation.
Definition Status.h:68
static Status failure(StatusCode code, Diagnostic diagnostic)
Construct a failed status with one diagnostic.
Definition Status.h:84
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
std::vector< Value > Array
Definition Value.h:33
static Value object(Object value)
Compatibility factory for an object value.
Definition Value.h:114
Explicit bounded contact lifecycle tracker; owns no solver reference.
void reset()
Clears prior keys so the next contacts enter again.
Result< std::vector< VolumeFluidContactEvent > > advance(std::span< const VolumeFluidContact > contacts, float distanceThreshold=.01f)
Diffs latest contacts against the preceding successful call.
Process-local generation-checked bridge between fluid samples and rigid bodies. @ownership Owns local...
unsigned lastClampedBodyCount() const
Returns how many linked bodies had a linear or angular impulse scaled in the last successful step.
Bounded, one-way fluid-advected secondary particle pool. @ownership Owns particles and capacity; no f...
Bounded runtime stream controller with explicit snapshot/restore.
Generates secondary particles at high-curl, low-density fluid source positions. @ownership Owns setti...
Spacing-driven planar/distribution jet controller with independent, owned stream phase.
CPU position-based free-volume fluid with a bounded spatial grid. @ownership Owns all particle state;...
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
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
GLSL compute kernels for the GPU surface-flow solver.
Definition FluidTarget.h:12
Value encodeVolumeFluid(const VolumeFluidSnapshot &snapshot)
Canonical schema-17 value projection, shared by JSON, scripts and authoring.
Result< std::vector< VolumeFluidViscosityColorKey > > decodeVolumeFluidViscosityColors(const Value &value)
Strict owning decode of at most 32 transient viscosity color keys; application validates domains.
Result< std::vector< VolumeFluidQueryShape > > decodeVolumeFluidQueries(const Value &value)
Strict owning decode of at most 256 transient mixed query descriptions.
Value encodeVolumeFluidContacts(const std::vector< VolumeFluidContact > &contacts)
Projects last-step contacts, including world-space point and opposite impulse.
VolumeFluidSdfSliceAxis
Local axis normal used by an explicit SDF diagnostic slice.
Result< VolumeFluidMaterial > decodeVolumeFluidMaterial(const Value &value)
Strict transient material decode; paint validates numeric domains before mutation.
Value encodeVolumeFluidFieldSamples(const std::vector< VolumeFluidFieldSample > &samples)
Projects owning field samples, preserving query order.
Result< std::vector< VolumeFluidSdfPose > > decodeVolumeFluidSdfPoses(const Value &value)
Strict transient decode of at most 16 SDF transform-only updates.
Value encodeVolumeFluidSimplexHits(const std::vector< VolumeFluidSimplexHit > &hits)
Projects owning Fluid3D-compatible simplex query results.
void exposeVolumeFluidType(ssq::Table &table)
Registers the script-owned volume-fluid class on the VM thread.
Result< VolumeFluidNozzlePose > decodeVolumeFluidNozzlePose(const Value &value)
Strict transient pose-argument decode; advanceMoving validates the unit quaternion.
Result< std::vector< VolumeFluidSimplex > > decodeVolumeFluidSimplexes(const Value &value)
Strict owning decode of at most 65536 point, edge or triangle topology entries.
Result< std::vector< VolumeFluidWindZone > > decodeVolumeFluidWindZones(const Value &value)
Strict owning decode of at most 64 transient Fluid3D-style wind zones.
Value encodeVolumeFluidContactEvents(const std::vector< VolumeFluidContactEvent > &events)
Projects owning Enter/Stay/Exit contact lifecycle events.
Result< std::vector< glm::vec4 > > decodeVolumeFluidColors(const Value &value)
Strict owning decode of at most 256 transient linear RGBA colors.
Result< std::vector< VolumeFluidStitch > > decodeVolumeFluidStitches(const Value &value)
Strict owning decode of at most 65536 Fluid3DStitcher particle pairs.
Value encodeVolumeFluidParticles(const std::vector< VolumeFluidParticle > &particles)
Projects an owning particle batch without copying the live solver.
Value encodeVolumeFluidDiffuse(const VolumeFluidDiffuseSnapshot &snapshot)
Projects the owning version-1 secondary particle pool state.
Result< std::vector< VolumeFluidHeightFieldCollider > > decodeVolumeFluidHeightFieldColliders(const Value &value)
Strict owning decode of at most 16 regular sampled height fields.
Result< std::vector< VolumeFluidDiffuseParticle > > decodeVolumeFluidDiffuseParticles(const Value &value)
Strict transient batch decode of at most 65536 particles; emit validates domains.
Value encodeVolumeFluidQueryHits(const std::vector< VolumeFluidQueryHit > &hits)
Projects owning mixed-query hits with their source query indices.
Result< std::vector< glm::vec3 > > decodeVolumeFluidFieldPositions(const Value &value)
Strict owning decode of up to 65536 transient world-space field query positions.
Result< VolumeFluidFoamSnapshot > decodeVolumeFluidFoam(const Value &value)
Strict decode with atomic-controller validation; rejects unknown fields and versions.
Result< std::vector< VolumeFluidColorKey > > decodeVolumeFluidColorGradient(const Value &value)
Strict owning decode of a 2..32-key normalized linear RGBA gradient.
Result< VolumeFluidDiffuseSnapshot > decodeVolumeFluidDiffuse(const Value &value)
Strict bounded decode; validates the entire pool before returning owning state.
Result< VolumeFluidEmission > decodeVolumeFluidEmission(const Value &value)
Strict structural decode; nozzle/material domain validation occurs atomically at emission.
Result< std::vector< VolumeFluidSdfCollider > > decodeVolumeFluidSdfColliders(const Value &value)
Strict owning decode of at most 16 sampled SDF colliders.
Result< std::vector< VolumeFluidThermalRule > > decodeVolumeFluidThermalRules(const Value &value)
Strict owning decode of at most 1024 transient thermal rules; step validates domains.
Result< std::vector< VolumeFluidParticle > > decodeVolumeFluidParticles(const Value &value)
Decodes an owning emission batch without serializing the live solver; admission validates domains.
Value encodeVolumeFluidParticleEvents(const VolumeFluidParticleEventBatch &batch)
Projects one owning bounded particle lifecycle-event drain.
Value encodeVolumeFluidFoam(const VolumeFluidFoamSnapshot &snapshot)
Projects owning schema-1 foam settings, credit text and RNG state.
Value encodeVolumeFluidDistanceHits(const std::vector< VolumeFluidDistanceHit > &hits)
Projects owning signed-distance hits including captured particle values.
Result< std::vector< VolumeFluidCollider > > decodeVolumeFluidColliders(const Value &value)
Strict transient collider batch decode; setColliders validates geometry atomically.
Value encodeVolumeFluidRayHits(const std::vector< VolumeFluidRayHit > &hits)
Projects owning ray hits including captured particle values.
Result< unsigned > emitVolumeFluidBurst(VolumeFluid &solver, const VolumeFluidEmission &e, unsigned count)
Generates at most 4096 particles and admits the entire burst atomically.
ssq::Table projectStatusResult(HSQUIRRELVM vm, const Status &status)
Project a checked native status that carries no payload.
ssq::Table projectResult(HSQUIRRELVM vm, Result< void > &&result)
Consume and project a void native Result using the common schema.
Result< Value > valueFromSquirrel(HSQUIRRELVM vm, SQInteger index, const SquirrelValueOptions &options)
Convert one Squirrel value into the canonical owning Value tree.
bool enabled
glm::uvec4 counts