TheOpenSpaceProgram / osp-magnum

A spaceship game
https://www.openspaceprogram.org/
MIT License
216 stars 32 forks source link

Update Longeron++ submodule #281

Closed Capital-Asterisk closed 5 months ago

Capital-Asterisk commented 6 months ago

Longeron++ is a library consisting of some OSP 'core' components at the time, that I split off to allow it to be used on different projects and be presented on its own. I made some updates to fix some ugly code.

This PR updates the Longeron++ submodule, and uses some of the new features with the planeta module. Many other parts of the code can be cleaned up in future changes.

osp::BitVector_t replacement

BitVectors are often used in OSP to represent sets of unique integer IDs (eg: [1, 2, 24, 83]). This is done for performance and simplicity: easily outperforming std::set or sorted std::vector of integers in many tasks. Iterating 'ones' bits can be done very quickly as we have 64 bit ints (not to mention SIMD). This is able to work in this project specifically as IDs are array indices and are close to zero; the highest ID correlates to the max number of objects.

osp::BitVector_t (aka: lgrn::BitView< std::vector<uint64_t> >) has an interface similar to std::bitset. The way it's used is kinda ugly:

// get these from somewhere. ActiveEnt is osp::StrongId<std::uint32_t, ...>
ActiveEnt foo = ActiveEnt{12}; 
ActiveEnt bar = ActiveEnt{63};

osp::BitVector_t ids;
osp::bitvector_resize(ids, 100); // yucky

// IDs converted to an integer to add to the set. two different methods:
ids.set(std::size_t(foo));
ids.set(bar.value);

// ids now contains [12, 63]

ids.test(foo.value); // = true

for (std::size_t const idInt : ids.ones())
{
    ActiveEnt id = ActiveEnt::from_index(idInt);
    // do something with id
}

ids.reset(bar.value); // Remove from set
ids.reset() // Remove all

This has lots of ugly integer conversions needed here and there, so I added lgrn::IdSetStl. This has an interface similar to std::set. Equivalent code to above:

// get these from somewhere. ActiveEnt is osp::StrongId<std::uint32_t, ...>
ActiveEnt foo = ActiveEnt{12}; 
ActiveEnt bar = ActiveEnt{63};

lgrn::IdSetStl ids;
ids.resize(100);

// Emplace is also supported (pass function arguments to ActiveEnt's constructor), but insert is recommended to disallow constructing from raw integers.
ids.insert(foo);
ids.insert(bar);

// ids now contains [12, 63]

ids.contains(foo); // = true

for (ActiveEnt const id : ids)
{
    // do something with id, no casting required
}

ids.erase(bar); // Remove from set
ids.clear() // Remove all

Id Registry improvements

Removed stinky lgrn::IdRegistry, completely replaced with lgrn::IdRegistryStl. IdRegistryStl now has iterators. Iterating all possible IDs previously looks something like this:

for (std::size_t const drawEntInt : rScene.m_scnRdr.m_drawIds.bitview().zeros())
{
    auto const drawEnt = DrawEnt(drawEntInt);
    // do something with drawEnt
}

This works because IdRegistryStl is internally just a bit view / bitvector; ones bits are used for free IDs, and zeros bits are used for existing ones. There are countless instances of this in the code (search for "bitview().zeros()"), which can now be replaced with:

for (DrawEnt const drawEnt : rScene.m_scnRdr.m_drawIds)
{
    // do something with drawEnt
}