diff --git a/engine/src/main/java/com/volmit/iris/engine/IrisEngine.java b/engine/src/main/java/com/volmit/iris/engine/IrisEngine.java index c6b5ccace..e380261bc 100644 --- a/engine/src/main/java/com/volmit/iris/engine/IrisEngine.java +++ b/engine/src/main/java/com/volmit/iris/engine/IrisEngine.java @@ -8,9 +8,15 @@ import com.volmit.iris.platform.PlatformNamespaceKey; import com.volmit.iris.platform.PlatformRegistry; import com.volmit.iris.platform.PlatformWorld; import lombok.Data; +import manifold.util.concurrent.ConcurrentWeakHashMap; + +import java.lang.ref.WeakReference; +import java.util.Map; +import java.util.Optional; @Data public class IrisEngine { + private static final Map> engineContext = new ConcurrentWeakHashMap<>(); private final IrisPlatform platform; private final EngineRegistry registry; private final EngineConfiguration configuration; @@ -41,4 +47,16 @@ public class IrisEngine { { return getPlatform().key(nsk); } + + public static Optional context() + { + WeakReference reference = engineContext.get(Thread.currentThread()); + + if(reference != null) + { + return Optional.ofNullable(reference.get()); + } + + return Optional.empty(); + } } diff --git a/engine/src/main/java/com/volmit/iris/engine/feature/IrisFeature.java b/engine/src/main/java/com/volmit/iris/engine/feature/IrisFeature.java index aaf1854dc..c618b6f9b 100644 --- a/engine/src/main/java/com/volmit/iris/engine/feature/IrisFeature.java +++ b/engine/src/main/java/com/volmit/iris/engine/feature/IrisFeature.java @@ -7,13 +7,25 @@ import lombok.Data; @Data public abstract class IrisFeature { private final String name; + private boolean heightAgnostic; public IrisFeature(String name, IrisEngine engine) { this.name = name; + this.heightAgnostic = true; } - public abstract S prepare(IrisEngine engine, IrisFeatureSizedTarget target); + public S prepare(IrisEngine engine, IrisFeatureSizedTarget target) + { + return onPrepare(engine, target); + } - public abstract void generate(IrisEngine engine, S state, IrisFeatureTarget target); + public void generate(IrisEngine engine, S state, IrisFeatureTarget target) + { + onGenerate(engine, state, target); + } + + public abstract S onPrepare(IrisEngine engine, IrisFeatureSizedTarget target); + + public abstract void onGenerate(IrisEngine engine, S state, IrisFeatureTarget target); } diff --git a/engine/src/main/java/com/volmit/iris/engine/feature/IrisFeatureSizedTarget.java b/engine/src/main/java/com/volmit/iris/engine/feature/IrisFeatureSizedTarget.java index 88956640b..9a61303e0 100644 --- a/engine/src/main/java/com/volmit/iris/engine/feature/IrisFeatureSizedTarget.java +++ b/engine/src/main/java/com/volmit/iris/engine/feature/IrisFeatureSizedTarget.java @@ -9,6 +9,9 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.util.List; +import java.util.stream.Stream; + @Builder @NoArgsConstructor @AllArgsConstructor @@ -27,6 +30,48 @@ public class IrisFeatureSizedTarget { @Builder.Default private final int offsetZ = 0; + Stream splitX() + { + if(width <= 1) { + return Stream.of(this); + } + + return Stream.of(IrisFeatureSizedTarget.builder() + .width(width/2).height(height).depth(depth) + .offsetX(offsetX).offsetY(offsetY).offsetZ(offsetZ).build(), + IrisFeatureSizedTarget.builder() + .width(width - (width/2)).height(height).depth(depth) + .offsetX(offsetX + (width/2)).offsetY(offsetY).offsetZ(offsetZ).build()); + } + + Stream splitY() + { + if(height <= 1) { + return Stream.of(this); + } + + return Stream.of(IrisFeatureSizedTarget.builder() + .width(width).height(height/2).depth(depth) + .offsetX(offsetX).offsetY(offsetY).offsetZ(offsetZ).build(), + IrisFeatureSizedTarget.builder() + .width(width).height(height - (height / 2)).depth(depth) + .offsetX(offsetX).offsetY(offsetY + (height/2)).offsetZ(offsetZ).build()); + } + + Stream splitZ() + { + if(depth <= 1) { + return Stream.of(this); + } + + return Stream.of(IrisFeatureSizedTarget.builder() + .width(width).height(height).depth(depth/2) + .offsetX(offsetX).offsetY(offsetY).offsetZ(offsetZ).build(), + IrisFeatureSizedTarget.builder() + .width(width).height(height).depth(depth - (depth/2)) + .offsetX(offsetX).offsetY(offsetY).offsetZ(offsetZ + (depth/2)).build()); + } + public int getAbsoluteMaxX() { return getOffsetX() + getWidth() - 1; diff --git a/engine/src/main/java/com/volmit/iris/engine/feature/IrisFeatureTask.java b/engine/src/main/java/com/volmit/iris/engine/feature/IrisFeatureTask.java new file mode 100644 index 000000000..ef32118d6 --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/feature/IrisFeatureTask.java @@ -0,0 +1,52 @@ +package com.volmit.iris.engine.feature; + +import com.volmit.iris.engine.IrisEngine; +import com.volmit.iris.platform.PlatformNamespaced; +import lombok.AllArgsConstructor; +import lombok.Builder; + +import java.util.concurrent.RecursiveTask; +import java.util.stream.Collectors; + +/** + * Continuously splits up a hunk of work in all 3 dimensions until the job size is within the + * specified limits. This allows a single hunk to be split until the ideal amount of tasks can run in parallel. + * @param The namespaced object type + * @param The feature state type + */ +@Builder +@AllArgsConstructor +public class IrisFeatureTask extends RecursiveTask> { + private final IrisEngine engine; + private final IrisFeature feature; + private final IrisFeatureSizedTarget size; + private final int verticalPrepareSize; + private final int horizontalPrepareSize; + private final boolean heightAgnostic; + + @Override + protected IrisPreparedFeature compute() { + if(!heightAgnostic && size.getHeight() > verticalPrepareSize * 2) { + invokeAll(size.splitY().map(this::with).collect(Collectors.toList())); + } + + else if(size.getWidth() > horizontalPrepareSize * 2) { + invokeAll(size.splitX().map(this::with).collect(Collectors.toList())); + } + + else if(size.getDepth() > horizontalPrepareSize * 2) { + invokeAll(size.splitZ().map(this::with).collect(Collectors.toList())); + } + + else { + return new IrisPreparedFeature<>(feature, size, feature.prepare(engine, size)); + } + + return null; + } + + private IrisFeatureTask with(IrisFeatureSizedTarget size) + { + return new IrisFeatureTask<>(engine, feature, size, verticalPrepareSize, horizontalPrepareSize, heightAgnostic); + } +} diff --git a/engine/src/main/java/com/volmit/iris/engine/feature/IrisPreparedFeature.java b/engine/src/main/java/com/volmit/iris/engine/feature/IrisPreparedFeature.java new file mode 100644 index 000000000..8a374ab1a --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/feature/IrisPreparedFeature.java @@ -0,0 +1,13 @@ +package com.volmit.iris.engine.feature; + +import com.volmit.iris.platform.PlatformNamespaced; +import lombok.Builder; +import lombok.Data; + +@Builder +@Data +public class IrisPreparedFeature { + private final IrisFeature feature; + private final IrisFeatureSizedTarget size; + private final S state; +} diff --git a/engine/src/main/java/com/volmit/iris/engine/feature/standard/FeatureTerrain.java b/engine/src/main/java/com/volmit/iris/engine/feature/standard/FeatureTerrain.java index 2d10617a0..fb01ca529 100644 --- a/engine/src/main/java/com/volmit/iris/engine/feature/standard/FeatureTerrain.java +++ b/engine/src/main/java/com/volmit/iris/engine/feature/standard/FeatureTerrain.java @@ -29,7 +29,7 @@ public class FeatureTerrain extends IrisFeature target) { + public void onGenerate(IrisEngine engine, TerrainFeatureState state, IrisFeatureTarget target) { for(int x : target.localX()) { for(int z : target.localZ()) { int h = state.getNoise().get(x, z);