Skip to content

Implement Buildup Brush#32

Draft
mchlopecki wants to merge 6 commits intodmlary:mainfrom
mchlopecki:buildup-paint-brush
Draft

Implement Buildup Brush#32
mchlopecki wants to merge 6 commits intodmlary:mainfrom
mchlopecki:buildup-paint-brush

Conversation

@mchlopecki
Copy link
Contributor

@mchlopecki mchlopecki commented Aug 8, 2025

DRAFT: Implements #17

I'm planning to tackle this feature and have outlined requirements and considerations for the feature.

To enable/disable the feature a button should be added to the toolbar and a default hotkey configured to toggle it as well.
toolbar_with_proposed_toggle

Rough TODO list:

  • Add buildup brush button to toolbar
    • Create buildup brush icon
  • Add buildup brush behavior
    • Updates preview cursor to appropriate cell
    • Creates tile on highest contiguous layer cursor regardless of depth
      Current implementation relies on collision masks, and moving up one cell along work axis if the cast is in an occupied cell.
    • Does not stack tiles if mouse is clicking and dragging on same cell between frames
    • Should probably implement a line-drawing algorithm for clicking and dragging across larger spaces

Possible options/toggles:

  • Max buildup height (default should either be off or high value)
  • Higher layer visibility
    • This will allow working on lower layers in case changes need to be made or gaps need to be filled
      I no longer believe that this feature make sense to be bundled with the buildup brush given the current implementation, but is still worth considering as a feature for non-buildup brush mode.
  • Allow "buildup erasing" that erases the highest touching layer instead of the working layer

Problem with my line-drawing branch:
Currently I have test cases that won't successfully compile because the code relies on Godot data types, and when managing memory for these structures, godot-cpp references an extern function that doesn't exist at time of test runs. I got around it by going into the LocalVector class and defining a wrapper that redirects everything to the c++ standard library Vector, but it isn't present in the branch so tests will fail. Also the "run_tests" command in scons needed tweaks for Windows, but I'm not stressing about that right now.

@mchlopecki
Copy link
Contributor Author

Here's my progress so far:

I started by polling the cell on the work plane that the cursor would hit and calculating a displacement upwards until an empty cell was found. The problem was that when I updated the preview cursor to follow this logic, often times the cell placement felt wrong and unintuitive. A birds-eye view while working would place the cell exactly where the cursor was working, but it was hard to get a sense of height. View angles near the horizon helped give a sense of height but you would often have a large difference between your cursor and the placed tile.

This current iteration, which I believe is good enough to test if you'd like, uses a raycast to poll the cell on the cursor. Now, the tiles will be placed on the cursor, which is much more intuitive. Unfortunately, because only the tiled_node has a physics collision, the int_node hexmap currently does not work in this implementation. Adding some collision to that int_node hexmap should make it work, but because that node has no visuals (debug or otherwise, as far as I can tell), it won't be the best user experience to use the buildup brush on that node. For the time being, the buildup brush could be only enabled for the tiled_node, but I don't see why we could work towards implementing it for both hexmaps.

If int_node hexmap is in fact desired, there could be a nice feature that the tiled_node could borrow. Currently, the tiled hexmap polls for a cell and has to do another check to see if it is occupied so that cells that have a geometry that doesn't fill the entire cell could be overwritten (the ocean tiles in the demo aren't a full cell height, so the raycast polls the occupied cell, and then I move up along the work axis into an unoccupied cell). If a generic "full cell only" map existed, that map could be polled instead. Since a "full cell" map is what the int_node would have if it gained collision, I could see the tiled hexmap borrowing it for ease of implementation (and possibly we could still allow for a setting that toggles the behavior from raycasting the "full cell" map or the actual geometry of the hexmap).

Other notes:
Currently the raycast checks for collisions on layer 1, which is hardcoded but is a simple tweak to get the actual layers the map exists on. However, I was wondering if there should be a designated layer for this tool's operation, and if we could or should make it a changeable setting (perhaps a user wants to "trace" along another physics body, or another tool uses the same layer) and whether or not we could/should remove that collision layer on run/exporting the project.

The editor cursor now has another update function that accepts a hexmap node to be polled for the buildup brush, and reports an error if its null. We could merge the two functions together and only have the cursor poll the hexmap if its not a nullptr, but I'm not sure whether that'll lead to future pains?

Anyhow, @dmlary, feel free to take a look and see if my progress is what you had in mind and if you have any thoughts.

@dmlary
Copy link
Owner

dmlary commented Aug 25, 2025

You're right about the collision problem with the HexMapIntNode.

That may be a show-stopper for this brush for the time being as the primary use case is HexMapIntNode with HexMapAutoTiledNode. It makes far less sense with HexMapTiledNode.

There are alternate picking options, but they're significantly more complex and maybe not worth it. I'm also guessing that if we did the collision against the HexMapAutoTiledNode cells, we'd probably also get strange behavior based on physic shapes not taking up the entire cell.

Hold off on this for now. Maybe something will come to mind in time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants