115 template <
typename World,
typename Sink>
118 using Shape =
typename World::shape_type;
121 World::schema_type::template contains<StairTag>,
122 "StairTransitions references a field absent from the schema.");
123 emit_for_feet(world, chunk, chunk, sink);
128 const auto coord_of_chunk = chunk_coord<Shape>(chunk);
129 const auto scan_foot_chunk = [&](
ChunkCoord3 neighbor) {
130 const auto key = chunk_key<Shape>(neighbor);
131 if constexpr (std::is_same_v<
typename World::residency_type,
136 if (!world.is_resident(key)) {
140 emit_for_feet(world, key, chunk, sink);
142 if (coord_of_chunk.z > 0) {
143 auto below = coord_of_chunk;
145 scan_foot_chunk(below);
147 if (coord_of_chunk.x > 0) {
148 auto west = coord_of_chunk;
150 scan_foot_chunk(west);
152 if (coord_of_chunk.x + 1 < Traits::chunk_count_x) {
153 auto east = coord_of_chunk;
155 scan_foot_chunk(east);
157 if (coord_of_chunk.y > 0) {
158 auto south = coord_of_chunk;
160 scan_foot_chunk(south);
162 if (coord_of_chunk.y + 1 < Traits::chunk_count_y) {
163 auto north = coord_of_chunk;
165 scan_foot_chunk(north);
173 template <
typename World,
typename Sink>
174 static void emit_for_feet(
const World& world,
ChunkKey foot_chunk,
175 ChunkKey origin_chunk, Sink&& sink) {
176 using Shape =
typename World::shape_type;
178 const auto& page = world.chunk(foot_chunk);
179 const auto origin = chunk_coord<Shape>(foot_chunk);
181 static_cast<std::int64_t
>(origin.x * Traits::chunk.x),
182 static_cast<std::int64_t
>(origin.y * Traits::chunk.y),
183 static_cast<std::int64_t
>(origin.z * Traits::chunk.z),
185 for (std::uint64_t raw_id = 0; raw_id < Traits::local_tile_count;
187 const auto value = page.template field<StairTag>(
LocalTileId{raw_id});
188 static_assert(std::is_integral_v<std::remove_cvref_t<
decltype(value)>>,
189 "StairTransitions requires an integral stair field.");
193 if constexpr (std::is_signed_v<std::remove_cvref_t<
decltype(value)>>) {
197 }
else if (value == 0) {
200 if (
static_cast<std::uint64_t
>(value) >
201 static_cast<std::uint64_t
>(StairDirection::NegativeY)) {
204 const auto direction =
205 static_cast<StairDirection
>(
static_cast<std::uint8_t
>(value));
206 const auto local = local_coord_of<Shape>(raw_id);
208 Coord3{base.x + local.x, base.y + local.y, base.z + local.z};
212 case StairDirection::PositiveX:
215 case StairDirection::NegativeX:
218 case StairDirection::PositiveY:
221 case StairDirection::NegativeY:
224 case StairDirection::None:
227 if (!contains<Shape>(landing)) {
232 const auto foot_chunk_coord = chunk_coord<Shape>(foot);
233 const auto landing_chunk_coord = chunk_coord<Shape>(landing);
234 const auto dx = foot_chunk_coord.x != landing_chunk_coord.x ? 1 : 0;
235 const auto dy = foot_chunk_coord.y != landing_chunk_coord.y ? 1 : 0;
236 const auto dz = foot_chunk_coord.z != landing_chunk_coord.z ? 1 : 0;
237 if (dx + dy + dz > 1) {
240 const auto landing_chunk = chunk_key<Shape>(landing_chunk_coord);
241 if (foot_chunk.value == origin_chunk.value) {
243 if (landing_chunk.value == origin_chunk.value) {
246 }
else if (landing_chunk.value == origin_chunk.value) {
252 template <
typename Shape>
253 [[nodiscard]]
static auto local_coord_of(std::uint64_t raw_id)
noexcept
255 const auto chunk = ShapeTraits<Shape>::chunk;
256 const auto xy =
static_cast<std::uint64_t
>(chunk.x) * chunk.y;
257 const auto z = raw_id / xy;
258 const auto rest = raw_id % xy;
260 static_cast<std::int64_t
>(rest % chunk.x),
261 static_cast<std::int64_t
>(rest / chunk.x),
262 static_cast<std::int64_t
>(z),