载入中...
搜索中...
未找到
VirtualGeometryTarget.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <utility>
6
8namespace {
9const EditorValue* field(const EditorValue& v, const char* k) {
10 const auto* o = v.getIf<EditorValue::Object>();
11 if (!o) return nullptr;
12 const auto i = o->find(k);
13 return i == o->end() ? nullptr : &i->second;
14}
15PropertyDescriptor prop(std::string path, PropertyType type, EditorValue value, const char* category, double minimum,
16 double maximum) {
19 d.displayNameKey = "editor.virtualgeometry." + path;
20 d.category = category;
21 d.type = type;
22 d.defaultValue = std::move(value);
23 d.flags = PropertyFlag::EditorOnly;
24 d.numeric.minimum = minimum;
25 d.numeric.maximum = maximum;
26 return d;
27}
28bool errors(const std::vector<EditorDiagnostic>& d) {
29 return std::any_of(d.begin(), d.end(), [](const auto& v) { return v.severity() == DiagnosticSeverity::Error; });
30}
31} // namespace
34 return {TargetId(id_),
35 "virtualgeometry-import",
37 false,
38 {CapabilityId("eve.editor.target.virtualgeometry-properties")}};
39}
41 return c == CapabilityId("eve.editor.target.virtualgeometry-properties") ? static_cast<IPropertyProvider*>(this)
42 : nullptr;
43}
44bool VirtualGeometryDocumentTarget::matches(const SelectionSnapshot& s) const {
45 return s.items.size() == 1 && s.items.front().target == TargetId(id_) && s.items.front().item.value() == id_;
46}
56 s.typeId = "virtualgeometry.import";
57 s.properties = {prop("source.asset", PropertyType::AssetRef, "", "source", 0, 0),
58 prop("cluster.maxVertices", PropertyType::Int, int64_t{64}, "cluster", 3, 256),
59 prop("cluster.maxTriangles", PropertyType::Int, int64_t{124}, "cluster", 1, 512),
60 prop("lod.levels", PropertyType::Int, int64_t{4}, "lod", 1, 16),
61 prop("lod.mergeFactor", PropertyType::Int, int64_t{4}, "lod", 2, 4),
62 prop("lod.targetRatio", PropertyType::Float, .5, "lod", .01, .99),
63 prop("preview.fov", PropertyType::Float, 60.0, "preview", 1, 179),
64 prop("preview.height", PropertyType::Int, int64_t{1080}, "preview", 16, 16384),
65 prop("preview.errorPixels", PropertyType::Float, 1.0, "preview", .01, 100),
66 prop("preview.nearDistance", PropertyType::Float, 1.0, "preview", .001, 1e9),
67 prop("preview.farDistance", PropertyType::Float, 1000.0, "preview", .001, 1e9),
68 prop("preview.samples", PropertyType::Int, int64_t{32}, "preview", 2, 512)};
69 s.properties[0].assetTypeFilters = {"model3d", "mesh"};
70 return s;
71}
72EditorValue VirtualGeometryDocumentTarget::contentValue() const {
73 const auto& v = value_;
74 return EditorValue::Object{{"source", v.sourceAsset},
75 {"maxVertices", int64_t{v.builder.maxVerticesPerCluster}},
76 {"maxTriangles", int64_t{v.builder.maxTrianglesPerCluster}},
77 {"levels", int64_t{v.builder.minLodLevels}},
78 {"mergeFactor", int64_t{v.builder.mergeFactor}},
79 {"targetRatio", v.builder.lodTargetRatio},
80 {"fov", v.previewFov},
81 {"height", int64_t{v.previewHeight}},
82 {"errorPixels", v.errorPixels},
83 {"nearDistance", v.nearDistance},
84 {"farDistance", v.farDistance},
85 {"samples", int64_t{v.distanceSamples}}};
86}
88 if (!matches(s) || !schema(s).find(p)) return {};
89 const auto& v = value_;
91 if (p == PropertyPath("source.asset"))
93 else if (p == PropertyPath("cluster.maxVertices"))
95 else if (p == PropertyPath("cluster.maxTriangles"))
97 else if (p == PropertyPath("lod.levels"))
98 value = int64_t{v.builder.minLodLevels};
99 else if (p == PropertyPath("lod.mergeFactor"))
100 value = int64_t{v.builder.mergeFactor};
101 else if (p == PropertyPath("lod.targetRatio"))
103 else if (p == PropertyPath("preview.fov"))
105 else if (p == PropertyPath("preview.height"))
106 value = int64_t{v.previewHeight};
107 else if (p == PropertyPath("preview.errorPixels"))
109 else if (p == PropertyPath("preview.nearDistance"))
111 else if (p == PropertyPath("preview.farDistance"))
113 else
114 value = int64_t{v.distanceSamples};
115 return {PropertyReadState::Value, std::move(value), {}};
116}
117Result<DomainOperation> VirtualGeometryDocumentTarget::replacement(EditorValue content,
118 std::string property) const {
120 op.type = "virtualgeometry.document.replace.v1";
121 op.inverseType = op.type;
122 op.target = TargetId(id_);
123 op.payload = std::move(content);
124 op.inverse = contentValue();
125 op.hasInverse = true;
126 op.affectedProperties.push_back(property);
127 op.mergeKey = "virtualgeometry:" + id_ + ":" + property;
128 return eve::editing::applied<DomainOperation>(std::move(op));
129}
131 const EditorValue& value,
132 PropertySetMode mode) const {
133 if (mode == PropertySetMode::Reset) return makeReset(s, p);
134 auto d = schema(s).find(p);
135 if (!matches(s) || !d || mode != PropertySetMode::Absolute || !validatePropertyValue(*d, value).ok())
136 return eve::editing::failed<DomainOperation>(EditorStatus::Rejected, RuleId("editor.virtualgeometry.set"),
137 "VirtualGeometry property is invalid");
138 auto candidate = *this;
139 auto& v = candidate.value_;
140 if (p == PropertyPath("source.asset"))
141 v.sourceAsset = *value.getIf<std::string>();
142 else if (p == PropertyPath("cluster.maxVertices"))
143 v.builder.maxVerticesPerCluster = static_cast<int>(*value.getIf<int64_t>());
144 else if (p == PropertyPath("cluster.maxTriangles"))
145 v.builder.maxTrianglesPerCluster = static_cast<int>(*value.getIf<int64_t>());
146 else if (p == PropertyPath("lod.levels"))
147 v.builder.minLodLevels = static_cast<int>(*value.getIf<int64_t>());
148 else if (p == PropertyPath("lod.mergeFactor"))
149 v.builder.mergeFactor = static_cast<int>(*value.getIf<int64_t>());
150 else if (p == PropertyPath("lod.targetRatio"))
151 v.builder.lodTargetRatio = static_cast<float>(*value.getIf<double>());
152 else if (p == PropertyPath("preview.fov"))
153 v.previewFov = static_cast<float>(*value.getIf<double>());
154 else if (p == PropertyPath("preview.height"))
155 v.previewHeight = static_cast<int>(*value.getIf<int64_t>());
156 else if (p == PropertyPath("preview.errorPixels"))
157 v.errorPixels = static_cast<float>(*value.getIf<double>());
158 else if (p == PropertyPath("preview.nearDistance"))
159 v.nearDistance = static_cast<float>(*value.getIf<double>());
160 else if (p == PropertyPath("preview.farDistance"))
161 v.farDistance = static_cast<float>(*value.getIf<double>());
162 else
163 v.distanceSamples = static_cast<int>(*value.getIf<int64_t>());
164 if (errors(candidate.validate()))
165 return eve::editing::failed<DomainOperation>(EditorStatus::Rejected, RuleId("editor.virtualgeometry.invalid"),
166 "VirtualGeometry edit violates importer invariants");
167 return replacement(candidate.contentValue(), p.value());
168}
170 const PropertyPath& p) const {
171 auto d = schema(s).find(p);
172 if (!d)
173 return eve::editing::failed<DomainOperation>(
174 EditorStatus::Unsupported, RuleId("editor.virtualgeometry.property"), "Unknown VirtualGeometry property");
175 return makeSet(s, p, d->defaultValue, PropertySetMode::Absolute);
176}
177std::vector<EditorDiagnostic> VirtualGeometryDocumentTarget::validate() const {
178 std::vector<EditorDiagnostic> d;
179 const auto& v = value_;
180 if (v.sourceAsset.empty())
182 RuleId("editor.virtualgeometry.source"),
183 DiagnosticSeverity::Warning,
184 "VirtualGeometry source asset is not assigned"));
185 if (v.builder.maxVerticesPerCluster < 3 || v.builder.maxVerticesPerCluster > 256 ||
186 v.builder.maxTrianglesPerCluster < 1 || v.builder.maxTrianglesPerCluster > 512 || v.builder.minLodLevels < 1 ||
187 v.builder.minLodLevels > 16 || v.builder.mergeFactor < 2 || v.builder.mergeFactor > 4 ||
188 !std::isfinite(v.builder.lodTargetRatio) || v.builder.lodTargetRatio <= 0 || v.builder.lodTargetRatio >= 1)
190 RuleId("editor.virtualgeometry.builder"), DiagnosticSeverity::Error,
191 "VirtualGeometry builder settings exceed supported invariants"));
192 if (!std::isfinite(v.previewFov) || v.previewFov < 1 || v.previewFov > 179 || v.previewHeight < 16 ||
193 v.previewHeight > 16384 || !std::isfinite(v.errorPixels) || v.errorPixels <= 0 || v.errorPixels > 100 ||
194 !std::isfinite(v.nearDistance) || !std::isfinite(v.farDistance) || v.nearDistance <= 0 ||
195 v.farDistance <= v.nearDistance || v.farDistance > 1e9f || v.distanceSamples < 2 || v.distanceSamples > 512)
197 RuleId("editor.virtualgeometry.preview"), DiagnosticSeverity::Error,
198 "VirtualGeometry LOD preview sweep is invalid"));
199 return d;
200}
202 if (op.target != TargetId(id_) || op.type != "virtualgeometry.document.replace.v1" ||
203 !op.payload.isWithinLimits(4, 64, 4096))
204 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.virtualgeometry.operation"),
205 "VirtualGeometry operation is invalid");
207 const auto* source = field(op.payload, "source");
208 const auto* s = source ? source->getIf<std::string>() : nullptr;
209 if (!s)
210 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.virtualgeometry.source"),
211 "VirtualGeometry source is invalid");
212 v.sourceAsset = *s;
213 int* ints[] = {&v.builder.maxVerticesPerCluster,
214 &v.builder.maxTrianglesPerCluster,
215 &v.builder.minLodLevels,
216 &v.builder.mergeFactor,
217 &v.previewHeight,
218 &v.distanceSamples};
219 const char* ifields[] = {"maxVertices", "maxTriangles", "levels", "mergeFactor", "height", "samples"};
220 for (int i = 0; i < 6; ++i) {
221 const auto* x = field(op.payload, ifields[i]);
222 const auto* n = x ? x->getIf<int64_t>() : nullptr;
223 if (!n)
224 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.virtualgeometry.integer"),
225 "VirtualGeometry integer is invalid");
226 *ints[i] = static_cast<int>(*n);
227 }
228 float* floats[] = {&v.builder.lodTargetRatio, &v.previewFov, &v.errorPixels, &v.nearDistance, &v.farDistance};
229 const char* ffields[] = {"targetRatio", "fov", "errorPixels", "nearDistance", "farDistance"};
230 for (int i = 0; i < 5; ++i) {
231 const auto* x = field(op.payload, ffields[i]);
232 const auto* n = x ? x->getIf<double>() : nullptr;
233 if (!n || !std::isfinite(*n))
234 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.virtualgeometry.number"),
235 "VirtualGeometry number is invalid");
236 *floats[i] = static_cast<float>(*n);
237 }
238 VirtualGeometryDocumentTarget candidate(id_);
239 candidate.value_ = v;
240 if (errors(candidate.validate()))
241 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.virtualgeometry.invalid"),
242 "VirtualGeometry document validation failed");
243 value_ = std::move(v);
244 bumpRevision();
245 widenDirty(0, 0);
246 return eve::editing::applied<void>();
247}
248std::unique_ptr<IDomainOperationTarget> VirtualGeometryDocumentTarget::cloneDomainState() const {
249 return std::make_unique<VirtualGeometryDocumentTarget>(*this);
250}
252 std::unique_ptr<IDomainOperationTarget> candidate) {
253 auto* typed = dynamic_cast<VirtualGeometryDocumentTarget*>(candidate.get());
254 if (!typed || typed->id_ != id_)
255 return eve::editing::failed<void>(EditorStatus::Conflict, RuleId("editor.virtualgeometry.candidate"),
256 "VirtualGeometry candidate mismatch");
257 *this = *typed;
258 return eve::editing::applied<void>();
259}
261 return EditorValue::Object{{"schemaVersion", int64_t{1}}, {"content", contentValue()}};
262}
264 const auto *v = field(snapshot, "schemaVersion"), *content = field(snapshot, "content");
265 const auto* version = v ? v->getIf<int64_t>() : nullptr;
266 if (!version || *version != 1 || !content)
267 return eve::editing::failed<void>(EditorStatus::Unsupported, RuleId("editor.virtualgeometry.snapshot"),
268 "Unsupported VirtualGeometry snapshot");
270 op.target = TargetId(id_);
271 op.type = "virtualgeometry.document.replace.v1";
272 op.payload = *content;
273 auto result = applyDomainOperation(op);
274 if (result.ok()) clearDirtyRegion();
275 return result;
276}
277} // namespace eve::virtualgeometry_editing
double value
float x
Definition AnimClip.cpp:738
const std::string & s
glm::vec4 p[6]
float maximum[3]
float minimum[3]
glm::vec3 n
Definition Grass.cpp:63
float v
std::int32_t c
std::string path
Definition PlayHost.cpp:110
std::string id
Definition PlayHost.cpp:108
float d
const char * category
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
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
UUID-backed identifier adapter for legacy textual boundaries.
Definition Identity.h:314
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.
virtual void clearDirtyRegion()=0
Clears dirty region.
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.
bool isWithinLimits(size_t maxDepth, size_t maxElements, size_t maxStringBytes) const
Validate nesting, element count and approximate string byte limits.
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
PropertyReadResult read(const SelectionSnapshot &selection, const PropertyPath &path) const override
Reads .
Result< DomainOperation > makeSet(const SelectionSnapshot &selection, const PropertyPath &path, const EditorValue &value, PropertySetMode mode) const override
Make set.
Result< void > loadSnapshot(const EditorValue &snapshot)
Loads snapshot.
Result< void > commitDomainState(std::unique_ptr< IDomainOperationTarget > candidate) override
Commits domain state.
const VirtualGeometryImportValue & value() const
Value.
eve::Result< eve::Revision > currentRevision(const SelectionSnapshot &selection) const override
Current revision.
std::unique_ptr< IDomainOperationTarget > cloneDomainState() const override
Clones domain state.
PropertySchema schema(const SelectionSnapshot &selection) const override
Schema.
std::vector< EditorDiagnostic > validate() const
Validate builder invariants, CPU budgets and preview sweep.
VirtualGeometryDocumentTarget(std::string id)
Virtual geometry document target.
Result< DomainOperation > makeReset(const SelectionSnapshot &selection, const PropertyPath &path) const override
Make reset.
Result< void > applyDomainOperation(const DomainOperation &operation) override
Applies domain operation.
void * queryCapability(const CapabilityId &capability) override
Query an optional target capability.
PropertySetMode
PropertySetMode public API.
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
const EditorValue * field(const EditorValue &value, const char *name)
editing::PropertyDescriptor PropertyDescriptor
DomainOperation public API.
std::vector< std::string > affectedProperties
PropertyReadResult public API.
EVENGINE_API_PLATFORM public API.
PropertyDescriptorLookup find(const PropertyPath &path) const
Finds find.
SelectionSnapshot public API.
Stable discovery metadata exposed by every editable target.
Authored virtual-geometry import and LOD-preview settings.
virtualgeometry::VirtualGeometryBuilder::Options builder