载入中...
搜索中...
未找到
VolumeFluidFactoryBindings.cpp
浏览该文件的文档.
1#include "fluids/VolumeFluidBindingInternal.inc"
2#if defined(EVE_FLUIDS_HAS_MODEL3D)
3#include "model3d/ModelData.h"
4#endif
5
6namespace eve::fluids {
7void exposeVolumeFluidFactory(ssq::Class& cls) {
8 const auto vm = cls.getHandle();
9 cls.addFunc("volumeThermalRuleDefaults", [vm](Fluids*) {
12 });
13 cls.addFunc("volumeWindZoneDefaults", [vm](Fluids*) {
15 });
16 cls.addFunc("volumeColliderDefaults", [vm](Fluids*) {
18 });
19 cls.addFunc("volumeSdfColliderDefaults", [vm](Fluids*) {
21 collider.sdf = MeshSdf::makeSphere(glm::vec3(0.f), .5f, {16, 16, 16});
23 });
24 cls.addFunc("volumeHeightFieldColliderDefaults", [vm](Fluids*) {
26 collider.heights.assign(4, 0.f);
28 });
29 cls.addFunc("volumeSdfPoseDefaults", [vm](Fluids*) {
31 });
32#if defined(EVE_FLUIDS_HAS_MODEL3D)
33 cls.addFunc("volumeSdfColliderFromModel", [vm](Fluids*, model3d::ModelData* model, int meshIndex, float sx,
34 float sy, float sz, int resolution) {
35 const auto failure = [&](const char* message) {
38 "fluids.volume.sdfColliderFromModel")));
39 };
40 if (!model || meshIndex < 0 || meshIndex >= model->getMeshCount() || resolution < 8 || resolution > 128)
41 return failure("Invalid model mesh slot or SDF resolution");
42 const int vertexCount = model->getVertexCount(meshIndex), faceCount = model->getFaceCount(meshIndex);
43 const uint64_t samples = uint64_t(resolution) * uint64_t(resolution) * uint64_t(resolution);
44 if (vertexCount <= 0 || vertexCount > 1000000 || faceCount <= 0 || faceCount > 65536 ||
45 samples * uint64_t(faceCount) > 4000000)
46 return failure("Model SDF bake exceeds geometry or 4M sample/triangle budget");
47 const glm::vec3 scale(sx, sy, sz);
48 if (!std::isfinite(sx) || !std::isfinite(sy) || !std::isfinite(sz) || sx == 0 || sy == 0 || sz == 0)
49 return failure("Invalid model SDF scale");
50 std::vector<glm::vec3> vertices(static_cast<size_t>(vertexCount), glm::vec3(0.f));
51 std::vector<uint32_t> indices;
52 indices.reserve(size_t(faceCount) * 3);
53 try {
54 for (int i = 0; i < vertexCount; ++i)
55 vertices[size_t(i)] = {model->getVertexPosition(meshIndex, i, 0) * sx,
56 model->getVertexPosition(meshIndex, i, 1) * sy,
57 model->getVertexPosition(meshIndex, i, 2) * sz};
58 for (int i = 0; i < faceCount; ++i)
59 for (int corner = 0; corner < 3; ++corner) {
60 const int index = model->getFaceVertexIndex(meshIndex, i, corner);
61 if (index < 0 || index >= vertexCount) return failure("Model contains invalid SDF triangle index");
62 indices.push_back(uint32_t(index));
63 }
64 for (size_t i = 0; i < indices.size(); i += 3)
65 if (glm::length(glm::cross(vertices[indices[i + 1]] - vertices[indices[i]],
66 vertices[indices[i + 2]] - vertices[indices[i]])) < 1e-8f)
67 return failure("Model contains degenerate SDF triangle");
68 } catch (const std::exception&) {
69 return failure("Could not read model mesh for SDF bake");
70 }
72 collider.sdf = MeshSdf::makeFromTriangles(vertices, indices, {resolution, resolution, resolution});
74 });
75 cls.addFunc("volumeEmissionFromModel", [vm](Fluids*, model3d::ModelData* model, int meshIndex, float sx, float sy,
76 float sz, float spacing) {
77 const auto failure = [&](const char* message) {
80 Diagnostic::error(DiagnosticCode::InvalidArgument, message, "fluids.volume.meshDistribution")));
81 };
82 if (!model || meshIndex < 0 || meshIndex >= model->getMeshCount()) return failure("Invalid model mesh slot");
83 const int vertexCount = model->getVertexCount(meshIndex), faceCount = model->getFaceCount(meshIndex);
84 if (vertexCount <= 0 || vertexCount > 1000000 || faceCount <= 0 || faceCount > 65536)
85 return failure("Model exceeds voxelization geometry budget");
86 std::vector<glm::vec3> vertices(size_t(vertexCount), glm::vec3(0.f));
87 std::vector<uint32_t> indices;
88 indices.reserve(size_t(faceCount) * 3);
89 try {
90 for (int i = 0; i < vertexCount; ++i)
91 for (int c = 0; c < 3; ++c) vertices[size_t(i)][c] = model->getVertexPosition(meshIndex, i, c);
92 for (int i = 0; i < faceCount; ++i)
93 for (int corner = 0; corner < 3; ++corner) {
94 const int index = model->getFaceVertexIndex(meshIndex, i, corner);
95 if (index < 0) return failure("Model contains a nontriangular or invalid face");
96 indices.push_back(uint32_t(index));
97 }
98 } catch (const eve::Exception& error) {
99 return failure(error.what());
100 }
102 if (!points) return script::projectStatusResult(vm, points.status());
103 VolumeFluidEmission emission;
105 emission.distribution = std::move(points).takeValue();
107 });
108#endif
109 cls.addFunc("volumeEmissionFromImage", [vm](Fluids*, image::ImageData* image, float pixelScale, float maximumSize,
110 float spacing, float threshold, bool srgb) {
111 if (!image || image->getWidth() <= 0 || image->getHeight() <= 0 || image->getFormat() != "RGBA8" ||
112 uint64_t(image->getWidth()) * image->getHeight() > 16777216 ||
113 uint64_t(image->getWidth()) * image->getHeight() * 4 != image->getSize() || !image->getData())
116 "Expected a valid RGBA8 image of at most 16M pixels",
117 "fluids.volume.imageDistribution")));
118 const unsigned width = unsigned(image->getWidth()), height = unsigned(image->getHeight());
119 const auto* bytes = static_cast<const uint8_t*>(image->getData());
120 std::vector<glm::vec4> pixels(size_t(width) * height);
121 for (unsigned y = 0; y < height; ++y)
122 for (unsigned x = 0; x < width; ++x) {
123 const size_t source = (size_t(height - 1 - y) * width + x) * 4;
124 glm::vec4 color;
125 for (int c = 0; c < 4; ++c) {
126 float value = float(bytes[source + size_t(c)]) / 255.f;
127 if (srgb && c < 3)
128 value = value <= .04045f ? value / 12.92f : std::pow((value + .055f) / 1.055f, 2.4f);
129 color[c] = value;
130 }
131 pixels[size_t(y) * width + x] = color;
132 }
133 auto points =
134 buildVolumeFluidImageDistribution(pixels, width, height, pixelScale, maximumSize, spacing, threshold);
135 if (!points) return script::projectStatusResult(vm, points.status());
136 VolumeFluidEmission emission;
138 emission.distribution = std::move(points).takeValue();
139 emission.prototype.color = glm::vec4(1.f);
141 });
142 cls.addFunc("volumeEmissionFromSphere", [vm](Fluids*, float radius, float spacing, bool surface) {
144 if (!points) return script::projectStatusResult(vm, points.status());
145 VolumeFluidEmission emission;
147 emission.distribution = std::move(points).takeValue();
149 });
150 cls.addFunc("volumeEmissionFromCube", [vm](Fluids*, float sx, float sy, float sz, float spacing, bool surface) {
152 if (!points) return script::projectStatusResult(vm, points.status());
153 VolumeFluidEmission emission;
155 emission.distribution = std::move(points).takeValue();
157 });
158 cls.addFunc("volumeEmissionFromEdge", [vm](Fluids*, float length, float spacing, float radialVelocityDegrees) {
159 auto points = buildVolumeFluidEdgeDistribution(length, spacing, radialVelocityDegrees);
160 if (!points) return script::projectStatusResult(vm, points.status());
161 VolumeFluidEmission emission;
163 emission.distribution = std::move(points).takeValue();
165 });
166 cls.addFunc("volumeEmissionFromDisk", [vm](Fluids*, float radius, float spacing, bool edgeEmission) {
168 if (!points) return script::projectStatusResult(vm, points.status());
169 VolumeFluidEmission emission;
171 emission.distribution = std::move(points).takeValue();
173 });
174 cls.addFunc("volumeEmissionDefaults", [vm](Fluids*) {
176 });
177 cls.addFunc("composeVolumeEmitterShapes", [vm](Fluids*, ssq::Object baseObject, ssq::Object shapesObject) {
178 auto baseValue = script::valueFromSquirrel(baseObject);
179 if (!baseValue) return script::projectStatusResult(vm, baseValue.status());
180 auto base = decodeVolumeFluidEmission(baseValue.value());
181 if (!base) return script::projectStatusResult(vm, base.status());
182 auto shapeValues = script::valueFromSquirrel(shapesObject);
183 if (!shapeValues) return script::projectStatusResult(vm, shapeValues.status());
184 if (!shapeValues.value().isArray() || shapeValues.value().arraySize() > 64)
187 "Emitter shapes must be an array of at most 64 descriptions",
188 "fluids.volume.emitterShapes")));
189 std::vector<VolumeFluidEmission> shapes;
190 shapes.reserve(shapeValues.value().arraySize());
191 for (size_t i = 0; i < shapeValues.value().arraySize(); ++i) {
192 auto shape = decodeVolumeFluidEmission(shapeValues.value().at(i));
193 if (!shape) return script::projectStatusResult(vm, shape.status());
194 shapes.push_back(std::move(shape).takeValue());
195 }
197 vm, composeVolumeFluidEmitterShapes(base.value(), shapes),
198 [](const VolumeFluidEmission& emission) { return encodeVolumeFluidEmission(emission); });
199 });
200 cls.addFunc("volumeFluidEmitterBlueprintDefaults", [vm](Fluids*) {
203 });
204 cls.addFunc("prepareVolumeFluidEmitterBlueprint3D", [vm](Fluids*, ssq::Object object) {
205 auto value = script::valueFromSquirrel(object);
206 if (!value) return script::projectStatusResult(vm, value.status());
207 auto blueprint = decodeVolumeFluidEmitterBlueprint3D(value.value());
208 if (!blueprint) return script::projectStatusResult(vm, blueprint.status());
209 auto prepared = prepareVolumeFluidEmitterBlueprint3D(blueprint.value());
210 if (!prepared) return script::projectStatusResult(vm, prepared.status());
213 });
214 cls.addFunc("volumeGranularEmitterBlueprintDefaults", [vm](Fluids*) {
217 });
218 cls.addFunc("prepareVolumeGranularEmitterBlueprint3D", [vm](Fluids*, ssq::Object object) {
219 auto value = script::valueFromSquirrel(object);
220 if (!value) return script::projectStatusResult(vm, value.status());
221 auto blueprint = decodeVolumeGranularEmitterBlueprint3D(value.value());
222 if (!blueprint) return script::projectStatusResult(vm, blueprint.status());
223 auto prepared = prepareVolumeGranularEmitterBlueprint3D(blueprint.value());
224 if (!prepared) return script::projectStatusResult(vm, prepared.status());
227 });
228 cls.addFunc("volumeEmitterBlueprintMetrics", [vm](Fluids*, float resolution, float restDensity, float smoothing) {
229 auto evaluated = evaluateVolumeFluidEmitterBlueprint3D(resolution, restDensity, smoothing);
230 if (!evaluated) return script::projectStatusResult(vm, evaluated.status());
231 Value metrics(Value::Object{});
232 metrics.set("particleSize", double(evaluated.value().particleSize));
233 metrics.set("particleMass", double(evaluated.value().particleMass));
234 metrics.set("smoothingRadius", double(evaluated.value().smoothingRadius));
235 return script::projectStatusResult(vm, Status::success(), std::move(metrics));
236 });
237 cls.addFunc("volumeDefaults", [vm](Fluids*) {
239 });
240 cls.addFunc("newVolumeSimulator", [vm](Fluids*, ssq::Object object) {
241 auto decoded = read(object);
242 if (!decoded) return script::projectStatusResult(vm, decoded.status());
243 auto created = VolumeFluid::create(decoded.value().settings);
244 if (!created) return script::projectStatusResult(vm, created.status());
245 auto owned = std::move(created).takeValue();
246 auto restored = owned->restore(decoded.value());
247 if (!restored) return script::projectStatusResult(vm, restored.status());
248 auto instance = script::makeOwnedSquirrelInstance<VolumeFluid>(vm, std::move(owned));
249 if (!instance) return script::projectStatusResult(vm, instance.status());
250 auto result = script::projectStatusResult(vm, Status::success(), std::move(instance).takeValue());
251 result.set("ownership", std::string("owned"));
252 return result;
253 });
254}
255
256} // namespace eve::fluids
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
ActiveSource owned
float length
Definition CaveMesh.cpp:94
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
std::string message
std::uint32_t vertexCount
ShaderImageInput shape
vk::UniqueImage image
std::vector< std::uint32_t > indices
std::int32_t c
std::uint32_t height
std::uint32_t width
std::array< float, 3 > scale
std::uint64_t bytes
std::string error
Definition Package.cpp:60
std::vector< Point > vertices
float radius
std::shared_ptr< const std::vector< glm::vec2 > > points
uint8_t * pixels
glm::mat4 model
int created
int spacing
uint32_t index
const UnitySourceAsset & source
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
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
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::map< std::string, Value > Object
Definition Value.h:34
void set(const std::string &key, Value value)
Insert or replace an object member.
Definition Value.cpp:118
Fluids module — factory + script binding.
Definition Fluids.h:203
static MeshSdf makeSphere(const glm::vec3 &center, float radius, const glm::ivec3 &dims)
Bake an analytic sphere into the field.
Definition FluidSdf.cpp:130
static MeshSdf makeFromTriangles(const std::vector< glm::vec3 > &positions, const std::vector< uint32_t > &indices, const glm::ivec3 &dims)
Voxelize a closed triangle mesh.
Definition FluidSdf.cpp:166
static Result< std::unique_ptr< VolumeFluid > > create(const VolumeFluidSettings &settings)
Validates settings before allocating an owning solver; errors publish no state.
Represents raw pixel data.
Definition ImageData.h:38
CPU-side decoded 3D model (Assimp scene owned via medialoader::ModelScene). Does not upload to GPU — ...
Definition ModelData.h:39
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< VolumeFluidDistributionPoint > > buildVolumeFluidEdgeDistribution(float length, float spacing, float radialVelocityDegrees)
Builds Fluid3D's edge lattice with per-sample radial velocity directions.
Result< std::vector< VolumeFluidDistributionPoint > > buildVolumeFluidCubeDistribution(glm::vec3 size, float spacing, bool surface)
Builds an Fluid3D-compatible box surface or volume lattice during setup.
Result< VolumeGranularEmitterBlueprint3D > decodeVolumeGranularEmitterBlueprint3D(const Value &value)
Strictly decodes one owning Fluid3D granular-emitter blueprint value.
Value encodeVolumeFluidEmitterBlueprint3D(const VolumeFluidEmitterBlueprint3D &blueprint)
Projects default or caller-prepared Fluid3D-emitter blueprint values.
Result< std::vector< VolumeFluidDistributionPoint > > buildVolumeFluidDiskDistribution(float radius, float spacing, bool edgeEmission)
Builds Fluid3D's concentric disk or circumference-only distribution.
EVENGINE_API_DOMAINS Result< std::vector< VolumeFluidDistributionPoint > > buildVolumeFluidMeshDistribution(std::span< const glm::vec3 > vertices, std::span< const uint32_t > indices, glm::vec3 scale, float spacing)
Voxelizes indexed triangles, returning surface and enclosed interior cell centers.
Value encodeVolumeFluidWindZone(const VolumeFluidWindZone &zone)
Projects one complete wind-zone description for script construction.
Value encodeVolumeFluidCollider(const VolumeFluidCollider &collider)
Projects an owning collider description for script construction.
Value encodeVolumeFluidHeightFieldCollider(const VolumeFluidHeightFieldCollider &collider)
Projects one complete owning regular height field.
Value encodeVolumeFluidSdfCollider(const VolumeFluidSdfCollider &collider)
Projects one complete owning SDF collider.
Value encodeVolumeFluidSdfPose(const VolumeFluidSdfPose &pose)
Projects one transform-only SDF update for script construction.
Result< VolumeFluidEmission > composeVolumeFluidEmitterShapes(const VolumeFluidEmission &base, std::span< const VolumeFluidEmission > shapes)
Builds one emitter distribution from an ordered set of Fluid3D emitter shapes.
Value encodeVolumeFluidThermalRule(const VolumeFluidThermalRule &rule)
Owning default-rule projection for script construction.
Result< std::vector< VolumeFluidDistributionPoint > > buildVolumeFluidSphereDistribution(float radius, float spacing, bool surface)
Builds an Fluid3D-compatible spherical surface or volume lattice during setup.
Result< VolumeFluidEmission > decodeVolumeFluidEmission(const Value &value)
Strict structural decode; nozzle/material domain validation occurs atomically at emission.
Value encodeVolumeFluidEmitterBlueprintApplication3D(const VolumeFluidEmitterBlueprintApplication3D &application)
Projects the complete native solver/emission setup produced from a blueprint.
Result< std::vector< VolumeFluidDistributionPoint > > buildVolumeFluidImageDistribution(std::span< const glm::vec4 > pixels, unsigned width, unsigned height, float pixelScale, float maximumSize, float spacing, float alphaThreshold)
Precomputes a bilinear alpha-masked image distribution on a spacing lattice.
Value encodeVolumeFluidEmission(const VolumeFluidEmission &emission)
Encodes a version-9 emission description including precomputed points and particle state.
Result< VolumeFluidEmitterBlueprintMetrics > evaluateVolumeFluidEmitterBlueprint3D(float resolution, float restDensity, float smoothing)
Evaluates Fluid3DEmitterBlueprintBase/Fluid3DEmitterBlueprint formulas in 3D.
Result< VolumeFluidEmitterBlueprintApplication3D > prepareVolumeFluidEmitterBlueprint3D(const VolumeFluidEmitterBlueprint3D &blueprint)
Atomically maps every Fluid3DEmitterBlueprint field to native 3D setup values.
Result< VolumeFluidEmitterBlueprintApplication3D > prepareVolumeGranularEmitterBlueprint3D(const VolumeGranularEmitterBlueprint3D &blueprint)
Atomically maps an Fluid3DGranularEmitterBlueprint to owned native 3D setup values.
Value encodeVolumeGranularEmitterBlueprint3D(const VolumeGranularEmitterBlueprint3D &blueprint)
Projects default or caller-prepared Fluid3D granular-emitter blueprint values.
void exposeVolumeFluidFactory(ssq::Class &cls)
Registers volume-fluid factories on the existing Fluids class.
Result< VolumeFluidEmitterBlueprint3D > decodeVolumeFluidEmitterBlueprint3D(const Value &value)
Strictly decodes one owning Fluid3D-emitter blueprint value.
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.
Owned obstacle sample; the caller updates moving obstacles before stepping.
Value-owned emission description; +Z is mapped to direction.
VolumeFluidParticle prototype
Material/color/data/lifetime copied to emitted particles; position/velocity are generated.
std::vector< VolumeFluidDistributionPoint > distribution
Precomputed shape points, used only by Distribution; maximum 4096, copied by value.
Owning Fluid3DEmitterBlueprint values used to prepare a native 3D emitter.
Owned regular-grid terrain collider matching Fluid3D height-field sample layout.
std::vector< float > heights
Row-major samples indexed as z * resolution.x + x, in normalized [0,1] height units.
glm::vec4 color
Linear RGBA color and four diffusing application channels.
Owned sampled mesh/SDF collider with a uniform world transform.
MeshSdf sdf
Local signed field; negative values represent mesh solid.
Transient transform update for one owned SDF collider, identified by stable label.
Canonical version-20 owning state; older codecs migrate collider, attachment, stitch,...
One-step contact transfer into user channels x/y; negative rate heats, positive cools.
Owning description of one transient ambient or spherical wind zone.
Owning Fluid3DGranularEmitterBlueprint values used to prepare a native 3D emitter.
glm::vec4 color