Faceted World Generation - neighboring data

Josharias

Conjurer of Grimoires
Contributor
World
SpecOps
When using facets to do tree generation, I came across a couple problems when it comes to extending the viewport of facet data outside the bounds of the original region (using border3d). This is important so that trees that straddle chunk boundaries can be generated in both chunks the same way.

1. Noise that is across the region boundary needs to be mirrored so that when the neighboring region generates the noise naturally, it matches.
Build mirroring into each facet producer? or can we build in something automatic to avoid errors?
2. Extending the viewport of facet data means that all (or most) facets need to also be extended the same amount so that the facet being produced can lookup values from other facets.
Dont allow facets to define a custom border? or fix all the facet's borders to the largest defined border?

Thanks for the input,
Josharias
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
1. Noise that is across the region boundary needs to be mirrored so that when the neighboring region generates the noise naturally, it matches.
Build mirroring into each facet producer? or can we build in something automatic to avoid errors?
I'm not sure what you mean. Noise functions should be deterministic and infinite - you don't generate noise for a region, you sample from the noise function. So regions will naturally match up, because they're adjacent samples of the same function. There should be no need for "mirroring" (not sure what you mean by this) or any other work to join regions.

2. Extending the viewport of facet data means that all (or most) facets need to also be extended the same amount so that the facet being produced can lookup values from other facets.
Dont allow facets to define a custom border? or fix all the facet's borders to the largest defined border?
Providers declare the border they need for facets they require. These borders should be accumulated over dependencies, so if provider A requires Facet 1 with a border of 1 to produce Facet 2, and provider B request Facet 2 with a border of 1, then Provider A will be told to produce for a region with a border of 2 by the world generation system. I thought this was already in place?



Regarding trees, I feel that the Facets only need to provide a region (probably a cylinder) and a seed for each tree, and the rasterizer should take care of the rest. If a tree overlaps chunks, then the full tree structure would be generated each time and then the relevant parts placed on the current chunk being generated. At the facet layer the concern is the positioning and size of each tree, and allowing later providers to clear trees for whatever structures they may add.
 

Josharias

Conjurer of Grimoires
Contributor
World
SpecOps
1. You are right, mirroring is the wrong concept. Matching the noise from one facet to the next is the root problem. After taking another look on how/why this problem exists, I found that it stems from iterating over chunk local coordinates to look up noise as apposed to world coordinates. I am not sure yet as to the implications of using world coordinates instead of chunk coordinates, but that would likely fix it.

If not using only world coordinates for this, could one bake in some trickery so that iterating over the region contents produces positions that are relative to the borderless region. So then for a 2d region with a border of 1, the top/left position in the bordered region would be (-1,-1). And the origin (0,0) would be the same position as it would if there were no border. (see the attached picture)


2. It doesnt look like borders have been completely implemented if that is the intent (at least, I havent found it). It looks like there are 2 different potential mechanisms to handle borders...
  • Region.getBorderForFacet(...) - which currently returns a fixed size border. But there is no mechanism to help get facets with a different border size. Should this instead be iterating through the facets, finding the largest border requested to apply to the supplied facet?
  • @Facet(value=xxx.class, border = @FacetBorder(top=x, bottom = x, sides = x)) - It would seem like this will ensure that when a facet of this type is requested by Region.getRegionFacet(...), it will have the specified border applied.
Am I anywhere close to what you were thinking with borders?
 

Attachments

Immortius

Lead Software Architect
Contributor
Architecture
GUI
1. You are right, mirroring is the wrong concept. Matching the noise from one facet to the next is the root problem. After taking another look on how/why this problem exists, I found that it stems from iterating over chunk local coordinates to look up noise as apposed to world coordinates. I am not sure yet as to the implications of using world coordinates instead of chunk coordinates, but that would likely fix it.
In the existing world gen code, this problem shouldn't exist - world coordinates are already used. For instance, in the PerlinBaseSurfaceProvider:

Code:
        Rect2i processRegion = facet.getWorldRegion();
        float[] noise = surfaceNoise.noise(processRegion);
facet.getWorldRegion() is what is sent to the noise - that is the absolute region, not the chunk regions. Anyhow, it is absolutely the intention that world coords are used when generating values from noise functions. It still makes sense to work in local coordinates (not chunk coordinates as such, the World/Region system shouldn't know anything about chunks as such) when combining facets or similar - the facets provide methods to work in either coordinate system.

Maybe I got this wrong in a provider?

2. It doesnt look like borders have been completely implemented if that is the intent (at least, I havent found it). It looks like there are 2 different potential mechanisms to handle borders...
  • Region.getBorderForFacet(...) - which currently returns a fixed size border. But there is no mechanism to help get facets with a different border size. Should this instead be iterating through the facets, finding the largest border requested to apply to the supplied facet?
  • @Facet(value=xxx.class, border = @FacetBorder(top=x, bottom = x, sides = x)) - It would seem like this will ensure that when a facet of this type is requested by Region.getRegionFacet(...), it will have the specified border applied.
Am I anywhere close to what you were thinking with borders?
Huh, odd. I could have sworn I got further. Maybe I'm confusing with the original implementation I threw away, and simply hardcoded the only border I needed when starting the second version. Anyway, I envisioned Region calculating the necessary border, yes. Possibly once upfront. The intent is the annotation is only saying how much border is needed for that required facet relative to the size of facet being generated by the provider.
 

Josharias

Conjurer of Grimoires
Contributor
World
SpecOps
Thank you for the thoughts Immortius.

1. In playing around with facets yesterday I think I have gotten a grasp of the local vs world coordinates and when it is appropriate to use either. It is unfortunate I dug in to the Flora provider first, it uses the NoiseTable which does not have the nifty world-local translation feature that the other noise classes have it doesnt use world coordinates. The PerlinBaseSurfaceProvider does indeed use the world coordinates as you described, it was easy to miss because there is so much other stuff in that class that is only dealing with local coordinates.
**edit** wait, the noise table may work as desired.

2. Ill try my hand at letting the Region be smart about borders.
 
Top