afritz1 / OpenTESArena

Open-source re-implementation of The Elder Scrolls: Arena.
MIT License
915 stars 68 forks source link

The Great Chunk System Conversion #181

Closed afritz1 closed 3 years ago

afritz1 commented 4 years ago

The engine has spent long enough relying on fixed-size grids for all the levels. It should be redesigned so that all systems rely on chunks of 64x64 voxels.

It appears that all world types -- interior, city, and wilderness -- can share the same chunk management code except for the per-chunk population function. The world types differ in how they retrieve data from the active level: interiors and cities have a clearly defined "level" with dimensions, but the wilderness does not have the notion of a level and is more or less countably infinite in size. For interiors and cities, if a chunk intersects the level's area, it obtains the voxel data from the level, otherwise it uses a fallback chunk depending on the world type (interiors: "default" empty, cities: wrapped chunk floor).

In all cases, the chunk manager should check which chunk the player is in, find the surrounding chunks, populate those, and free any other chunks that are no longer active. When a chunk is freed, it should notify the entity manager so it can mark the contained entities for deletion in whatever way makes sense (either deleting them from existence or storing them in memory for later). The chunk manager should not store any data about entities.

Phase 1:

Convert VoxelGrid space from NSInt/int/EWInt to SNInt/int/WEInt so it is just a transpose of Arena WEInt/int/SNInt coordinates instead of both a transpose and completely different origin. No more gridWidth-1 calculations, no more town/village wilderness start point being in the wrong place. Document somewhere that "the original game is left-handed coordinates, +X west/+Z south" and "OpenTESArena is right-handed coordinates, +X south/+Z west".

Phase 2:

Change ChunkInt2 to SNInt/WEInt and ChunkVoxelInt2 to SNInt/WEInt instead of the initially-suggested C array layout. Change everything over from VoxelGrid to ChunkManager and Chunk. One example in the software renderer: the ray cast DDA loop would keep track of the current chunk and would update it once it has stepped across a chunk boundary (0 or CHUNK_DIM-1). Some ray cast functions need adjacent voxels, so they will need the chunk manager passed as an argument.

All modern world space coordinates are right-handed (or should be if they are not). When modern coordinates are projected in the renderer, they become left-handed in camera space as expected. The current voxel grid code treats +X as north and +Z as east but the proposed chunk changes are closer to the original game's while remaining a right-handed system: +X south and +Z west. The chunk code will also share the original game's origin at the top-right; this removes the need for a lot of the gridWidth-1 transforms, etc..

Related: #148.

afritz1 commented 4 years ago

Phase 1 done, next (huge) step is to remove all uses of NewInt2 and replace them with chunk coordinates [-inf, inf] and chunk voxel coordinates [0, 63].

afritz1 commented 3 years ago

If possible, the chunk size should be configurable and independent of any of the original game's data, so we can have things like 32x32 chunks or 16x16 depending on how it makes sense with engine systems and allocations.

afritz1 commented 3 years ago

Finally made it through all the compiler errors of the transition. Still lots of broken things to fix and DebugNotImplemented()'s to implement.

afritz1 commented 3 years ago

200 is merged.