载入中...
搜索中...
未找到
Destruction.cpp
浏览该文件的文档.
2
3#include "common/Exception.h"
4#include "common/Json.h"
5#include "common/Value.h"
6#include "physics/World3D.h"
7
8#include <simplesquirrel/simplesquirrel.hpp>
9
10#include <functional>
11#include <utility>
12
13namespace eve::physics {
14
16
22
24 auto parsed = eve::Value::fromJson(json);
25 if (!parsed) return eve::Result<GeometryCollectionAsset>::failure(parsed.status());
26 return GeometryCollectionAsset::fromValue(parsed.value());
27}
28
32
36
38 World3D* world, const GeometryCollectionAsset& asset, float originX, float originY, float originZ) {
39 if (!world)
41 eve::DiagnosticCode::InvalidArgument, "geometry-collection create requires a World3D", "world"));
42 return GeometryCollectionInstance::create(*world, asset, originX, originY, originZ);
43}
44
46 auto created = makeWeldedBoxesFixture(strainThreshold);
47 if (!created.ok()) {
48 const auto* diagnostic = created.status().primaryDiagnostic();
49 throw Exception("Destruction.newWeldedBoxesFixture: %s",
50 diagnostic ? diagnostic->message().c_str() : "creation failed");
51 }
52 return new GeometryCollectionAsset(std::move(created.value()));
53}
54
56 auto created = makeClusterPillarFixture(interClusterStrain);
57 if (!created.ok()) {
58 const auto* diagnostic = created.status().primaryDiagnostic();
59 throw Exception("Destruction.newClusterPillarFixture: %s",
60 diagnostic ? diagnostic->message().c_str() : "creation failed");
61 }
62 return new GeometryCollectionAsset(std::move(created.value()));
63}
64
66 auto decoded = assetFromJson(json);
67 if (!decoded.ok()) {
68 const auto* diagnostic = decoded.status().primaryDiagnostic();
69 throw Exception("Destruction.assetFromJson: %s",
70 diagnostic ? diagnostic->message().c_str() : "decode failed");
71 }
72 return new GeometryCollectionAsset(std::move(decoded.value()));
73}
74
76 float originX, float originY, float originZ) {
77 if (!asset) throw Exception("Destruction.createInstance: asset is null");
78 auto created = createInstance(world, *asset, originX, originY, originZ);
79 if (!created.ok()) {
80 const auto* diagnostic = created.status().primaryDiagnostic();
81 throw Exception("Destruction.createInstance: %s",
82 diagnostic ? diagnostic->message().c_str() : "creation failed");
83 }
84 return created.value().release();
85}
86
87namespace {
88
89DestructionField makeStrainField(float x, float y, float z, float radius, float magnitude) {
90 DestructionField field;
92 field.falloff = DestructionFieldFalloff::Linear;
93 field.centerX = x;
94 field.centerY = y;
95 field.centerZ = z;
96 field.radius = radius;
97 field.magnitude = magnitude;
98 return field;
99}
100
101DestructionField makeAnchorField(float x, float y, float z, float radius) {
102 DestructionField field;
103 field.kind = DestructionFieldKind::Anchor;
104 field.falloff = DestructionFieldFalloff::None;
105 field.centerX = x;
106 field.centerY = y;
107 field.centerZ = z;
108 field.radius = radius;
109 field.magnitude = 1.f;
110 return field;
111}
112
113DestructionField makeSleepField(float x, float y, float z, float radius) {
114 DestructionField field;
115 field.kind = DestructionFieldKind::Sleep;
116 field.falloff = DestructionFieldFalloff::None;
117 field.centerX = x;
118 field.centerY = y;
119 field.centerZ = z;
120 field.radius = radius;
121 field.magnitude = 1.f;
122 return field;
123}
124
125DestructionField makeImpulseField(float x, float y, float z, float radius, float magnitude, float dirX, float dirY,
126 float dirZ) {
127 DestructionField field;
130 field.centerX = x;
131 field.centerY = y;
132 field.centerZ = z;
133 field.radius = radius;
134 field.magnitude = magnitude;
135 field.dirX = dirX;
136 field.dirY = dirY;
137 field.dirZ = dirZ;
138 return field;
139}
140
141void applyStrainFieldScript(GeometryCollectionInstance* self, float x, float y, float z, float radius,
142 float magnitude) {
143 if (!self) throw Exception("GeometryCollectionInstance.applyStrainField: null");
144 auto result = self->applyField(makeStrainField(x, y, z, radius, magnitude));
145 if (!result.ok()) {
146 const auto* diagnostic = result.status().primaryDiagnostic();
147 throw Exception("GeometryCollectionInstance.applyStrainField: %s",
148 diagnostic ? diagnostic->message().c_str() : "failed");
149 }
150}
151
152void applyAnchorFieldScript(GeometryCollectionInstance* self, float x, float y, float z, float radius) {
153 if (!self) throw Exception("GeometryCollectionInstance.applyAnchorField: null");
154 auto result = self->applyField(makeAnchorField(x, y, z, radius));
155 if (!result.ok()) {
156 const auto* diagnostic = result.status().primaryDiagnostic();
157 throw Exception("GeometryCollectionInstance.applyAnchorField: %s",
158 diagnostic ? diagnostic->message().c_str() : "failed");
159 }
160}
161
162void applySleepFieldScript(GeometryCollectionInstance* self, float x, float y, float z, float radius) {
163 if (!self) throw Exception("GeometryCollectionInstance.applySleepField: null");
164 auto result = self->applyField(makeSleepField(x, y, z, radius));
165 if (!result.ok()) {
166 const auto* diagnostic = result.status().primaryDiagnostic();
167 throw Exception("GeometryCollectionInstance.applySleepField: %s",
168 diagnostic ? diagnostic->message().c_str() : "failed");
169 }
170}
171
172void applyImpulseFieldScript(GeometryCollectionInstance* self, float x, float y, float z, float radius,
173 float magnitude, float dirX, float dirY, float dirZ) {
174 if (!self) throw Exception("GeometryCollectionInstance.applyImpulseField: null");
175 auto result = self->applyField(makeImpulseField(x, y, z, radius, magnitude, dirX, dirY, dirZ));
176 if (!result.ok()) {
177 const auto* diagnostic = result.status().primaryDiagnostic();
178 throw Exception("GeometryCollectionInstance.applyImpulseField: %s",
179 diagnostic ? diagnostic->message().c_str() : "failed");
180 }
181}
182
183void stepScript(GeometryCollectionInstance* self, int tick, float dtSeconds) {
184 if (!self) throw Exception("GeometryCollectionInstance.step: null");
185 auto delta = eve::Duration::fromSeconds(dtSeconds);
186 if (!delta.ok()) throw Exception("GeometryCollectionInstance.step: invalid dt");
187 eve::SimulationStep step{eve::SimulationTick{static_cast<std::uint64_t>(tick)}, delta.value()};
188 auto result = self->step(step);
189 if (!result.ok()) {
190 const auto* diagnostic = result.status().primaryDiagnostic();
191 throw Exception("GeometryCollectionInstance.step: %s",
192 diagnostic ? diagnostic->message().c_str() : "failed");
193 }
194}
195
196int boneStateScript(GeometryCollectionInstance* self, int boneIndex) {
197 if (!self) throw Exception("GeometryCollectionInstance.boneState: null");
198 return static_cast<int>(self->boneState(boneIndex));
199}
200
201void setStepBudgetScript(GeometryCollectionInstance* self, int maxEdgeBreaks, int maxSleeps) {
202 if (!self) throw Exception("GeometryCollectionInstance.setStepBudget: null");
203 if (maxEdgeBreaks < 0 || maxSleeps < 0)
204 throw Exception("GeometryCollectionInstance.setStepBudget: budgets must be non-negative");
205 DestructionStepBudget budget;
206 budget.maxEdgeBreaksPerStep = maxEdgeBreaks;
207 budget.maxSleepsPerStep = maxSleeps;
208 self->setStepBudget(budget);
209}
210
211std::string captureSnapshotJsonScript(GeometryCollectionInstance* self) {
212 if (!self) throw Exception("GeometryCollectionInstance.captureSnapshotJson: null");
213 auto snapshot = self->captureSnapshot();
214 if (!snapshot.ok()) {
215 const auto* diagnostic = snapshot.status().primaryDiagnostic();
216 throw Exception("GeometryCollectionInstance.captureSnapshotJson: %s",
217 diagnostic ? diagnostic->message().c_str() : "failed");
218 }
219 auto encoded = snapshot.value().toValue();
220 if (!encoded.ok()) {
221 const auto* diagnostic = encoded.status().primaryDiagnostic();
222 throw Exception("GeometryCollectionInstance.captureSnapshotJson: %s",
223 diagnostic ? diagnostic->message().c_str() : "encode failed");
224 }
225 auto json = encoded.value().toJson();
226 if (!json.ok()) {
227 const auto* diagnostic = json.status().primaryDiagnostic();
228 throw Exception("GeometryCollectionInstance.captureSnapshotJson: %s",
229 diagnostic ? diagnostic->message().c_str() : "json failed");
230 }
231 return json.value();
232}
233
234void restoreSnapshotJsonScript(GeometryCollectionInstance* self, const std::string& json) {
235 if (!self) throw Exception("GeometryCollectionInstance.restoreSnapshotJson: null");
236 auto parsed = eve::Value::fromJson(json);
237 if (!parsed.ok()) {
238 const auto* diagnostic = parsed.status().primaryDiagnostic();
239 throw Exception("GeometryCollectionInstance.restoreSnapshotJson: %s",
240 diagnostic ? diagnostic->message().c_str() : "parse failed");
241 }
242 auto snapshot = GeometryCollectionInstanceSnapshot::fromValue(parsed.value());
243 if (!snapshot.ok()) {
244 const auto* diagnostic = snapshot.status().primaryDiagnostic();
245 throw Exception("GeometryCollectionInstance.restoreSnapshotJson: %s",
246 diagnostic ? diagnostic->message().c_str() : "decode failed");
247 }
248 auto restored = self->restoreSnapshot(snapshot.value());
249 if (!restored.ok()) {
250 const auto* diagnostic = restored.status().primaryDiagnostic();
251 throw Exception("GeometryCollectionInstance.restoreSnapshotJson: %s",
252 diagnostic ? diagnostic->message().c_str() : "restore failed");
253 }
254}
255
256} // namespace
257
258void Destruction::expose(ssq::Table& table) {
259 auto cls = table.addClass(name, Destruction::create, false);
260 expose(cls);
261
262 auto asset = table.addClass<GeometryCollectionAsset>(
263 "GeometryCollectionAsset", std::function<GeometryCollectionAsset*()>([]() -> GeometryCollectionAsset* {
264 return nullptr;
265 }),
266 true);
267 (void)asset;
268
269 auto instance = table.addClass<GeometryCollectionInstance>(
270 "GeometryCollectionInstance",
271 std::function<GeometryCollectionInstance*()>([]() -> GeometryCollectionInstance* { return nullptr; }), true);
272 instance.addFunc("applyStrainField", applyStrainFieldScript);
273 instance.addFunc("applyAnchorField", applyAnchorFieldScript);
274 instance.addFunc("applySleepField", applySleepFieldScript);
275 instance.addFunc("applyImpulseField", applyImpulseFieldScript);
276 instance.addFunc("step", stepScript);
277 instance.addFunc("setStepBudget", setStepBudgetScript);
278 instance.addFunc("boneCount", [](GeometryCollectionInstance* self) { return self->boneCount(); });
279 instance.addFunc("edgeCount", [](GeometryCollectionInstance* self) { return self->edgeCount(); });
280 instance.addFunc("boneState", boneStateScript);
281 instance.addFunc("boneClusterId", [](GeometryCollectionInstance* self, int boneIndex) {
282 return self->boneClusterId(boneIndex);
283 });
284 instance.addFunc("edgeStrain", [](GeometryCollectionInstance* self, int edgeIndex) {
285 return self->edgeStrain(edgeIndex);
286 });
287 instance.addFunc("isEdgeBroken", [](GeometryCollectionInstance* self, int edgeIndex) {
288 return self->isEdgeBroken(edgeIndex);
289 });
290 instance.addFunc("detachEventCount", [](GeometryCollectionInstance* self) {
291 return self->detachEventCount();
292 });
293 instance.addFunc("clusterBreakEventCount", [](GeometryCollectionInstance* self) {
294 return self->clusterBreakEventCount();
295 });
296 instance.addFunc("pendingEdgeBreakCount", [](GeometryCollectionInstance* self) {
297 return self->pendingEdgeBreakCount();
298 });
299 instance.addFunc("hasLiveWorld", [](GeometryCollectionInstance* self) { return self->hasLiveWorld(); });
300 instance.addFunc("sleepBatchRevision", [](GeometryCollectionInstance* self) {
301 return static_cast<int>(self->sleepBatchRevision());
302 });
303 instance.addFunc("captureSnapshotJson", captureSnapshotJsonScript);
304 instance.addFunc("restoreSnapshotJson", restoreSnapshotJsonScript);
305 instance.addFunc("releaseBodies", [](GeometryCollectionInstance* self) { self->releaseBodies(); });
306}
307
308void Destruction::expose(ssq::Class& cls) {
309 cls.addFunc("registerGeometryCollectionSchema", [](Destruction* self) {
310 auto result = self->registerGeometryCollectionSchema();
311 if (!result.ok()) {
312 const auto* diagnostic = result.status().primaryDiagnostic();
313 throw Exception("Destruction.registerGeometryCollectionSchema: %s",
314 diagnostic ? diagnostic->message().c_str() : "failed");
315 }
316 });
317 cls.addFunc("newWeldedBoxesFixture", &Destruction::newWeldedBoxesFixtureScript);
318 cls.addFunc("newClusterPillarFixture", &Destruction::newClusterPillarFixtureScript);
319 cls.addFunc("assetFromJson", &Destruction::assetFromJsonScript);
320 cls.addFunc("createInstance", &Destruction::createInstanceScript);
321}
322
323} // namespace eve::physics
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
HSQOBJECT cls
Definition ECS.cpp:21
std::string name
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
World3D * world
float radius
int created
SimulationTick tick
float step
Definition TreeMesh.cpp:314
Owning, renderer-independent dynamic values.
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
static Result< Duration > fromSeconds(double seconds)
Convert finite seconds to the nearest nanosecond.
Definition Time.cpp:16
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Result< Value > fromJson(std::string_view json)
Parse one strict JSON value into an owning Value.
Definition Value.cpp:57
constexpr std::uint64_t value() const noexcept
Returns the underlying value at an explicit protocol boundary.
Optional physics satellite for Chaos-style geometry-collection destruction.
Definition Destruction.h:22
GeometryCollectionInstance * createInstanceScript(World3D *world, GeometryCollectionAsset *asset, float originX, float originY, float originZ)
Script facade: create instance or throw; VM owns the pointer.
GeometryCollectionAsset * newClusterPillarFixtureScript(float interClusterStrain)
Script facade: cluster pillar fixture or throw.
eve::Result< GeometryCollectionAsset > makeWeldedBoxesFixture(float strainThreshold=1.f)
Build the two-box welded fixture used by demos and tests.
GeometryCollectionAsset * assetFromJsonScript(const std::string &json)
Script facade: decode asset JSON or throw; VM owns the pointer.
eve::Result< std::unique_ptr< GeometryCollectionInstance > > createInstance(World3D *world, const GeometryCollectionAsset &asset, float originX, float originY, float originZ)
Instantiate a collection against a live World3D. @ownership Caller owns the returned instance.
GeometryCollectionAsset * newWeldedBoxesFixtureScript(float strainThreshold)
Script facade: welded boxes fixture or throw.
eve::Result< GeometryCollectionAsset > makeClusterPillarFixture(float interClusterStrain=0.8f)
Build the two-cluster anchored pillar fixture used by demos.
eve::Result< void > registerGeometryCollectionSchema()
Registers asset physics:geometry-collection@2 and instance snapshot physics:geometry-collection-insta...
eve::Result< GeometryCollectionAsset > assetFromJson(const std::string &json)
Decode a versioned asset document.
Runtime owner of bone state, connection strain, and PhysicsLink bindings.
static eve::Result< std::unique_ptr< GeometryCollectionInstance > > create(World3D &world, const GeometryCollectionAsset &asset, float originX, float originY, float originZ)
Create bodies for every bone and bind PhysicsLinks.
Box3D rigid-body world. Script coordinates are meters (Box3D native), unlike 2D World which uses pixe...
Definition World3D.h:63
Optional physics backend for vehicle mobility and body attach.
Definition Climbing.h:36
const EditorValue * field(const EditorValue &value, const char *name)
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
One deterministic fixed-step emitted by SimulationClock.
Definition Time.h:158
One volumetric influence applied to a geometry-collection instance.
Immutable cooked/authored geometry-collection asset.
static eve::Result< void > ensureSchemaRegistered()
Idempotently register version 2 in the process schema registry.
static eve::Result< GeometryCollectionAsset > fromValue(const eve::Value &value)
Decode and validate version 2, or migrate version 1 (cluster fields = 0).
static eve::Result< GeometryCollectionAsset > makeWeldedBoxesFixture(float strainThreshold=1.f)
Build a two-box welded fixture used by tests and the smoke example.
static eve::Result< GeometryCollectionAsset > makeClusterPillarFixture(float interClusterStrain=0.8f)
Build a 2×3 anchored pillar with two cook clusters for demos.
static eve::Result< void > ensureSchemaRegistered()
Idempotently register version 1 in the process schema registry.
static eve::Result< GeometryCollectionInstanceSnapshot > fromValue(const eve::Value &value)
Decode and validate version 1 transactionally.