Skip to content

Conversation

@donmccurdy
Copy link
Member

@donmccurdy donmccurdy commented Nov 18, 2025

Description

The texture atlas used for billboard rendering has 1px padding enabled by default (borderWidthInPixels), but the padding is only applied around the edges of the atlas — not between images. I believe this is the cause, or at least a cause, of #4525. This PR introduces internal padding, so that the existing option affects spacing between images in the atlas. Image subregions have no padding and are unaffected.

Screenshots:

before after
border_before border_after

Issue number and link

Testing plan

See unit tests and #13037, and:

http://localhost:8080/Apps/Sandcastle2/index.html?id=itwin-feature-service

With 'points' (pins) enabled and all other features hidden, this is a good example showing the top of one pin bleeding into the bottom of the pin above it in the texture atlas.

Author checklist

  • I have submitted a Contributor License Agreement
  • I have added my name to CONTRIBUTORS.md
  • I have updated CHANGES.md with a short summary of my change
  • I have added or updated unit tests to ensure consistent code coverage
  • I have updated the inline documentation, and included code examples where relevant
  • I have performed a self-review of my code

PR Dependency Tree

This tree was auto-generated by Charcoal

@github-actions
Copy link

Thank you for the pull request, @donmccurdy!

✅ We can confirm we have a CLA on file for you.

Copy link
Contributor

@jjspace jjspace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that adding padding to the atlas works but that's just filling the last row of pixels with transparency isn't it? Would it be better to say that the billboards are displaying 1 px too much from the atlas?

@donmccurdy donmccurdy force-pushed the chore/textureatlas-visual-tests branch from 9ea515f to bd5aa3f Compare November 18, 2025 21:40
@donmccurdy donmccurdy force-pushed the fix/textureatlas-border-internal branch 2 times, most recently from 84c0e3a to 807b0e7 Compare November 18, 2025 21:45
@donmccurdy
Copy link
Member Author

donmccurdy commented Nov 18, 2025

I see that adding padding to the atlas works but that's just filling the last row of pixels with transparency isn't it?

@jjspace previously the atlas put an N-pixel border around the perimeter of the entire atlas, then packed billboards tightly inside of that. With this PR the primary goal is to have interior N-pixel borders between billboards as well.

Implementation-wise, there's more than one way to get there. TexturePacker.js implements binary tree bin packing, where each time a billboard is added, we find an empty rectangle (the large outer square below), insert the image (yellow tile below), and create two new empty rectangles in the remaining space for future use:

large square divided into an image tile surrounded by 2 empty rectangles

Image Credit: Jake Gordon

The solution in this PR is that each time we do an insertion and create those two empty rectangles in the remaining space, we offset them from the inserted billboard by N pixels so that space isn't considered available:

split_annotated

Entirely possible there are other better ways to do this! With the visual tests I'm hoping it will be easier to see the tradeoffs if we decide to change how the padding is handled.


Would it be better to say that the billboards are displaying 1 px too much from the atlas?

I think it's a combination of linear filtering in the texture sampler and perhaps some numerical precision limits. I also tested switching to 'nearest' filtering and while that definitely reduces the visual artifacts here, it doesn't solve them entirely (which this PR does, as far as I can tell). I may do a separate PR for the texture filtering, but that has other tradeoffs and wouldn't be the right fix for this specific issue probably.

In #13020 there was also some discussion of mipmapping billboards, and padding between billboards would be a hard prerequisite to that.

Finally, I suspect this may have been the original intention of the TexturePacker.js borderWidthInPixels option? Or at least I can't think of a reason that padding on the atlas perimeter would have been useful without also including internal padding.

@donmccurdy donmccurdy force-pushed the fix/textureatlas-border-internal branch from 807b0e7 to 1802637 Compare November 20, 2025 14:52
@donmccurdy donmccurdy requested a review from jjspace November 20, 2025 14:56
Base automatically changed from chore/textureatlas-visual-tests to main November 20, 2025 17:39
Copy link
Contributor

@jjspace jjspace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanations @donmccurdy, I think this makes sense given that. Just one more small question then I think this is good to go

@donmccurdy donmccurdy force-pushed the fix/textureatlas-border-internal branch from 1802637 to 4e3980c Compare November 21, 2025 16:44
@donmccurdy
Copy link
Member Author

Added the new test case, good call on that, thanks @jjspace!

@donmccurdy donmccurdy requested a review from jjspace November 21, 2025 16:46
Copy link
Contributor

@jjspace jjspace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for the updates

@jjspace jjspace added this pull request to the merge queue Nov 21, 2025
Merged via the queue into main with commit ee2b381 Nov 21, 2025
9 checks passed
@jjspace jjspace deleted the fix/textureatlas-border-internal branch November 21, 2025 17:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rouge outline and boxes around label's characters and billboards

3 participants