46 const std::optional<sequence::SequenceCamera2DView>& camera)
noexcept
49 if (!std::ranges::all_of(camera->worldTransform.values,
50 [](
float value) { return std::isfinite(value); }) ||
51 (camera->scale && !std::isfinite(*camera->scale)))
return;
52 cameraCenter_ = boundsCenter(camera->worldTransform, camera->bounds);
53 const auto origin = transformPoint(camera->worldTransform, 0, 0);
54 const auto xAxis = transformPoint(camera->worldTransform, 1'000'000, 0);
55 const auto dx =
static_cast<double>(
xAxis[0]) - origin[0];
56 const auto dy =
static_cast<double>(
xAxis[1]) - origin[1];
57 cameraRotation_ =
dx != 0.0 ||
dy != 0.0
58 ?
static_cast<float>(-std::atan2(
dy,
dx)) : 0.0F;
59 if (camera->scale) cameraScale_ = *camera->scale;
62 originShift.values[6] = -
static_cast<float>(cameraCenter_[0]);
63 originShift.values[7] = -
static_cast<float>(cameraCenter_[1]);
68 if (cameraScale_ != 0.0F)
72 1.0F / cameraScale_, -cameraRotation_));
73 if (std::ranges::all_of(
inverse.values,
74 [](
float value) { return std::isfinite(value); }))
80 const std::vector<sequence::SequenceBitmapRenderItem>& items,
83 std::set<sequence::SequenceNodeId>
incoming;
84 for (
const auto& item : items)
86 if (!
incoming.insert(item.node).second)
87 return std::unexpected(
"duplicate 2D sequence render node");
88 if (!std::ranges::all_of(item.worldTransform.values,
89 [](
float value) { return std::isfinite(value); }))
90 return std::unexpected(
"non-finite 2D sequence matrix");
94 std::map<sequence::SequenceNodeId, std::shared_ptr<const data::BitmapRuntimeAsset>>
assets;
95 using Owner = std::pair<data::DataId, sequence::SequenceNodeId>;
96 std::map<Owner, sequence::SequenceNodeId>
shells;
97 for (
const auto& item : items)
99 auto asset = item.runtimeAsset;
102 auto decoded =
cache.resolve(item.contentsDataId, item.metadata.type, item.bytes);
106 assets.emplace(item.node, asset);
107 if (modernMenuSkin_ && (item.rootSequenceDataId == 0x000502DA ||
108 item.rootSequenceDataId == 0x000502DC || item.rootSequenceDataId == 0x000502DE))
113 assets[item.node] = modernMenuSkin_->substitute(item.rootSequenceDataId, asset,
true);
116 if (modernSkin_ && modernSkin_->supportsCardFaceIn(item.rootSequenceDataId))
120 assets[item.node] = modernSkin_->substitute(item.rootSequenceDataId, asset,
true);
123 if ((modernSkin_ && modernSkin_->supports(item.rootSequenceDataId)) ||
124 (modernMenuSkin_ && modernMenuSkin_->supports(item.rootSequenceDataId)))
126 const Owner owner{item.rootSequenceDataId, item.rootSequenceNode};
128 const auto area = std::uint64_t(asset->image.width) * asset->image.height;
130 assets.at(
it->second)->image.height)
131 shells[owner] = item.node;
134 for (
const auto& [owner, node] :
shells)
136 const auto& item = *std::ranges::find_if(items, [node](
const auto& item) {
return item.node == node; });
140 raster.values[0] =
float(item.bounds->right-item.bounds->left) /
assets.at(node)->image.width;
141 raster.values[4] =
float(item.bounds->bottom-item.bounds->top) /
assets.at(node)->image.height;
147 const bool menuOwner = modernMenuSkin_ && modernMenuSkin_->supports(owner.first);
148 const bool idleCard=modernSkin_ && modernSkin_->supportsIdleCardPresentation(owner.first);
149 const bool auctionToken=modernSkin_ && modernSkin_->supportsAuctionToken(owner.first);
150 auto replacement =
menuOwner ? modernMenuSkin_->substitute(owner.first,
assets.at(node),
true) :
152 idleCard ||
auctionToken || (!item.bounds && item.metadata.originX == 0 && item.metadata.originY == 0) ?
153 std::optional<std::uint16_t>{idleCard || auctionToken ? item.rootSequencePriority : item.priority} : std::nullopt,
155 if (replacement ==
assets.at(node))
continue;
156 assets[node] = std::move(replacement);
157 for (
const auto& item : items)
158 if (item.node != node &&
Owner{item.rootSequenceDataId, item.rootSequenceNode} == owner)
160 modernMenuSkin_->substitute(owner.first,
assets.at(item.node),
false) :
161 modernSkin_->substitute(owner.first,
assets.at(item.node),
false);
163 std::map<sequence::SequenceNodeId, SequenceWorld2DObject>
next;
164 std::vector<sequence::SequenceNodeId> order;
166 for (
const auto& item : items)
168 auto asset =
assets.at(item.node);
173 if (!asset->image.width || !asset->image.height)
174 return std::unexpected(
"zero-sized 2D bitmap asset");
176 if (asset->presentationRect)
178 const auto&
box = *asset->presentationRect;
179 if (!std::ranges::all_of(
box, [](
float v) {
return std::isfinite(v); }) ||
181 return std::unexpected(
"invalid 2D presentation rectangle");
187 else if (item.bounds)
189 const auto&
box = *item.bounds;
190 const auto width =
static_cast<std::int64_t
>(
box.right) -
box.left;
191 const auto height =
static_cast<std::int64_t
>(
box.bottom) -
box.top;
193 static_cast<float>(asset->image.width);
195 static_cast<float>(asset->image.height);
203 if (asset->preferLinearFiltering && item.metadata.width && item.metadata.height)
205 rasterToSequence.values[0] =
static_cast<float>(item.metadata.width) / asset->image.width;
206 rasterToSequence.values[4] =
static_cast<float>(item.metadata.height) / asset->image.height;
212 if (!std::ranges::all_of(positioned.values,
213 [](
float value) { return std::isfinite(value); }))
214 return std::unexpected(
"non-finite 2D bitmap placement");
215 const auto*
old = find(item.node);
217 else if (
old->asset != asset ||
old->priority != item.priority ||
218 old->worldTransform.values != positioned.values) ++stats.
moved;
220 next.emplace(item.node, SequenceWorld2DObject{item.node, item.contentsDataId,
221 item.priority, item.clock, positioned, asset});
222 order.push_back(item.node);
224 for (
const auto& [
id,
object] : objects_)