Skip to content

[core] ximesh files support [terrain type / limit widescan]#9686

Merged
WinterSolstice8 merged 1 commit intoLandSandBoat:basefrom
sruon:navmesh_ground
Apr 2, 2026
Merged

[core] ximesh files support [terrain type / limit widescan]#9686
WinterSolstice8 merged 1 commit intoLandSandBoat:basefrom
sruon:navmesh_ground

Conversation

@sruon
Copy link
Copy Markdown
Contributor

@sruon sruon commented Mar 30, 2026

I affirm:

  • I understand that if I do not agree to the following points by completing the checkboxes my PR will be ignored.
  • I understand I should leave resolving conversations to the LandSandBoat team so that reviewers won't miss what was said.
  • I have read and understood the Contributing Guide and the Code of Conduct.
  • I have tested my code and the things my code has changed since the last commit in the PR and will test after any later commits.

What does this pull request do?

Credits

I'll start with this because I legitimately can't claim credit for any of this, I am merely the plumber here.

  • @InoUno for creating the ximeshes files and making them available - also for the excellent xi-visualizer tool from which the bulk of the logic has been lifted
  • Aamace (not on GitHub), the creator of Xim, which has been useful in figuring out the actual MZB format (ref src/jsMain/kotlin/xim/resource/ZoneDefParser.kt)
  • @Xenonsmurf for NavMesh-Builder which I referenced for similar purposes
  • @atom0s for general code snippets here and there and other client decomps relating to client handling of zone data

Description

Wires in a new CZoneMesh class on CZone that loads, decompresses and prepares ximesh data for consumption.
Exposes a general query API to get a cell information starting from world coords and a handful of helpers for terrain and floorId.

There's a lot of data in there which we are not using at the moment, such as barriers but they may come in useful when we swap LOS to use this.
I didn't port the raycast from xi-visualizer at this time.

Note that this introduces zlibstatic as a dependency.
Note that the zone_mesh.cpp code defers to Detour for dtClosestHeightPointTriangle but it is not a hard dependency,

What this enables

  • Get terrain material at a given world coor.
    • Includes an example of restricting Phuabo Tidal Wave to only occur over water.
    • This will be key to making scent-based tracking work accurately
  • Get floor id at a given coord
    • Restricts Widescan to only entities on same floor as PC.
    • May be useful to start segmenting entities queries by floor in the future?

!pos has been updated to display the information.

Memory

568MB usage for all zones on a single process
The key optimization is the deduplication done at load time to reuse blocks and packed everything tightly.

Perf

As a test I added query in the AI tick path to see how it would cope under future load. About 2us per call in most of my testing.

Screenshot 2026-03-30 012007

Loading itself is around 100ms per zone

Screenshot 2026-03-30 012246

Steps to test these changes

xi_test has terrain tests.

Draft because there's a slight issue with a dozen zone name not matching the ximesh filename, need to fix that first.
Added temporary rewriting of mesh filename for the few odd zones. Will work out the real fix in ximeshes repository at some point.

@sruon sruon force-pushed the navmesh_ground branch 4 times, most recently from 03e1716 to 93f729d Compare March 31, 2026 00:44
@sruon sruon marked this pull request as ready for review March 31, 2026 00:46
@sruon sruon force-pushed the navmesh_ground branch 2 times, most recently from 054420a to 19a6454 Compare March 31, 2026 07:13
@LandSandBoat LandSandBoat deleted a comment from github-actions Bot Mar 31, 2026
Copy link
Copy Markdown
Contributor

@zach2good zach2good left a comment

Choose a reason for hiding this comment

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

Excellent work here! (A million thanks to all the original authors and reference people too!)

Comment thread src/map/lua/lua_baseentity.cpp Outdated
Comment on lines +19859 to +19860
SOL_REGISTER("getTerrainType", CLuaBaseEntity::getTerrainType);
SOL_REGISTER("getFloorId", CLuaBaseEntity::getFloorId);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd probably name these currentTerrainType or getCurrentTerrainType etc., or I'd defer to the zone:
zone:getTerrainType(player:getPos()) etc.

Just to really hammer home what's going on - but any of these changes can come in later PRs

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Moved to zone bindings.

player:setPos(54.84, -27.5, 16)
assert(player:getFloorId() == 15)
end)
end)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd probably want a couple more cases like for in Ronfaure, where our eventual scent testing will likely be tested - in the river and on the banks being different, etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added a couple positions in East Ronfaure river

Comment thread src/map/zone.h Outdated
std::unique_ptr<CNavMesh> m_navMesh;
std::unique_ptr<ZoneLos> lineOfSight;

auto zoneMesh() const -> CZoneMesh*;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is there any scenario where a zonemesh WONT be populated? Otherwise I'd push to return a reference here instead of a pointer. If the loading can fail or some zones don't have one, then we'd want to return a Maybe, so we enforce the null checking

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Is there any scenario where a zonemesh WONT be populated?

Right now we fail gracefully and let the load process continue as there are a couple naming issues we have to resolve but maybe we should back out early if the mesh doesn't load for any reason in the future.

I swapped it to Maybe for the time being.

Comment thread CMakeLists.txt
find_package(Threads REQUIRED)
link_libraries(${CMAKE_THREAD_LIBS_INIT})

CPMAddPackage(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does this mean we could get rid of our checked-in zlib stuff in the future?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

What we have in zlib.cpp isn't actually zlib, it's the FFXI custom Huffman table algorithm

@sruon sruon force-pushed the navmesh_ground branch 2 times, most recently from 00a542c to f07e93b Compare April 1, 2026 00:50
Co-Authored-By: InoUno <774909+InoUno@users.noreply.github.com>
Co-Authored-By: atom0s <atom0s@users.noreply.github.com>
Co-Authored-By: Mike Murphy <6765305+xenonsmurf@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@zach2good zach2good left a comment

Choose a reason for hiding this comment

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

Bellissimo 👍

@WinterSolstice8 WinterSolstice8 merged commit fb0cb46 into LandSandBoat:base Apr 2, 2026
10 checks passed
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.

4 participants