载入中...
搜索中...
未找到
LightingTarget.cpp
浏览该文件的文档.
2
3#include <cmath>
4#include <utility>
5
6namespace eve::lighting_editing {
7namespace {
8
9PropertyDescriptor property(const char* path, const char* label, const char* category, PropertyType type,
11 PropertyDescriptor result;
12 result.path = PropertyPath(path);
13 result.displayNameKey = label;
14 result.category = category;
15 result.type = type;
16 result.flags = PropertyFlag::Runtime;
17 result.defaultValue = std::move(defaultValue);
18 return result;
19}
20
21void numeric(PropertySchema& schema, const char* path, const char* label, const char* category, double value,
22 double minimum, double maximum, double step = 0.01) {
23 auto descriptor = property(path, label, category, PropertyType::Float, value);
24 descriptor.numeric.minimum = minimum;
25 descriptor.numeric.maximum = maximum;
26 descriptor.numeric.step = step;
27 schema.properties.push_back(std::move(descriptor));
28}
29
30PropertySchema lightSchema() {
31 PropertySchema schema;
32 schema.typeId = "graphics.light3d";
33 auto type = property("light.type", "editor.light.type", "light", PropertyType::Enum, "point");
34 type.enumItems = {"point", "dir", "spot"};
35 schema.properties.push_back(std::move(type));
36 schema.properties.push_back(property("light.enabled", "editor.light.enabled", "light", PropertyType::Bool, true));
37 schema.properties.push_back(property("transform.position", "editor.light.position", "transform", PropertyType::Vec3,
38 EditorValue::Array{0.0, 0.0, 0.0}));
39 schema.properties.push_back(property("transform.direction", "editor.light.direction", "transform",
40 PropertyType::Vec3, EditorValue::Array{0.4, 1.0, 0.3}));
41 schema.properties.push_back(property("light.color", "editor.light.color", "light", PropertyType::Color,
42 EditorValue::Array{1.0, 1.0, 1.0, 1.0}));
43 numeric(schema, "light.intensity", "editor.light.intensity", "light", 1.0, 0.0, 100000.0);
44 numeric(schema, "light.radius", "editor.light.radius", "light", 8.0, 0.001, 100000.0);
45 numeric(schema, "light.spot-angle", "editor.light.spot-angle", "light", 30.0, 0.1, 89.0);
46 numeric(schema, "light.spot-softness", "editor.light.spot-softness", "light", 0.35, 0.0, 1.0);
47 schema.properties.push_back(
48 property("shadow.cast", "editor.light.cast-shadow", "shadow", PropertyType::Bool, false));
49 auto shadowMethod =
50 property("shadow.method", "editor.light.shadow-method", "shadow", PropertyType::Enum, "auto");
51 shadowMethod.enumItems = {"auto", "none", "csm", "perspective", "cube"};
52 schema.properties.push_back(std::move(shadowMethod));
53 numeric(schema, "shadow.bias", "editor.light.shadow-bias", "shadow", 0.0, 0.0, 1.0, 0.0001);
54 numeric(schema, "shadow.strength", "editor.light.shadow-strength", "shadow", 1.0, 0.0, 1.0);
55 schema.properties.push_back(
56 property("volumetric.enabled", "editor.light.volumetric", "volumetric", PropertyType::Bool, false));
57 numeric(schema, "volumetric.intensity", "editor.light.volumetric-intensity", "volumetric", 1.0, 0.0, 100.0);
58 schema.properties.push_back(property("volumetric.only", "editor.light.volumetric-only", "volumetric",
59 PropertyType::Bool, false));
60 return schema;
61}
62
63PropertySchema environmentSchema() {
64 PropertySchema schema;
65 schema.typeId = "graphics.environment";
66 auto mode = property("environment.mode", "editor.environment.mode", "environment", PropertyType::Enum, "static");
67 mode.enumItems = {"static", "daynight", "weather"};
68 schema.properties.push_back(std::move(mode));
69 schema.properties.push_back(property("environment.sky-color", "editor.environment.sky-color", "environment",
70 PropertyType::Color, EditorValue::Array{0.4, 0.6, 1.0, 1.0}));
71 schema.properties.push_back(property("environment.ambient-color", "editor.environment.ambient-color", "environment",
72 PropertyType::Color, EditorValue::Array{0.12, 0.12, 0.14, 1.0}));
73 numeric(schema, "environment.exposure", "editor.environment.exposure", "environment", 1.0, 0.05, 8.0);
74 numeric(schema, "daynight.time", "editor.environment.time", "daynight", 12.0, 0.0, 24.0, 0.1);
75 numeric(schema, "daynight.speed", "editor.environment.speed", "daynight", 1.0, 0.0, 1000.0, 0.1);
76 schema.properties.push_back(
77 property("daynight.paused", "editor.environment.paused", "daynight", PropertyType::Bool, false));
78 numeric(schema, "daynight.turbidity", "editor.environment.turbidity", "daynight", 2.5, 1.5, 10.0);
79 numeric(schema, "daynight.mie", "editor.environment.mie", "daynight", 1.0, 0.0, 4.0);
80 schema.properties.push_back(
81 property("daynight.skybox", "editor.environment.skybox", "daynight", PropertyType::Bool, true));
82 auto preset =
83 property("weather.preset", "editor.environment.weather-preset", "weather", PropertyType::Enum, "clear");
84 preset.enumItems = {"clear", "drizzle", "rain", "storm", "snow", "fog"};
85 schema.properties.push_back(std::move(preset));
86 numeric(schema, "weather.intensity", "editor.environment.weather-intensity", "weather", 0.0, 0.0, 1.0);
87 numeric(schema, "weather.wind-speed", "editor.environment.wind-speed", "weather", 0.0, 0.0, 200.0);
88 numeric(schema, "weather.wind-direction", "editor.environment.wind-direction", "weather", 0.0, -360.0, 360.0);
89 schema.properties.push_back(
90 property("weather.lightning", "editor.environment.lightning", "weather", PropertyType::Bool, false));
91 schema.properties.push_back(property("weather.fog-color", "editor.environment.fog-color", "weather",
92 PropertyType::Color, EditorValue::Array{0.5, 0.55, 0.65, 1.0}));
93 numeric(schema, "weather.fog-density", "editor.environment.fog-density", "weather", 0.0, 0.0, 10.0);
94 schema.properties.push_back(property("weather.environment-enabled", "editor.environment.weather-owned", "weather",
95 PropertyType::Bool, false));
96 return schema;
97}
98
99const EditorValue* field(const EditorValue& value, const char* key) {
100 const auto* object = value.getIf<EditorValue::Object>();
101 if (!object) return nullptr;
102 const auto found = object->find(key);
103 return found == object->end() ? nullptr : &found->second;
104}
105
106} // namespace
107
108LightingPropertyTargetBase::LightingPropertyTargetBase(std::string id, std::string targetType, PropertySchema schema)
109 : id_(std::move(id)), targetType_(std::move(targetType)), schema_(std::move(schema)) {
110 for (const PropertyDescriptor& descriptor : schema_.properties)
111 values_[descriptor.path.value()] = descriptor.defaultValue;
112}
113
115 TargetDescriptor result;
116 result.id = TargetId(id_);
117 result.type = targetType_;
118 result.revision = revisionValue();
119 result.capabilities = {CapabilityId("eve.editor.target.lighting-properties")};
120 return result;
121}
122
124 return capability == CapabilityId("eve.editor.target.lighting-properties") ? static_cast<IPropertyProvider*>(this)
125 : nullptr;
126}
127
129 if (operation.target != TargetId(id_))
130 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.lighting.target-mismatch"),
131 "Lighting operation targets another document");
132 if (operation.type != "lighting.property.set.v1")
133 return eve::editing::failed<void>(EditorStatus::Unsupported, RuleId("editor.lighting.operation-unsupported"),
134 "Lighting operation is unsupported: " + operation.type);
135 const EditorValue* pathValue = field(operation.payload, "path");
136 const EditorValue* assigned = field(operation.payload, "value");
137 const auto* path = pathValue ? pathValue->getIf<std::string>() : nullptr;
139 if (!descriptor || !assigned)
140 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.lighting.invalid-operation"),
141 "Lighting operation requires a known path and value");
142 auto valid = validatePropertyValue(*descriptor, *assigned);
143 if (!valid.ok()) return valid;
144 values_[*path] = *assigned;
145 bumpRevision();
146 widenDirty(0, 0);
147 return eve::editing::applied<void>();
148}
149
151 if (!selectionMatches(selection))
153 eve::DiagnosticCode::InvalidArgument, "Selection does not belong to this lighting target",
154 "editor.lighting.selection", {}, "editor.LightingPropertyTargetBase"));
156}
157
159
161 const PropertyPath& path) const {
162 if (!selectionMatches(selection)) return {};
163 const auto found = values_.find(path.value());
164 return found == values_.end() ? PropertyReadResult{}
165 : PropertyReadResult{PropertyReadState::Value, found->second, {}};
166}
167
169 const PropertyPath& path, const EditorValue& assigned,
170 PropertySetMode mode) const {
171 if (!selectionMatches(selection))
172 return eve::editing::failed<DomainOperation>(EditorStatus::Rejected, RuleId("editor.lighting.selection"),
173 "Selection does not belong to this lighting target");
174 if (mode == PropertySetMode::Reset) return makeReset(selection, path);
175 if (mode != PropertySetMode::Absolute)
176 return eve::editing::failed<DomainOperation>(EditorStatus::Unsupported, RuleId("editor.lighting.set-mode"),
177 "Lighting properties require absolute assignment");
178 auto descriptor = schema_.find(path);
179 if (!descriptor)
180 return eve::editing::failed<DomainOperation>(EditorStatus::Unsupported,
181 RuleId("editor.lighting.property-unsupported"),
182 "Lighting property is unknown: " + path.value());
183 auto valid = validatePropertyValue(*descriptor, assigned);
184 if (!valid.ok()) return Result<DomainOperation>::failure(valid.status());
185 const auto previous = values_.find(path.value());
186 EditorValue::Object payload{{"path", path.value()}, {"value", assigned}};
187 EditorValue::Object inverse{{"path", path.value()}, {"value", previous->second}};
189 operation.type = "lighting.property.set.v1";
190 operation.target = TargetId(id_);
191 operation.payload = EditorValue(std::move(payload));
192 operation.inverse = EditorValue(std::move(inverse));
193 operation.hasInverse = true;
194 operation.affectedProperties.push_back(path.value());
195 operation.mergeKey = "lighting:" + id_ + ":" + path.value();
196 return eve::editing::applied<DomainOperation>(std::move(operation));
197}
198
200 const PropertyPath& path) const {
201 auto descriptor = schema_.find(path);
202 if (!descriptor)
203 return eve::editing::failed<DomainOperation>(EditorStatus::Unsupported,
204 RuleId("editor.lighting.property-unsupported"),
205 "Lighting property is unknown: " + path.value());
206 return makeSet(selection, path, descriptor->defaultValue, PropertySetMode::Absolute);
207}
208
211 for (const auto& [path, assigned] : values_) properties[path] = assigned;
212 return EditorValue::Object{{"schemaVersion", int64_t{1}}, {"properties", std::move(properties)}};
213}
214
216 const EditorValue* versionValue = field(snapshot, "schemaVersion");
217 const EditorValue* propertiesValue = field(snapshot, "properties");
218 const auto* version = versionValue ? versionValue->getIf<int64_t>() : nullptr;
219 const auto* properties = propertiesValue ? propertiesValue->getIf<EditorValue::Object>() : nullptr;
220 if (!version || *version != 1 || !properties)
221 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.lighting.invalid-snapshot"),
222 "Lighting snapshot requires schema version one and properties");
223 std::map<std::string, EditorValue> candidate;
224 for (const PropertyDescriptor& descriptor : schema_.properties)
225 candidate[descriptor.path.value()] = descriptor.defaultValue;
226 for (const auto& [path, assigned] : *properties) {
227 auto descriptor = schema_.find(PropertyPath(path));
228 if (!descriptor)
229 return eve::editing::failed<void>(EditorStatus::Unsupported, RuleId("editor.lighting.snapshot-property"),
230 "Lighting snapshot contains an unknown property: " + path);
231 auto valid = validatePropertyValue(*descriptor, assigned);
232 if (!valid.ok()) return valid;
233 candidate[path] = assigned;
234 }
235 values_ = std::move(candidate);
236 bumpRevision();
238 return eve::editing::applied<void>();
239}
240
241const EditorValue* LightingPropertyTargetBase::value(const std::string& path) const {
242 const auto found = values_.find(path);
243 return found == values_.end() ? nullptr : &found->second;
244}
245
246bool LightingPropertyTargetBase::selectionMatches(const SelectionSnapshot& selection) const {
247 return selection.items.size() == 1 && selection.items.front().target == TargetId(id_);
248}
249
251 : LightingPropertyTargetBase(std::move(id), "light3d-document", lightSchema()) {}
252
253std::vector<EditorDiagnostic> Light3DDocumentTarget::validate() const {
254 std::vector<EditorDiagnostic> diagnostics;
255 const auto* type = value("light.type")->getIf<std::string>();
256 const auto* direction = value("transform.direction")->getIf<EditorValue::Array>();
257 const auto* castShadow = value("shadow.cast")->getIf<bool>();
258 const auto* methodName = value("shadow.method") ? value("shadow.method")->getIf<std::string>()
259 : nullptr;
260 if (direction && direction->size() == 3) {
261 double lengthSquared = 0.0;
262 for (const EditorValue& component : *direction) {
263 const auto* number = component.getIf<double>();
264 if (number) lengthSquared += *number * *number;
265 }
266 if (lengthSquared <= 1e-12)
268 RuleId("editor.light.zero-direction"), DiagnosticSeverity::Error, "Light direction cannot be zero"));
269 }
270 const std::string method = methodName ? *methodName : "auto";
271 if (type && castShadow && *castShadow) {
272 if (*type == "point" && (method == "auto" || method == "cube"))
274 RuleId("editor.light.point-shadow-reserved"), DiagnosticSeverity::Warning,
275 "Point cube shadows are reserved; enable via gfx.setShadowSchemePointEnabled when the atlas path ships"));
276 if (*type == "dir" && method != "auto" && method != "none" && method != "csm")
278 RuleId("editor.light.dir-shadow-method"), DiagnosticSeverity::Warning,
279 "Directional lights only support csm (or auto/none) shadow methods"));
280 if (*type == "spot" && method != "auto" && method != "none" && method != "perspective")
282 RuleId("editor.light.spot-shadow-method"), DiagnosticSeverity::Warning,
283 "Spot lights only support perspective (or auto/none) shadow methods"));
284 if (*type == "point" && method != "auto" && method != "none" && method != "cube")
286 RuleId("editor.light.point-shadow-method"), DiagnosticSeverity::Warning,
287 "Point lights only support cube (or auto/none) shadow methods"));
288 }
289 return diagnostics;
290}
291
293 : LightingPropertyTargetBase(std::move(id), "environment-document", environmentSchema()) {}
294
295std::vector<EditorDiagnostic> EnvironmentDocumentTarget::validate() const {
296 std::vector<EditorDiagnostic> diagnostics;
297 const auto* mode = value("environment.mode")->getIf<std::string>();
298 const auto* lightning = value("weather.lightning")->getIf<bool>();
299 const auto* preset = value("weather.preset")->getIf<std::string>();
300 if (mode && *mode == "weather" && lightning && *lightning && preset && *preset != "storm")
302 RuleId("editor.environment.lightning-preset"), DiagnosticSeverity::Warning,
303 "Lightning is enabled outside the storm preset"));
304 const auto* weatherOwned = value("weather.environment-enabled")->getIf<bool>();
305 if (mode && *mode == "daynight" && weatherOwned && *weatherOwned)
307 RuleId("editor.environment.multiple-owners"), DiagnosticSeverity::Error,
308 "DayNight and Weather cannot both own the environment"));
309 return diagnostics;
310}
311
312} // namespace eve::lighting_editing
ActionParameterOperation operation
double value
Value::Object payload
std::string descriptor
std::string label
std::uint16_t method
float maximum[3]
float minimum[3]
std::uint32_t key
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
RoadLaneDirection direction
double number
bool found
float step
Definition TreeMesh.cpp:314
const char * category
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.
eve::OptionalRef< C > capability()
Query a capability through its interface-owned stable identity.
virtual void clearDirtyRegion()=0
Clears dirty region.
UI-independent property access for an immutable selection snapshot.
PropertyDescriptorLookup public API.
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.
EnvironmentDocumentTarget(std::string id)
Environment document target.
std::vector< EditorDiagnostic > validate() const
Validate mode-dependent atmosphere and weather settings.
std::vector< EditorDiagnostic > validate() const
Validate direction, shadow and point/directional cross-field rules.
Light3DDocumentTarget(std::string id)
Light 3 d document target.
Shared property-document implementation for light and environment targets.
PropertySchema schema(const SelectionSnapshot &selection) const override
Schema.
const EditorValue * value(const std::string &path) const
Read a typed runtime-neutral property value by path.
EditorValue snapshotValue() const
Capture deterministic values for document persistence.
Result< void > applyDomainOperation(const DomainOperation &operation) override
Applies domain operation.
void * queryCapability(const CapabilityId &capability) override
Query an optional target capability.
PropertyReadResult read(const SelectionSnapshot &selection, const PropertyPath &path) const override
Reads .
LightingPropertyTargetBase(std::string id, std::string targetType, PropertySchema schema)
Construct a concrete property target from its schema and defaults.
TargetDescriptor describe() const override
Describe.
Result< void > loadSnapshot(const EditorValue &snapshot)
Atomically load a schema-version-one property snapshot.
Result< DomainOperation > makeSet(const SelectionSnapshot &selection, const PropertyPath &path, const EditorValue &value, PropertySetMode mode) const override
Make set.
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.
const char * defaultValue
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.
editing::Value EditorValue
editing::PropertyPath PropertyPath
editing::PropertyType PropertyType
editing::CapabilityId CapabilityId
editing::TargetId TargetId
editing::PropertySchema PropertySchema
editing::RuleId RuleId
editing::PropertyDescriptor PropertyDescriptor
const EditorValue * field(const EditorValue &value, const char *name)
DomainOperation public API.
PropertyDescriptor 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.
std::vector< SelectionItem > items
Stable discovery metadata exposed by every editable target.
std::vector< CapabilityId > capabilities