diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 11877c7dc3..59f0e61f88 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -2495,7 +2495,12 @@ void DrawBridgeMiddle(const TileInfo *ti) int z = bridge_z - BRIDGE_Z_START; /* Add a bounding box that separates the bridge from things below it. */ - AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, x, y, 16, 16, 1, bridge_z - TILE_HEIGHT + BB_Z_SEPARATOR); + ViewportSortableSpriteSpecialFlags special_flags = VSSF_NONE; + if (IsPlainRailTile(ti->tile) && (GetTrackBits(ti->tile) & (TRACK_BIT_LEFT | TRACK_BIT_RIGHT | TRACK_BIT_LOWER)) != 0) { + /* Problematic diagonal rail track is underneath this bridge */ + special_flags = VSSSF_SORT_SPECIAL | VSSSF_SORT_SORT_BRIDGE_BB; + } + AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, x, y, 16, 16, 1, bridge_z - TILE_HEIGHT + BB_Z_SEPARATOR, false, 0, 0, 0, nullptr, special_flags); /* Draw Trambits as SpriteCombine */ if (transport_type == TRANSPORT_ROAD || transport_type == TRANSPORT_RAIL) StartSpriteCombine(); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 9e8c43db13..33004e0269 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1862,12 +1862,14 @@ static void DoDrawVehicle(const Vehicle *v) } } + ViewportSortableSpriteSpecialFlags special_flags = IsDiagonalDirection(v->direction) ? VSSF_NONE : VSSSF_SORT_SPECIAL | VSSSF_SORT_DIAG_VEH; + StartSpriteCombine(); for (uint i = 0; i < v->sprite_seq.count; ++i) { PaletteID pal2 = v->sprite_seq.seq[i].pal; if (!pal2 || (v->vehstatus & VS_CRASHED)) pal2 = pal; AddSortableSpriteToDraw(v->sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs, - v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs); + v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs, 0, nullptr, special_flags); } EndSpriteCombine(); } diff --git a/src/viewport.cpp b/src/viewport.cpp index 598d415998..8bd27dc3a6 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -287,6 +287,7 @@ struct ViewportDrawerDynamic { TileSpriteToDrawVector tile_sprites_to_draw; ParentSpriteToDrawVector parent_sprites_to_draw; std::vector parent_sprite_sets; + ParentSpriteToDrawSubSpriteHolder parent_sprite_subsprites; ChildScreenSpriteToDrawVector child_screen_sprites_to_draw; btree::btree_map bridge_to_map_x; btree::btree_map bridge_to_map_y; @@ -1231,8 +1232,9 @@ static void AddCombinedSprite(SpriteID image, PaletteID pal, int x, int y, int z * @param bb_offset_y bounding box extent towards negative Y (world), * @param bb_offset_z bounding box extent towards negative Z (world) * @param sub Only draw a part of the sprite. + * @param special_flags Special flags (special sorting, etc). */ -void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w, int h, int dz, int z, bool transparent, int bb_offset_x, int bb_offset_y, int bb_offset_z, const SubSprite *sub) +void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w, int h, int dz, int z, bool transparent, int bb_offset_x, int bb_offset_y, int bb_offset_z, const SubSprite *sub, ViewportSortableSpriteSpecialFlags special_flags) { int32_t left, right, top, bottom; @@ -1298,7 +1300,9 @@ void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w, ps.image = image; ps.pal = pal; - ps.sub = sub; + _vdd->parent_sprite_subsprites.Set(&ps, sub); + ps.special_flags = special_flags; + ps.xmin = x + bb_offset_x; ps.xmax = x + std::max(bb_offset_x, w) - 1; @@ -1327,6 +1331,11 @@ void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w, } } +void SetLastSortableSpriteToDrawSpecialFlags(ViewportSortableSpriteSpecialFlags flags) +{ + _vdd->parent_sprites_to_draw.back().special_flags = flags; +} + /** * Starts a block of sprites, which are "combined" into a single bounding box. * @@ -2143,11 +2152,75 @@ static bool ViewportSortParentSpritesChecker() return true; } +static void ViewportSortParentSpritesSingleComparison(ParentSpriteToDraw *ps, ParentSpriteToDraw *ps2, ParentSpriteToDraw *ps_to_move, ParentSpriteToDraw **psd, ParentSpriteToDraw **psd2) +{ + /* Decide which comparator to use, based on whether the bounding + * boxes overlap + */ + if (ps->xmax >= ps2->xmin && ps->xmin <= ps2->xmax && // overlap in X? + ps->ymax >= ps2->ymin && ps->ymin <= ps2->ymax && // overlap in Y? + ps->zmax >= ps2->zmin && ps->zmin <= ps2->zmax) { // overlap in Z? + /* Use X+Y+Z as the sorting order, so sprites closer to the bottom of + * the screen and with higher Z elevation, are drawn in front. + * Here X,Y,Z are the coordinates of the "center of mass" of the sprite, + * i.e. X=(left+right)/2, etc. + * However, since we only care about order, don't actually divide / 2 + */ + if (ps->xmin + ps->xmax + ps->ymin + ps->ymax + ps->zmin + ps->zmax <= + ps2->xmin + ps2->xmax + ps2->ymin + ps2->ymax + ps2->zmin + ps2->zmax) { + return; + } + } else { + /* We only change the order, if it is definite. + * I.e. every single order of X, Y, Z says ps2 is behind ps or they overlap. + * That is: If one partial order says ps behind ps2, do not change the order. + */ + if (ps->xmax < ps2->xmin || + ps->ymax < ps2->ymin || + ps->zmax < ps2->zmin) { + return; + } + } + + /* Move ps_to_move (ps2) in front of ps */ + ParentSpriteToDraw *temp = ps_to_move; + for (auto psd3 = psd2; psd3 > psd; psd3--) { + *psd3 = *(psd3 - 1); + } + *psd = temp; +} + +bool ViewportSortParentSpritesSpecial(ParentSpriteToDraw *ps, ParentSpriteToDraw *ps2, ParentSpriteToDraw **psd, ParentSpriteToDraw **psd2) +{ + ParentSpriteToDraw temp; + + auto is_bridge_diag_veh_comparison = [&](ParentSpriteToDraw *a, ParentSpriteToDraw *b) -> bool { + if ((a->special_flags & VSSSF_SORT_SPECIAL_TYPE_MASK) == VSSSF_SORT_SORT_BRIDGE_BB && (b->special_flags & VSSSF_SORT_SPECIAL_TYPE_MASK) == VSSSF_SORT_DIAG_VEH && a->zmin > b->zmax) { + temp = *a; + temp.xmax += 4; + temp.ymax += 4; + return true; + } + return false; + }; + + if (is_bridge_diag_veh_comparison(ps, ps2)) { + ViewportSortParentSpritesSingleComparison(&temp, ps2, ps2, psd, psd2); + return true; + } + if (is_bridge_diag_veh_comparison(ps2, ps)) { + ViewportSortParentSpritesSingleComparison(ps, &temp, ps2, psd, psd2); + return true; + } + + return false; +} + /** Sort parent sprites pointer array */ static void ViewportSortParentSprites(ParentSpriteToSortVector *psdv) { - auto psdvend = psdv->end(); - auto psd = psdv->begin(); + ParentSpriteToDraw ** const psdvend = psdv->data() + psdv->size(); + ParentSpriteToDraw **psd = psdv->data(); while (psd != psdvend) { ParentSpriteToDraw *ps = *psd; @@ -2157,46 +2230,18 @@ static void ViewportSortParentSprites(ParentSpriteToSortVector *psdv) } ps->SetComparisonDone(true); + const bool is_special = (ps->special_flags & VSSSF_SORT_SPECIAL) != 0; for (auto psd2 = psd + 1; psd2 != psdvend; psd2++) { ParentSpriteToDraw *ps2 = *psd2; if (ps2->IsComparisonDone()) continue; - /* Decide which comparator to use, based on whether the bounding - * boxes overlap - */ - if (ps->xmax >= ps2->xmin && ps->xmin <= ps2->xmax && // overlap in X? - ps->ymax >= ps2->ymin && ps->ymin <= ps2->ymax && // overlap in Y? - ps->zmax >= ps2->zmin && ps->zmin <= ps2->zmax) { // overlap in Z? - /* Use X+Y+Z as the sorting order, so sprites closer to the bottom of - * the screen and with higher Z elevation, are drawn in front. - * Here X,Y,Z are the coordinates of the "center of mass" of the sprite, - * i.e. X=(left+right)/2, etc. - * However, since we only care about order, don't actually divide / 2 - */ - if (ps->xmin + ps->xmax + ps->ymin + ps->ymax + ps->zmin + ps->zmax <= - ps2->xmin + ps2->xmax + ps2->ymin + ps2->ymax + ps2->zmin + ps2->zmax) { - continue; - } - } else { - /* We only change the order, if it is definite. - * I.e. every single order of X, Y, Z says ps2 is behind ps or they overlap. - * That is: If one partial order says ps behind ps2, do not change the order. - */ - if (ps->xmax < ps2->xmin || - ps->ymax < ps2->ymin || - ps->zmax < ps2->zmin) { - continue; - } + if (is_special && (ps2->special_flags & VSSSF_SORT_SPECIAL) != 0) { + if (ViewportSortParentSpritesSpecial(ps, ps2, psd, psd2)) continue; } - /* Move ps2 in front of ps */ - ParentSpriteToDraw *temp = ps2; - for (auto psd3 = psd2; psd3 > psd; psd3--) { - *psd3 = *(psd3 - 1); - } - *psd = temp; + ViewportSortParentSpritesSingleComparison(ps, ps2, ps2, psd, psd2); } } } @@ -2204,7 +2249,7 @@ static void ViewportSortParentSprites(ParentSpriteToSortVector *psdv) static void ViewportDrawParentSprites(const ViewportDrawerDynamic *vdd, const DrawPixelInfo *dpi, const ParentSpriteToSortVector *psd, const ChildScreenSpriteToDrawVector *csstdv) { for (const ParentSpriteToDraw *ps : *psd) { - if (ps->image != SPR_EMPTY_BOUNDING_BOX) DrawSpriteViewport(vdd->sprite_data, dpi, ps->image, ps->pal, ps->x, ps->y, ps->sub); + if (ps->image != SPR_EMPTY_BOUNDING_BOX) DrawSpriteViewport(vdd->sprite_data, dpi, ps->image, ps->pal, ps->x, ps->y, vdd->parent_sprite_subsprites.Get(ps)); int child_idx = ps->first_child; while (child_idx >= 0) { @@ -4140,6 +4185,7 @@ static void ViewportDoDrawPhase3(Viewport *vp) _vdd->tile_sprites_to_draw.clear(); _vdd->parent_sprites_to_draw.clear(); _vdd->parent_sprite_sets.clear(); + _vdd->parent_sprite_subsprites.Clear(); _vdd->child_screen_sprites_to_draw.clear(); _vdd->sprite_data.Clear(); diff --git a/src/viewport_func.h b/src/viewport_func.h index 6f8c336949..b607b20d24 100644 --- a/src/viewport_func.h +++ b/src/viewport_func.h @@ -72,9 +72,18 @@ inline void MaxZoomInOut(ZoomStateChange how, Window *w) void OffsetGroundSprite(int x, int y); +enum ViewportSortableSpriteSpecialFlags : uint8_t { + VSSF_NONE = 0, + VSSSF_SORT_SPECIAL = 0x80, ///< When sorting sprites, if both sprites have this set, special sorting rules apply + VSSSF_SORT_SPECIAL_TYPE_MASK = 1, ///< Mask to use for getting the special type + VSSSF_SORT_DIAG_VEH = 0, ///< This is a vehicle moving diagonally with respect to the tile axes + VSSSF_SORT_SORT_BRIDGE_BB = 1, ///< This is a bridge BB helper sprite +}; +DECLARE_ENUM_AS_BIT_SET(ViewportSortableSpriteSpecialFlags); + void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub = nullptr, int extra_offs_x = 0, int extra_offs_y = 0); void DrawGroundSpriteAt(SpriteID image, PaletteID pal, int32_t x, int32_t y, int z, const SubSprite *sub = nullptr, int extra_offs_x = 0, int extra_offs_y = 0); -void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w, int h, int dz, int z, bool transparent = false, int bb_offset_x = 0, int bb_offset_y = 0, int bb_offset_z = 0, const SubSprite *sub = nullptr); +void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w, int h, int dz, int z, bool transparent = false, int bb_offset_x = 0, int bb_offset_y = 0, int bb_offset_z = 0, const SubSprite *sub = nullptr, ViewportSortableSpriteSpecialFlags special_flags = VSSF_NONE); void AddChildSpriteScreen(SpriteID image, PaletteID pal, int x, int y, bool transparent = false, const SubSprite *sub = nullptr, bool scale = true, ChildScreenSpritePositionMode position_mode = ChildScreenSpritePositionMode::Relative); void ViewportAddString(ViewportDrawerDynamic *vdd, const DrawPixelInfo *dpi, ZoomLevel small_from, const ViewportSign *sign, StringID string_normal, StringID string_small, StringID string_small_shadow, uint64_t params_1, uint64_t params_2 = 0, Colours colour = INVALID_COLOUR); diff --git a/src/viewport_sprite_sorter.h b/src/viewport_sprite_sorter.h index 011390c2c3..271c22052c 100644 --- a/src/viewport_sprite_sorter.h +++ b/src/viewport_sprite_sorter.h @@ -34,7 +34,14 @@ struct __attribute__ ((aligned (16))) ParentSpriteToDraw { SpriteID image; ///< sprite to draw PaletteID pal; ///< palette to use - const SubSprite *sub; ///< only draw a rectangular part of the sprite +#ifdef POINTER_IS_64BIT + int32_t sub_idx; ///< only draw a rectangular part of the sprite (store the actual pointer elsewhere to save space in this struct) +#else + const SubSprite *sub_ptr; ///< only draw a rectangular part of the sprite +#endif + uint8_t special_flags; ///< special flags + + /* 3 bytes spare! */ int32_t left; ///< minimal screen X coordinate of sprite (= x + sprite->x_offs), reference point for child sprites int32_t top; ///< minimal screen Y coordinate of sprite (= y + sprite->y_offs), reference point for child sprites @@ -51,11 +58,45 @@ static_assert(sizeof(ParentSpriteToDraw) <= 64); typedef std::vector ParentSpriteToSortVector; +#ifdef POINTER_IS_64BIT +struct ParentSpriteToDrawSubSpriteHolder { + std::vector subsprites; + + const SubSprite *Get(const ParentSpriteToDraw *ps) const + { + return ps->sub_idx >= 0 ? this->subsprites[ps->sub_idx] : nullptr; + } + + void Set(ParentSpriteToDraw *ps, const SubSprite *sub) + { + if (sub == nullptr) { + ps->sub_idx = -1; + } else { + ps->sub_idx = (int32_t)this->subsprites.size(); + this->subsprites.push_back(sub); + } + } + + void Clear() + { + this->subsprites.clear(); + } +}; +#else +struct ParentSpriteToDrawSubSpriteHolder { + const SubSprite *Get(const ParentSpriteToDraw *ps) const { return ps->sub_ptr; } + void Set(ParentSpriteToDraw *ps, const SubSprite *sub) { ps->sub_ptr = sub; } + void Clear() {} +}; +#endif + /** Type for method for checking whether a viewport sprite sorter exists. */ typedef bool (*VpSorterChecker)(); /** Type for the actual viewport sprite sorter. */ typedef void (*VpSpriteSorter)(ParentSpriteToSortVector *psd); +bool ViewportSortParentSpritesSpecial(ParentSpriteToDraw *ps, ParentSpriteToDraw *ps2, ParentSpriteToDraw **psd, ParentSpriteToDraw **psd2); + #ifdef WITH_SSE bool ViewportSortParentSpritesSSE41Checker(); void ViewportSortParentSpritesSSE41(ParentSpriteToSortVector *psdv); diff --git a/src/viewport_sprite_sorter_sse4.cpp b/src/viewport_sprite_sorter_sse4.cpp index 5a11cab248..dbc6af2240 100644 --- a/src/viewport_sprite_sorter_sse4.cpp +++ b/src/viewport_sprite_sorter_sse4.cpp @@ -13,6 +13,7 @@ #include "cpu.h" #include "smmintrin.h" #include "viewport_sprite_sorter.h" +#include "viewport_func.h" #include "safeguards.h" @@ -28,8 +29,8 @@ GNU_TARGET("sse4.1") void ViewportSortParentSpritesSSE41(ParentSpriteToSortVector *psdv) { const __m128i mask_ptest = _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0); - auto const psdvend = psdv->end(); - auto psd = psdv->begin(); + ParentSpriteToDraw ** const psdvend = psdv->data() + psdv->size(); + ParentSpriteToDraw **psd = psdv->data(); while (psd != psdvend) { ParentSpriteToDraw * const ps = *psd; @@ -39,12 +40,17 @@ void ViewportSortParentSpritesSSE41(ParentSpriteToSortVector *psdv) } ps->SetComparisonDone(true); + const bool is_special = (ps->special_flags & VSSSF_SORT_SPECIAL) != 0; for (auto psd2 = psd + 1; psd2 != psdvend; psd2++) { ParentSpriteToDraw * const ps2 = *psd2; if (ps2->IsComparisonDone()) continue; + if (is_special && (ps2->special_flags & VSSSF_SORT_SPECIAL) != 0) { + if (ViewportSortParentSpritesSpecial(ps, ps2, psd, psd2)) continue; + } + /* * Decide which comparator to use, based on whether the bounding boxes overlap *