Skip to content

OpenGL does not render masked sprites correctly #22820

@mrmbernardi

Description

@mrmbernardi

Operating System

Linux

OpenRCT2 build

OpenRCT2, v0.4.14

Base game

RollerCoaster Tycoon 2

Area(s) with this issue?

No response

Describe the issue

The masked sprites are used to smooth the look of the transition between slopes in the following screenshot. The top window is software and the bottom window is OpenGL. Note the way software correctly has big solid pixels. OpenGL appears to have different colours within the same pixel, which should not be possible.

screenshot-2024-09-25_02:40:26

The cause is that OpenGL interpolates across the full size of the mask and colour sprites. Rather, it should just interpolate the same area across both (the minimum area of the two).

Offending code:

fTexColour = vec3(mix(vTexColourBounds.xy, vTexColourBounds.zw, m), vTexColourAtlas);
fTexMask = vec3(mix(vTexMaskBounds.xy, vTexMaskBounds.zw, m), vTexMaskAtlas);

void OpenGLDrawingContext::DrawSpriteRawMasked(
DrawPixelInfo& dpi, int32_t x, int32_t y, const ImageId maskImage, const ImageId colourImage)
{
CalculcateClipping(dpi);
auto g1ElementMask = GfxGetG1Element(maskImage);
auto g1ElementColour = GfxGetG1Element(colourImage);
if (g1ElementMask == nullptr || g1ElementColour == nullptr)
{
return;
}
const auto textureMask = _textureCache->GetOrLoadImageTexture(maskImage);
const auto textureColour = _textureCache->GetOrLoadImageTexture(colourImage);
int32_t drawOffsetX = g1ElementMask->x_offset;
int32_t drawOffsetY = g1ElementMask->y_offset;
int32_t drawWidth = std::min(g1ElementMask->width, g1ElementColour->width);
int32_t drawHeight = std::min(g1ElementMask->height, g1ElementColour->height);
int32_t left = x + drawOffsetX;
int32_t top = y + drawOffsetY;
int32_t right = left + drawWidth;
int32_t bottom = top + drawHeight;
if (left > right)
{
std::swap(left, right);
}
if (top > bottom)
{
std::swap(top, bottom);
}
left -= dpi.x;
top -= dpi.y;
right -= dpi.x;
bottom -= dpi.y;
left = dpi.zoom_level.ApplyInversedTo(left);
top = dpi.zoom_level.ApplyInversedTo(top);
right = dpi.zoom_level.ApplyInversedTo(right);
bottom = dpi.zoom_level.ApplyInversedTo(bottom);
left += _spriteOffset.x;
top += _spriteOffset.y;
right += _spriteOffset.x;
bottom += _spriteOffset.y;
DrawRectCommand& command = _commandBuffers.rects.allocate();
command.clip = { _clipLeft, _clipTop, _clipRight, _clipBottom };
command.texColourAtlas = textureColour.index;
command.texColourBounds = textureColour.normalizedBounds;
command.texMaskAtlas = textureMask.index;
command.texMaskBounds = textureMask.normalizedBounds;
command.palettes = { 0, 0, 0 };
command.flags = DrawRectCommand::FLAG_MASK;
command.colour = 0;
command.bounds = { left, top, right, bottom };
command.depth = _drawCount++;
}

Steps to reproduce

Look at a masked sprite. They appear on zoom levels 0 or lower for some terrain slope changes and surface changes.

Attachments

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    OpenGLRelates to the OpenGL graphic rendering system.bugSomething went wrong.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions