Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
SequenceRuntime.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "ResourceRuntime.hpp"
5#include "SequenceClock.hpp"
7
8#include <array>
9#include <memory>
10#include <string>
11
12namespace monopoly::sequence
13{
22 {
25 std::size_t offset{};
26 std::string detail;
27 std::variant<std::monostate, data::DataError, data::ChunkError,
29 };
31 {
32 std::size_t maximumDepth{64}; // root counts as one; hard safety ceiling 128
33 std::size_t maximumDescriptions{4096};
34 std::size_t maximumReferences{65'536};
35 };
36
38 {
43 // Resolved exactly as LI_SEQNCR_StartUpSequence does for 3D mesh
44 // records. Empty for records which do not expose renderable content.
45 std::optional<data::DataId> contentsDataId;
46 // One program index for each record in the schedule, in DISK order.
47 std::vector<std::size_t> childDescriptions;
48 // Runtime-only TakeOverAndPlayBinkVideo option; never decoded from DAT.
50 // LE_SEQNCR_Start clears the internal TAB offset only for raw bitmaps.
52 };
53
54 // Immutable, bounded description DAG. Shared sublists are not expanded
55 // exponentially; cycles are rejected by (DATA ID, chunk offset). Every
56 // CNK lease needed by the supported tree is acquired before publication.
57 // Currently executable: grouping/indirect, 2D bitmap, sound, video,
58 // preloader, legacy 3D model/mesh, 3D camera and transform/FOV/audio tweekers.
59 // Attributes are immutable input.
60 // Other decoded kinds and attributes fail explicitly; no fake renderer.
62 {
63 public:
64 [[nodiscard]] static std::expected<std::shared_ptr<const SequenceProgram>, RuntimeError>
66 std::size_t offset = 0, DescriptionLimits limits = {});
67 // Runtime loading also applies Monopoly's default PrepareSequenceData:
68 // raw assets, or the direct bitmap/sound children of a grouping. As in
69 // Main.cpp, UseReferenceCounts is false: the warmed cache stays evictable.
70 [[nodiscard]] static std::expected<std::shared_ptr<const SequenceProgram>, RuntimeError>
71 load(std::shared_ptr<const data::ResourceSnapshot> resources, data::DataId id,
72 std::size_t offset = 0, DescriptionLimits limits = {});
73 [[nodiscard]] static std::expected<std::shared_ptr<const SequenceProgram>, RuntimeError>
75 DescriptionLimits limits = {});
76 // External media is owned by the video backend. Its real EOF, not a
77 // guessed sequence duration, ends this runtime-created video record.
78 [[nodiscard]] static std::expected<std::shared_ptr<const SequenceProgram>, RuntimeError>
79 runtimeVideo(data::DataId id, std::string fileName,
82 bool binkDoubleSize = false);
83 [[nodiscard]] std::span<const SequenceDescription> descriptions() const noexcept;
84 [[nodiscard]] std::shared_ptr<const data::ResourceSnapshot> resources() const noexcept;
85 private:
86 SequenceProgram() = default;
87 std::vector<SequenceDescription> descriptions_;
88 std::shared_ptr<const data::ResourceSnapshot> resources_;
89 };
90
91 using SequenceNodeId = std::uint64_t; // zero means no node; never reused
94 {
99 std::size_t offset{};
100 std::uint16_t priority{};
101 std::uint8_t label{};
102 std::uint8_t sequenceType{};
103 std::uint8_t endingAction{};
104 std::int32_t clock{};
105 };
107 {
108 std::int16_t meshIndexA{};
109 std::int16_t meshIndexB{};
111 auto operator<=>(const SequenceMeshChoice3D&) const = default;
112 };
120 {
124 // Only a Camera chunk replaces the slot's previous scale.
125 std::optional<float> scale;
126 };
128 {
130 std::uint8_t label{};
131 std::uint16_t priority{};
132 std::int32_t clock{};
134 float fieldOfView{0.7853981633974483F};
135 float nearPlane{1.0F};
136 float farPlane{5000.0F};
137 };
139 {
143 std::size_t offset{};
144 std::uint16_t priority{};
145 std::uint8_t label{};
146 std::int32_t clock{};
147 std::int32_t endTime{};
148 std::uint8_t timeMultiple{};
149 bool paused{};
151 std::uint8_t dimensionality{};
152 std::optional<data::DataId> contentsDataId;
158 std::vector<SequenceNodeId> children; // runtime priority order
160 std::uint16_t pitch{};
161 std::uint8_t volume{100};
162 std::int8_t panning{};
163 };
164
166 {
168 std::int32_t sequenceClock{};
169 std::int32_t endTime{};
170 std::uint8_t dimensionality{};
171 std::optional<Matrix3D> sequenceToWorldTransformation;
172 };
173
175 {
178 std::uint16_t priority{};
179 std::int32_t clock{};
181 std::optional<data::Sequence2DBoundingBoxAttribute> bounds;
183 // Presentation provenance; leaf placement and ordering stay authored.
186 std::uint16_t rootSequencePriority{};
187 };
189 {
192 std::uint16_t priority{};
193 std::int32_t clock{};
194 std::uint8_t endingAction{};
195 std::uint8_t dimensionality{};
196 std::uint16_t pitch{};
197 std::uint8_t volume{100};
198 std::int8_t panning{};
199 std::optional<std::int32_t> screenCenterX2D;
200 std::string fileName;
201 bool paused{};
202 std::uint64_t seekGeneration{};
203 std::optional<SequenceSpatial2DView> spatial2D;
204 // Direct buffer commands bypass positional attenuation/panning until
205 // the next SequenceMoved. Already included in volume/panning above.
206 std::optional<std::uint8_t> volumeOverride;
207 std::optional<std::int8_t> panningOverride;
208 };
210 {
212 std::uint16_t priority{};
213 std::int32_t clock{};
215 std::string fileName;
216 std::optional<data::Sequence2DBoundingBoxAttribute> boundingBox;
218 std::uint8_t endingAction{};
220 std::int32_t elapsedParentClock{};
221 std::uint16_t pitch{};
222 std::uint8_t volume{100};
223 std::int8_t panning{};
224 };
226 {
227 std::array<float, 3> minimum{};
228 std::array<float, 3> maximum{};
229 auto operator<=>(const SequenceBounds3D&) const = default;
230 };
231
233 {
235 std::uint8_t dimensionality{};
237 std::optional<data::Sequence2DBoundingBoxAttribute> bounds2D;
238 std::optional<SequenceBounds3D> bounds3D;
239 bool onScreen{true};
240 };
241
243 {
246 // Top-level CNK that owns this live mesh. Modern presentation can use
247 // this to distinguish a resting token sequence from movement sequences
248 // even when both reference the same HMD.
250 std::uint16_t priority{};
251 std::int32_t clock{};
254 std::optional<SequenceBounds3D> bounds;
255 std::optional<data::DataId> textureMapDataId;
256 std::optional<data::DataId> jointPositionsDataId;
257 // Qualification provenance; priority above stays the authored leaf
258 // ordering priority. Source L_Seqncr.cpp:3797-3799,5318-5319 gives
259 // user priority only to the root, not its disk-born children.
260 std::uint16_t rootSequencePriority{};
261 };
263 {
264 std::size_t maximumLiveNodes{65'536};
265 std::size_t maximumBirthsPerOperation{65'536};
266 };
267
268 // CPU forest below the historical implicit root. No SDL, UI callbacks,
269 // scene or GPU objects. All mutations are on the owner thread. Children
270 // are uniquely owned; handles/views never expose mutable node pointers.
271 // Start is staged. An update/seek failure destroys the forest explicitly
272 // instead of leaving a partially evaluated tree looking successful.
274 {
275 public:
276 explicit SequenceRuntime(RuntimeLimits limits = {});
280
281 [[nodiscard]] std::expected<SequenceNodeId, RuntimeError> start(
282 std::shared_ptr<const SequenceProgram> program,
283 std::uint16_t priority = 0, ClockStartOptions options = {},
284 std::optional<SequenceTransform> initialTransform = std::nullopt,
285 std::uint8_t labelOverride = 0);
286 [[nodiscard]] std::expected<void, RuntimeError> requestSoundFailure(SequenceNodeId id);
287 [[nodiscard]] std::expected<void, RuntimeError> requestSoundClock(
288 SequenceNodeId id, std::int32_t mediaClock);
289 [[nodiscard]] std::expected<void, RuntimeError> requestVideoClock(
290 SequenceNodeId node, std::int32_t mediaClock, std::int32_t duration, bool ended);
291 [[nodiscard]] std::expected<void, RuntimeError> update(std::int32_t parentClock);
292 [[nodiscard]] std::expected<void, RuntimeError> stop(SequenceNodeId node);
293 void stopAll();
294 [[nodiscard]] std::expected<void, RuntimeError> setPaused(SequenceNodeId node, bool paused);
295 [[nodiscard]] std::expected<void, RuntimeError> seek(SequenceNodeId node, std::int32_t time);
296 [[nodiscard]] std::expected<void, RuntimeError> setEndingAction(SequenceNodeId node, std::uint8_t action);
297 [[nodiscard]] std::expected<void, RuntimeError> setVolume(
298 SequenceNodeId node, std::uint8_t volume);
299 [[nodiscard]] std::expected<void, RuntimeError> setPitch(
300 SequenceNodeId node, std::uint16_t pitch);
301 [[nodiscard]] std::expected<void, RuntimeError> setPanning(
302 SequenceNodeId node, std::int8_t panning);
303 [[nodiscard]] std::expected<void, RuntimeError>
304 setScrollingWorldVisibility(SequenceNodeId node, bool onScreen);
305
306 // Historical command targeting excludes nested records (offset != 0),
307 // including during whole-tree searches. Duplicate matches are legal.
308 [[nodiscard]] std::vector<SequenceNodeId> matching(data::DataId id,
309 std::uint16_t priority, bool wholeTree = false) const;
310 [[nodiscard]] std::size_t stopMatching(data::DataId id,
311 std::uint16_t priority, bool wholeTree = false);
312 [[nodiscard]] std::expected<std::size_t, RuntimeError> setEndingActionMatching(
313 data::DataId id, std::uint16_t priority, std::uint8_t action,
314 bool wholeTree = false);
315 [[nodiscard]] std::size_t setVolumeMatching(
316 data::DataId id, std::uint16_t priority, std::uint8_t volume,
317 bool wholeTree = false);
318 [[nodiscard]] std::size_t setPitchMatching(
319 data::DataId id, std::uint16_t priority, std::uint16_t pitch,
320 bool wholeTree = false);
321 [[nodiscard]] std::size_t setPanningMatching(
322 data::DataId id, std::uint16_t priority, std::int8_t panning,
323 bool wholeTree = false);
324 [[nodiscard]] std::size_t moveMatching(data::DataId id,
325 std::uint16_t priority, const SequenceTransform& transform,
326 bool wholeTree = false);
327 [[nodiscard]] std::size_t forceRedrawMatching(data::DataId id,
328 std::uint16_t priority, bool wholeTree = false);
329
330 [[nodiscard]] std::optional<SequenceNodeView> inspect(SequenceNodeId node) const;
331 // Source-compatible LE_SEQNCR_GetInfo subset used by Monopoly: first
332 // DataID/priority match at offset zero, optionally including descendants.
333 [[nodiscard]] std::optional<SequenceInfoView> info(data::DataId id,
334 std::uint16_t priority, bool wholeTree = false) const;
335 // LE_SEQNCR_IsSequenceFinished: true when the first matching sequence
336 // has reached its end time or when no matching sequence is active.
338 data::DataId id, std::uint16_t priority,
339 bool wholeTree = false) const;
340 // LE_SEQNCR_GetChildMeshWorldMatrix: select the first matching top-level
341 // root, then search only that root/subtree for the first active 3D mesh.
342 [[nodiscard]] std::optional<Matrix3D> childMeshWorldMatrix(
343 data::DataId id, std::uint16_t priority) const;
344 // Active 3D mesh leaves in runtime traversal order. This is CPU render
345 // intent only: no HMD decoding, GPU resource or render-slot ownership.
346 // Active 2D bitmap leaves in runtime traversal order.
347 [[nodiscard]] std::vector<SequenceBitmapInstanceView> bitmapInstances() const;
348 [[nodiscard]] std::vector<SequenceSoundInstanceView> soundInstances() const;
349 [[nodiscard]] std::vector<SequenceVideoInstanceView> videoInstances() const;
350 [[nodiscard]] std::vector<SequenceMeshInstanceView> meshInstances() const;
351 [[nodiscard]] std::vector<SequenceScrollingWorldView>
353 // Active 3D camera sequences with raw ArtLib FOV semantics. Projection
354 // interpretation remains the renderer's responsibility.
355 [[nodiscard]] std::vector<SequenceCamera3DView> cameraInstances() const;
356 // Any current label owner in 2D can drive the render-slot camera.
357 // Bitmap bounds are replaced by resolved image metadata at publication.
358 [[nodiscard]] std::optional<SequenceCamera2DView> camera2DForLabel(
359 std::uint8_t label) const;
360 // Any current 3D label owner supplies the pose; non-camera nodes use
361 // the retail half-FOV and clip defaults. The newest owner wins, and
362 // removing it does not restore an older overlapping owner.
363 [[nodiscard]] std::optional<SequenceCamera3DView> cameraForLabel(
364 std::uint8_t label) const;
365 [[nodiscard]] std::vector<SequenceNodeId> roots() const;
366 [[nodiscard]] std::size_t liveNodeCount() const noexcept;
367 // Diagnostic lifecycle trace for the last operation, not ArtLib label
368 // notifications/callbacks. Cleared on each mutation: no growing queue.
370
371 private:
372 struct Node;
373 using Nodes = std::vector<std::unique_ptr<Node>>;
374 [[nodiscard]] Node* find(SequenceNodeId id) const;
375 [[nodiscard]] std::expected<std::unique_ptr<Node>, RuntimeError> create(
376 std::shared_ptr<const SequenceProgram> program, std::size_t description,
377 Node* parent, std::uint16_t priority, ClockStartOptions options,
378 std::uint8_t labelOverride = 0);
379 void insert(Nodes& siblings, std::unique_ptr<Node> node);
380 void emit(SequenceEventKind kind, const Node& node);
381 void destroyChildren(Node& node);
382 void destroy(std::unique_ptr<Node>& node);
383 void erase(Node& node);
384 void clearForest();
385 void forceAncestors(Node& node);
386 void forceDescendants(Node& node);
387 void clearRedrawFlags();
388 void move(Node& node, const SequenceTransform& transform);
389 void updatePosition(Node& node);
390 [[nodiscard]] std::expected<void, RuntimeError> birthChildren(Node& node,
391 std::optional<std::int32_t> previous);
392 [[nodiscard]] std::expected<void, RuntimeError> rebuildChildren(Node& node);
393 [[nodiscard]] std::expected<bool, RuntimeError> seekNode(Node& node, std::int32_t time);
394 [[nodiscard]] std::expected<void, RuntimeError> applyTweeker(Node& node);
395 [[nodiscard]] std::expected<bool, RuntimeError> updateNode(Node& node, std::int32_t parentClock);
396 RuntimeLimits limits_;
397 Nodes roots_;
398 std::vector<SequenceEvent> events_;
399 SequenceNodeId nextId_{1};
400 std::size_t liveNodes_{};
401 std::size_t births_{};
402 std::int32_t parentClock_{};
403 bool clockStarted_{};
404 // Mirrors the single legacy LE_SEQNCR_LabelArray shared by
405 // cameras and explicitly labelled sequences. The newest owner wins;
406 // deleting it does not restore an older overlapping owner.
407 std::array<SequenceNodeId, 256> labelOwners_{};
408 };
409}
std::size_t offset
std::uint16_t priority
std::shared_ptr< const data::HmdTextureImage > source
unsigned index
std::string_view label
Kind kind
std::uint16_t pitch
std::uint8_t volume
std::int8_t panning
std::span< const SequenceDescription > descriptions() const noexcept
std::shared_ptr< const data::ResourceSnapshot > resources() const noexcept
static std::expected< std::shared_ptr< const SequenceProgram >, RuntimeError > rawBitmap(data::DataId id, data::LegacyDataType sourceType, DescriptionLimits limits={})
static std::expected< std::shared_ptr< const SequenceProgram >, RuntimeError > load(const data::DataSource &source, data::DataId id, std::size_t offset=0, DescriptionLimits limits={})
static std::expected< std::shared_ptr< const SequenceProgram >, RuntimeError > runtimeVideo(data::DataId id, std::string fileName, data::SequenceVideoData options, data::Sequence2DBoundingBoxAttribute bounds, bool binkDoubleSize=false)
std::expected< void, RuntimeError > setEndingAction(SequenceNodeId node, std::uint8_t action)
std::expected< void, RuntimeError > setVolume(SequenceNodeId node, std::uint8_t volume)
std::vector< SequenceScrollingWorldView > scrollingWorldInstances() const
SequenceRuntime(const SequenceRuntime &)=delete
SequenceRuntime & operator=(const SequenceRuntime &)=delete
bool isSequenceFinished(data::DataId id, std::uint16_t priority, bool wholeTree=false) const
std::vector< SequenceVideoInstanceView > videoInstances() const
std::vector< SequenceNodeId > matching(data::DataId id, std::uint16_t priority, bool wholeTree=false) const
std::size_t setPitchMatching(data::DataId id, std::uint16_t priority, std::uint16_t pitch, bool wholeTree=false)
std::expected< void, RuntimeError > requestSoundClock(SequenceNodeId id, std::int32_t mediaClock)
std::size_t forceRedrawMatching(data::DataId id, std::uint16_t priority, bool wholeTree=false)
std::expected< void, RuntimeError > setPaused(SequenceNodeId node, bool paused)
std::expected< void, RuntimeError > update(std::int32_t parentClock)
std::expected< void, RuntimeError > requestVideoClock(SequenceNodeId node, std::int32_t mediaClock, std::int32_t duration, bool ended)
std::optional< Matrix3D > childMeshWorldMatrix(data::DataId id, std::uint16_t priority) const
std::size_t moveMatching(data::DataId id, std::uint16_t priority, const SequenceTransform &transform, bool wholeTree=false)
std::expected< void, RuntimeError > stop(SequenceNodeId node)
std::optional< SequenceCamera2DView > camera2DForLabel(std::uint8_t label) const
std::size_t setPanningMatching(data::DataId id, std::uint16_t priority, std::int8_t panning, bool wholeTree=false)
std::expected< void, RuntimeError > setPitch(SequenceNodeId node, std::uint16_t pitch)
std::size_t stopMatching(data::DataId id, std::uint16_t priority, bool wholeTree=false)
std::vector< SequenceNodeId > roots() const
std::expected< std::size_t, RuntimeError > setEndingActionMatching(data::DataId id, std::uint16_t priority, std::uint8_t action, bool wholeTree=false)
std::vector< SequenceBitmapInstanceView > bitmapInstances() const
std::expected< SequenceNodeId, RuntimeError > start(std::shared_ptr< const SequenceProgram > program, std::uint16_t priority=0, ClockStartOptions options={}, std::optional< SequenceTransform > initialTransform=std::nullopt, std::uint8_t labelOverride=0)
std::vector< SequenceSoundInstanceView > soundInstances() const
std::size_t liveNodeCount() const noexcept
std::size_t setVolumeMatching(data::DataId id, std::uint16_t priority, std::uint8_t volume, bool wholeTree=false)
std::span< const SequenceEvent > events() const noexcept
std::expected< void, RuntimeError > requestSoundFailure(SequenceNodeId id)
std::expected< void, RuntimeError > setScrollingWorldVisibility(SequenceNodeId node, bool onScreen)
std::vector< SequenceCamera3DView > cameraInstances() const
std::vector< SequenceMeshInstanceView > meshInstances() const
std::optional< SequenceInfoView > info(data::DataId id, std::uint16_t priority, bool wholeTree=false) const
std::expected< void, RuntimeError > setPanning(SequenceNodeId node, std::int8_t panning)
std::expected< void, RuntimeError > seek(SequenceNodeId node, std::int32_t time)
std::optional< SequenceCamera3DView > cameraForLabel(std::uint8_t label) const
std::optional< SequenceNodeView > inspect(SequenceNodeId node) const
std::uint32_t DataId
Definition DataBanks.hpp:10
std::variant< std::monostate, Matrix2D, Matrix3D > SequenceTransform
std::uint64_t SequenceNodeId
std::variant< std::monostate, data::DataError, data::ChunkError, data::SequenceError, ClockError > cause
std::optional< data::Sequence2DBoundingBoxAttribute > bounds
auto operator<=>(const SequenceBounds3D &) const =default
data::Sequence2DBoundingBoxAttribute bounds
std::vector< std::size_t > childDescriptions
data::LegacySequenceAttributes attributes
std::optional< data::DataId > contentsDataId
std::optional< Matrix3D > sequenceToWorldTransformation
auto operator<=>(const SequenceMeshChoice3D &) const =default
std::optional< data::DataId > textureMapDataId
std::optional< data::DataId > jointPositionsDataId
std::optional< SequenceBounds3D > bounds
std::vector< SequenceNodeId > children
std::optional< data::DataId > contentsDataId
std::optional< SequenceBounds3D > bounds3D
std::optional< data::Sequence2DBoundingBoxAttribute > bounds2D
std::optional< SequenceSpatial2DView > spatial2D
data::Sequence2DBoundingBoxAttribute bounds
std::optional< data::Sequence2DBoundingBoxAttribute > boundingBox