载入中...
搜索中...
未找到
Rope.cpp
浏览该文件的文档.
1#include "physics/rope/Rope.h"
2
3#include "common/Exception.h"
4#include "common/Json.h"
7
8#include <simplesquirrel/simplesquirrel.hpp>
9
10#include <cstdint>
11#include <functional>
12#include <memory>
13#include <optional>
14#include <utility>
15
16namespace eve::physics {
17namespace {
18
19constexpr auto kRopeCreateSchemaId = "physics:rope3d-create";
20
22 std::optional<double> minimum = {}, std::optional<double> maximum = {},
23 std::string defaultJson = {}) {
25 field.name = std::move(name);
26 field.type = type;
27 field.required = required;
28 field.minimum = minimum;
29 field.maximum = maximum;
30 field.defaultJson = std::move(defaultJson);
31 return field;
32}
33
34eve::schema::SchemaDefinition ropeCreateSchema() {
37 schema.id = kRopeCreateSchemaId;
38 schema.version = 1;
39 schema.title = "3D Rope Creation Parameters";
40 schema.description = "Validated construction and initial solver settings for Rope3D.";
41 schema.additionalProperties = false;
42 schema.fields = {
43 ropeField("particleCount", ValueType::Integer, true, 2, 100000),
44 ropeField("startX", ValueType::Number, true),
45 ropeField("startY", ValueType::Number, true),
46 ropeField("startZ", ValueType::Number, true),
47 ropeField("endX", ValueType::Number, true),
48 ropeField("endY", ValueType::Number, true),
49 ropeField("endZ", ValueType::Number, true),
50 ropeField("gravityX", ValueType::Number, false, {}, {}, "0"),
51 ropeField("gravityY", ValueType::Number, false, {}, {}, "-9.81"),
52 ropeField("gravityZ", ValueType::Number, false, {}, {}, "0"),
53 ropeField("stretchCompliance", ValueType::Number, false, 0, {}, "0"),
54 ropeField("distanceConstraintsEnabled", ValueType::Boolean, false, {}, {}, "true"),
55 ropeField("bendCompliance", ValueType::Number, false, 0, {}, "0.002"),
56 ropeField("bendConstraintsEnabled", ValueType::Boolean, false, {}, {}, "true"),
57 ropeField("maxBending", ValueType::Number, false, 0, 0.5, "0.025"),
58 ropeField("plasticYield", ValueType::Number, false, 0, 0.5, "0"),
59 ropeField("plasticCreep", ValueType::Number, false, 0, {}, "0"),
60 ropeField("maxCompression", ValueType::Number, false, 0, 1, "0"),
61 ropeField("damping", ValueType::Number, false, 0, 1, "0.01"),
62 ropeField("particleMass", ValueType::Number, false, 0.000001, {}, "0.1"),
63 ropeField("radius", ValueType::Number, false, 0.000001, {}, "0.04"),
64 ropeField("collisionFriction", ValueType::Number, false, 0, 1, "0"),
65 ropeField("collisionRestitution", ValueType::Number, false, 0, 1, "0"),
66 ropeField("continuousCollision", ValueType::Boolean, false, {}, {}, "true"),
67 ropeField("selfCollision", ValueType::Boolean, false, {}, {}, "true"),
68 ropeField("pinStart", ValueType::Boolean, false, {}, {}, "false"),
69 ropeField("pinEnd", ValueType::Boolean, false, {}, {}, "false"),
70 ropeField("tearingEnabled", ValueType::Boolean, false, {}, {}, "false"),
71 ropeField("tearResistance", ValueType::Number, false, 0.000001, {}, "1000"),
72 ropeField("maxTearsPerStep", ValueType::Integer, false, 1, 1000, "1"),
73 };
74 return schema;
75}
76
77eve::Result<void> ensureRopeCreateSchema() {
78 if (eve::schema::SchemaRegistry::resolve(kRopeCreateSchemaId, 1)) return eve::Result<void>::success();
79 auto registration = eve::schema::SchemaRegistry::registerVersioned(ropeCreateSchema());
80 if (!registration.ok()) return eve::Result<void>::failure(registration.status());
82}
83
84bool ropeChangeOrThrow(eve::Result<RopeTopologyChange> result, const char* operation) {
85 if (result.ok()) return result.value() == RopeTopologyChange::Changed;
86 const auto* diagnostic = result.status().primaryDiagnostic();
87 throw Exception("Rope3D.%s: %s", operation, diagnostic ? diagnostic->message().c_str() : "operation failed");
88}
89
90bool ropeColliderChangeOrThrow(eve::Result<RopeColliderChange> result, const char* operation) {
91 if (result.ok()) return result.value() == RopeColliderChange::Changed;
92 const auto* diagnostic = result.status().primaryDiagnostic();
93 throw Exception("Rope3D.%s: %s", operation, diagnostic ? diagnostic->message().c_str() : "operation failed");
94}
95
96std::int64_t ropeColliderIdOrThrow(eve::Result<RopeColliderId> result, const char* operation) {
97 if (result.ok()) return static_cast<std::int64_t>(result.value().value);
98 const auto* diagnostic = result.status().primaryDiagnostic();
99 throw Exception("Rope3D.%s: %s", operation, diagnostic ? diagnostic->message().c_str() : "operation failed");
100}
101
102} // namespace
103
105
106Rope3D* Rope::newRope3D(int count, float sx, float sy, float sz, float ex, float ey, float ez) {
107 return new Rope3D(count, sx, sy, sz, ex, ey, ez);
108}
109
110eve::Result<void> Rope::registerRope3DCreateSchema() { return ensureRopeCreateSchema(); }
111
113 auto registered = registerRope3DCreateSchema();
114 if (!registered.ok()) return eve::Result<Rope3D*>::failure(registered.status());
115 const auto errors = eve::schema::SchemaRegistry::validate(kRopeCreateSchemaId, 1, json);
116 if (!errors.empty())
118 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, errors.front().message, errors.front().path,
119 eve::DiagnosticDetails{{"schemaId", kRopeCreateSchemaId}, {"schemaVersion", "1"}},
120 "physics.rope3d.create-schema"));
121 std::string parseError;
122 auto document = eve::json::Document::parse(json, &parseError);
123 if (!document.valid())
126 eve::DiagnosticDetails{{"schemaId", kRopeCreateSchemaId}, {"schemaVersion", "1"}},
127 "physics.rope3d.create-schema"));
128 const auto root = document.root();
129 try {
130 auto rope = std::make_unique<Rope3D>(root.getInt("particleCount"), root.getFloat("startX"),
131 root.getFloat("startY"), root.getFloat("startZ"), root.getFloat("endX"),
132 root.getFloat("endY"), root.getFloat("endZ"));
133 rope->setGravity(root.getFloat("gravityX", 0.f), root.getFloat("gravityY", -9.81f),
134 root.getFloat("gravityZ", 0.f));
135 rope->setStretchCompliance(root.getFloat("stretchCompliance", 0.f));
136 rope->setDistanceConstraintsEnabled(root.getBool("distanceConstraintsEnabled", true));
137 rope->setBendCompliance(root.getFloat("bendCompliance", 0.002f));
138 rope->setBendConstraintsEnabled(root.getBool("bendConstraintsEnabled", true));
139 rope->setMaxBending(root.getFloat("maxBending", 0.025f));
140 rope->setPlasticity(root.getFloat("plasticYield", 0.f), root.getFloat("plasticCreep", 0.f));
141 rope->setMaxCompression(root.getFloat("maxCompression", 0.f));
142 rope->setDamping(root.getFloat("damping", 0.01f));
143 rope->setParticleMass(root.getFloat("particleMass", 0.1f));
144 rope->setRadius(root.getFloat("radius", 0.04f));
145 rope->setCollisionFriction(root.getFloat("collisionFriction", 0.f));
146 rope->setCollisionRestitution(root.getFloat("collisionRestitution", 0.f));
147 rope->setContinuousCollision(root.getBool("continuousCollision", true));
148 rope->setSelfCollision(root.getBool("selfCollision", true));
149 if (root.getBool("pinStart", false)) {
150 auto pinned = rope->pin(0);
151 if (!pinned.ok()) return eve::Result<Rope3D*>::failure(pinned.status());
152 }
153 if (root.getBool("pinEnd", false)) {
154 auto pinned = rope->pin(rope->getParticleCount() - 1);
155 if (!pinned.ok()) return eve::Result<Rope3D*>::failure(pinned.status());
156 }
157 if (root.getBool("tearingEnabled", false))
158 rope->setTearing(root.getFloat("tearResistance", 1000.f), root.getInt("maxTearsPerStep", 1));
160 } catch (const std::exception& exception) {
163 eve::DiagnosticDetails{{"schemaId", kRopeCreateSchemaId}, {"schemaVersion", "1"}},
164 "physics.rope3d.create-schema"));
165 }
166}
167
168Rope3D* Rope::newRope3DFromJsonScript(const std::string& json) {
169 auto created = newRope3DFromJson(json);
170 if (created.ok()) return created.value();
171 const auto* diagnostic = created.status().primaryDiagnostic();
172 throw Exception("Rope.newRope3DFromJson: %s", diagnostic ? diagnostic->message().c_str() : "creation failed");
173}
174
175void Rope::expose(ssq::Table& table) {
176 auto cls = table.addClass(name, Rope::create, false);
177 expose(cls);
178 auto rope = table.addClass<Rope3D>("Rope3D", std::function<Rope3D*()>([]() -> Rope3D* { return nullptr; }), true);
179 rope.addFunc("update", &Rope3D::update);
180 rope.addFunc("setGravity", &Rope3D::setGravity);
181 rope.addFunc("getGravityX", &Rope3D::getGravityX);
182 rope.addFunc("getGravityY", &Rope3D::getGravityY);
183 rope.addFunc("getGravityZ", &Rope3D::getGravityZ);
184 rope.addFunc("setStretchCompliance", &Rope3D::setStretchCompliance);
185 rope.addFunc("setDistanceConstraintsEnabled", &Rope3D::setDistanceConstraintsEnabled);
186 rope.addFunc("getDistanceConstraintsEnabled", [](Rope3D* self) { return self->getDistanceConstraintsEnabled(); });
187 rope.addFunc("setBendCompliance", &Rope3D::setBendCompliance);
188 rope.addFunc("setBendConstraintsEnabled", &Rope3D::setBendConstraintsEnabled);
189 rope.addFunc("getBendConstraintsEnabled", [](Rope3D* self) { return self->getBendConstraintsEnabled(); });
190 rope.addFunc("setMaxBending", &Rope3D::setMaxBending);
191 rope.addFunc("getMaxBending", [](Rope3D* self) { return self->getMaxBending(); });
192 rope.addFunc("setPlasticity", &Rope3D::setPlasticity);
193 rope.addFunc("getPlasticYield", [](Rope3D* self) { return self->getPlasticYield(); });
194 rope.addFunc("getPlasticCreep", [](Rope3D* self) { return self->getPlasticCreep(); });
195 rope.addFunc("getBendPlasticity", &Rope3D::getBendPlasticity);
196 rope.addFunc("setMaxCompression", &Rope3D::setMaxCompression);
197 rope.addFunc("setDamping", &Rope3D::setDamping);
198 rope.addFunc("getDamping", &Rope3D::getDamping);
199 rope.addFunc("setParticleMass", &Rope3D::setParticleMass);
200 rope.addFunc("getParticleMass", &Rope3D::getParticleMass);
201 rope.addFunc("setRadius", &Rope3D::setRadius);
202 rope.addFunc("getRadius", &Rope3D::getRadius);
203 rope.addFunc("setCollisionFriction", &Rope3D::setCollisionFriction);
204 rope.addFunc("getCollisionFriction", [](Rope3D* self) { return self->getCollisionFriction(); });
205 rope.addFunc("setCollisionRestitution", &Rope3D::setCollisionRestitution);
206 rope.addFunc("getCollisionRestitution", [](Rope3D* self) { return self->getCollisionRestitution(); });
207 rope.addFunc("setContinuousCollision", &Rope3D::setContinuousCollision);
208 rope.addFunc("getContinuousCollision", [](Rope3D* self) { return self->getContinuousCollision(); });
209 rope.addFunc("setCollideWorld", &Rope3D::setCollideWorld);
210 rope.addFunc("getCollideWorld", [](Rope3D* self) { return self->getCollideWorld(); });
211 rope.addFunc("setCollideSdf", &Rope3D::setCollideSdf);
212 rope.addFunc("getCollideSdf", [](Rope3D* self) { return self->getCollideSdf(); });
213 rope.addFunc("clearColliders", &Rope3D::clearColliders);
214 rope.addFunc("setSelfCollision", &Rope3D::setSelfCollision);
215 rope.addFunc("getSelfCollision", &Rope3D::getSelfCollision);
216 rope.addFunc("setBounds", &Rope3D::setBounds);
217 rope.addFunc("clearBounds", &Rope3D::clearBounds);
218 rope.addFunc("isAttached", &Rope3D::isAttached);
219 rope.addFunc("applyForce", &Rope3D::applyForce);
220 rope.addFunc("getRestLength", &Rope3D::getRestLength);
221 rope.addFunc("calculateLength", &Rope3D::calculateLength);
222 rope.addFunc("isElementActive", &Rope3D::isElementActive);
223 rope.addFunc("getElementForce", &Rope3D::getElementForce);
224 rope.addFunc("getTopologyRevision", [](Rope3D* self) { return self->getTopologyRevision(); });
225 rope.addFunc("getLastTornElementCount", [](Rope3D* self) { return self->getLastTornElementCount(); });
226 rope.addFunc("getLastTornElement", &Rope3D::getLastTornElement);
227 rope.addFunc("setTearing", &Rope3D::setTearing);
228 rope.addFunc("disableTearing", &Rope3D::disableTearing);
229 rope.addFunc("getParticleCount", &Rope3D::getParticleCount);
230 rope.addFunc("getParticleX", &Rope3D::getParticleX);
231 rope.addFunc("getParticleY", &Rope3D::getParticleY);
232 rope.addFunc("getParticleZ", &Rope3D::getParticleZ);
233 rope.addFunc("getSampleX", &Rope3D::getSampleX);
234 rope.addFunc("getSampleY", &Rope3D::getSampleY);
235 rope.addFunc("getSampleZ", &Rope3D::getSampleZ);
236 rope.addFunc("getSampleTangentX", &Rope3D::getSampleTangentX);
237 rope.addFunc("getSampleTangentY", &Rope3D::getSampleTangentY);
238 rope.addFunc("getSampleTangentZ", &Rope3D::getSampleTangentZ);
239 rope.addFunc("addSphereCollider", [](Rope3D* self, float x, float y, float z, float radius) {
240 return ropeColliderIdOrThrow(self->addSphereCollider(x, y, z, radius), "addSphereCollider");
241 });
242 rope.addFunc("addPlaneCollider", [](Rope3D* self, float x, float y, float z, float nx, float ny, float nz) {
243 return ropeColliderIdOrThrow(self->addPlaneCollider(x, y, z, nx, ny, nz), "addPlaneCollider");
244 });
245 rope.addFunc("moveSphereCollider", [](Rope3D* self, std::int64_t id, float x, float y, float z) {
246 return ropeColliderChangeOrThrow(
247 self->moveSphereCollider(RopeColliderId{static_cast<std::uint64_t>(id)}, x, y, z), "moveSphereCollider");
248 });
249 rope.addFunc("removeCollider", [](Rope3D* self, std::int64_t id) {
250 return ropeColliderChangeOrThrow(self->removeCollider(RopeColliderId{static_cast<std::uint64_t>(id)}),
251 "removeCollider");
252 });
253 rope.addFunc("pin", [](Rope3D* self, int index) { return ropeChangeOrThrow(self->pin(index), "pin"); });
254 rope.addFunc("attach", [](Rope3D* self, int index, float x, float y, float z) {
255 return ropeChangeOrThrow(self->attach(index, x, y, z), "attach");
256 });
257 rope.addFunc("moveAttachment", [](Rope3D* self, int index, float x, float y, float z) {
258 return ropeChangeOrThrow(self->moveAttachment(index, x, y, z), "moveAttachment");
259 });
260 rope.addFunc("detach", [](Rope3D* self, int index) { return ropeChangeOrThrow(self->detach(index), "detach"); });
261 rope.addFunc("cut", [](Rope3D* self, int index) { return ropeChangeOrThrow(self->cut(index), "cut"); });
262 rope.addFunc("repair", [](Rope3D* self, int index) { return ropeChangeOrThrow(self->repair(index), "repair"); });
263 rope.addFunc("setRestLength", [](Rope3D* self, float length) {
264 return ropeChangeOrThrow(self->setRestLength(length), "setRestLength");
265 });
266 rope.addFunc("changeLength", [](Rope3D* self, float length, float spacing, bool fromEnd) {
267 return ropeChangeOrThrow(self->changeLength(length, spacing, fromEnd), "changeLength");
268 });
269}
270
271void Rope::expose(ssq::Class& cls) {
272 cls.addFunc("getName", &Rope::getName);
273 cls.addFunc("newRope3D", &Rope::newRope3D);
274 cls.addFunc("newRope3DFromJson", &Rope::newRope3DFromJsonScript);
275}
276
277} // namespace eve::physics
ActionParameterOperation operation
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int root
Definition AnimSmr.cpp:119
float length
Definition CaveMesh.cpp:94
float nx
float nz
float ny
HSQOBJECT cls
Definition ECS.cpp:21
float maximum[3]
float minimum[3]
eve::ResourcePin pinned
bool required
std::string name
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
float radius
int created
std::uint32_t count
int spacing
uint32_t index
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
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
const T & value() const &
Borrow the value from a const lvalue after checking success.
Definition Result.h:308
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:255
const Status & status() const noexcept
Inspect the structured operation status.
Definition Result.h:269
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
const Diagnostic * primaryDiagnostic() const noexcept
First diagnostic, or null when no diagnostic was supplied.
Definition Status.h:135
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
Type type() const
Return the active value type.
static Document parse(const std::string &text, std::string *error=nullptr)
Parse.
Definition Json.cpp:581
Renderer-independent particle rope with XPBD stretch and bend constraints.
Definition Rope3D.h:38
Optional physics satellite that owns rope construction, schema, and script bindings.
Definition Rope.h:15
eve::Result< void > registerRope3DCreateSchema()
Registers physics:rope3d-create@1 for tools and editors.
Definition Rope.cpp:110
Rope3D * newRope3D(int particleCount, float startX, float startY, float startZ, float endX, float endY, float endZ)
Creates a straight renderer-independent rope.
Definition Rope.cpp:106
eve::Result< Rope3D * > newRope3DFromJson(const std::string &json)
Validates a versioned creation document and transfers the new rope to the caller.
Definition Rope.cpp:112
static eve::Result< SchemaRegistrationStatus > registerVersioned(const SchemaDefinition &definition)
Registers a new exact (id, version) entry without replacing it.
static std::vector< ValidationError > validate(const std::string &schemaId, const std::string &json)
Validates JSON text against the highest registered schema version.
static const SchemaDefinition * resolve(const std::string &schemaId, int schemaVersion)
Resolves one exact (schemaId, schemaVersion) pair, or nullptr.
Optional physics backend for vehicle mobility and body attach.
Definition Climbing.h:36
const EditorValue * field(const EditorValue &value, const char *name)
ValueType
JSON-compatible value kinds understood by a schema field.
Definition SchemaTypes.h:16
std::vector< DiagnosticDetail > DiagnosticDetails
Owning collection of diagnostic details with stable insertion order.
Definition Diagnostic.h:88
Metadata and validation constraints for one object member.
Definition SchemaTypes.h:77
Versioned runtime schema for a JSON object.
Definition SchemaTypes.h:93
std::vector< FieldDefinition > fields
Definition SchemaTypes.h:99