-
Notifications
You must be signed in to change notification settings - Fork 19
Set up an initial framework to allow megastructures to spawn in the world #454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
feb3991
to
3624275
Compare
Please rebase for clarity. |
3624275
to
345639b
Compare
Done! Thanks for reviewing the other PRs so quickly! |
345639b
to
1a7c2bc
Compare
e59b65d
to
b9a4c40
Compare
I think this is ready for another review, as I've fixed the worst readability issues. The lint build step will fail until #456 is merged. EDIT: I just realized that some of my comments on the implementation of the new EDIT2: Done. |
80cbf9c
to
26ed3de
Compare
26ed3de
to
99dc590
Compare
17e70ab
to
4d39487
Compare
To provide something to test with, an option has been added to scatter a random assortment of horospheres throughout the world.
4d39487
to
a056b96
Compare
@Ralith: I finally got around to addressing all of the PR comments. For re-reviewing this, I believe the main thing to focus on is One notable logic change I made is in Minor: Also, I would like to double-check that |
This PR adds all the fundamental additions required to support the random spawning of large structures. To demonstrate this framework, a temporary option is available to spawn a random assortment of horospheres throughout the world.
While I tried to document the code well to explain the reasoning behind the algorithm, a summary would be worthwhile, so I'll repeat the summary I gave in Discord a while back (with a few changes):
One nice thing about the dodecahedral tiling, which isn't true of every tiling but works in our case, is that every time you get to a neighbor of a node, you are crossing a plane that divides the space into two halves. You can never cross that plane again without crossing a descender. Here is a 2D representation:

A horosphere in any given position can only be generated by one unique node. The criteria for a node to be able to generate a horosphere are as follows:
Following these two rules and carefully choosing a probability distribution within a given node allows you to generate a random distribution of horospheres in a completely isotropic way.
Implementation-wise, each node containing part of a horosphere needs to have a reference to said horosphere. To avoid unbounded memory usage, we can "forget" about a horosphere the moment we cross a plane that leaves that horosphere behind.
The problem is that some of these horospheres may intersect with each other. To prevent this, one naive approach would be to just skip generating a horosphere if it would overlap with one of the horospheres it (or a parent) already generated. The issue is that it doesn't capture all possible overlaps because two nodes that aren't ancestors of each other can generate intersecting horospheres. The picture below shows a 2D example, where dots with arrows show which node owns which horocycle.

The problem arises because although these two nodes initially appear unrelated, they are related after all because it is possible to reach the same node from both of them. Nodes of the same depth in the graph with this property need to be somewhat aware of each other to avoid causing conflicts when they generate any kind of megastructure. In this PR, I call them "peer nodes", and a fair amount of logic is needed to determine which nodes are peer nodes. See the PR's diff for details.
To get the full list of potential horospheres a node needs to know about, there are three steps:
For more detail on step 3, a candidate can only become a horosphere if it doesn't intersect with any other inherited horospheres or higher-priority candidate horospheres known by the node we're focused on or any of its peers. Candidate horosphere priority can be chosen pretty much arbitrarily. This PR just uses their node-local w-coordinate.
The interaction of steps 1, 2, and 3 means that the order in which various properties are initialized in various nodes interact in intricate ways, so it is no longer feasible to populate
NodeState
for each node in the order it's added to the graph. Instead, we split it intoPartialNodeState
(for nodes that completed step 2), andNodeState
(for nodes that completed step 3).One consequence of the importance of peer nodes is that it effectively widens node paths in the graph. In practice, this should be fine, as we already tend to want to form a large sphere of nodes around each player, but it may increase the cost of some things. I don't expect it to be an important bottleneck, though.
For a visual of this, see the below image. For the circled node to have a fully set up

NodeState
, all red nodes need aNodeState
, and all blue nodes need aPartialNodeState
.Fortunately, this approach should be reusable for any kind of megastructure, as the only property this relies on is the fact that it can be bounded by a plane.
One concern I have had in the past is that this intersection test is somewhat asymmetric, so isotropy may no longer be preserved. I don't think this is a major concern, as things look correct. Perhaps there is a way that someone could use this to find the origin if they're lost using some tricky analysis on the distribution of megastructures, but I don't think that's possible, and if it is, it probably isn't something we need to address.
For simplicity, this PR only allows a node to reference at most one horosphere, which effectively means that all horospheres are separated so that the convex hulls of the nodes containing them will never intersect.