载入中...
搜索中...
未找到
SoftBody3DDefinition.cpp
浏览该文件的文档.
2
4
5#include <cmath>
6#include <initializer_list>
7#include <limits>
8#include <string>
9
10namespace eve::physics {
11namespace {
12
13const eve::Value* field(const eve::Value::Object& object, std::string_view name) {
14 const auto found = object.find(std::string(name));
15 return found == object.end() ? nullptr : &found->second;
16}
17
18bool hasExactFields(const eve::Value::Object& object, std::initializer_list<std::string_view> expected) {
19 if (object.size() != expected.size()) return false;
20 for (const auto name : expected)
21 if (!object.contains(std::string(name))) return false;
22 return true;
23}
24
25eve::Result<int> integer(const eve::Value::Object& object, std::string_view name) {
26 const eve::Value* value = field(object, name);
27 if (!value || !value->isInt64())
29 eve::DiagnosticCode::InvalidArgument, "soft-body field must be an integer", std::string(name)));
30 const auto parsed = value->asInt();
31 if (parsed < std::numeric_limits<int>::min() || parsed > std::numeric_limits<int>::max())
33 eve::DiagnosticCode::InvalidArgument, "soft-body integer is outside int range", std::string(name)));
34 return eve::Result<int>::success(static_cast<int>(parsed));
35}
36
37eve::Result<float> number(const eve::Value::Object& object, std::string_view name) {
38 const eve::Value* value = field(object, name);
39 if (!value || !value->isNumeric())
41 eve::DiagnosticCode::InvalidArgument, "soft-body field must be numeric", std::string(name)));
42 const double parsed = value->isDouble() ? value->asDouble() : static_cast<double>(value->asInt());
43 if (!std::isfinite(parsed) || parsed < -std::numeric_limits<float>::max() ||
44 parsed > std::numeric_limits<float>::max())
46 eve::DiagnosticCode::InvalidArgument, "soft-body number must be a finite float", std::string(name)));
47 return eve::Result<float>::success(static_cast<float>(parsed));
48}
49
50eve::Result<bool> boolean(const eve::Value::Object& object, std::string_view name) {
51 const eve::Value* value = field(object, name);
52 if (!value || !value->isBool())
54 "soft-body field must be boolean", std::string(name)));
55 return eve::Result<bool>::success(value->asBool());
56}
57
58} // namespace
59
61 if (cols < 2 || rows < 2 || layers < 2)
63 "soft-body lattice dimensions must each be at least 2",
64 "dimensions"));
65 const std::int64_t count = std::int64_t(cols) * std::int64_t(rows) * std::int64_t(layers);
66 if (count > 1000000)
68 eve::DiagnosticCode::InvalidArgument, "soft-body exceeds the one-million-particle limit", "dimensions"));
69 const auto finite = [](float value) { return std::isfinite(value); };
70 if (!finite(spacing) || spacing <= 0.f)
72 "spacing must be finite and positive", "spacing"));
73 if (!finite(originX) || !finite(originY) || !finite(originZ))
75 "origin must contain finite coordinates", "origin"));
78 "gravity must contain finite coordinates", "gravity"));
79 if (!finite(deformationResistance) || deformationResistance < 0.f || deformationResistance > 1.f)
81 eve::DiagnosticCode::InvalidArgument, "deformationResistance must be in [0, 1]", "deformationResistance"));
82 if (iterations < 1 || iterations > 32)
84 "iterations must be in [1, 32]", "iterations"));
85 if (!finite(damping) || damping < 0.f || damping > 1.f)
87 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "damping must be in [0, 1]", "damping"));
88 if (!finite(particleRadius) || particleRadius <= 0.f)
90 eve::DiagnosticCode::InvalidArgument, "particleRadius must be finite and positive", "particleRadius"));
91 if (!finite(particleMass) || particleMass <= 0.f)
93 eve::DiagnosticCode::InvalidArgument, "particleMass must be finite and positive", "particleMass"));
95 plasticYield < 0.f || plasticCreep < 0.f || plasticCreep > 1.f || plasticRecovery < 0.f ||
96 plasticRecovery > 1.f || maxDeformation < 0.f)
98 "plasticity fields are outside their supported ranges",
99 "plasticity"));
101}
102
104 auto valid = validate();
105 if (!valid) return eve::Result<eve::Value>::failure(valid.status());
107 object["schema"] = std::string(SchemaId);
108 object["schemaVersion"] = static_cast<std::int64_t>(SchemaVersion);
109 object["cols"] = static_cast<std::int64_t>(cols);
110 object["rows"] = static_cast<std::int64_t>(rows);
111 object["layers"] = static_cast<std::int64_t>(layers);
112 object["spacing"] = spacing;
113 object["originX"] = originX;
114 object["originY"] = originY;
115 object["originZ"] = originZ;
116 object["gravityX"] = gravityX;
117 object["gravityY"] = gravityY;
118 object["gravityZ"] = gravityZ;
119 object["deformationResistance"] = deformationResistance;
120 object["iterations"] = static_cast<std::int64_t>(iterations);
121 object["damping"] = damping;
122 object["particleRadius"] = particleRadius;
123 object["particleMass"] = particleMass;
124 object["plasticYield"] = plasticYield;
125 object["plasticCreep"] = plasticCreep;
126 object["plasticRecovery"] = plasticRecovery;
127 object["maxDeformation"] = maxDeformation;
128 object["selfCollision"] = selfCollision;
129 return eve::Result<eve::Value>::success(eve::Value(std::move(object)));
130}
131
135 auto fieldDefinition = [](const char* name, ValueType type, const char* title, const char* description,
136 std::string defaultJson) {
137 FieldDefinition field;
138 field.name = name;
139 field.type = type;
140 field.required = true;
141 field.title = title;
142 field.description = description;
143 field.defaultJson = std::move(defaultJson);
144 return field;
145 };
146 auto integerField = [&](const char* name, const char* title, const char* description, int value, double minimum,
147 double maximum) {
148 auto field = fieldDefinition(name, ValueType::Integer, title, description, std::to_string(value));
149 field.minimum = minimum;
150 field.maximum = maximum;
151 return field;
152 };
153 auto numberField = [&](const char* name, const char* title, const char* description, float value, double minimum,
154 double maximum) {
155 auto field = fieldDefinition(name, ValueType::Number, title, description, std::to_string(value));
156 field.minimum = minimum;
157 field.maximum = maximum;
158 return field;
159 };
160
161 SoftBody3DDefinition defaults;
163 schema.id = std::string(SchemaId);
164 schema.version = static_cast<int>(SchemaVersion);
165 schema.title = "Volumetric soft body";
166 schema.description = "Validated lattice, solver, particle and plasticity creation parameters.";
167 schema.additionalProperties = false;
168 schema.fields = {
169 fieldDefinition("schema", ValueType::String, "Schema", "Stable schema identifier", "\"physics:softbody3d\""),
170 integerField("schemaVersion", "Schema version", "Exact supported schema version", 1, 1, 1),
171 integerField("cols", "Columns", "Particle count along local X", defaults.cols, 2, 1000000),
172 integerField("rows", "Rows", "Particle count along local Y", defaults.rows, 2, 1000000),
173 integerField("layers", "Layers", "Particle count along local Z", defaults.layers, 2, 1000000),
174 numberField("spacing", "Spacing", "Rest distance in meters", defaults.spacing, 0.0001, 100000),
175 numberField("originX", "Origin X", "Minimum rest X coordinate", defaults.originX, -100000, 100000),
176 numberField("originY", "Origin Y", "Minimum rest Y coordinate", defaults.originY, -100000, 100000),
177 numberField("originZ", "Origin Z", "Minimum rest Z coordinate", defaults.originZ, -100000, 100000),
178 numberField("gravityX", "Gravity X", "World X acceleration", defaults.gravityX, -1000, 1000),
179 numberField("gravityY", "Gravity Y", "World Y acceleration", defaults.gravityY, -1000, 1000),
180 numberField("gravityZ", "Gravity Z", "World Z acceleration", defaults.gravityZ, -1000, 1000),
181 numberField("deformationResistance", "Deformation resistance", "Shape-matching strength",
182 defaults.deformationResistance, 0, 1),
183 integerField("iterations", "Iterations", "Constraint iterations per substep", defaults.iterations, 1, 32),
184 numberField("damping", "Damping", "Velocity damping", defaults.damping, 0, 1),
185 numberField("particleRadius", "Particle radius", "Collision radius in meters", defaults.particleRadius, 0.0001,
186 100000),
187 numberField("particleMass", "Particle mass", "Particle mass in kilograms", defaults.particleMass, 0.0001,
188 100000),
189 numberField("plasticYield", "Plastic yield", "Strain threshold for plastic deformation", defaults.plasticYield,
190 0, 1000),
191 numberField("plasticCreep", "Plastic creep", "Permanent deformation rate", defaults.plasticCreep, 0, 1),
192 numberField("plasticRecovery", "Plastic recovery", "Rest-shape recovery rate", defaults.plasticRecovery, 0, 1),
193 numberField("maxDeformation", "Maximum deformation", "Maximum plastic displacement", defaults.maxDeformation, 0,
194 100000),
195 fieldDefinition("selfCollision", ValueType::Boolean, "Self collision", "Resolve non-neighbor overlap",
196 defaults.selfCollision ? "true" : "false")};
197 return schema;
198}
199
207
209 const auto* object = value.getIf<eve::Value::Object>();
210 if (!object)
212 eve::DiagnosticCode::InvalidArgument, "soft-body definition must be an object", "softbody3d"));
213 const std::initializer_list<std::string_view> fields = {"schema",
214 "schemaVersion",
215 "cols",
216 "rows",
217 "layers",
218 "spacing",
219 "originX",
220 "originY",
221 "originZ",
222 "gravityX",
223 "gravityY",
224 "gravityZ",
225 "deformationResistance",
226 "iterations",
227 "damping",
228 "particleRadius",
229 "particleMass",
230 "plasticYield",
231 "plasticCreep",
232 "plasticRecovery",
233 "maxDeformation",
234 "selfCollision"};
235 if (!hasExactFields(*object, fields))
237 eve::DiagnosticCode::InvalidArgument, "soft-body definition has missing or unknown fields", "softbody3d"));
238 const eve::Value* schema = field(*object, "schema");
239 if (!schema || !schema->isString() || schema->asString() != SchemaId)
241 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "unexpected soft-body schema id", "schema"));
242 auto version = integer(*object, "schemaVersion");
243 if (!version || version.value() != static_cast<int>(SchemaVersion))
245 eve::DiagnosticCode::InvalidArgument, "unsupported soft-body schema version", "schemaVersion"));
246
248 auto readInt = [&](std::string_view name, int& target) -> eve::Result<void> {
249 auto parsed = integer(*object, name);
250 if (!parsed) return eve::Result<void>::failure(parsed.status());
251 target = parsed.value();
253 };
254 auto readFloat = [&](std::string_view name, float& target) -> eve::Result<void> {
255 auto parsed = number(*object, name);
256 if (!parsed) return eve::Result<void>::failure(parsed.status());
257 target = parsed.value();
259 };
260#define EVE_SOFTBODY_READ(reader, member) \
261 do { \
262 auto parsed = reader(#member, result.member); \
263 if (!parsed) return eve::Result<SoftBody3DDefinition>::failure(parsed.status()); \
264 } while (false)
265 EVE_SOFTBODY_READ(readInt, cols);
266 EVE_SOFTBODY_READ(readInt, rows);
267 EVE_SOFTBODY_READ(readInt, layers);
268 EVE_SOFTBODY_READ(readFloat, spacing);
269 EVE_SOFTBODY_READ(readFloat, originX);
270 EVE_SOFTBODY_READ(readFloat, originY);
271 EVE_SOFTBODY_READ(readFloat, originZ);
272 EVE_SOFTBODY_READ(readFloat, gravityX);
273 EVE_SOFTBODY_READ(readFloat, gravityY);
274 EVE_SOFTBODY_READ(readFloat, gravityZ);
277 EVE_SOFTBODY_READ(readFloat, damping);
284#undef EVE_SOFTBODY_READ
285 auto collision = boolean(*object, "selfCollision");
286 if (!collision) return eve::Result<SoftBody3DDefinition>::failure(collision.status());
287 result.selfCollision = collision.value();
288 auto valid = result.validate();
290 return eve::Result<SoftBody3DDefinition>::success(std::move(result));
291}
292
293} // namespace eve::physics
LogicalId target
double value
std::string title
float maximum[3]
float minimum[3]
std::string description
std::string name
bool valid
bool finite
std::vector< std::string > fields
Definition PlayHost.cpp:111
double number
bool found
bool boolean
std::string string
#define EVE_SOFTBODY_READ(reader, member)
std::uint32_t count
Json object
float size
Definition TreeMesh.cpp:156
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
const std::string & asString() const
Return the string payload; the caller must have checked the kind.
Definition Value.cpp:87
std::map< std::string, Value > Object
Definition Value.h:34
bool isString() const noexcept
Return true when this value is a string.
Definition Value.h:95
Type type() const
Return the active value type.
static eve::Result< SchemaRegistrationStatus > registerVersioned(const SchemaDefinition &definition)
Registers a new exact (id, version) entry without replacing it.
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
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.
Versioned, owning creation definition for one volumetric soft body.
eve::Result< eve::Value > toValue() const
Encode the complete canonical version-1 value.
static constexpr std::string_view SchemaId
static eve::Result< SoftBody3DDefinition > fromValue(const eve::Value &value)
Decode and validate version 1 transactionally without partial publication.
static eve::Result< void > ensureSchemaRegistered()
Idempotently register version 1 in the process schema registry.
static eve::schema::SchemaDefinition schemaDefinition()
Build the runtime/tooling schema whose constraints mirror validate().
eve::Result< void > validate() const
Validate all fields and the aggregate one-million-particle limit.
static constexpr std::uint32_t SchemaVersion
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