载入中...
搜索中...
未找到
PhysicsTarget.cpp
浏览该文件的文档.
2
3#include <utility>
4
5namespace eve::physics_editing {
6namespace {
7
8const EditorValue* field(const EditorValue& value, const char* key) {
9 const auto* object = value.getIf<EditorValue::Object>();
10 if (!object) return nullptr;
11 const auto found = object->find(key);
12 return found == object->end() ? nullptr : &found->second;
13}
14
15PropertyDescriptor property(const char* path, const char* label, const char* category, PropertyType type,
17 PropertyDescriptor result;
18 result.path = PropertyPath(path);
19 result.displayNameKey = label;
20 result.category = category;
21 result.type = type;
22 result.flags = PropertyFlag::Runtime;
23 result.defaultValue = std::move(value);
24 return result;
25}
26
27} // namespace
28
29PhysicsColliderTarget::PhysicsColliderTarget(std::string id, int dimensions)
30 : id_(std::move(id)), dimensions_(dimensions == 2 ? 2 : 3), values_(defaults()) {}
31
33 return {TargetId(id_),
34 dimensions_ == 2 ? "physics-collider-2d" : "physics-collider-3d",
36 false,
37 {CapabilityId("eve.editor.target.physics-collider")}};
38}
39
40void* PhysicsColliderTarget::queryCapability(const CapabilityId& capability) {
41 return capability == CapabilityId("eve.editor.target.physics-collider") ? static_cast<IPropertyProvider*>(this)
42 : nullptr;
43}
44
46 if (operation.target != TargetId(id_))
47 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.physics.target-mismatch"),
48 "Physics operation targets another collider");
49 if (operation.type != "physics.collider.property.set.v1")
50 return eve::editing::failed<void>(EditorStatus::Unsupported, RuleId("editor.physics.operation-unsupported"),
51 "Unsupported physics collider operation: " + operation.type);
52 const EditorValue* pathValue = field(operation.payload, "path");
53 const EditorValue* value = field(operation.payload, "value");
54 const auto* path = pathValue ? pathValue->getIf<std::string>() : nullptr;
55 if (!path || !value)
56 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.physics.operation-payload"),
57 "Physics collider operation requires path and value");
58 auto descriptor = colliderSchema().find(PropertyPath(*path));
59 if (!descriptor)
60 return eve::editing::failed<void>(EditorStatus::Unsupported, RuleId("editor.physics.property-unsupported"),
61 "Unknown physics collider property: " + *path);
62 auto valid = validateAssignment(*descriptor, *value);
63 if (!valid.ok()) return valid;
64 values_[*path] = *value;
66 widenDirty(0, 0);
67 return eve::editing::applied<void>();
68}
69
70std::unique_ptr<IDomainOperationTarget> PhysicsColliderTarget::cloneDomainState() const {
71 return std::make_unique<PhysicsColliderTarget>(*this);
72}
73
74Result<void> PhysicsColliderTarget::commitDomainState(std::unique_ptr<IDomainOperationTarget> candidate) {
75 auto* typed = dynamic_cast<PhysicsColliderTarget*>(candidate.get());
76 if (!typed || typed->id_ != id_ || typed->dimensions_ != dimensions_)
77 return eve::editing::failed<void>(EditorStatus::Conflict, RuleId("editor.physics.candidate-mismatch"),
78 "Collider candidate belongs to another target");
79 *this = *typed;
80 return eve::editing::applied<void>();
81}
82
84 if (!selectionMatches(selection))
86 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "Selection does not belong to this collider",
87 "editor.physics.selection", {}, "editor.PhysicsColliderTarget"));
89}
90
91PropertySchema PhysicsColliderTarget::schema(const SelectionSnapshot&) const { return colliderSchema(); }
92
94 if (!selectionMatches(selection)) return {};
95 const auto found = values_.find(path.value());
96 return found == values_.end() ? PropertyReadResult{}
97 : PropertyReadResult{PropertyReadState::Value, found->second, {}};
98}
99
101 const PropertyPath& path, const EditorValue& value,
102 PropertySetMode mode) const {
103 if (!selectionMatches(selection))
104 return eve::editing::failed<DomainOperation>(EditorStatus::Rejected, RuleId("editor.physics.selection"),
105 "Selection does not belong to this collider");
106 if (mode == PropertySetMode::Reset) return makeReset(selection, path);
107 if (mode != PropertySetMode::Absolute)
108 return eve::editing::failed<DomainOperation>(EditorStatus::Unsupported, RuleId("editor.physics.property-mode"),
109 "Collider properties require absolute assignment");
110 auto descriptor = colliderSchema().find(path);
111 if (!descriptor)
112 return eve::editing::failed<DomainOperation>(EditorStatus::Unsupported,
113 RuleId("editor.physics.property-unsupported"),
114 "Unknown collider property: " + path.value());
115 auto valid = validateAssignment(*descriptor, value);
116 if (!valid.ok()) return Result<DomainOperation>::failure(valid.status());
117 const auto previous = values_.find(path.value());
118 auto payload = [&](const EditorValue& assigned) {
120 object["path"] = path.value();
121 object["value"] = assigned;
122 return EditorValue(std::move(object));
123 };
125 operation.type = "physics.collider.property.set.v1";
126 operation.target = TargetId(id_);
127 operation.payload = payload(value);
128 operation.inverse = payload(previous->second);
129 operation.hasInverse = true;
130 operation.affectedProperties.push_back(path.value());
131 operation.mergeKey = "physics.collider:" + id_ + ":" + path.value();
132 return eve::editing::applied<DomainOperation>(std::move(operation));
133}
134
136 const PropertyPath& path) const {
137 auto descriptor = colliderSchema().find(path);
138 if (!descriptor)
139 return eve::editing::failed<DomainOperation>(EditorStatus::Unsupported,
140 RuleId("editor.physics.property-unsupported"),
141 "Unknown collider property: " + path.value());
142 return makeSet(selection, path, descriptor->defaultValue, PropertySetMode::Absolute);
143}
144
145PropertySchema PhysicsColliderTarget::colliderSchema() const {
146 PropertySchema result;
147 result.typeId = dimensions_ == 2 ? "physics.collider2d" : "physics.collider3d";
148 auto body = property("body.type", "editor.physics.body-type", "body", PropertyType::Enum, "static");
149 body.enumItems = {"static", "kinematic", "dynamic"};
150 result.properties.push_back(std::move(body));
151 auto shape = property("shape.kind", "editor.physics.shape", "shape", PropertyType::Enum,
152 dimensions_ == 2 ? EditorValue("box") : EditorValue("box"));
153 shape.enumItems = dimensions_ == 2 ? std::vector<std::string>{"box", "circle", "polygon", "chain"}
154 : std::vector<std::string>{"box", "sphere", "capsule",
155 "convex-hull", "triangle-mesh", "height-field"};
156 result.properties.push_back(std::move(shape));
157 auto vector = [&](const char* path, const char* label, EditorValue value) {
158 result.properties.push_back(property(
159 path, label, "shape", dimensions_ == 2 ? PropertyType::Vec2 : PropertyType::Vec3, std::move(value)));
160 };
161 vector(
162 "shape.offset", "editor.physics.offset",
163 dimensions_ == 2 ? EditorValue(EditorValue::Array{0.0, 0.0}) : EditorValue(EditorValue::Array{0.0, 0.0, 0.0}));
164 vector(
165 "shape.size", "editor.physics.size",
166 dimensions_ == 2 ? EditorValue(EditorValue::Array{1.0, 1.0}) : EditorValue(EditorValue::Array{1.0, 1.0, 1.0}));
167 auto numeric = [&](const char* path, const char* label, const char* category, double value, double minimum,
168 double maximum) {
169 auto descriptor = property(path, label, category, PropertyType::Float, value);
170 descriptor.numeric.minimum = minimum;
171 descriptor.numeric.maximum = maximum;
172 descriptor.numeric.step = 0.01;
173 result.properties.push_back(std::move(descriptor));
174 };
175 numeric("shape.radius", "editor.physics.radius", "shape", 0.5, 0.001, 100000.0);
176 numeric("shape.capsule-height", "editor.physics.capsule-height", "shape", 1.0, 0.0, 100000.0);
177 numeric("material.density", "editor.physics.density", "material", 1.0, 0.0, 100000.0);
178 numeric("material.friction", "editor.physics.friction", "material", 0.2, 0.0, 1.0);
179 numeric("material.restitution", "editor.physics.restitution", "material", 0.0, 0.0, 1.0);
180 result.properties.push_back(
181 property("shape.sensor", "editor.physics.sensor", "collision", PropertyType::Bool, false));
182 auto integer = [&](const char* path, const char* label, int64_t value) {
183 auto descriptor = property(path, label, "collision", PropertyType::Int, value);
184 descriptor.numeric.minimum = 0.0;
185 descriptor.numeric.maximum = 2147483647.0;
186 result.properties.push_back(std::move(descriptor));
187 };
188 integer("collision.category", "editor.physics.category", 1);
189 integer("collision.mask", "editor.physics.mask", 2147483647);
190 auto asset = property("shape.asset", "editor.physics.shape-asset", "shape", PropertyType::AssetRef, "");
191 asset.assetTypeFilters = {"mesh", "height-field", "polygon"};
192 result.properties.push_back(std::move(asset));
193 return result;
194}
195
196std::map<std::string, EditorValue> PhysicsColliderTarget::defaults() const {
197 std::map<std::string, EditorValue> result;
198 for (const PropertyDescriptor& descriptor : colliderSchema().properties)
199 result.emplace(descriptor.path.value(), descriptor.defaultValue);
200 return result;
201}
202
203Result<void> PhysicsColliderTarget::validateAssignment(const PropertyDescriptor& descriptor,
204 const EditorValue& value) const {
205 auto result = validatePropertyValue(descriptor, value);
206 if (!result.ok()) return result;
207 if (descriptor.path == PropertyPath("shape.size") || descriptor.path == PropertyPath("shape.offset")) {
208 const auto* tuple = value.getIf<EditorValue::Array>();
209 const std::size_t expected = dimensions_ == 2 ? 2 : 3;
210 if (!tuple || tuple->size() != expected)
211 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.physics.vector-size"),
212 "Physics vector has the wrong dimensionality");
213 for (const EditorValue& component : *tuple) {
214 const auto* number = component.getIf<double>();
215 if (!number || (descriptor.path == PropertyPath("shape.size") && *number <= 0.0))
216 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.physics.vector-value"),
217 "Physics size components must be positive numbers");
218 }
219 }
220 return eve::editing::applied<void>();
221}
222
223bool PhysicsColliderTarget::selectionMatches(const SelectionSnapshot& selection) const {
224 return selection.items.size() == 1 && selection.items.front().target == TargetId(id_);
225}
226
227} // namespace eve::physics_editing
ActionParameterOperation operation
double value
Value::Object payload
std::string descriptor
std::string label
float maximum[3]
float minimum[3]
std::uint32_t key
ShaderImageInput shape
std::map< std::string, PropertyRule > properties
bool valid
graphics::Canvas * previous
std::string path
Definition PlayHost.cpp:110
std::string id
Definition PlayHost.cpp:108
double number
bool found
Json object
const char * category
std::string body
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
const std::string & value() const noexcept
Returns the exact legacy or canonical compatibility spelling.
Definition Identity.h:362
void bumpRevision()
Advance the revision without touching the dirty region.
Revision revisionValue() const noexcept
Return the revision as the shared protocol revision type.
void widenDirty()
Mark an unknown area dirty without advancing the revision.
eve::OptionalRef< C > capability()
Query a capability through its interface-owned stable identity.
UI-independent property access for an immutable selection snapshot.
Dotted property path (layout.size) compared and persisted as text.
Definition EditingIds.h:78
Deterministic owning value tree shared by authoring hosts.
std::vector< Value > Array
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
Serializable, backend-neutral 2D/3D collider authoring target.
PropertySchema schema(const SelectionSnapshot &selection) const override
Schema.
PropertyReadResult read(const SelectionSnapshot &selection, const PropertyPath &path) const override
Reads .
Result< void > commitDomainState(std::unique_ptr< IDomainOperationTarget > candidate) override
Commits domain state.
Result< DomainOperation > makeReset(const SelectionSnapshot &selection, const PropertyPath &path) const override
Make reset.
eve::Result< eve::Revision > currentRevision(const SelectionSnapshot &selection) const override
Current revision.
Result< DomainOperation > makeSet(const SelectionSnapshot &selection, const PropertyPath &path, const EditorValue &value, PropertySetMode mode) const override
Make set.
std::unique_ptr< IDomainOperationTarget > cloneDomainState() const override
Clones domain state.
void * queryCapability(const CapabilityId &capability) override
Query an optional target capability.
PhysicsColliderTarget(std::string id, int dimensions=3)
Physics collider target.
TargetDescriptor describe() const override
Describe.
Result< void > applyDomainOperation(const DomainOperation &operation) override
Applies domain operation.
const char * defaultValue
editing::PropertyDescriptor PropertyDescriptor
editing::PropertyPath PropertyPath
editing::TargetId TargetId
PropertyType
PropertyType public API.
Result< void > validatePropertyValue(const PropertyDescriptor &descriptor, const Value &value)
Validate property value.
editing::Value EditorValue
const EditorValue * field(const EditorValue &value, const char *name)
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.
enum EVENGINE_API_FOUNDATION Array
OT_ARRAY (editable via Runtime array APIs / ReflectedPropertyModel).
Definition Runtime.h:120
DomainOperation public API.
PropertyReadResult public API.
EVENGINE_API_PLATFORM public API.
std::vector< PropertyDescriptor > properties
PropertyDescriptorLookup find(const PropertyPath &path) const
Finds find.
SelectionSnapshot public API.
Stable discovery metadata exposed by every editable target.