Compare commits

...

197 Commits

Author SHA1 Message Date
Zoë 4917160123 Update README.md 2023-10-17 18:32:00 -06:00
Zoë Gidiere d71f7d4c36 clarify value and change default 2023-10-03 21:03:44 -06:00
Zoë Gidiere 84898a7a6b fixes 2023-10-03 20:03:46 -06:00
Zoë Gidiere 0c1a6efc72 refractor classes 2023-10-03 19:42:15 -06:00
Zoë Gidiere 200281f140 dep orev1 2023-10-03 11:58:32 -06:00
Zoë Gidiere f1ea8074de config-ore-v2 2023-10-03 11:54:18 -06:00
Astrash f896a9a278 Bump noise3d addon minor version 2023-10-03 11:01:50 +11:00
Astrash 79b3b34669 Add slant API for noise3d 2023-10-03 09:51:39 +11:00
Zoë 158deaa971 Merge pull request #408 from PolyhedralDev/dev/physics
Allows you to create a block with physics enabled
2023-10-01 19:07:15 -06:00
Astrash e51e025e9c Implement cubic spline sampler 2023-10-02 11:14:43 +11:00
Zoë Gidiere 8e0d64dccd Make bukkit work 2023-09-30 10:49:28 -06:00
Zoë Gidiere 23f47de10a fix pack loading error 2023-09-29 23:35:17 -06:00
Zoë Gidiere 89651597c2 default to info logging 2023-09-29 23:32:40 -06:00
Zoë 5c0c833b70 Update LifecycleEntryPoint.java 2023-09-29 23:09:41 -06:00
Zoë Gidiere 33d654dc8e impl fabric 2023-09-29 23:05:05 -06:00
Zoë Gidiere f0c602d7e7 implement physics on the api side
we will see if platform changes are needed
2023-09-29 22:10:03 -06:00
Zoë Gidiere 0e37a839fe We do a little commonifying 2023-09-29 21:44:12 -06:00
Astrash 3f9ead0d66 Remove repeated code in cellular sampler 2023-09-27 13:39:51 +10:00
David W 5eeb5af6c4 Add cell center offset return to CELLULAR sampler (#407)
* Add offset lookup return to cellular sampler

* bump noise function plugin version

* revert version to 1.1.0

* rename OffsetNoiseLookup, switch axis orientation

* rename return type aswell in cellcampler
2023-09-21 22:23:49 +00:00
Astrash 81e354f91c Use tectonic properly 2023-07-18 22:06:08 +10:00
Astrash aab28ff4f9 Bump version to 6.4.0 2023-07-18 14:32:12 +10:00
Astrash 0e3a756011 Bump config-noise-function to v1.1.0 2023-07-18 14:29:54 +10:00
Astrash 02198e1b88 Implement distance sampler 2023-07-18 14:29:28 +10:00
Astrash 00aeb98419 Implement translation sampler 2023-07-18 14:27:36 +10:00
Astrash 1a784b51ac Implement expression normalizer sampler 2023-07-18 14:25:07 +10:00
Astrash 34c0895c1f Make metalist injection error more user friendly 2023-07-16 22:46:23 +10:00
Astrash 379fa601a3 Meta annotate LINEAR_HEIGHTMAP sampler 2023-07-16 17:04:51 +10:00
Astrash fcbf51d80b Allow Range keys to be meta annotated 2023-07-16 11:51:51 +10:00
Astrash 9d83dfd164 Bump version to 6.3.2 2023-07-16 11:49:14 +10:00
Astrashh 72686601ee Merge pull request #406 from PolyhedralDev/ver/6.3.1
Ver/6.3.1
2023-07-15 07:04:10 +10:00
Astrash 73baaec6cd Bump version to 6.3.1 2023-07-11 09:33:57 +10:00
Astrashh 0be7213ee5 Merge pull request #401 from PolyhedralDev/dev/reduce-pipeline-caching
Reduce pipeline v2 caching
2023-06-20 10:10:35 +10:00
Astrash 3f3e2fe97c Reduce pipeline v2 caching 2023-06-20 09:57:43 +10:00
dfsek 549edd11ea update ServerCommandSourceMixin 2023-06-15 17:27:26 -07:00
dfsek 36f89946d4 update SignBlockEntityMixin 2023-06-15 17:26:04 -07:00
dfsek 18644d6100 update fabric dependencies 2023-06-15 17:22:21 -07:00
dfsek 9d38ee4329 repackage NMS 1.20.1 2023-06-15 17:18:36 -07:00
dfsek b75a8f85e4 restore NMSChunkGeneratorDelegate#getBaseColumn 2023-06-15 17:16:20 -07:00
dfsek aad58f5968 update nms imports 2023-06-15 17:14:45 -07:00
dfsek a548c30484 clean up unused bstats relocation 2023-06-15 17:11:53 -07:00
dfsek 9ba46ae3a5 make new bukkit NMS module 2023-06-15 17:11:32 -07:00
dfsek 49efbed6f5 update versions 2023-06-15 17:11:04 -07:00
dfsek 4001a56100 Merge pull request #400 from PolyhedralDev/ver/6.3.0
make forge compile
2023-06-15 15:55:49 -07:00
dfsek f46f35d2ad make forge compile 2023-06-15 15:52:36 -07:00
dfsek 70dbd2f2c0 Merge pull request #399 from PolyhedralDev/ver/6.3.0
Ver/6.3.0
2023-06-15 14:05:02 -07:00
dfsek bf1be62d54 Merge pull request #385 from PolyhedralDev/dev/img-lib
Image library
2023-06-15 14:00:44 -07:00
Astrash a5cbce3667 Optimize cardinal rotations for RotateColorSampler 2023-06-14 12:07:37 +10:00
Astrash d0591f292e Use primitive int over Integer 2023-06-14 11:15:25 +10:00
dfsek 27874ce0a5 Merge pull request #396 from ccorp2002/master
1.19.4 Support...?
2023-06-10 15:55:12 -07:00
dfsek 170687abdb remove unused mixin 2023-06-10 15:53:27 -07:00
dfsek 46b61d841d remove unneeded buildscript 2023-06-10 15:50:45 -07:00
dfsek 41b7021121 fix fabric pack reloading 2023-06-10 15:49:33 -07:00
dfsek 183255140b remove unused RegistrarInstance class 2023-06-10 15:28:46 -07:00
dfsek bea8f97179 fix bukkit pack reloading 2023-06-09 03:33:47 -07:00
dfsek 60fec05e12 Merge remote-tracking branch 'origin/ver/6.3.0'
# Conflicts:
#	build.gradle.kts
#	buildSrc/src/main/kotlin/Versions.kt
#	platforms/bukkit/build.gradle.kts
#	platforms/bukkit/nms/v1_19_R1/build.gradle.kts
#	platforms/bukkit/nms/v1_19_R2/build.gradle.kts
#	platforms/mixin-common/src/main/java/com/dfsek/terra/mod/ModPlatform.java
#	platforms/mixin-common/src/main/java/com/dfsek/terra/mod/generation/TerraBiomeSource.java
#	platforms/mixin-common/src/main/java/com/dfsek/terra/mod/util/PresetUtil.java
#	platforms/mixin-lifecycle/src/main/java/com/dfsek/terra/lifecycle/LifecyclePlatform.java
#	platforms/mixin-lifecycle/src/main/java/com/dfsek/terra/lifecycle/mixin/lifecycle/RegistryLoaderMixin.java
#	platforms/mixin-lifecycle/src/main/java/com/dfsek/terra/lifecycle/util/LifecycleUtil.java
2023-06-09 03:16:18 -07:00
dfsek e79cc21c11 1.19.4 fabric 2023-06-09 03:04:41 -07:00
Astrash 5188477a6d Merge branch 'ver/6.3.0' into dev/img-lib 2023-05-02 14:21:31 +10:00
Astrash 58b743e6e8 Distance transform documentation 2023-05-02 14:15:20 +10:00
Astrash 9fa5307a60 Remove image sampler addon
The image sampler is implemented as the 'CHANNEL' sampler provided
directly by the library-image addon instead.
2023-05-02 13:41:42 +10:00
Astrash b4ea09929c ColorUtil documentation 2023-05-02 13:37:22 +10:00
Astrash 09d847bc5a Add argb method to Channel class 2023-05-02 13:12:23 +10:00
Astrash dacddef5d6 rgb -> argb 2023-05-02 13:04:23 +10:00
Astrash 3c593c7013 Fix method call 2023-05-02 13:03:13 +10:00
Astrash 36d0ef77fb Add image support addon for biome pipeline 2023-05-02 12:58:37 +10:00
Astrash 105be0c346 Implement color parsing from strings 2023-05-02 12:58:29 +10:00
Astrash 39d21fbe08 Implement channel noise sampler 2023-05-01 13:58:56 +10:00
Astrash 50fc589001 Move distance transform to appropriate package 2023-05-01 13:58:18 +10:00
Astrash ffa55cb7a3 Implement premultiplication in ColorUtil 2023-05-01 13:57:07 +10:00
Astrash ff0985bd31 Make imagelib MathUtil 2023-05-01 13:10:58 +10:00
Astrash 622fed96e5 Use colorsampler package name instead of sampler 2023-05-01 12:48:13 +10:00
Astrash c219eff149 Implement distance transform sampler 2023-05-01 12:45:26 +10:00
Astrash 514e7065e2 Update image provider v2 readme 2023-05-01 12:02:53 +10:00
dfsek 0a16453f98 remove devlaunch lazydfu 2023-04-13 18:20:16 -07:00
dfsek ca2fe27fb3 fix preset creation 2023-04-13 18:15:35 -07:00
dfsek 6f2c01ceb3 update biome builder 2023-04-13 17:20:20 -07:00
dfsek 8b74a5dee0 implement biomeStream 2023-04-13 16:45:09 -07:00
dfsek 460a7651bc fix bukkit build 2023-04-13 16:44:01 -07:00
dfsek b7a6b839e6 Merge remote-tracking branch 'origin/dev/1.19.3'
# Conflicts:
#	buildSrc/src/main/kotlin/Versions.kt
#	platforms/bukkit/build.gradle.kts
2023-04-13 16:34:43 -07:00
dfsek 75bff93ecd update mod versions 2023-04-13 16:31:49 -07:00
dfsek 57a45f08f0 re-package bukkit 1.19.4 code 2023-04-13 16:31:41 -07:00
C_Corp2002 ba35b56016 Delete AwfulBukkitHacks.java.txt
Unneeded.
2023-04-13 15:40:13 -07:00
C_Corp2002 8fd10956e4 Update build.gradle.kts 2023-04-12 00:51:49 -07:00
C_Corp2002 9a5c1302ac Fixing...?
We gottem.
2023-04-12 00:51:12 -07:00
C_Corp2002 f918f1ef66 An attempt at 1.19.4 Support
I tried.
2023-04-09 21:08:38 -07:00
Astrash 2afcee28a6 Base key names on internal names 2023-03-01 13:39:09 +11:00
Astrash 6efff02c19 Implement stitched image support 2023-03-01 13:38:36 +11:00
Astrash d3e0831d9e Use config type based image loading
Rename `path` key to `image`
2023-03-01 13:28:45 +11:00
Astrash 345012810a Fix biome-provider-image-v2 addon manifest 2023-03-01 13:22:39 +11:00
Astrash 73e0899e7c Only use WorldEdit relative offset for v2 sponge schematics 2023-02-28 13:05:36 +11:00
Astrash 8deae0480c Add support for WorldEdit schematic relative offsets 2023-02-28 11:58:39 +11:00
Astrash 7b87498751 Don't load same image multiple times 2023-02-07 11:59:03 +11:00
Astrash 33f1aa07d3 Mark BufferedImageLoader as deprecated 2023-02-07 11:38:12 +11:00
Astrash 3ab671827d Re-add old image sampler to config-noise-function w/ deprecation notice 2023-02-07 11:31:23 +11:00
Astrash 4d17edef80 Add deprecation notice for biome-provider-image addon 2023-02-07 11:31:23 +11:00
Astrash 9d5b33f130 Re-add original image biome provider
This is so we aren't breaking existing packs
2023-02-07 11:31:23 +11:00
Astrash 97c0dcad9d Rename image lib biome provider to v2 2023-02-07 11:31:23 +11:00
Astrash ef4fe4eb7a Revert "Bump image provider major version"
This reverts commit f1bf3990c1.
2023-02-07 11:31:23 +11:00
Astrash 9514641e1e Revert "Bump config-noise-function major version"
This reverts commit 68875cc17b.
2023-02-07 11:31:23 +11:00
Astrash e6c51bcfd0 Merge branch 'ver/6.3.0' into dev/img-lib
fogor import
2023-02-07 11:30:54 +11:00
Astrash 084ecb9ad8 Forgot to delet sout 2023-02-06 20:36:28 +11:00
Astrash 5cc58babca Bump version to 6.3.0 2023-02-06 19:18:08 +11:00
Astrash b10130c5c6 Cache loaded BufferedImages 2023-02-06 19:18:08 +11:00
dfsek 5bc34eb626 update version to 6.2.2 2023-02-06 19:18:08 +11:00
dfsek 7d74245109 make forge compile 2023-02-06 19:18:08 +11:00
dfsek 11b6080413 update bukkit to 1.19.3 2023-02-06 19:18:08 +11:00
dfsek 4df23e464b begin working on Forge 2023-02-06 19:18:08 +11:00
dfsek 7ea5747f8e clean up sysout logging 2023-02-06 19:18:08 +11:00
dfsek caad76f6dd add mixin to inject flora 2023-02-06 19:18:08 +11:00
dfsek ba2f24f1f5 1.19.3 launch and enter world 2023-02-06 19:18:08 +11:00
dfsek 57bb6bca94 evil registry hacks 2023-02-06 19:18:08 +11:00
dfsek 4bb09b126a continue registry wrangling 2023-02-06 19:18:08 +11:00
dfsek ae96d8f526 fix inject parameters in NoiseConfigMixin 2023-02-06 19:18:08 +11:00
dfsek 4a918d00a3 allow registry manager to be overwritten 2023-02-06 19:18:08 +11:00
dfsek cd65785de4 fix SaveLoadingMixin 2023-02-06 19:18:08 +11:00
dfsek 51cd4cd4b7 fix RegistryMixin target class 2023-02-06 19:18:08 +11:00
dfsek e7efdd61a6 fabric compiles now 2023-02-06 19:18:08 +11:00
dfsek 0006762ff3 update platform biome delegate logic 2023-02-06 19:18:08 +11:00
dfsek 4e4627d11d update minecraft data config templates 2023-02-06 19:18:08 +11:00
dfsek 228b26f7c4 more work on evil update 2023-02-06 19:18:08 +11:00
dfsek ef846d53ad update MinecraftChunkGeneratorWrapper and its codec 2023-02-06 19:18:08 +11:00
dfsek f6f7529cb5 begin working on fabric 1.19.3 2023-02-06 19:18:08 +11:00
dfsek 606315ea64 update platform versions to 1.19.3 2023-02-06 19:18:08 +11:00
Astrash 46f7c95314 Implement BiomeProvider#getBaseBiome in pipeline v2 2023-01-30 12:57:50 +11:00
dfsek 4d826c880c update version to 6.2.2 2022-12-21 23:49:15 -07:00
dfsek 6da3acc8a1 make forge compile 2022-12-21 23:49:06 -07:00
dfsek 8fff27fddd update bukkit to 1.19.3 2022-12-21 23:00:40 -07:00
dfsek 75673b5b8e begin working on Forge 2022-12-21 21:59:39 -07:00
dfsek 5ded3552d3 clean up sysout logging 2022-12-21 21:38:40 -07:00
dfsek ded308c01c add mixin to inject flora 2022-12-21 21:34:44 -07:00
dfsek ee336b01a6 1.19.3 launch and enter world 2022-12-21 21:14:38 -07:00
dfsek 66465f27ff evil registry hacks 2022-12-21 14:41:43 -07:00
dfsek 764a4fa535 continue registry wrangling 2022-12-19 01:27:35 -07:00
dfsek 5dd5c37055 fix inject parameters in NoiseConfigMixin 2022-12-18 22:54:30 -07:00
dfsek 2e0f892fff allow registry manager to be overwritten 2022-12-18 22:53:10 -07:00
dfsek cad0e4105c fix SaveLoadingMixin 2022-12-18 22:49:59 -07:00
dfsek b10898b837 fix RegistryMixin target class 2022-12-18 22:46:57 -07:00
dfsek 6255ac7379 fabric compiles now 2022-12-18 22:44:53 -07:00
dfsek c90ca076ab update platform biome delegate logic 2022-12-18 22:44:06 -07:00
dfsek 73af05bf09 update minecraft data config templates 2022-12-18 22:39:18 -07:00
dfsek 84eab0de4a more work on evil update 2022-12-18 22:03:00 -07:00
dfsek 393a868e6a update MinecraftChunkGeneratorWrapper and its codec 2022-12-18 20:05:17 -07:00
dfsek 8955e4bb81 begin working on fabric 1.19.3 2022-12-14 19:14:16 -07:00
dfsek bae2af80c8 update platform versions to 1.19.3 2022-12-14 18:28:25 -07:00
Astrashh 6826f44770 Merge pull request #386 from PolyhedralDev/dev/dot-product-slant-again
Refactor Noise3D palettes to optionally use dot product slants
2022-11-29 13:28:23 +11:00
Astrash 1b5095dd36 Refactor Noise3d palettes to support multiple slant methods 2022-11-26 19:18:58 +11:00
Astrash 95992cc49b Don't apply align to single image fallback 2022-11-26 14:43:15 +11:00
Astrash 7b0185ba7c Add translate color sampler 2022-11-26 14:42:12 +11:00
Astrash 6b7fb82202 Put mutator color samplers in their own packages 2022-11-26 14:21:02 +11:00
Astrash f246c8ada3 Put image based color samplers in own packages 2022-11-26 14:14:15 +11:00
Astrash a97273f358 Skip color dist calc if exact match 2022-11-26 14:05:39 +11:00
Astrash 878bede60b Name changes 2022-11-26 13:52:15 +11:00
Astrash b771e108b6 Use color sampler as fallback instead of constant 2022-11-26 13:52:15 +11:00
Astrash 5c916f7758 Add rotate color sampler 2022-11-26 13:52:05 +11:00
Astrash e9db14f52c Wrap BufferedImage access 2022-11-26 13:49:10 +11:00
Astrash b3f072d689 Properly ignore alpha of lookup map 2022-11-25 14:11:16 +11:00
Astrash 4fdef98bd9 Use generic key mapping function 2022-11-25 14:10:07 +11:00
Astrash 8670c4cdf3 ColorPicker -> ColorSampler 2022-11-25 10:17:56 +11:00
Astrash 64c2a41d19 Ignore alpha channel by default with ExactColorConverter 2022-11-24 13:56:11 +11:00
Astrash 4e225a6592 Add utility methods for zero-ing color channels 2022-11-24 13:55:36 +11:00
Astrash c491ac5b24 ColorUtil.Channel getChannel() -> from() 2022-11-24 13:55:16 +11:00
Astrash 68875cc17b Bump config-noise-function major version
* Removed IMAGE sampler
2022-11-24 12:06:51 +11:00
Astrash f1bf3990c1 Bump image provider major version 2022-11-24 12:06:51 +11:00
Astrash 4334b16ded Use color pickers in image sampler 2022-11-24 12:06:51 +11:00
Astrash 274f864d6a Move channels into image lib 2022-11-24 12:06:51 +11:00
Astrash 329d94ba9c Move image sampler to separate addon 2022-11-24 12:06:51 +11:00
Astrash 03ab463723 Rewrite the image biome provider to use the image library 2022-11-24 12:06:51 +11:00
Astrash 3580267532 Image library initial implementation
*Moves the BufferedImage loader into the library, rather than being
a loader implemented in common/implementation
2022-11-24 12:06:51 +11:00
dfsek 358e09d05b Merge pull request #383 from PolyhedralDev/ver/6.2.1
6.2.1 but for real this time
2022-11-16 14:03:21 -07:00
dfsek 1f7c4ee4e7 rename from "2" to "v2" 2022-11-16 14:02:34 -07:00
dfsek b3100e01e2 Merge remote-tracking branch 'origin/master' into ver/6.2.1 2022-11-16 13:49:13 -07:00
Astrash 02d3445dd9 Include deprecation notice for pipeline v1 2022-11-16 16:01:28 +11:00
Astrash c1c85174ed Re-add original pipeline implementation 2022-11-16 16:01:28 +11:00
Astrash 6cdef5d4ad Move pipeline reimplementation to separate addon 2022-11-16 16:01:27 +11:00
Astrash 57cbea4000 Replace sout with logger debug calls 2022-11-16 16:01:27 +11:00
Astrash 3522b5f17c Remove test class from source 2022-11-16 16:01:27 +11:00
Astrash 5c58bd54a3 Ephemeral -> Placeholder 2022-11-16 16:01:27 +11:00
Astrash ee6d475ad0 Move reimplementation classes into main package 2022-11-16 16:01:27 +11:00
Astrash d170b4e0fd Handle resolution and biome blending 2022-11-16 16:01:27 +11:00
Astrash bc324df7ca Working state for pipeline implementation 2022-11-16 16:01:27 +11:00
dfsek 9b75410ea7 configure bukkit resource config properly 2022-11-15 21:09:16 -07:00
dfsek 44550e8d8e Merge pull request #382 from PolyhedralDev/ver/6.2.1
update version
2022-11-15 19:20:18 -07:00
dfsek 12845a6004 update version 2022-11-15 19:17:14 -07:00
dfsek 140295602c Merge pull request #381 from PolyhedralDev/ver/6.2.1
Ver/6.2.1
2022-11-10 01:16:39 -07:00
dfsek 547f4884aa fix beard 2022-11-09 18:52:36 -07:00
dfsek 72e5ac76e6 update paper dev bundle to 1.19.2 2022-11-09 18:48:09 -07:00
dfsek 7d3bdeee8f Merge pull request #379 from PolyhedralDev/fix/ender_crystal
Fix End Crystals on Bukkit
2022-11-09 18:18:57 -07:00
dfsek e471950047 Merge pull request #380 from PolyhedralDev/fix/bukkit-beard
implement beardification on Bukkit (closes #370)
2022-11-09 18:18:42 -07:00
dfsek 1306dad776 implement beardification on Bukkit (closes #370) 2022-11-07 17:49:49 -07:00
dfsek eb9685c734 fix #378 2022-11-07 17:31:41 -07:00
solonovamax e8fa5514cc Clean up language in code of conduct's license & attribution section 2022-09-06 10:42:05 -04:00
solonovamax f1290b36d8 Fix more weird list formatting in code of conduct 2022-09-06 10:40:43 -04:00
solonovamax befc0c6636 Fix an oopsie in the code of conduct with weird list formatting 2022-09-06 10:15:01 -04:00
Zoë f851276fc2 Merge pull request #351 from Browsit/patch-2
Fix type mismatch
2022-08-26 10:56:45 -05:00
Zoë bc334fa214 Merge pull request #350 from Browsit/patch-1
Update BUG_REPORT.md
2022-08-26 10:56:26 -05:00
Browsit 829696ccc8 Use jar URI prefix
Fixes `Path component should be '/'` on Windows
2022-07-21 02:35:38 -04:00
Browsit 43adf056c3 Fix type mismatch
Just pulled the master branch and got the error:

`C:\Users\{owner}\IdeaProjects\Terra\buildSrc\src\main\kotlin\DistributionConfig.kt: (49, 39): Type mismatch: inferred type is Path! but URI! was expected`
2022-07-20 22:27:22 -04:00
Browsit f8a602790a Update BUG_REPORT.md 2022-07-20 08:28:58 -04:00
256 changed files with 8059 additions and 996 deletions
+1 -1
View File
@@ -43,7 +43,7 @@ assignees: ""
is Terra that is causing the issue.
- [ ] I have checked that this is an issue with Terra and not an issue with the
pack I am using.
<!-- If this is an issue with the default Terra pack, please open an issue on the pack repo: https://github.com/PolyhedralDev/TerraDefaultConfig/issues/new -->
<!-- If this is an issue with the default Terra pack, please open an issue on the pack repo: https://github.com/PolyhedralDev/TerraOverworldConfig/issues/new -->
- [ ] I have attached a copy of the `latest.log` file
- [ ] I have filled out and provided all the appropriate information.
+14 -34
View File
@@ -315,19 +315,15 @@ Terra has a global moderation team which is currently comprised of the following
members:
- solonovamax
-
discord: [@solonovamax#6983](https://discord.com/channels/@me/566146322273402881)*
- discord: [@solonovamax#6983](https://discord.com/channels/@me/566146322273402881)*
- github: [@solonovamax](https://github.com/solonovamax)
-
email: [solonovamax@12oclockpoint.com](mailto:solonovamax@12oclockpoint.com)
- email: [solonovamax@12oclockpoint.com](mailto:solonovamax@12oclockpoint.com)
- dfsek
-
discord: [@dfsek#4208](https://discord.com/channels/@me/378350362236682240)*
- discord: [@dfsek#4208](https://discord.com/channels/@me/378350362236682240)*
- github: [@dfsek](https://github.com/dfsek)
- email: [dfsek@protonmail.com](mailto:dfsek@protonmail.com)
- duplex (duplexsystem)
-
discord: [@Duplex#0797](https://discord.com/channels/@me/356822848641171456)*
- discord: [@Duplex#0797](https://discord.com/channels/@me/356822848641171456)*
- github: [@duplexsystem](https://github.com/duplexsystem)
- email: [duplexsys@protonmail.com](mailto:duplexsys@protonmail.com)
@@ -401,32 +397,17 @@ issue on this github repo, or contact a member of the global moderation team.
## License and Attribution
This set of guidelines is distributed under a
[Creative Commons Attribution-ShareAlike license](https://creativecommons.org/licenses/by-sa/3.0/)
.
[Creative Commons Attribution-ShareAlike license](https://creativecommons.org/licenses/by-sa/3.0/).
These guidelines have been adapted from
[Mozilla's Community Participation Guidelines](https://www.mozilla.org/en-US/about/governance/policies/participation/)
, which were adapted from:
These guidelines have been adapted with modifications from the following sources:
- Mozilla's original Community Participation Guidelines
- [Mozilla's Community Participation Guidelines](https://www.mozilla.org/en-US/about/governance/policies/participation/)
- The [Ubuntu Code of Conduct](https://ubuntu.com/community/code-of-conduct)
-
Mozilla's [View Source Conference Code of Conduct](https://viewsourceconf.org/berlin-2016/code-of-conduct/)
- And
the [Rust Language Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct)
which in turn were based
on [Stumptown Syndicate's Citizen Code of Conduct](http://citizencodeofconduct.org/)
, along with some adapted text from
the [LGBTQ in Technology Code of Conduct](https://lgbtq.technology/coc.html) and
the [WisCon code of conduct](http://wiscon.net/policies/anti-harassment/code-of-conduct/)
.
It was then modified by solonovamax with various inclusions from
the [LGBTQ in Technology Code of Conduct](https://lgbtq.technology/coc.html) and
a few other sources.
- Mozilla's [View Source Conference Code of Conduct](https://viewsourceconf.org/berlin-2016/code-of-conduct/)
- The [Rust Language Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct)
- The [Stumptown Syndicate's Citizen Code of Conduct](http://citizencodeofconduct.org/)
- The [LGBTQ in Technology Code of Conduct](https://lgbtq.technology/coc.html)
- The [WisCon code of conduct](http://wiscon.net/policies/anti-harassment/code-of-conduct/)
## Notes
@@ -443,7 +424,6 @@ people are not comfortable
using [neopronouns](https://www.mypronouns.org/neopronouns). But if someone
refuses to use your more common pronouns, you should report them to us.
Additionally, you may not ask people to use unreasonable pronouns, such as '
acab/acabself', 'that/bitch', 'ur/mom', or
'dream/dreamself' (pronouns related to real people, eg. the minecraft youtuber '
dreamwastaken'). Doing so will be considered mockery of individuals who use
acab/acabself', 'that/bitch', 'ur/mom', or anything else that may be considered
disrectful. Doing so will be considered mockery of individuals who use
non-standard pronouns and is very disrespectful.
+4 -3
View File
@@ -1,8 +1,8 @@
preRelease(true)
versionProjects(":common:api", version("6.2.0"))
versionProjects(":common:implementation", version("6.2.0"))
versionProjects(":platforms", version("6.2.0"))
versionProjects(":common:api", version("6.4.0"))
versionProjects(":common:implementation", version("6.4.0"))
versionProjects(":platforms", version("6.4.0"))
allprojects {
@@ -42,6 +42,7 @@ afterEvaluate {
forImmediateSubProjects(":platforms") {
configureDistribution()
}
project(":platforms:bukkit:common").configureDistribution()
forSubProjects(":common:addons") {
apply(plugin = "com.github.johnrengelman.shadow")
@@ -1,6 +1,7 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import java.io.File
import java.io.FileWriter
import java.net.URI
import java.net.URL
import java.nio.file.FileSystems
import java.nio.file.Files
@@ -44,7 +45,7 @@ fun Project.configureDistribution() {
doLast {
// https://github.com/johnrengelman/shadow/issues/111
val dest = tasks.named<ShadowJar>("shadowJar").get().archiveFile.get().asFile.toPath()
val dest = URI.create("jar:" + tasks.named<ShadowJar>("shadowJar").get().archiveFile.get().asFile.toURI())
FileSystems.newFileSystem(dest, mapOf("create" to "false"), null).use { fs ->
forSubProjects(":common:addons") {
+9 -10
View File
@@ -4,7 +4,7 @@ object Versions {
const val paralithic = "0.7.0"
const val strata = "1.1.1"
const val cloud = "1.7.0"
const val cloud = "1.8.0"
const val slf4j = "1.7.36"
const val log4j_slf4j_impl = "2.14.1"
@@ -19,39 +19,38 @@ object Versions {
object Fabric {
const val fabricLoader = "0.14.8"
const val fabricAPI = "0.57.0+1.19"
const val fabricAPI = "0.83.1+1.20.1"
}
object Quilt {
const val quiltLoader = "0.17.0"
const val fabricApi = "2.0.0-beta.4+0.57.0-1.19"
const val fabricApi = "6.0.0-beta.3+0.76.0-1.19.4"
}
object Mod {
const val mixin = "0.11.2+mixin.0.8.5"
const val minecraft = "1.19"
const val yarn = "$minecraft+build.1"
const val fabricLoader = "0.14.2"
const val minecraft = "1.20.1"
const val yarn = "$minecraft+build.2"
const val fabricLoader = "0.14.21"
const val architecuryLoom = "0.12.0.290"
const val architecturyPlugin = "3.4-SNAPSHOT"
const val loomQuiltflower = "1.7.1"
const val lazyDfu = "0.1.2"
}
object Forge {
const val forge = "${Mod.minecraft}-41.0.63"
const val forge = "${Mod.minecraft}-47.0.3"
const val burningwave = "12.53.0"
}
object Bukkit {
const val paper = "1.18.2-R0.1-SNAPSHOT"
const val paperLib = "1.0.5"
const val minecraft = "1.19"
const val minecraft = "1.20.1"
const val reflectionRemapper = "0.1.0-SNAPSHOT"
const val paperDevBundle = "1.20.1-R0.1-SNAPSHOT"
}
object Sponge {
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-2021 Polyhedral Development
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,5 @@
# biome-provider-image-v2
Implements and registers the `IMAGE` biome provider, which
utilizes various config types provided by the `library-image` addon to
distribute biomes based on images.
@@ -0,0 +1,12 @@
version = version("1.0.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
compileOnlyApi(project(":common:addons:library-image"))
implementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
testImplementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
}
tasks.named<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") {
relocate("net.jafama", "com.dfsek.terra.addons.biome.image.lib.jafama")
}
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.image.v2;
import java.util.Optional;
import com.dfsek.terra.addons.image.converter.ColorConverter;
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class ImageBiomeProvider implements BiomeProvider {
private final int resolution;
private final ColorConverter<Biome> colorConverter;
private final ColorSampler colorSampler;
public ImageBiomeProvider(ColorConverter<Biome> colorConverter, ColorSampler colorSampler, int resolution) {
this.resolution = resolution;
this.colorConverter = colorConverter;
this.colorSampler = colorSampler;
}
@Override
public Biome getBiome(int x, int y, int z, long seed) {
return getBiome(x, z);
}
public Biome getBiome(int x, int z) {
x /= resolution;
z /= resolution;
return colorConverter.apply(colorSampler.apply(x, z));
}
@Override
public Optional<Biome> getBaseBiome(int x, int z, long seed) {
return Optional.of(getBiome(x, z));
}
@Override
public Iterable<Biome> getBiomes() {
return colorConverter.getEntries();
}
}
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.image.v2;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.function.Supplier;
import com.dfsek.terra.addons.biome.image.v2.config.ImageProviderTemplate;
import com.dfsek.terra.addons.biome.image.v2.config.converter.ClosestBiomeColorConverterTemplate;
import com.dfsek.terra.addons.biome.image.v2.config.converter.ExactBiomeColorConverterTemplate;
import com.dfsek.terra.addons.biome.image.v2.config.converter.mapping.DefinedBiomeColorMappingTemplate;
import com.dfsek.terra.addons.image.converter.ColorConverter;
import com.dfsek.terra.addons.image.converter.mapping.BiomeDefinedColorMapping;
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class ImageBiomeProviderAddon implements AddonInitializer {
public static final TypeKey<Supplier<ObjectTemplate<BiomeProvider>>> PROVIDER_REGISTRY_KEY = new TypeKey<>() {
};
public static final TypeKey<Supplier<ObjectTemplate<ColorConverter<Biome>>>> BIOME_COLOR_CONVERTER_REGISTRY_KEY = new TypeKey<>() {
};
public static final TypeKey<Supplier<ObjectTemplate<ColorMapping<Biome>>>> BIOME_COLOR_MAPPING_REGISTRY_KEY = new TypeKey<>() {
};
@Inject
private Platform platform;
@Inject
private BaseAddon addon;
@Override
public void initialize() {
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, ConfigPackPreLoadEvent.class)
.priority(501)
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<BiomeProvider>>> providerRegistry = event.getPack().getOrCreateRegistry(
PROVIDER_REGISTRY_KEY);
providerRegistry.register(addon.key("IMAGE"), ImageProviderTemplate::new);
})
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<ColorConverter<Biome>>>> biomeColorConverterRegistry = event.getPack().getOrCreateRegistry(
BIOME_COLOR_CONVERTER_REGISTRY_KEY);
biomeColorConverterRegistry.register(addon.key("EXACT"), ExactBiomeColorConverterTemplate::new);
biomeColorConverterRegistry.register(addon.key("CLOSEST"), ClosestBiomeColorConverterTemplate::new);
})
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<ColorMapping<Biome>>>> biomeColorMappingRegistry = event.getPack().getOrCreateRegistry(
BIOME_COLOR_MAPPING_REGISTRY_KEY);
biomeColorMappingRegistry.register(addon.key("USE_BIOME_COLORS"), () -> () -> new BiomeDefinedColorMapping<>(event.getPack().getRegistry(Biome.class), b -> b));
biomeColorMappingRegistry.register(addon.key("MAP"), DefinedBiomeColorMappingTemplate::new);
})
.failThrough();
}
}
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.image.v2.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Description;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.addons.biome.image.v2.ImageBiomeProvider;
import com.dfsek.terra.addons.image.converter.ColorConverter;
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
@SuppressWarnings("FieldMayBeFinal")
public class ImageProviderTemplate implements ObjectTemplate<BiomeProvider> {
@Value("resolution")
@Default
@Description("Sets the resolution at which to sample the image.")
private int resolution = 1;
@Value("color-sampler")
private ColorSampler colorSampler;
@Value("color-conversion")
private ColorConverter<Biome> colorConverter;
@Override
public BiomeProvider get() {
return new ImageBiomeProvider(colorConverter, colorSampler, resolution);
}
}
@@ -0,0 +1,19 @@
package com.dfsek.terra.addons.biome.image.v2.config.converter;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.image.config.converter.ClosestColorConverterTemplate;
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
import com.dfsek.terra.api.world.biome.Biome;
public class ClosestBiomeColorConverterTemplate extends ClosestColorConverterTemplate<Biome> {
@Value("match")
private ColorMapping<Biome> match;
@Override
protected ColorMapping<Biome> getMapping() {
return match;
}
}
@@ -0,0 +1,37 @@
package com.dfsek.terra.addons.biome.image.v2.config.converter;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.image.config.converter.ExactColorConverterTemplate;
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
import com.dfsek.terra.api.world.biome.Biome;
public class ExactBiomeColorConverterTemplate extends ExactColorConverterTemplate<Biome> {
@Value("match")
private ColorMapping<Biome> match;
@Value("else")
private Biome fallback;
@Value("ignore-alpha")
@Default
private boolean ignoreAlpha = true;
@Override
protected ColorMapping<Biome> getMapping() {
return match;
}
@Override
protected Biome getFallback() {
return fallback;
}
@Override
protected boolean ignoreAlpha() {
return ignoreAlpha;
}
}
@@ -0,0 +1,24 @@
package com.dfsek.terra.addons.biome.image.v2.config.converter.mapping;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.Map;
import com.dfsek.terra.addons.image.config.ColorLoader.ColorString;
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
import com.dfsek.terra.addons.image.util.MapUtil;
import com.dfsek.terra.api.world.biome.Biome;
public class DefinedBiomeColorMappingTemplate implements ObjectTemplate<ColorMapping<Biome>> {
@Value("map")
Map<ColorString, Biome> map;
@Override
public ColorMapping<Biome> get() {
var map = MapUtil.mapKeys(this.map, ColorString::getColor);
return () -> map;
}
}
@@ -0,0 +1,14 @@
schema-version: 1
contributors:
- Terra contributors
id: biome-provider-image-v2
version: @VERSION@
entrypoints:
- "com.dfsek.terra.addons.biome.image.v2.ImageBiomeProviderAddon"
website:
issues: https://github.com/PolyhedralDev/Terra/issues
source: https://github.com/PolyhedralDev/Terra
docs: https://terra.polydev.org
license: MIT License
depends:
library-image: "1.+"
@@ -8,6 +8,8 @@
package com.dfsek.terra.addons.biome.image;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.Supplier;
@@ -24,6 +26,9 @@ import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class ImageBiomeProviderAddon implements AddonInitializer {
private static final Logger logger = LoggerFactory.getLogger(ImageBiomeProviderAddon.class);
public static final TypeKey<Supplier<ObjectTemplate<BiomeProvider>>> PROVIDER_REGISTRY_KEY = new TypeKey<>() {
};
@@ -45,5 +50,6 @@ public class ImageBiomeProviderAddon implements AddonInitializer {
() -> new ImageProviderTemplate(event.getPack().getRegistry(Biome.class)));
})
.failThrough();
logger.warn("The biome-provider-image addon is deprecated and scheduled for removal in Terra 7.0. It is recommended to use the biome-provider-image-v2 addon for future pack development instead.");
}
}
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-2021 Polyhedral Development
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,12 @@
# biome-provider-pipeline-2
The second version of the Biome Pipeline, a procedural biome provider that uses a series
of "stages" to apply "mutations" to a 2D grid of biomes.
Version 2 is a re-implementation of the original addon with the primary goal of providing
consistent scaling for noise relative to the world
(See https://github.com/PolyhedralDev/Terra/issues/264 for more details), and has been
included as a separate addon to maintain parity with packs utilizing the first version.
This addon registers the `PIPELINE` biome provider type, and all associated
configurations.
@@ -0,0 +1,12 @@
version = version("1.0.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
implementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
testImplementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
}
tasks.named<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") {
relocate("net.jafama", "com.dfsek.terra.addons.biome.pipeline.lib.jafama")
}
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.function.Supplier;
import com.dfsek.terra.addons.biome.pipeline.v2.config.BiomePipelineTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.config.PipelineBiomeLoader;
import com.dfsek.terra.addons.biome.pipeline.v2.config.source.SamplerSourceTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.expander.ExpanderStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator.BorderListStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator.BorderStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator.ReplaceListStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator.ReplaceStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator.SmoothStageTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPostLoadEvent;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class BiomePipelineAddon implements AddonInitializer {
public static final TypeKey<Supplier<ObjectTemplate<Source>>> SOURCE_REGISTRY_KEY = new TypeKey<>() {
};
public static final TypeKey<Supplier<ObjectTemplate<Stage>>> STAGE_REGISTRY_KEY = new TypeKey<>() {
};
public static final TypeKey<Supplier<ObjectTemplate<BiomeProvider>>> PROVIDER_REGISTRY_KEY = new TypeKey<>() {
};
@Inject
private Platform platform;
@Inject
private BaseAddon addon;
@Override
public void initialize() {
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, ConfigPackPreLoadEvent.class)
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<BiomeProvider>>> providerRegistry = event.getPack().getOrCreateRegistry(
PROVIDER_REGISTRY_KEY);
providerRegistry.register(addon.key("PIPELINE"), BiomePipelineTemplate::new);
})
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<Source>>> sourceRegistry = event.getPack().getOrCreateRegistry(
SOURCE_REGISTRY_KEY);
sourceRegistry.register(addon.key("SAMPLER"), SamplerSourceTemplate::new);
})
.then(event -> {
CheckedRegistry<Supplier<ObjectTemplate<Stage>>> stageRegistry = event.getPack().getOrCreateRegistry(
STAGE_REGISTRY_KEY);
stageRegistry.register(addon.key("FRACTAL_EXPAND"), ExpanderStageTemplate::new);
stageRegistry.register(addon.key("SMOOTH"), SmoothStageTemplate::new);
stageRegistry.register(addon.key("REPLACE"), ReplaceStageTemplate::new);
stageRegistry.register(addon.key("REPLACE_LIST"), ReplaceListStageTemplate::new);
stageRegistry.register(addon.key("BORDER"), BorderStageTemplate::new);
stageRegistry.register(addon.key("BORDER_LIST"), BorderListStageTemplate::new);
})
.failThrough();
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, ConfigPackPostLoadEvent.class)
.then(event -> {
Registry<Biome> biomeRegistry = event.getPack().getRegistry(Biome.class);
event.getPack().applyLoader(PipelineBiome.class, new PipelineBiomeLoader(biomeRegistry));
});
}
}
@@ -0,0 +1,71 @@
package com.dfsek.terra.addons.biome.pipeline.v2;
import java.util.function.Consumer;
import com.dfsek.terra.api.util.Column;
import com.dfsek.terra.api.util.function.IntIntObjConsumer;
import com.dfsek.terra.api.util.function.IntObjConsumer;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class BiomePipelineColumn implements Column<Biome> {
private final int min;
private final int max;
private final int x;
private final int z;
private final Biome biome;
protected BiomePipelineColumn(BiomeProvider biomeProvider, int min, int max, int x, int z, long seed) {
this.min = min;
this.max = max;
this.x = x;
this.z = z;
this.biome = biomeProvider.getBiome(x, 0, z, seed);
}
@Override
public int getMinY() {
return min;
}
@Override
public int getMaxY() {
return max;
}
@Override
public int getX() {
return x;
}
@Override
public int getZ() {
return z;
}
@Override
public Biome get(int y) {
return biome;
}
@Override
public void forRanges(int resolution, IntIntObjConsumer<Biome> consumer) {
consumer.accept(min, max, biome);
}
@Override
public void forEach(Consumer<Biome> consumer) {
for(int y = min; y < max; y++) {
consumer.accept(biome);
}
}
@Override
public void forEach(IntObjConsumer<Biome> consumer) {
for(int y = min; y < max; y++) {
consumer.accept(y, biome);
}
}
}
@@ -0,0 +1,115 @@
package com.dfsek.terra.addons.biome.pipeline.v2;
import com.dfsek.terra.addons.biome.pipeline.v2.api.BiomeChunk;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Pipeline;
import com.dfsek.terra.addons.biome.pipeline.v2.api.SeededVector;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import net.jafama.FastMath;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.StreamSupport;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.registry.key.StringIdentifiable;
import com.dfsek.terra.api.util.Column;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class PipelineBiomeProvider implements BiomeProvider {
private final LoadingCache<SeededVector, BiomeChunk> biomeChunkCache;
private final int chunkSize;
private final int resolution;
private final NoiseSampler mutator;
private final double noiseAmp;
private final Set<Biome> biomes;
public PipelineBiomeProvider(Pipeline pipeline, int resolution, NoiseSampler mutator, double noiseAmp) {
this.resolution = resolution;
this.mutator = mutator;
this.noiseAmp = noiseAmp;
this.chunkSize = pipeline.getChunkSize();
this.biomeChunkCache = Caffeine.newBuilder()
.maximumSize(64)
.build(pipeline::generateChunk);
Set<PipelineBiome> biomeSet = new HashSet<>();
pipeline.getSource().getBiomes().forEach(biomeSet::add);
Iterable<PipelineBiome> result = biomeSet;
for(Stage stage : pipeline.getStages()) {
result = stage.getBiomes(result);
}
this.biomes = new HashSet<>();
Iterable<PipelineBiome> finalResult = result;
result.forEach(pipelineBiome -> {
if(pipelineBiome.isPlaceholder()) {
StringBuilder biomeList = new StringBuilder("\n");
StreamSupport.stream(finalResult.spliterator(), false)
.sorted(Comparator.comparing(StringIdentifiable::getID))
.forEach(delegate -> biomeList
.append(" - ")
.append(delegate.getID())
.append(':')
.append(delegate.getClass().getCanonicalName())
.append('\n'));
throw new IllegalArgumentException("Biome Pipeline leaks placeholder biome \"" + pipelineBiome.getID() +
"\". Ensure there is a stage to guarantee replacement of the placeholder biome. Biomes: " +
biomeList);
}
this.biomes.add(pipelineBiome.getBiome());
});
}
@Override
public Biome getBiome(int x, int y, int z, long seed) {
return getBiome(x, z, seed);
}
public Biome getBiome(int x, int z, long seed) {
x += mutator.noise(seed + 1, x, z) * noiseAmp;
z += mutator.noise(seed + 2, x, z) * noiseAmp;
x /= resolution;
z /= resolution;
int chunkX = FastMath.floorDiv(x, chunkSize);
int chunkZ = FastMath.floorDiv(z, chunkSize);
int chunkWorldX = chunkX * chunkSize;
int chunkWorldZ = chunkZ * chunkSize;
int xInChunk = x - chunkWorldX;
int zInChunk = z - chunkWorldZ;
return biomeChunkCache.get(new SeededVector(seed, chunkWorldX, chunkWorldZ)).get(xInChunk, zInChunk).getBiome();
}
@Override
public Iterable<Biome> getBiomes() {
return biomes;
}
@Override
public Optional<Biome> getBaseBiome(int x, int z, long seed) {
return Optional.of(getBiome(x, z, seed));
}
@Override
public Column<Biome> getColumn(int x, int z, long seed, int min, int max) {
return new BiomePipelineColumn(this, min, max, x, z, seed);
}
@Override
public int resolution() {
return resolution;
}
}
@@ -0,0 +1,10 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
public interface BiomeChunk {
PipelineBiome get(int xInChunk, int zInChunk);
}
@@ -0,0 +1,29 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api;
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl.ViewPoint;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
/**
* Resizes the internal grid of a BiomeChunk when applied, serves the purpose of
* filling in null biomes as a result of this resizing.
*/
public interface Expander extends Stage {
PipelineBiome fillBiome(ViewPoint viewPoint);
@Override
default int maxRelativeReadDistance() {
return 0;
}
@Override
default PipelineBiome apply(ViewPoint viewPoint) {
PipelineBiome currentBiome = viewPoint.getBiome();
if(currentBiome == null) {
return fillBiome(viewPoint);
} else {
return currentBiome;
}
}
}
@@ -0,0 +1,14 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api;
import java.util.List;
public interface Pipeline {
BiomeChunk generateChunk(SeededVector worldCoordinates);
int getChunkSize();
Source getSource();
List<Stage> getStages();
}
@@ -0,0 +1,19 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api;
public record SeededVector(long seed, int x, int z) {
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + z;
return 31 * code + ((int) (seed ^ (seed >>> 32)));
}
}
@@ -0,0 +1,11 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
public interface Source {
PipelineBiome get(long seed, int x, int z);
Iterable<PipelineBiome> getBiomes();
}
@@ -0,0 +1,15 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api;
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl.ViewPoint;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
public interface Stage {
PipelineBiome apply(ViewPoint viewPoint);
int maxRelativeReadDistance();
default Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) {
return biomes;
}
}
@@ -0,0 +1,40 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api.biome;
import java.util.Set;
import com.dfsek.terra.api.world.biome.Biome;
public final class DelegatedPipelineBiome implements PipelineBiome {
private final Biome biome;
public DelegatedPipelineBiome(Biome biome) {
this.biome = biome;
}
@Override
public Biome getBiome() {
return biome;
}
@Override
public int hashCode() {
return biome.hashCode();
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof DelegatedPipelineBiome that)) return false;
return that.biome.equals(this.biome);
}
@Override
public Set<String> getTags() {
return biome.getTags();
}
@Override
public String getID() {
return biome.getID();
}
}
@@ -0,0 +1,35 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api.biome;
import java.util.Set;
import com.dfsek.terra.api.registry.key.StringIdentifiable;
import com.dfsek.terra.api.world.biome.Biome;
public interface PipelineBiome extends StringIdentifiable {
Biome getBiome();
static PipelineBiome placeholder(String id) {
return new PlaceholderPipelineBiome(id);
}
static PipelineBiome from(Biome biome) {
return new DelegatedPipelineBiome(biome);
}
static PipelineBiome self() {
return SelfPipelineBiome.INSTANCE;
}
Set<String> getTags();
default boolean isPlaceholder() {
return false;
}
default boolean isSelf() {
return false;
}
}
@@ -0,0 +1,51 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api.biome;
import java.util.HashSet;
import java.util.Set;
import com.dfsek.terra.api.world.biome.Biome;
final class PlaceholderPipelineBiome implements PipelineBiome {
private final Set<String> tags;
private final String id;
public PlaceholderPipelineBiome(String id) {
this.id = id;
tags = new HashSet<>();
tags.add(id);
tags.add("ALL");
}
@Override
public Biome getBiome() {
throw new UnsupportedOperationException("Cannot get raw biome from placeholder pipeline biome");
}
@Override
public Set<String> getTags() {
return tags;
}
@Override
public String getID() {
return id;
}
@Override
public boolean isPlaceholder() {
return true;
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof PlaceholderPipelineBiome that)) return false;
return this.id.equals(that.id);
}
}
@@ -0,0 +1,40 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api.biome;
import java.util.Collections;
import java.util.Set;
import com.dfsek.terra.api.world.biome.Biome;
final class SelfPipelineBiome implements PipelineBiome {
public static final SelfPipelineBiome INSTANCE = new SelfPipelineBiome();
private SelfPipelineBiome() {
}
@Override
public Biome getBiome() {
throw new UnsupportedOperationException("Cannot get biome from self delegate");
}
@Override
public boolean isSelf() {
return true;
}
@Override
public boolean isPlaceholder() {
return true;
}
@Override
public Set<String> getTags() {
return Collections.emptySet();
}
@Override
public String getID() {
return "SELF";
}
}
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.config;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Description;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.List;
import com.dfsek.terra.addons.biome.pipeline.v2.PipelineBiomeProvider;
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.PipelineImpl;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
@SuppressWarnings({ "FieldMayBeFinal", "unused" })
public class BiomePipelineTemplate implements ObjectTemplate<BiomeProvider> {
@Value("resolution")
@Default
@Description("""
The resolution at which to sample biomes.
Larger values are quadratically faster, but produce lower quality results.
For example, a value of 3 would sample every 3 blocks.""")
protected @Meta int resolution = 1;
@Value("pipeline.source")
@Description("The Biome Source to use for initial population of biomes.")
private @Meta Source source;
@Value("pipeline.stages")
@Description("A list of pipeline stages to apply to the result of #source")
private @Meta List<@Meta Stage> stages;
@Value("blend.sampler")
@Default
@Description("A sampler to use for blending the edges of biomes via domain warping.")
protected @Meta NoiseSampler blendSampler = NoiseSampler.zero();
@Value("blend.amplitude")
@Default
@Description("The amplitude at which to perform blending.")
protected @Meta double blendAmplitude = 0d;
@Override
public BiomeProvider get() {
return new PipelineBiomeProvider(new PipelineImpl(source, stages, resolution, 128), resolution, blendSampler, blendAmplitude);
}
}
@@ -0,0 +1,32 @@
package com.dfsek.terra.addons.biome.pipeline.v2.config;
import com.dfsek.tectonic.api.depth.DepthTracker;
import com.dfsek.tectonic.api.exception.LoadException;
import com.dfsek.tectonic.api.loader.ConfigLoader;
import com.dfsek.tectonic.api.loader.type.TypeLoader;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.AnnotatedType;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.world.biome.Biome;
public class PipelineBiomeLoader implements TypeLoader<PipelineBiome> {
private final Registry<Biome> biomeRegistry;
public PipelineBiomeLoader(Registry<Biome> biomeRegistry) {
this.biomeRegistry = biomeRegistry;
}
@Override
public PipelineBiome load(@NotNull AnnotatedType t, @NotNull Object c, @NotNull ConfigLoader loader, DepthTracker depthTracker)
throws LoadException {
if(c.equals("SELF")) return PipelineBiome.self();
return biomeRegistry
.getByID((String) c)
.map(PipelineBiome::from)
.orElseGet(() -> PipelineBiome.placeholder((String) c));
}
}
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.config.source;
import com.dfsek.tectonic.api.config.template.annotations.Description;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.v2.source.SamplerSource;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class SamplerSourceTemplate extends SourceTemplate {
@Value("sampler")
@Description("The sampler used to distribute biomes.")
private @Meta NoiseSampler noise;
@Value("biomes")
@Description("The biomes to be distributed.")
private @Meta ProbabilityCollection<@Meta PipelineBiome> biomes;
@Override
public Source get() {
return new SamplerSource(biomes, noise);
}
}
@@ -0,0 +1,17 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.config.source;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source;
public abstract class SourceTemplate implements ObjectTemplate<Source> {
}
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage;
import com.dfsek.tectonic.api.config.template.annotations.Description;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
public abstract class StageTemplate implements ObjectTemplate<Stage> {
@Value("sampler")
@Description("Sampler to use for stage distribution.")
protected @Meta NoiseSampler noise;
}
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage.expander;
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Expander;
import com.dfsek.terra.addons.biome.pipeline.v2.stage.expander.FractalExpander;
public class ExpanderStageTemplate extends StageTemplate {
@Override
public Expander get() {
return new FractalExpander(noise);
}
}
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import java.util.Map;
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators.BorderListStage;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
@SuppressWarnings("unused")
public class BorderListStageTemplate extends StageTemplate {
@Value("from")
private @Meta String from;
@Value("default-replace")
private @Meta String defaultReplace;
@Value("default-to")
private @Meta ProbabilityCollection<@Meta PipelineBiome> defaultTo;
@Value("replace")
private @Meta Map<@Meta PipelineBiome, @Meta ProbabilityCollection<@Meta PipelineBiome>> replace;
@Override
public Stage get() {
return new BorderListStage(replace, from, defaultReplace, noise, defaultTo);
}
}
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators.BorderStage;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
@SuppressWarnings("unused")
public class BorderStageTemplate extends StageTemplate {
@Value("from")
private @Meta String from;
@Value("replace")
private @Meta String replace;
@Value("to")
private @Meta ProbabilityCollection<@Meta PipelineBiome> to;
@Override
public Stage get() {
return new BorderStage(from, replace, noise, to);
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import java.util.Map;
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators.ReplaceListStage;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
@SuppressWarnings("unused")
public class ReplaceListStageTemplate extends StageTemplate {
@Value("default-from")
private @Meta String defaultFrom;
@Value("default-to")
private @Meta ProbabilityCollection<@Meta PipelineBiome> defaultTo;
@Value("to")
private @Meta Map<@Meta PipelineBiome, @Meta ProbabilityCollection<@Meta PipelineBiome>> replace;
@Override
public Stage get() {
return new ReplaceListStage(replace, defaultFrom, defaultTo, noise);
}
}
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators.ReplaceStage;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
@SuppressWarnings("unused")
public class ReplaceStageTemplate extends StageTemplate {
@Value("from")
private @Meta String from;
@Value("to")
private @Meta ProbabilityCollection<@Meta PipelineBiome> to;
@Override
public Stage get() {
return new ReplaceStage(from, to, noise);
}
}
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator;
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.StageTemplate;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators.SmoothStage;
public class SmoothStageTemplate extends StageTemplate {
@Override
public Stage get() {
return new SmoothStage(noise);
}
}
@@ -0,0 +1,217 @@
package com.dfsek.terra.addons.biome.pipeline.v2.pipeline;
import com.dfsek.terra.addons.biome.pipeline.v2.api.BiomeChunk;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Expander;
import com.dfsek.terra.addons.biome.pipeline.v2.api.SeededVector;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import net.jafama.FastMath;
import java.util.List;
public class BiomeChunkImpl implements BiomeChunk {
private PipelineBiome[][] biomes;
private final SeededVector worldOrigin;
private final int chunkOriginArrayIndex;
private final int worldCoordinateScale;
public BiomeChunkImpl(SeededVector worldOrigin, PipelineImpl pipeline) {
this.worldOrigin = worldOrigin;
this.chunkOriginArrayIndex = pipeline.getChunkOriginArrayIndex();
this.worldCoordinateScale = pipeline.getResolution();
int size = pipeline.getArraySize();
int expanderCount = pipeline.getExpanderCount();
int expansionsApplied = 0;
// Allocate working arrays
this.biomes = new PipelineBiome[size][size];
PipelineBiome[][] lookupArray = new PipelineBiome[size][size];
// A second lookup array is required such that stage application doesn't affect lookups, otherwise application may cascade
// Construct working grid
int gridOrigin = 0;
int gridInterval = calculateGridInterval(expanderCount, expansionsApplied);
int gridSize = (size / gridInterval);
gridSize += expanderCount > 0 ? 1 : 0; // Add an extra border if expansion occurs
// Fill working grid with initial cells
for(int gridX = 0; gridX < gridSize; gridX++) {
for(int gridZ = 0; gridZ < gridSize; gridZ++) {
int xIndex = gridOrigin + gridX * gridInterval;
int zIndex = gridOrigin + gridZ * gridInterval;
biomes[xIndex][zIndex] = pipeline.getSource().get(worldOrigin.seed(), xIndexToWorldCoordinate(xIndex), zIndexToWorldCoordinate(zIndex));
}
}
for(Stage stage : pipeline.getStages()) {
if(stage instanceof Expander) {
// Shrink working grid size, the expander will fill in null cells (as a result of shrinking the grid) during mutation
expansionsApplied++;
gridInterval = calculateGridInterval(expanderCount, expansionsApplied);
gridSize = expandSize(gridSize);
}
int stageReadDistance = stage.maxRelativeReadDistance();
if(stageReadDistance > 0) {
// Discard edges such that adjacent lookups are only ran on valid cells
gridSize = contractBordersFromSize(gridSize, stageReadDistance);
gridOrigin += stageReadDistance * gridInterval;
}
// Cycle arrays, the previously populated array is swapped to be used for lookups, and the result of the stage application
// overwrites the previous lookup array. This saves having to allocate a new array copy each time
PipelineBiome[][] tempArray = biomes;
biomes = lookupArray;
lookupArray = tempArray;
// Apply stage to working grid
for(int gridZ = 0; gridZ < gridSize; gridZ = gridZ + 1) {
for(int gridX = 0; gridX < gridSize; gridX = gridX + 1) {
int xIndex = gridOrigin + gridX * gridInterval;
int zIndex = gridOrigin + gridZ * gridInterval;
biomes[xIndex][zIndex] = stage.apply(new ViewPoint(this, gridInterval, gridX, gridZ, xIndex, zIndex, lookupArray));
}
}
}
}
@Override
public PipelineBiome get(int xInChunk, int zInChunk) {
int xIndex = xInChunk + chunkOriginArrayIndex;
int zIndex = zInChunk + chunkOriginArrayIndex;
return biomes[xIndex][zIndex];
}
private int xIndexToWorldCoordinate(int xIndex) {
return (worldOrigin.x() + xIndex - chunkOriginArrayIndex) * worldCoordinateScale;
}
private int zIndexToWorldCoordinate(int zIndex) {
return (worldOrigin.z() + zIndex - chunkOriginArrayIndex) * worldCoordinateScale;
}
protected static int initialSizeToArraySize(int expanderCount, int initialSize) {
int size = initialSize;
for(int i = 0; i < expanderCount; i++) {
size = expandSize(size);
}
return size;
}
protected static int calculateChunkOriginArrayIndex(int totalExpanderCount, List<Stage> stages) {
int finalGridOrigin = calculateFinalGridOrigin(totalExpanderCount, stages);
int initialGridInterval = calculateGridInterval(totalExpanderCount, 0);
// Round the final grid origin up to the nearest multiple of initialGridInterval, such that each
// chunk samples points on the same overall grid.
// Without this, shared chunk borders (required because of adjacent cell reads) will not be identical
// because points would be sampled on grids at different offsets, resulting in artifacts at borders.
return FastMath.ceilToInt((double) finalGridOrigin / initialGridInterval) * initialGridInterval;
}
private static int calculateFinalGridOrigin(int totalExpanderCount, List<Stage> stages) {
int gridOrigin = 0;
int expansionsApplied = 0;
int gridInterval = calculateGridInterval(totalExpanderCount, expansionsApplied);
for (Stage stage : stages) {
if (stage instanceof Expander) {
expansionsApplied++;
gridInterval = calculateGridInterval(totalExpanderCount, expansionsApplied);
}
gridOrigin += stage.maxRelativeReadDistance() * gridInterval;
}
return gridOrigin;
}
protected static int calculateChunkSize(int arraySize, int chunkOriginArrayIndex, int totalExpanderCount) {
return contractBordersFromSize(arraySize, chunkOriginArrayIndex) - (totalExpanderCount > 0 ? 1 : 0);
}
private static int expandSize(int size) {
return size * 2 - 1;
}
private static int contractBordersFromSize(int size, int border) {
return size - border * 2;
}
private static int calculateGridInterval(int totalExpansions, int expansionsApplied) {
return 1 << (totalExpansions - expansionsApplied);
}
private SeededVector getOrigin() {
return worldOrigin;
}
/**
* Represents a point on the operating grid within the biomes array
*/
public static class ViewPoint {
private final BiomeChunkImpl chunk;
private final PipelineBiome biome;
private final int gridInterval;
private final int gridX;
private final int gridZ;
private final int xIndex;
private final int zIndex;
private final PipelineBiome[][] lookupArray;
private ViewPoint(BiomeChunkImpl chunk, int gridInterval, int gridX, int gridZ, int xIndex, int zIndex, PipelineBiome[][] lookupArray) {
this.chunk = chunk;
this.gridInterval = gridInterval;
this.gridX = gridX;
this.gridZ = gridZ;
this.xIndex = xIndex;
this.zIndex = zIndex;
this.lookupArray = lookupArray;
this.biome = lookupArray[xIndex][zIndex];
}
public PipelineBiome getRelativeBiome(int x, int z) {
int lookupXIndex = this.xIndex + x * gridInterval;
int lookupZIndex = this.zIndex + z * gridInterval;
return lookupArray[lookupXIndex][lookupZIndex];
}
public PipelineBiome getBiome() {
return biome;
}
/**
* @return X position of the point relative to the operating grid
*/
public int gridX() {
return gridX;
}
/**
* @return Z position of the point relative to the operating grid
*/
public int gridZ() {
return gridZ;
}
/**
* @return X position of the point in the world
*/
public int worldX() {
return chunk.xIndexToWorldCoordinate(xIndex);
}
/**
* @return Z position of the point in the world
*/
public int worldZ() {
return chunk.zIndexToWorldCoordinate(zIndex);
}
public long worldSeed() {
return chunk.getOrigin().seed();
}
}
}
@@ -0,0 +1,92 @@
package com.dfsek.terra.addons.biome.pipeline.v2.pipeline;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import com.dfsek.terra.addons.biome.pipeline.v2.api.BiomeChunk;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Expander;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Pipeline;
import com.dfsek.terra.addons.biome.pipeline.v2.api.SeededVector;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
public class PipelineImpl implements Pipeline {
private static final Logger logger = LoggerFactory.getLogger(PipelineImpl.class);
private final Source source;
private final List<Stage> stages;
private final int chunkSize;
private final int expanderCount;
private final int arraySize;
private final int chunkOriginArrayIndex;
private final int resolution;
public PipelineImpl(Source source, List<Stage> stages, int resolution, int idealChunkArraySize) {
this.source = source;
this.stages = stages;
this.resolution = resolution;
this.expanderCount = (int) stages.stream().filter(s -> s instanceof Expander).count();
// Optimize for the ideal array size
int arraySize;
int chunkOriginArrayIndex;
int chunkSize;
int initialSize = 1;
while (true) {
arraySize = BiomeChunkImpl.initialSizeToArraySize(expanderCount, initialSize);
chunkOriginArrayIndex = BiomeChunkImpl.calculateChunkOriginArrayIndex(expanderCount, stages);
chunkSize = BiomeChunkImpl.calculateChunkSize(arraySize, chunkOriginArrayIndex, expanderCount);
if (chunkSize > 1 && arraySize >= idealChunkArraySize) break;
initialSize++;
}
this.arraySize = arraySize;
this.chunkOriginArrayIndex = chunkOriginArrayIndex;
this.chunkSize = chunkSize;
logger.debug("Initialized a new biome pipeline:");
logger.debug("Array size: {} (Target: {})", arraySize, idealChunkArraySize);
logger.debug("Internal array origin: {}", chunkOriginArrayIndex);
logger.debug("Chunk size: {}", chunkSize);
}
@Override
public BiomeChunk generateChunk(SeededVector worldCoordinates) {
return new BiomeChunkImpl(worldCoordinates, this);
}
@Override
public int getChunkSize() {
return chunkSize;
}
@Override
public Source getSource() {
return source;
}
@Override
public List<Stage> getStages() {
return stages;
}
protected int getExpanderCount() {
return expanderCount;
}
protected int getArraySize() {
return arraySize;
}
protected int getChunkOriginArrayIndex() {
return chunkOriginArrayIndex;
}
protected int getResolution() {
return resolution;
}
}
@@ -0,0 +1,27 @@
package com.dfsek.terra.addons.biome.pipeline.v2.source;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class SamplerSource implements Source {
private final ProbabilityCollection<PipelineBiome> biomes;
private final NoiseSampler sampler;
public SamplerSource(ProbabilityCollection<PipelineBiome> biomes, NoiseSampler sampler) {
this.biomes = biomes;
this.sampler = sampler;
}
@Override
public PipelineBiome get(long seed, int x, int z) {
return biomes.get(sampler, x, z, seed);
}
@Override
public Iterable<PipelineBiome> getBiomes() {
return biomes.getContents();
}
}
@@ -0,0 +1,27 @@
package com.dfsek.terra.addons.biome.pipeline.v2.source;
import java.util.Collections;
import java.util.Set;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source;
public class SingleSource implements Source {
private final PipelineBiome biome;
public SingleSource(PipelineBiome biome) {
this.biome = biome;
}
@Override
public PipelineBiome get(long seed, int x, int z) {
return biome;
}
@Override
public Set<PipelineBiome> getBiomes() {
return Collections.singleton(biome);
}
}
@@ -0,0 +1,37 @@
package com.dfsek.terra.addons.biome.pipeline.v2.stage.expander;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Expander;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl;
import com.dfsek.terra.api.noise.NoiseSampler;
public class FractalExpander implements Expander {
private final NoiseSampler sampler;
public FractalExpander(NoiseSampler sampler) {
this.sampler = sampler;
}
@Override
public PipelineBiome fillBiome(BiomeChunkImpl.ViewPoint viewPoint) {
int xMod2 = viewPoint.gridX() % 2;
int zMod2 = viewPoint.gridZ() % 2;
double roll = sampler.noise(viewPoint.worldSeed(), viewPoint.worldX(), viewPoint.worldZ());
if (xMod2 == 1 && zMod2 == 0) { // Pick one of 2 neighbors on X axis randomly
return roll > 0 ? viewPoint.getRelativeBiome(-1, 0) : viewPoint.getRelativeBiome(1, 0);
} else if (xMod2 == 0 && zMod2 == 1) { // Pick one of 2 neighbors on Z axis randomly
return roll > 0 ? viewPoint.getRelativeBiome(0, -1) : viewPoint.getRelativeBiome(0, 1);
} else { // Pick one of 4 corners randomly
return roll > 0 ?
roll > 0.25 ? viewPoint.getRelativeBiome(-1, 1) : viewPoint.getRelativeBiome(1, 1) :
roll > -0.25 ? viewPoint.getRelativeBiome(-1, -1) : viewPoint.getRelativeBiome(1, -1);
}
}
}
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
import com.dfsek.terra.api.util.vector.Vector2Int;
public class BorderListStage implements Stage {
private final String border;
private final NoiseSampler noiseSampler;
private final ProbabilityCollection<PipelineBiome> replaceDefault;
private final String defaultReplace;
private final Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace;
private final Vector2Int[] borderPoints;
public BorderListStage(Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace, String border, String defaultReplace,
NoiseSampler noiseSampler, ProbabilityCollection<PipelineBiome> replaceDefault) {
this.border = border;
this.noiseSampler = noiseSampler;
this.replaceDefault = replaceDefault;
this.defaultReplace = defaultReplace;
this.replace = replace;
List<Vector2Int> points = new ArrayList<>();
for(int x = -1; x <= 1; x++) {
for(int z = -1; z <= 1; z++) {
if(x == 0 && z == 0) continue;
points.add(Vector2Int.of(x, z));
}
}
this.borderPoints = points.toArray(new Vector2Int[0]);
}
@Override
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) {
Set<PipelineBiome> biomeSet = new HashSet<>();
biomes.forEach(biomeSet::add);
biomeSet.addAll(replaceDefault.getContents().stream().filter(Predicate.not(PipelineBiome::isSelf)).toList());
replace.forEach((biome, collection) -> biomeSet.addAll(collection.getContents()));
return biomeSet;
}
@Override
public PipelineBiome apply(BiomeChunkImpl.ViewPoint viewPoint) {
PipelineBiome center = viewPoint.getBiome();
if(center.getTags().contains(defaultReplace)) {
for(Vector2Int point : borderPoints) {
PipelineBiome current = viewPoint.getRelativeBiome(point.getX(), point.getZ());
if(current != null && current.getTags().contains(border)) {
if(replace.containsKey(center)) {
PipelineBiome replacement = replace.get(center).get(noiseSampler, viewPoint.worldX(), viewPoint.worldZ(),
viewPoint.worldSeed());
return replacement.isSelf() ? center : replacement;
}
PipelineBiome replacement = replaceDefault.get(noiseSampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed());
return replacement.isSelf() ? center : replacement;
}
}
}
return center;
}
@Override
public int maxRelativeReadDistance() {
return 1;
}
}
@@ -0,0 +1,81 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
import com.dfsek.terra.api.util.vector.Vector2Int;
public class BorderStage implements Stage {
private final String border;
private final NoiseSampler noiseSampler;
private final ProbabilityCollection<PipelineBiome> replace;
private final String replaceTag;
private final Vector2Int[] borderPoints;
public BorderStage(String border, String replaceTag, NoiseSampler noiseSampler, ProbabilityCollection<PipelineBiome> replace) {
this.border = border;
this.noiseSampler = noiseSampler;
this.replace = replace;
this.replaceTag = replaceTag;
List<Vector2Int> points = new ArrayList<>();
for(int x = -1; x <= 1; x++) {
for(int z = -1; z <= 1; z++) {
if(x == 0 && z == 0) continue;
points.add(Vector2Int.of(x, z));
}
}
this.borderPoints = points.toArray(new Vector2Int[0]);
}
@Override
public PipelineBiome apply(BiomeChunkImpl.ViewPoint viewPoint) {
PipelineBiome center = viewPoint.getBiome();
if(center.getTags().contains(replaceTag)) {
for(Vector2Int point : borderPoints) {
PipelineBiome current = viewPoint.getRelativeBiome(point.getX(), point.getZ());
if(current != null && current.getTags().contains(border)) {
PipelineBiome replacement = replace.get(noiseSampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed());
return replacement.isSelf() ? center : replacement;
}
}
}
return center;
}
@Override
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) {
Set<PipelineBiome> biomeSet = new HashSet<>();
biomes.forEach(biomeSet::add);
biomeSet.addAll(
replace
.getContents()
.stream()
.filter(
Predicate.not(PipelineBiome::isSelf)
)
.toList()
);
return biomeSet;
}
@Override
public int maxRelativeReadDistance() {
return 1;
}
}
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class ReplaceListStage implements Stage {
private final Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace;
private final NoiseSampler sampler;
private final ProbabilityCollection<PipelineBiome> replaceDefault;
private final String defaultTag;
public ReplaceListStage(Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace, String defaultTag,
ProbabilityCollection<PipelineBiome> replaceDefault, NoiseSampler sampler) {
this.replace = replace;
this.sampler = sampler;
this.defaultTag = defaultTag;
this.replaceDefault = replaceDefault;
}
@Override
public PipelineBiome apply(BiomeChunkImpl.ViewPoint viewPoint) {
PipelineBiome center = viewPoint.getBiome();
if(replace.containsKey(center)) {
PipelineBiome biome = replace.get(center).get(sampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed());
return biome.isSelf() ? viewPoint.getBiome() : biome;
}
if(viewPoint.getBiome().getTags().contains(defaultTag)) {
PipelineBiome biome = replaceDefault.get(sampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed());
return biome.isSelf() ? viewPoint.getBiome() : biome;
}
return center;
}
@Override
public int maxRelativeReadDistance() {
return 0;
}
@Override
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) {
Set<PipelineBiome> biomeSet = new HashSet<>();
Set<PipelineBiome> reject = new HashSet<>();
biomes.forEach(biome -> {
if(!biome.getTags().contains(defaultTag) && !replace.containsKey(biome)) {
biomeSet.add(biome);
} else {
reject.add(biome);
}
});
biomeSet.addAll(replaceDefault.getContents().stream().flatMap(terraBiome -> {
if(terraBiome.isSelf()) return reject.stream();
return Stream.of(terraBiome);
}).toList());
replace.forEach((biome, collection) -> biomeSet.addAll(collection.getContents().stream().map(terraBiome -> {
if(terraBiome.isSelf()) return biome;
return terraBiome;
}).toList()));
return biomeSet;
}
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class ReplaceStage implements Stage {
private final String replaceableTag;
private final ProbabilityCollection<PipelineBiome> replace;
private final NoiseSampler sampler;
public ReplaceStage(String replaceable, ProbabilityCollection<PipelineBiome> replace, NoiseSampler sampler) {
this.replaceableTag = replaceable;
this.replace = replace;
this.sampler = sampler;
}
@Override
public PipelineBiome apply(BiomeChunkImpl.ViewPoint viewPoint) {
if(viewPoint.getBiome().getTags().contains(replaceableTag)) {
PipelineBiome biome = replace.get(sampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed());
return biome.isSelf() ? viewPoint.getBiome() : biome;
}
return viewPoint.getBiome();
}
@Override
public int maxRelativeReadDistance() {
return 0;
}
@Override
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) {
Set<PipelineBiome> biomeSet = new HashSet<>();
Set<PipelineBiome> reject = new HashSet<>();
biomes.forEach(biome -> {
if(!biome.getTags().contains(replaceableTag)) {
biomeSet.add(biome);
} else {
reject.add(biome);
}
});
biomeSet.addAll(replace.getContents().stream().flatMap(terraBiome -> {
if(terraBiome.isSelf()) return reject.stream();
return Stream.of(terraBiome);
}).toList());
return biomeSet;
}
}
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators;
import java.util.Objects;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl;
import com.dfsek.terra.api.noise.NoiseSampler;
public class SmoothStage implements Stage {
private final NoiseSampler sampler;
public SmoothStage(NoiseSampler sampler) {
this.sampler = sampler;
}
@Override
public PipelineBiome apply(BiomeChunkImpl.ViewPoint viewPoint) {
PipelineBiome top = viewPoint.getRelativeBiome(1, 0);
PipelineBiome bottom = viewPoint.getRelativeBiome(-1, 0);
PipelineBiome left = viewPoint.getRelativeBiome(0, 1);
PipelineBiome right = viewPoint.getRelativeBiome(0, -1);
double roll = sampler.noise(viewPoint.worldSeed(), viewPoint.worldX(), viewPoint.worldZ());
boolean vert = Objects.equals(top, bottom);
boolean horiz = Objects.equals(left, right);
if(vert && horiz) {
return roll > 0 ?
roll > 0.25 ? left : right :
roll > -0.25 ? top : bottom;
}
if(vert) {
return roll > 0 ? top : bottom;
}
if(horiz) {
return roll > 0 ? left : right;
}
return viewPoint.getBiome();
}
@Override
public int maxRelativeReadDistance() {
return 1;
}
}
@@ -0,0 +1,12 @@
schema-version: 1
contributors:
- Terra contributors
id: biome-provider-pipeline-v2
version: @VERSION@
entrypoints:
- "com.dfsek.terra.addons.biome.pipeline.v2.BiomePipelineAddon"
website:
issues: https://github.com/PolyhedralDev/Terra/issues
source: https://github.com/PolyhedralDev/Terra
docs: https://terra.polydev.org
license: MIT License
@@ -8,6 +8,8 @@
package com.dfsek.terra.addons.biome.pipeline;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.Supplier;
@@ -39,6 +41,8 @@ import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class BiomePipelineAddon implements AddonInitializer {
private static final Logger logger = LoggerFactory.getLogger(BiomePipelineAddon.class);
public static final TypeKey<Supplier<ObjectTemplate<BiomeSource>>> SOURCE_REGISTRY_KEY = new TypeKey<>() {
};
@@ -85,5 +89,7 @@ public class BiomePipelineAddon implements AddonInitializer {
Registry<Biome> biomeRegistry = event.getPack().getRegistry(Biome.class);
event.getPack().applyLoader(BiomeDelegate.class, new BiomeDelegateLoader(biomeRegistry));
});
logger.warn("The biome-provider-pipeline addon is deprecated and scheduled for removal in Terra 7.0. It is recommended to use the biome-provider-pipeline-v2 addon for future pack development instead.");
}
}
@@ -1,4 +1,4 @@
version = version("1.1.0")
version = version("1.2.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
@@ -11,9 +11,10 @@ import com.dfsek.terra.addons.chunkgenerator.config.NoiseChunkGeneratorPackConfi
import com.dfsek.terra.addons.chunkgenerator.config.noise.BiomeNoiseConfigTemplate;
import com.dfsek.terra.addons.chunkgenerator.config.noise.BiomeNoiseProperties;
import com.dfsek.terra.addons.chunkgenerator.config.palette.BiomePaletteTemplate;
import com.dfsek.terra.addons.chunkgenerator.config.palette.PaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.config.palette.SlantLayer;
import com.dfsek.terra.addons.chunkgenerator.config.palette.slant.SlantLayerTemplate;
import com.dfsek.terra.addons.chunkgenerator.generation.NoiseChunkGenerator3D;
import com.dfsek.terra.addons.chunkgenerator.palette.BiomePaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
@@ -36,15 +37,20 @@ public class NoiseChunkGenerator3DAddon implements AddonInitializer {
@Override
public void initialize() {
PropertyKey<PaletteInfo> paletteInfoPropertyKey = Context.create(PaletteInfo.class);
PropertyKey<BiomePaletteInfo> paletteInfoPropertyKey = Context.create(BiomePaletteInfo.class);
PropertyKey<BiomeNoiseProperties> noisePropertiesPropertyKey = Context.create(BiomeNoiseProperties.class);
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, ConfigPackPreLoadEvent.class)
.priority(1000)
.then(event -> {
event.getPack().applyLoader(SlantHolder.CalculationMethod.class,
(type, o, loader, depthTracker) -> SlantHolder.CalculationMethod.valueOf((String) o));
NoiseChunkGeneratorPackConfigTemplate config = event.loadTemplate(new NoiseChunkGeneratorPackConfigTemplate());
event.getPack().getContext().put(config);
event.getPack()
.getOrCreateRegistry(ChunkGeneratorProvider.class)
.register(addon.key("NOISE_3D"),
@@ -53,7 +59,7 @@ public class NoiseChunkGenerator3DAddon implements AddonInitializer {
config.getVerticalRes(), noisePropertiesPropertyKey,
paletteInfoPropertyKey));
event.getPack()
.applyLoader(SlantLayer.class, SlantLayer::new);
.applyLoader(SlantHolder.Layer.class, SlantLayerTemplate::new);
})
.failThrough();
@@ -62,8 +68,10 @@ public class NoiseChunkGenerator3DAddon implements AddonInitializer {
.register(addon, ConfigurationLoadEvent.class)
.then(event -> {
if(event.is(Biome.class)) {
NoiseChunkGeneratorPackConfigTemplate config = event.getPack().getContext().get(NoiseChunkGeneratorPackConfigTemplate.class);
event.getLoadedObject(Biome.class).getContext().put(paletteInfoPropertyKey,
event.load(new BiomePaletteTemplate(platform)).get());
event.load(new BiomePaletteTemplate(platform, config.getSlantCalculationMethod())).get());
event.getLoadedObject(Biome.class).getContext().put(noisePropertiesPropertyKey,
event.load(new BiomeNoiseConfigTemplate()).get());
}
@@ -4,10 +4,12 @@ import com.dfsek.tectonic.api.config.template.ConfigTemplate;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.properties.Properties;
public class NoiseChunkGeneratorPackConfigTemplate implements ConfigTemplate {
public class NoiseChunkGeneratorPackConfigTemplate implements ConfigTemplate, Properties {
@Value("blend.terrain.elevation")
@Default
private @Meta int elevationBlend = 4;
@@ -20,6 +22,10 @@ public class NoiseChunkGeneratorPackConfigTemplate implements ConfigTemplate {
@Default
private @Meta int verticalRes = 2;
@Value("slant.calculation-method")
@Default
private SlantHolder.@Meta CalculationMethod slantCalculationMethod = SlantHolder.CalculationMethod.Derivative;
public int getElevationBlend() {
return elevationBlend;
}
@@ -31,4 +37,8 @@ public class NoiseChunkGeneratorPackConfigTemplate implements ConfigTemplate {
public int getVerticalRes() {
return verticalRes;
}
public SlantHolder.CalculationMethod getSlantCalculationMethod() {
return slantCalculationMethod;
}
}
@@ -15,25 +15,23 @@ import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolderBuilder;
import com.dfsek.terra.addons.chunkgenerator.palette.SlantHolder;
import com.dfsek.terra.addons.chunkgenerator.palette.BiomePaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.world.chunk.generation.util.Palette;
public class BiomePaletteTemplate implements ObjectTemplate<PaletteInfo> {
public class BiomePaletteTemplate implements ObjectTemplate<BiomePaletteInfo> {
private final Platform platform;
@Value("slant")
@Default
@Description("The slant palettes to use in this biome.")
private @Meta List<@Meta SlantLayer> slant = Collections.emptyList();
private @Meta List<SlantHolder.@Meta Layer> slantLayers = Collections.emptyList();
@Value("slant-depth")
@Default
@@ -63,27 +61,16 @@ public class BiomePaletteTemplate implements ObjectTemplate<PaletteInfo> {
@Default
private @Meta boolean updatePalette = false;
public BiomePaletteTemplate(Platform platform) { this.platform = platform; }
private final SlantHolder.CalculationMethod slantCalculationMethod;
public BiomePaletteTemplate(Platform platform, SlantHolder.CalculationMethod slantCalculationMethod) {
this.platform = platform;
this.slantCalculationMethod = slantCalculationMethod;
}
@Override
public PaletteInfo get() {
PaletteHolderBuilder builder = new PaletteHolderBuilder();
for(Map<Palette, Integer> layer : palettes) {
for(Entry<Palette, Integer> entry : layer.entrySet()) {
builder.add(entry.getValue(), entry.getKey());
}
}
TreeMap<Double, PaletteHolder> slantLayers = new TreeMap<>();
double minThreshold = Double.MAX_VALUE;
for(SlantLayer layer : slant) {
double threshold = layer.getThreshold();
if(threshold < minThreshold) minThreshold = threshold;
slantLayers.put(threshold, layer.getPalette());
}
return new PaletteInfo(builder.build(), SlantHolder.of(slantLayers, minThreshold), oceanPalette, seaLevel, slantDepth,
updatePalette);
public BiomePaletteInfo get() {
return new BiomePaletteInfo(PaletteHolder.of(palettes), SlantHolder.of(slantLayers, slantDepth, slantCalculationMethod),
oceanPalette, seaLevel, updatePalette);
}
}
@@ -1,22 +0,0 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.chunkgenerator.config.palette;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
import com.dfsek.terra.addons.chunkgenerator.palette.SlantHolder;
import com.dfsek.terra.api.properties.Properties;
import com.dfsek.terra.api.world.chunk.generation.util.Palette;
public record PaletteInfo(PaletteHolder paletteHolder,
SlantHolder slantHolder,
Palette ocean,
int seaLevel,
int maxSlantDepth,
boolean updatePaletteWhenCarving) implements Properties {
}
@@ -1,42 +0,0 @@
package com.dfsek.terra.addons.chunkgenerator.config.palette;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolderBuilder;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.world.chunk.generation.util.Palette;
public class SlantLayer implements ObjectTemplate<SlantLayer> {
@Value("threshold")
private @Meta double threshold;
@Value("palette")
private @Meta List<@Meta Map<@Meta Palette, @Meta Integer>> palettes;
@Override
public SlantLayer get() {
return this;
}
public double getThreshold() {
return threshold;
}
public PaletteHolder getPalette() {
PaletteHolderBuilder builder = new PaletteHolderBuilder();
for(Map<Palette, Integer> layer : palettes) {
for(Entry<Palette, Integer> entry : layer.entrySet()) {
builder.add(entry.getValue(), entry.getKey());
}
}
return builder.build();
}
}
@@ -0,0 +1,27 @@
package com.dfsek.terra.addons.chunkgenerator.config.palette.slant;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.List;
import java.util.Map;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.world.chunk.generation.util.Palette;
public class SlantLayerTemplate implements ObjectTemplate<SlantHolder.Layer> {
@Value("threshold")
private @Meta double threshold;
@Value("palette")
private @Meta List<@Meta Map<@Meta Palette, @Meta Integer>> palettes;
@Override
public SlantHolder.Layer get() {
return new SlantHolder.Layer(PaletteHolder.of(palettes), threshold);
}
}
@@ -12,7 +12,7 @@ import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.addons.chunkgenerator.config.noise.BiomeNoiseProperties;
import com.dfsek.terra.addons.chunkgenerator.config.palette.PaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.palette.BiomePaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.generation.math.PaletteUtil;
import com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation.LazilyEvaluatedInterpolator;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
@@ -40,13 +40,13 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
private final int carverHorizontalResolution;
private final int carverVerticalResolution;
private final PropertyKey<PaletteInfo> paletteInfoPropertyKey;
private final PropertyKey<BiomePaletteInfo> paletteInfoPropertyKey;
private final PropertyKey<BiomeNoiseProperties> noisePropertiesKey;
public NoiseChunkGenerator3D(ConfigPack pack, Platform platform, int elevationBlend, int carverHorizontalResolution,
int carverVerticalResolution,
PropertyKey<BiomeNoiseProperties> noisePropertiesKey,
PropertyKey<PaletteInfo> paletteInfoPropertyKey) {
PropertyKey<BiomePaletteInfo> paletteInfoPropertyKey) {
this.platform = platform;
this.air = platform.getWorldHandle().air();
this.carverHorizontalResolution = carverHorizontalResolution;
@@ -97,7 +97,7 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
for(int y = world.getMaxHeight() - 1; y >= world.getMinHeight(); y--) {
Biome biome = biomeColumn.get(y);
PaletteInfo paletteInfo = biome.getContext().get(paletteInfoPropertyKey);
BiomePaletteInfo paletteInfo = biome.getContext().get(paletteInfoPropertyKey);
int sea = paletteInfo.seaLevel();
Palette seaPalette = paletteInfo.ocean();
@@ -131,7 +131,7 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
Biome biome = biomeProvider.getBiome(x, y, z, world.getSeed());
Sampler3D sampler = samplerCache.get(x, z, world, biomeProvider);
PaletteInfo paletteInfo = biome.getContext().get(paletteInfoPropertyKey);
BiomePaletteInfo paletteInfo = biome.getContext().get(paletteInfoPropertyKey);
int fdX = FastMath.floorMod(x, 16);
int fdZ = FastMath.floorMod(z, 16);
@@ -155,6 +155,16 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
return biomeProvider.getBiome(x, y, z, world.getSeed()).getContext().get(paletteInfoPropertyKey).paletteHolder().getPalette(y);
}
public double getSlant(int x, int y, int z, WorldProperties world, BiomeProvider biomeProvider) {
int fdX = FastMath.floorMod(x, 16);
int fdZ = FastMath.floorMod(z, 16);
return biomeProvider.getBiome(x, y, z, world.getSeed())
.getContext()
.get(paletteInfoPropertyKey)
.slantHolder()
.calculateSlant(samplerCache.get(x, z, world, biomeProvider), fdX, y, fdZ);
}
public SamplerProvider samplerProvider() {
return samplerCache;
}
@@ -7,40 +7,23 @@
package com.dfsek.terra.addons.chunkgenerator.generation.math;
import com.dfsek.terra.addons.chunkgenerator.config.palette.PaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.palette.BiomePaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
import com.dfsek.terra.addons.chunkgenerator.palette.SlantHolder;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.api.world.chunk.generation.util.Palette;
public final class PaletteUtil {
/**
* Derivative constant.
*/
private static final double DERIVATIVE_DIST = 0.55;
public static Palette getPalette(int x, int y, int z, Sampler3D sampler, PaletteInfo paletteInfo, int depth) {
SlantHolder slant = paletteInfo.slantHolder();
if(!slant.isEmpty() && depth <= paletteInfo.maxSlantDepth()) {
double slope = derivative(sampler, x, y, z);
if(slope > slant.getMinSlope()) {
return slant.getPalette(slope).getPalette(y);
public static Palette getPalette(int x, int y, int z, Sampler3D sampler, BiomePaletteInfo paletteInfo, int depth) {
SlantHolder slantHolder = paletteInfo.slantHolder();
if(slantHolder.isAboveDepth(depth)) {
double slant = slantHolder.calculateSlant(sampler, x, y, z);
if(slantHolder.isInSlantThreshold(slant)) {
return slantHolder.getPalette(slant).getPalette(y);
}
}
return paletteInfo.paletteHolder().getPalette(y);
}
public static double derivative(Sampler3D sampler, double x, double y, double z) {
double baseSample = sampler.sample(x, y, z);
double xVal1 = (sampler.sample(x + DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double xVal2 = (sampler.sample(x - DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double zVal1 = (sampler.sample(x, y, z + DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double zVal2 = (sampler.sample(x, y, z - DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double yVal1 = (sampler.sample(x, y + DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
double yVal2 = (sampler.sample(x, y - DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
return Math.sqrt(((xVal2 - xVal1) * (xVal2 - xVal1)) + ((zVal2 - zVal1) * (zVal2 - zVal1)) + ((yVal2 - yVal1) * (yVal2 - yVal1)));
}
}
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.chunkgenerator.palette;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.api.properties.Properties;
import com.dfsek.terra.api.world.chunk.generation.util.Palette;
public record BiomePaletteInfo(PaletteHolder paletteHolder,
SlantHolder slantHolder,
Palette ocean,
int seaLevel,
boolean updatePaletteWhenCarving) implements Properties {
}
@@ -7,6 +7,13 @@
package com.dfsek.terra.addons.chunkgenerator.palette;
import net.jafama.FastMath;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import com.dfsek.terra.api.world.chunk.generation.util.Palette;
@@ -27,4 +34,43 @@ public class PaletteHolder {
: palettes[palettes.length - 1]
: palettes[0];
}
public static PaletteHolder of(List<Map<Palette, Integer>> palettes) {
PaletteHolderBuilder builder = new PaletteHolderBuilder();
for(Map<Palette, Integer> layer : palettes) {
for(Entry<Palette, Integer> entry : layer.entrySet()) {
builder.add(entry.getValue(), entry.getKey());
}
}
return builder.build();
}
private static class PaletteHolderBuilder {
private final TreeMap<Integer, Palette> paletteMap = new TreeMap<>();
public PaletteHolderBuilder add(int y, Palette palette) {
paletteMap.put(y, palette);
return this;
}
public PaletteHolder build() {
int min = FastMath.min(paletteMap.keySet().stream().min(Integer::compareTo).orElse(0), 0);
int max = FastMath.max(paletteMap.keySet().stream().max(Integer::compareTo).orElse(255), 255);
Palette[] palettes = new Palette[paletteMap.lastKey() + 1 - min];
for(int y = min; y <= FastMath.max(paletteMap.lastKey(), max); y++) {
Palette d = null;
for(Entry<Integer, Palette> e : paletteMap.entrySet()) {
if(e.getKey() >= y) {
d = e.getValue();
break;
}
}
if(d == null) throw new IllegalArgumentException("No palette for Y=" + y);
palettes[y - min] = d;
}
return new PaletteHolder(palettes, -min);
}
}
}
@@ -1,45 +0,0 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.chunkgenerator.palette;
import net.jafama.FastMath;
import java.util.Map;
import java.util.TreeMap;
import com.dfsek.terra.api.world.chunk.generation.util.Palette;
public class PaletteHolderBuilder {
private final TreeMap<Integer, Palette> paletteMap = new TreeMap<>();
public PaletteHolderBuilder add(int y, Palette palette) {
paletteMap.put(y, palette);
return this;
}
public PaletteHolder build() {
int min = FastMath.min(paletteMap.keySet().stream().min(Integer::compareTo).orElse(0), 0);
int max = FastMath.max(paletteMap.keySet().stream().max(Integer::compareTo).orElse(255), 255);
Palette[] palettes = new Palette[paletteMap.lastKey() + 1 - min];
for(int y = min; y <= FastMath.max(paletteMap.lastKey(), max); y++) {
Palette d = null;
for(Map.Entry<Integer, Palette> e : paletteMap.entrySet()) {
if(e.getKey() >= y) {
d = e.getValue();
break;
}
}
if(d == null) throw new IllegalArgumentException("No palette for Y=" + y);
palettes[y - min] = d;
}
return new PaletteHolder(palettes, -min);
}
}
@@ -1,64 +0,0 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.chunkgenerator.palette;
import java.util.Map.Entry;
import java.util.TreeMap;
public class SlantHolder {
private final TreeMap<Double, PaletteHolder> layers;
private final double minSlope;
private SlantHolder(TreeMap<Double, PaletteHolder> layers, double minSlope) {
this.layers = layers;
this.minSlope = minSlope;
}
public static SlantHolder of(TreeMap<Double, PaletteHolder> layers, double minSlope) {
if(layers.size() == 1) {
Entry<Double, PaletteHolder> firstEntry = layers.firstEntry();
return new Single(firstEntry.getValue(), minSlope);
}
return new SlantHolder(layers, minSlope);
}
public boolean isEmpty() {
return layers.isEmpty();
}
public PaletteHolder getPalette(double slope) {
return layers.floorEntry(slope).getValue();
}
public double getMinSlope() {
return minSlope;
}
private static final class Single extends SlantHolder {
private final PaletteHolder layers;
public Single(PaletteHolder layers, double minSlope) {
super(of(minSlope, layers), minSlope);
this.layers = layers;
}
private static TreeMap<Double, PaletteHolder> of(double v, PaletteHolder layer) {
TreeMap<Double, PaletteHolder> map = new TreeMap<>();
map.put(v, layer);
return map;
}
@Override
public PaletteHolder getPalette(double slope) {
return layers;
}
}
}
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.chunkgenerator.palette.slant;
import java.util.List;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
public class MultipleSlantHolder extends SlantHolderImpl {
private final NavigableMap<Double, PaletteHolder> layers;
private final double slantThreshold;
MultipleSlantHolder(List<SlantHolder.Layer> slant, int slantDepth, CalculationMethod calculationMethod) {
super(slantDepth, calculationMethod);
NavigableMap<Double, PaletteHolder> layers = new TreeMap<>(slant.stream().collect(Collectors.toMap(SlantHolder.Layer::threshold, SlantHolder.Layer::palette)));
Stream<Double> thresholds = layers.keySet().stream();
double slantThreshold = floorToThreshold ?
thresholds.min(Double::compare).orElseThrow() :
thresholds.max(Double::compare).orElseThrow();
this.layers = layers;
this.slantThreshold = slantThreshold;
}
@Override
protected double getSlantThreshold() {
return slantThreshold;
}
@Override
public PaletteHolder getPalette(double slant) {
return (floorToThreshold ?
layers.floorEntry(slant) :
layers.ceilingEntry(slant)
).getValue();
}
}
@@ -0,0 +1,24 @@
package com.dfsek.terra.addons.chunkgenerator.palette.slant;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
final class SingleSlantHolder extends SlantHolderImpl {
private final SlantHolder.Layer layer;
public SingleSlantHolder(SlantHolder.Layer layer, int slantDepth, CalculationMethod calculationMethod) {
super(slantDepth, calculationMethod);
this.layer = layer;
}
@Override
public PaletteHolder getPalette(double slant) {
return layer.palette();
}
@Override
protected double getSlantThreshold() {
return layer.threshold();
}
}
@@ -0,0 +1,116 @@
package com.dfsek.terra.addons.chunkgenerator.palette.slant;
import java.util.List;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
import com.dfsek.terra.addons.chunkgenerator.palette.PaletteHolder;
import com.dfsek.terra.api.util.vector.Vector3;
public interface SlantHolder {
static SlantHolder of(List<SlantHolder.Layer> layers, int slantDepth, CalculationMethod calculationMethod) {
if(layers.isEmpty()) {
return EMPTY;
} else if(layers.size() == 1) {
return new SingleSlantHolder(layers.get(0), slantDepth, calculationMethod);
}
return new MultipleSlantHolder(layers, slantDepth, calculationMethod);
}
double calculateSlant(Sampler3D sampler, double x, double y, double z);
boolean isAboveDepth(int depth);
boolean isInSlantThreshold(double slant);
PaletteHolder getPalette(double slant);
record Layer(PaletteHolder palette, double threshold) {
}
enum CalculationMethod {
DotProduct {
private static final Vector3 DOT_PRODUCT_DIRECTION = Vector3.of(0, 1, 0);
private static final Vector3[] DOT_PRODUCT_SAMPLE_POINTS = {
Vector3.of(0, 0, -DERIVATIVE_DIST),
Vector3.of(0, 0, DERIVATIVE_DIST),
Vector3.of(0, -DERIVATIVE_DIST, 0),
Vector3.of(0, DERIVATIVE_DIST, 0),
Vector3.of(-DERIVATIVE_DIST, 0, 0),
Vector3.of(DERIVATIVE_DIST, 0, 0)
};
@Override
public double slant(Sampler3D sampler, double x, double y, double z) {
Vector3.Mutable normalApproximation = Vector3.Mutable.of(0, 0, 0);
for(Vector3 point : DOT_PRODUCT_SAMPLE_POINTS) {
var scalar = -sampler.sample(x+point.getX(), y+point.getY(), z+point.getZ());
normalApproximation.add(point.mutable().multiply(scalar));
}
return DOT_PRODUCT_DIRECTION.dot(normalApproximation.normalize());
}
@Override
public boolean floorToThreshold() {
return false;
}
},
Derivative {
@Override
public double slant(Sampler3D sampler, double x, double y, double z) {
double baseSample = sampler.sample(x, y, z);
double xVal1 = (sampler.sample(x + DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double xVal2 = (sampler.sample(x - DERIVATIVE_DIST, y, z) - baseSample) / DERIVATIVE_DIST;
double zVal1 = (sampler.sample(x, y, z + DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double zVal2 = (sampler.sample(x, y, z - DERIVATIVE_DIST) - baseSample) / DERIVATIVE_DIST;
double yVal1 = (sampler.sample(x, y + DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
double yVal2 = (sampler.sample(x, y - DERIVATIVE_DIST, z) - baseSample) / DERIVATIVE_DIST;
return Math.sqrt(((xVal2 - xVal1) * (xVal2 - xVal1)) + ((zVal2 - zVal1) * (zVal2 - zVal1)) + ((yVal2 - yVal1) * (yVal2 - yVal1)));
}
@Override
public boolean floorToThreshold() {
return true;
}
};
private static final double DERIVATIVE_DIST = 0.55;
public abstract double slant(Sampler3D sampler, double x, double y, double z);
/*
* Controls whether palettes should be applied before or after their respective thresholds.
*
* If true, slant values will map to the palette of the next floor threshold, otherwise they
* will map to the ceiling.
*/
public abstract boolean floorToThreshold();
}
SlantHolder EMPTY = new SlantHolder() {
@Override
public double calculateSlant(Sampler3D sampler, double x, double y, double z) {
throw new UnsupportedOperationException("Empty holder should not calculate slant");
}
@Override
public boolean isAboveDepth(int depth) {
return false;
}
@Override
public boolean isInSlantThreshold(double slant) {
return false;
}
@Override
public PaletteHolder getPalette(double slant) {
throw new UnsupportedOperationException("Empty holder cannot return a palette");
}
};
}
@@ -0,0 +1,38 @@
package com.dfsek.terra.addons.chunkgenerator.palette.slant;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
public abstract class SlantHolderImpl implements SlantHolder {
private final SlantHolder.CalculationMethod calculationMethod;
private final int slantDepth;
protected final boolean floorToThreshold;
protected SlantHolderImpl(int slantDepth, CalculationMethod calculationMethod) {
this.floorToThreshold = calculationMethod.floorToThreshold();
this.calculationMethod = calculationMethod;
this.slantDepth = slantDepth;
}
protected abstract double getSlantThreshold();
@Override
public final double calculateSlant(Sampler3D sampler, double x, double y, double z) {
return calculationMethod.slant(sampler, x, y, z);
}
@Override
public final boolean isAboveDepth(int depth) {
return depth <= slantDepth;
}
@Override
public final boolean isInSlantThreshold(double slant) {
return (floorToThreshold ?
slant > getSlantThreshold() :
slant < getSlantThreshold()
);
}
}
@@ -1,6 +1,6 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
version = version("1.0.0")
version = version("1.1.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
@@ -14,6 +14,7 @@ import java.util.Map;
import java.util.function.Supplier;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.addons.noise.config.CubicSplinePointTemplate;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.BinaryArithmeticTemplate;
import com.dfsek.terra.addons.noise.config.templates.DomainWarpTemplate;
@@ -21,8 +22,10 @@ import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.config.templates.ImageSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.KernelTemplate;
import com.dfsek.terra.addons.noise.config.templates.LinearHeightmapSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.TranslateSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.CellularNoiseTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.ConstantNoiseTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.DistanceSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.ExpressionFunctionTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.GaborNoiseTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.SimpleNoiseTemplate;
@@ -30,11 +33,14 @@ import com.dfsek.terra.addons.noise.config.templates.noise.fractal.BrownianMotio
import com.dfsek.terra.addons.noise.config.templates.noise.fractal.PingPongTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.fractal.RidgedFractalTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ClampNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.CubicSplineNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ExpressionNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.LinearNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.NormalNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.PosterizationNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ProbabilityNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ScaleNormalizerTemplate;
import com.dfsek.terra.addons.noise.math.CubicSpline;
import com.dfsek.terra.addons.noise.samplers.arithmetic.AdditionSampler;
import com.dfsek.terra.addons.noise.samplers.arithmetic.DivisionSampler;
import com.dfsek.terra.addons.noise.samplers.arithmetic.MaxSampler;
@@ -42,6 +48,7 @@ import com.dfsek.terra.addons.noise.samplers.arithmetic.MinSampler;
import com.dfsek.terra.addons.noise.samplers.arithmetic.MultiplicationSampler;
import com.dfsek.terra.addons.noise.samplers.arithmetic.SubtractionSampler;
import com.dfsek.terra.addons.noise.samplers.noise.CellularSampler;
import com.dfsek.terra.addons.noise.samplers.noise.DistanceSampler;
import com.dfsek.terra.addons.noise.samplers.noise.random.GaussianNoiseSampler;
import com.dfsek.terra.addons.noise.samplers.noise.random.PositiveWhiteNoiseSampler;
import com.dfsek.terra.addons.noise.samplers.noise.random.WhiteNoiseSampler;
@@ -83,8 +90,11 @@ public class NoiseAddon implements AddonInitializer {
(type, o, loader, depthTracker) -> CellularSampler.DistanceFunction.valueOf((String) o))
.applyLoader(CellularSampler.ReturnType.class,
(type, o, loader, depthTracker) -> CellularSampler.ReturnType.valueOf((String) o))
.applyLoader(DistanceSampler.DistanceFunction.class,
(type, o, loader, depthTracker) -> DistanceSampler.DistanceFunction.valueOf((String) o))
.applyLoader(DimensionApplicableNoiseSampler.class, DimensionApplicableNoiseSampler::new)
.applyLoader(FunctionTemplate.class, FunctionTemplate::new);
.applyLoader(FunctionTemplate.class, FunctionTemplate::new)
.applyLoader(CubicSpline.Point.class, CubicSplinePointTemplate::new);
noiseRegistry.register(addon.key("LINEAR"), LinearNormalizerTemplate::new);
noiseRegistry.register(addon.key("NORMAL"), NormalNormalizerTemplate::new);
@@ -92,9 +102,10 @@ public class NoiseAddon implements AddonInitializer {
noiseRegistry.register(addon.key("PROBABILITY"), ProbabilityNormalizerTemplate::new);
noiseRegistry.register(addon.key("SCALE"), ScaleNormalizerTemplate::new);
noiseRegistry.register(addon.key("POSTERIZATION"), PosterizationNormalizerTemplate::new);
noiseRegistry.register(addon.key("CUBIC_SPLINE"), CubicSplineNormalizerTemplate::new);
noiseRegistry.register(addon.key("IMAGE"), ImageSamplerTemplate::new);
noiseRegistry.register(addon.key("DOMAIN_WARP"), DomainWarpTemplate::new);
noiseRegistry.register(addon.key("FBM"), BrownianMotionTemplate::new);
@@ -116,12 +127,15 @@ public class NoiseAddon implements AddonInitializer {
noiseRegistry.register(addon.key("WHITE_NOISE"), () -> new SimpleNoiseTemplate(WhiteNoiseSampler::new));
noiseRegistry.register(addon.key("POSITIVE_WHITE_NOISE"), () -> new SimpleNoiseTemplate(PositiveWhiteNoiseSampler::new));
noiseRegistry.register(addon.key("GAUSSIAN"), () -> new SimpleNoiseTemplate(GaussianNoiseSampler::new));
noiseRegistry.register(addon.key("DISTANCE"), DistanceSamplerTemplate::new);
noiseRegistry.register(addon.key("CONSTANT"), ConstantNoiseTemplate::new);
noiseRegistry.register(addon.key("KERNEL"), KernelTemplate::new);
noiseRegistry.register(addon.key("LINEAR_HEIGHTMAP"), LinearHeightmapSamplerTemplate::new);
noiseRegistry.register(addon.key("TRANSLATE"), TranslateSamplerTemplate::new);
noiseRegistry.register(addon.key("ADD"), () -> new BinaryArithmeticTemplate<>(AdditionSampler::new));
noiseRegistry.register(addon.key("SUB"), () -> new BinaryArithmeticTemplate<>(SubtractionSampler::new));
@@ -134,7 +148,7 @@ public class NoiseAddon implements AddonInitializer {
Map<String, DimensionApplicableNoiseSampler> packSamplers = new LinkedHashMap<>();
Map<String, FunctionTemplate> packFunctions = new LinkedHashMap<>();
noiseRegistry.register(addon.key("EXPRESSION"), () -> new ExpressionFunctionTemplate(packSamplers, packFunctions));
noiseRegistry.register(addon.key("EXPRESSION_NORMALIZER"), () -> new ExpressionNormalizerTemplate(packSamplers, packFunctions));
NoiseConfigPackTemplate template = event.loadTemplate(new NoiseConfigPackTemplate());
packSamplers.putAll(template.getSamplers());
@@ -0,0 +1,25 @@
package com.dfsek.terra.addons.noise.config;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.addons.noise.math.CubicSpline.Point;
import com.dfsek.terra.api.config.meta.Meta;
public class CubicSplinePointTemplate implements ObjectTemplate<Point> {
@Value("from")
private @Meta double from;
@Value("to")
private @Meta double to;
@Value("gradient")
private @Meta double gradient;
@Override
public Point get() {
return new Point(from, to, gradient);
}
}
@@ -8,6 +8,8 @@
package com.dfsek.terra.addons.noise.config.templates;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.image.BufferedImage;
@@ -19,6 +21,10 @@ import com.dfsek.terra.api.noise.NoiseSampler;
@SuppressWarnings({ "unused", "FieldMayBeFinal" })
public class ImageSamplerTemplate extends SamplerTemplate<ImageSampler> {
private static final Logger logger = LoggerFactory.getLogger(ImageSamplerTemplate.class);
private static boolean used = false;
@Value("image")
private @Meta BufferedImage image;
@@ -30,6 +36,12 @@ public class ImageSamplerTemplate extends SamplerTemplate<ImageSampler> {
@Override
public NoiseSampler get() {
if(!used) {
logger.warn("The IMAGE NoiseSampler implemented by the config-noise-function addon is deprecated. " +
"It is recommended to use the IMAGE NoiseSampler implemented by the config-noise-image " +
"addon instead.");
used = true;
}
return new ImageSampler(image, channel, frequency);
}
}
@@ -4,6 +4,7 @@ import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.samplers.LinearHeightmapSampler;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
@@ -11,14 +12,14 @@ import com.dfsek.terra.api.noise.NoiseSampler;
public class LinearHeightmapSamplerTemplate extends SamplerTemplate<LinearHeightmapSampler> {
@Value("sampler")
@Default
private NoiseSampler sampler = NoiseSampler.zero();
private @Meta NoiseSampler sampler = NoiseSampler.zero();
@Value("base")
private double base;
private @Meta double base;
@Value("scale")
@Default
private double scale = 1;
private @Meta double scale = 1;
@Override
public NoiseSampler get() {
@@ -0,0 +1,32 @@
package com.dfsek.terra.addons.noise.config.templates;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.samplers.TranslateSampler;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
public class TranslateSamplerTemplate extends SamplerTemplate<TranslateSampler> {
@Value("sampler")
private NoiseSampler sampler;
@Value("x")
@Default
private @Meta double x = 0;
@Value("y")
@Default
private @Meta double y = 0;
@Value("z")
@Default
private @Meta double z = 0;
@Override
public NoiseSampler get() {
return new TranslateSampler(sampler, x, y ,z);
}
}
@@ -0,0 +1,42 @@
package com.dfsek.terra.addons.noise.config.templates.noise;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.config.templates.SamplerTemplate;
import com.dfsek.terra.addons.noise.samplers.noise.DistanceSampler;
import com.dfsek.terra.addons.noise.samplers.noise.DistanceSampler.DistanceFunction;
import com.dfsek.terra.api.config.meta.Meta;
public class DistanceSamplerTemplate extends SamplerTemplate<DistanceSampler> {
@Value("distance-function")
@Default
private DistanceSampler.@Meta DistanceFunction distanceFunction = DistanceFunction.Euclidean;
@Value("point.x")
@Default
private @Meta double x = 0;
@Value("point.y")
@Default
private @Meta double y = 0;
@Value("point.z")
@Default
private @Meta double z = 0;
@Value("normalize")
@Default
private @Meta boolean normalize = false;
@Value("radius")
@Default
private @Meta double normalizeRadius = 100;
@Override
public DistanceSampler get() {
return new DistanceSampler(distanceFunction, x, y, z, normalize, normalizeRadius);
}
}
@@ -8,7 +8,6 @@
package com.dfsek.terra.addons.noise.config.templates.noise;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.paralithic.functions.Function;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
@@ -19,17 +18,16 @@ import java.util.Map;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.config.templates.SamplerTemplate;
import com.dfsek.terra.addons.noise.paralithic.defined.UserDefinedFunction;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction2;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction3;
import com.dfsek.terra.addons.noise.samplers.noise.ExpressionFunction;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import static com.dfsek.terra.addons.noise.paralithic.FunctionUtil.convertFunctionsAndSamplers;
@SuppressWarnings({ "FieldMayBeFinal", "unused" })
public class ExpressionFunctionTemplate extends SamplerTemplate<ExpressionFunction> {
private final Map<String, DimensionApplicableNoiseSampler> otherFunctions;
private final Map<String, DimensionApplicableNoiseSampler> globalSamplers;
private final Map<String, FunctionTemplate> globalFunctions;
@Value("variables")
@Default
@@ -43,44 +41,19 @@ public class ExpressionFunctionTemplate extends SamplerTemplate<ExpressionFuncti
@Default
private @Meta LinkedHashMap<String, @Meta FunctionTemplate> functions = new LinkedHashMap<>();
public ExpressionFunctionTemplate(Map<String, DimensionApplicableNoiseSampler> otherFunctions, Map<String, FunctionTemplate> samplers) {
this.otherFunctions = otherFunctions;
this.globalFunctions = samplers;
public ExpressionFunctionTemplate(Map<String, DimensionApplicableNoiseSampler> globalSamplers, Map<String, FunctionTemplate> globalFunctions) {
this.globalSamplers = globalSamplers;
this.globalFunctions = globalFunctions;
}
@Override
public NoiseSampler get() {
var mergedFunctions = new HashMap<>(globalFunctions); mergedFunctions.putAll(functions);
var mergedSamplers = new HashMap<>(globalSamplers); mergedSamplers.putAll(samplers);
try {
Map<String, Function> noiseFunctionMap = generateFunctions();
return new ExpressionFunction(noiseFunctionMap, expression, vars);
return new ExpressionFunction(convertFunctionsAndSamplers(mergedFunctions, mergedSamplers), expression, vars);
} catch(ParseException e) {
throw new RuntimeException("Failed to parse expression.", e);
}
}
private Map<String, Function> generateFunctions() throws ParseException {
Map<String, Function> noiseFunctionMap = new HashMap<>();
for(Map.Entry<String, FunctionTemplate> entry : globalFunctions.entrySet()) {
noiseFunctionMap.put(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue()));
}
for(Map.Entry<String, FunctionTemplate> entry : functions.entrySet()) {
noiseFunctionMap.put(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue()));
}
otherFunctions.forEach((id, function) -> {
if(function.getDimensions() == 2) {
noiseFunctionMap.put(id, new NoiseFunction2(function.getSampler()));
} else noiseFunctionMap.put(id, new NoiseFunction3(function.getSampler()));
});
samplers.forEach((id, function) -> {
if(function.getDimensions() == 2) {
noiseFunctionMap.put(id, new NoiseFunction2(function.getSampler()));
} else noiseFunctionMap.put(id, new NoiseFunction3(function.getSampler()));
});
return noiseFunctionMap;
}
}
@@ -0,0 +1,23 @@
package com.dfsek.terra.addons.noise.config.templates.normalizer;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.math.CubicSpline;
import com.dfsek.terra.addons.noise.math.CubicSpline.Point;
import com.dfsek.terra.addons.noise.normalizer.CubicSplineNoiseSampler;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import java.util.List;
public class CubicSplineNormalizerTemplate extends NormalizerTemplate<CubicSplineNoiseSampler> {
@Value("points")
private @Meta List<@Meta Point> points;
@Override
public NoiseSampler get() {
return new CubicSplineNoiseSampler(function, new CubicSpline(points));
}
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.noise.config.templates.normalizer;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.normalizer.ExpressionNormalizer;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import static com.dfsek.terra.addons.noise.paralithic.FunctionUtil.convertFunctionsAndSamplers;
@SuppressWarnings({ "unused", "FieldMayBeFinal" })
public class ExpressionNormalizerTemplate extends NormalizerTemplate<ExpressionNormalizer> {
private final Map<String, DimensionApplicableNoiseSampler> globalSamplers;
private final Map<String, FunctionTemplate> globalFunctions;
@Value("expression")
private @Meta String expression;
@Value("variables")
@Default
private @Meta Map<String, @Meta Double> vars = new HashMap<>();
@Value("samplers")
@Default
private @Meta LinkedHashMap<String, @Meta DimensionApplicableNoiseSampler> samplers = new LinkedHashMap<>();
@Value("functions")
@Default
private @Meta LinkedHashMap<String, @Meta FunctionTemplate> functions = new LinkedHashMap<>();
public ExpressionNormalizerTemplate(Map<String, DimensionApplicableNoiseSampler> globalSamplers, Map<String, FunctionTemplate> globalFunctions) {
this.globalSamplers = globalSamplers;
this.globalFunctions = globalFunctions;
}
@Override
public NoiseSampler get() {
var mergedFunctions = new HashMap<>(globalFunctions); mergedFunctions.putAll(functions);
var mergedSamplers = new HashMap<>(globalSamplers); mergedSamplers.putAll(samplers);
try {
return new ExpressionNormalizer(function, convertFunctionsAndSamplers(mergedFunctions, mergedSamplers), expression, vars);
} catch(ParseException e) {
throw new RuntimeException("Failed to parse expression.", e);
}
}
}
@@ -0,0 +1,89 @@
package com.dfsek.terra.addons.noise.math;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
public class CubicSpline {
private final double[] fromValues;
private final double[] toValues;
private final double[] gradients;
public CubicSpline(List<Point> points) {
Collections.sort(points);
this.fromValues = new double[points.size()];
this.toValues = new double[points.size()];
this.gradients = new double[points.size()];
for(int i = 0; i < points.size(); i++) {
fromValues[i] = points.get(i).from;
toValues[i] = points.get(i).to;
gradients[i] = points.get(i).gradient;
}
}
public double apply(double in) {
return calculate(in, fromValues, toValues, gradients);
}
public static double calculate(double in, double[] fromValues, double[] toValues, double[] gradients) {
int pointIdx = floorBinarySearch(in, fromValues) - 1;
int pointIdxLast = fromValues.length - 1;
if (pointIdx < 0) { // If to left of first point return linear function intersecting said point using point's gradient
return gradients[0] * (in - fromValues[0]) + toValues[0];
} else if (pointIdx == pointIdxLast) { // Do same if to right of last point
return gradients[pointIdxLast] * (in - fromValues[pointIdxLast]) + toValues[pointIdxLast];
} else {
double fromLeft = fromValues[pointIdx];
double fromRight = fromValues[pointIdx + 1];
double toLeft = toValues[pointIdx];
double toRight = toValues[pointIdx + 1];
double gradientLeft = gradients[pointIdx];
double gradientRight = gradients[pointIdx + 1];
double fromDelta = fromRight - fromLeft;
double toDelta = toRight - toLeft;
double t = (in - fromLeft) / fromDelta;
return lerp(t, toLeft, toRight) + t * (1.0F - t) * lerp(t, gradientLeft * fromDelta - toDelta, -gradientRight * fromDelta + toDelta);
}
}
private static int floorBinarySearch(double targetValue, double[] values) {
int left = 0;
int right = values.length;
int idx = right - left;
while (idx > 0) {
int halfDelta = idx / 2;
int mid = left + halfDelta;
if (targetValue < values[mid]) {
idx = halfDelta;
} else {
left = mid + 1;
idx -= halfDelta + 1;
}
}
return left;
}
private static double lerp(double t, double a, double b) {
return a + t * (b - a);
}
public record Point(double from, double to, double gradient) implements Comparable<Point> {
@Override
public int compareTo(@NotNull CubicSpline.Point o) {
return Double.compare(from, o.from);
}
}
}
@@ -0,0 +1,20 @@
package com.dfsek.terra.addons.noise.normalizer;
import com.dfsek.terra.addons.noise.math.CubicSpline;
import com.dfsek.terra.api.noise.NoiseSampler;
public class CubicSplineNoiseSampler extends Normalizer {
private final CubicSpline spline;
public CubicSplineNoiseSampler(NoiseSampler sampler, CubicSpline spline) {
super(sampler);
this.spline = spline;
}
@Override
public double normalize(double in) {
return spline.apply(in);
}
}
@@ -0,0 +1,33 @@
package com.dfsek.terra.addons.noise.normalizer;
import com.dfsek.paralithic.Expression;
import com.dfsek.paralithic.eval.parser.Parser;
import com.dfsek.paralithic.eval.parser.Scope;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.paralithic.functions.Function;
import com.dfsek.terra.api.noise.NoiseSampler;
import java.util.Map;
public class ExpressionNormalizer extends Normalizer {
private final Expression expression;
public ExpressionNormalizer(NoiseSampler sampler, Map<String, Function> functions, String eq, Map<String, Double> vars)
throws ParseException {
super(sampler);
Parser p = new Parser();
Scope scope = new Scope();
scope.addInvocationVariable("in");
vars.forEach(scope::create);
functions.forEach(p::registerFunction);
expression = p.parse(eq, scope);
}
@Override
public double normalize(double in) {
return expression.evaluate(in);
}
}
@@ -0,0 +1,31 @@
package com.dfsek.terra.addons.noise.paralithic;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.paralithic.functions.Function;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.paralithic.defined.UserDefinedFunction;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction2;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction3;
import java.util.HashMap;
import java.util.Map;
public class FunctionUtil {
private FunctionUtil() {}
public static Map<String, Function> convertFunctionsAndSamplers(Map<String, FunctionTemplate> functions,
Map<String, DimensionApplicableNoiseSampler> samplers) throws ParseException {
Map<String, Function> functionMap = new HashMap<>();
for(Map.Entry<String, FunctionTemplate> entry : functions.entrySet()) {
functionMap.put(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue()));
}
samplers.forEach((id, sampler) -> functionMap.put(id,
sampler.getDimensions() == 2 ?
new NoiseFunction2(sampler.getSampler()) :
new NoiseFunction3(sampler.getSampler())));
return functionMap;
}
}
@@ -0,0 +1,27 @@
package com.dfsek.terra.addons.noise.samplers;
import com.dfsek.terra.api.noise.NoiseSampler;
public class TranslateSampler implements NoiseSampler {
private final NoiseSampler sampler;
private final double dx, dy, dz;
public TranslateSampler(NoiseSampler sampler, double dx, double dy, double dz) {
this.sampler = sampler;
this.dx = dx;
this.dy = dy;
this.dz = dz;
}
@Override
public double noise(long seed, double x, double y) {
return sampler.noise(seed, x - dx, y - dz);
}
@Override
public double noise(long seed, double x, double y, double z) {
return sampler.noise(seed, x - dx, y - dy, z - dz);
}
}
@@ -240,99 +240,37 @@ public class CellularSampler extends NoiseFunction {
double centerX = x;
double centerY = y;
switch(distanceFunction) {
default:
case Euclidean:
case EuclideanSq:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = vecX * vecX + vecY * vecY;
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = switch(distanceFunction) {
case Manhattan -> fastAbs(vecX) + fastAbs(vecY);
case Hybrid -> (fastAbs(vecX) + fastAbs(vecY)) + (vecX * vecX + vecY * vecY);
default -> vecX * vecX + vecY * vecY;
};
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
break;
case Manhattan:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = fastAbs(vecX) + fastAbs(vecY);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
case Hybrid:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = (fastAbs(vecX) + fastAbs(vecY)) + (vecX * vecX + vecY * vecY);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
if(distanceFunction == DistanceFunction.Euclidean && returnType != ReturnType.CellValue) {
@@ -351,6 +289,7 @@ public class CellularSampler extends NoiseFunction {
case Distance2Mul -> distance1 * distance0 * 0.5 - 1;
case Distance2Div -> distance0 / distance1 - 1;
case NoiseLookup -> noiseLookup.noise(sl, centerX, centerY);
case LocalNoiseLookup -> noiseLookup.noise(sl, x / frequency - centerX, y / frequency - centerY);
case Distance3 -> distance2 - 1;
case Distance3Add -> (distance2 + distance0) * 0.5 - 1;
case Distance3Sub -> distance2 - distance0 - 1;
@@ -382,120 +321,47 @@ public class CellularSampler extends NoiseFunction {
double centerY = y;
double centerZ = z;
switch(distanceFunction) {
case Euclidean:
case EuclideanSq:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
double newDistance = vecX * vecX + vecY * vecY + vecZ * vecZ;
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
centerZ = ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
case Manhattan:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
double newDistance = fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
centerZ = ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
case Hybrid:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
double newDistance = (fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ)) +
(vecX * vecX + vecY * vecY + vecZ * vecZ);
double newDistance = 0;
switch(distanceFunction) {
case Euclidean, EuclideanSq -> newDistance = vecX * vecX + vecY * vecY + vecZ * vecZ;
case Manhattan -> newDistance = fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ);
case Hybrid -> {
newDistance = (fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ)) + (vecX * vecX + vecY * vecY + vecZ * vecZ);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
centerZ = ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
centerZ = ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
break;
default:
break;
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
if(distanceFunction == DistanceFunction.Euclidean && returnType != ReturnType.CellValue) {
@@ -514,6 +380,7 @@ public class CellularSampler extends NoiseFunction {
case Distance2Mul -> distance1 * distance0 * 0.5 - 1;
case Distance2Div -> distance0 / distance1 - 1;
case NoiseLookup -> noiseLookup.noise(sl, centerX, centerY, centerZ);
case LocalNoiseLookup -> noiseLookup.noise(sl, x / frequency - centerX, y / frequency - centerY, z / frequency - centerZ);
case Distance3 -> distance2 - 1;
case Distance3Add -> (distance2 + distance0) * 0.5 - 1;
case Distance3Sub -> distance2 - distance0 - 1;
@@ -540,6 +407,7 @@ public class CellularSampler extends NoiseFunction {
Distance2Mul,
Distance2Div,
NoiseLookup,
LocalNoiseLookup,
Distance3,
Distance3Add,
Distance3Sub,
@@ -0,0 +1,66 @@
package com.dfsek.terra.addons.noise.samplers.noise;
public class DistanceSampler extends NoiseFunction {
private final DistanceFunction distanceFunction;
private final double ox, oy, oz;
private final boolean normalize;
private final double radius;
private final double distanceAtRadius;
public DistanceSampler(DistanceFunction distanceFunction, double ox, double oy, double oz, boolean normalize, double radius) {
frequency = 1;
this.distanceFunction = distanceFunction;
this.ox = ox;
this.oy = oy;
this.oz = oz;
this.normalize = normalize;
this.radius = radius;
this.distanceAtRadius = distance2d(distanceFunction, radius, 0); // distance2d and distance3d should return the same value
}
@Override
public double getNoiseRaw(long seed, double x, double y) {
double dx = x - ox;
double dy = y - oz;
if (normalize && (fastAbs(dx) > radius || fastAbs(dy) > radius)) return 1;
double dist = distance2d(distanceFunction, dx, dy);
if (normalize) return fastMin(((2*dist)/distanceAtRadius)-1, 1);
return dist;
}
@Override
public double getNoiseRaw(long seed, double x, double y, double z) {
double dx = x - ox;
double dy = y - oy;
double dz = z - oz;
if(normalize && (fastAbs(dx) > radius || fastAbs(dy) > radius || fastAbs(dz) > radius)) return 1;
double dist = distance3d(distanceFunction, dx, dy, dz);
if (normalize) return fastMin(((2*dist)/distanceAtRadius)-1, 1);
return dist;
}
private static double distance2d(DistanceFunction distanceFunction, double x, double z) {
return switch(distanceFunction) {
case Euclidean -> fastSqrt(x*x + z*z);
case EuclideanSq -> x*x + z*z;
case Manhattan -> fastAbs(x) + fastAbs(z);
};
}
private static double distance3d(DistanceFunction distanceFunction, double x, double y, double z) {
return switch(distanceFunction) {
case Euclidean -> fastSqrt(x*x + y*y + z*z);
case EuclideanSq -> x*x + y*y + z*z;
case Manhattan -> fastAbs(x) + fastAbs(y) + fastAbs(z);
};
}
public enum DistanceFunction {
Euclidean,
EuclideanSq,
Manhattan
}
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-2021 Polyhedral Development
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+3
View File
@@ -0,0 +1,3 @@
# config-ore-v2
Registers the default configuration for Terra Ores, `ORE`.
@@ -0,0 +1,11 @@
version = version("1.0.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
implementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
testImplementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
}
tasks.named<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") {
relocate("net.jafama", "com.dfsek.terra.addons.ore.lib.jafama")
}
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.ore.v2;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
public class OreAddon implements AddonInitializer {
@Inject
private Platform platform;
@Inject
private BaseAddon addon;
@Override
public void initialize() {
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, ConfigPackPreLoadEvent.class)
.then(event -> event.getPack().registerConfigType(new OreConfigType(), addon.key("ORE"), 1))
.failThrough();
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.ore.v2;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.config.ConfigFactory;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.config.ConfigType;
import com.dfsek.terra.api.structure.Structure;
import com.dfsek.terra.api.util.reflection.TypeKey;
public class OreConfigType implements ConfigType<OreTemplate, Structure> {
public static final TypeKey<Structure> ORE_TYPE_TOKEN = new TypeKey<>() {
};
private final OreFactory factory = new OreFactory();
@Override
public OreTemplate getTemplate(ConfigPack pack, Platform platform) {
return new OreTemplate();
}
@Override
public ConfigFactory<OreTemplate, Structure> getFactory() {
return factory;
}
@Override
public TypeKey<Structure> getTypeKey() {
return ORE_TYPE_TOKEN;
}
}
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.ore.v2;
import com.dfsek.terra.addons.ore.v2.ores.VanillaOre;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.config.ConfigFactory;
import com.dfsek.terra.api.structure.Structure;
public class OreFactory implements ConfigFactory<OreTemplate, Structure> {
@Override
public VanillaOre build(OreTemplate config, Platform platform) {
BlockState m = config.getMaterial();
return new VanillaOre(m, config.getSize(), config.getReplaceable(), config.doPhysics(), config.isExposed(),
config.getMaterialOverrides());
}
}
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.ore.v2;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Description;
import com.dfsek.tectonic.api.config.template.annotations.Final;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import java.util.HashMap;
import java.util.Map;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.config.AbstractableTemplate;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.collection.MaterialSet;
@SuppressWarnings({ "unused", "FieldMayBeFinal" })
public class OreTemplate implements AbstractableTemplate {
@Value("id")
@Final
private String id;
@Value("material")
private @Meta BlockState material;
@Value("material-overrides")
@Default
private @Meta Map<@Meta BlockType, @Meta BlockState> materials = new HashMap<>();
@Value("replace")
private @Meta MaterialSet replaceable;
@Value("physics")
@Default
private @Meta boolean physics = false;
@Value("size")
private @Meta double size;
@Value("exposed")
@Default
@Description("The chance that ore blocks bordering air will be discarded as candidates for ore. 0 = 0%, 1 = 100%")
private @Meta double exposed = 0.0f;
public boolean doPhysics() {
return physics;
}
public double getSize() {
return size;
}
public BlockState getMaterial() {
return material;
}
public MaterialSet getReplaceable() {
return replaceable;
}
public String getID() {
return id;
}
public Map<BlockType, BlockState> getMaterialOverrides() {
return materials;
}
public double isExposed() {
return exposed;
}
}
@@ -0,0 +1,188 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.ore.v2.ores;
import net.jafama.FastMath;
import java.util.BitSet;
import java.util.Map;
import java.util.Random;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.structure.Structure;
import com.dfsek.terra.api.util.Rotation;
import com.dfsek.terra.api.util.collection.MaterialSet;
import com.dfsek.terra.api.util.vector.Vector3Int;
import com.dfsek.terra.api.world.WritableWorld;
public class VanillaOre implements Structure {
private final BlockState material;
private final double size;
private final MaterialSet replaceable;
private final boolean applyGravity;
private final double exposed;
private final Map<BlockType, BlockState> materials;
public VanillaOre(BlockState material, double size, MaterialSet replaceable, boolean applyGravity,
double exposed, Map<BlockType, BlockState> materials) {
this.material = material;
this.size = size;
this.replaceable = replaceable;
this.applyGravity = applyGravity;
this.exposed = exposed;
this.materials = materials;
}
protected static boolean shouldNotDiscard(Random random, double chance) {
if(chance <= 0.0F) {
return true;
} else if(chance >= 1.0F) {
return false;
} else {
return random.nextFloat() >= chance;
}
}
public static double lerp(double t, double v0, double v1) {
return v0 + t * (v1 - v0);
}
@Override
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
float f = random.nextFloat() * (float) Math.PI;
double g = size / 8.0F;
int i = (int) FastMath.ceil((size / 16.0F * 2.0F + 1.0F) / 2.0F);
double startX = (double) location.getX() + FastMath.sin(f) * g;
double endX = (double) location.getX() - FastMath.sin(f) * g;
double startZ = (double) location.getZ() + FastMath.cos(f) * g;
double endZ = (double) location.getZ() - FastMath.cos(f) * g;
double startY = location.getY() + random.nextInt(3) - 2;
double endY = location.getY() + random.nextInt(3) - 2;
int x = (int) (location.getX() - FastMath.ceil(g) - i);
int y = location.getY() - 2 - i;
int z = (int) (location.getZ() - FastMath.ceil(g) - i);
int horizontalSize = (int) (2 * (FastMath.ceil(g) + i));
int verticalSize = 2 * (2 + i);
int i1 = 0;
BitSet bitSet = new BitSet(horizontalSize * verticalSize * horizontalSize);
int j1 = (int) size;
double[] ds = new double[j1 * 4];
for(int k1 = 0; k1 < j1; ++k1) {
float f1 = (float) k1 / (float) j1;
double d1 = lerp(f1, startX, endX);
double e1 = lerp(f1, startY, endY);
double g1 = lerp(f1, startZ, endZ);
double h1 = random.nextDouble() * (double) j1 / 16.0;
double l1 = ((FastMath.sin((float) Math.PI * f1) + 1.0F) * h1 + 1.0) / 2.0;
ds[k1 * 4] = d1;
ds[k1 * 4 + 1] = e1;
ds[k1 * 4 + 2] = g1;
ds[k1 * 4 + 3] = l1;
}
for(int k1 = 0; k1 < j1 - 1; ++k1) {
if(!(ds[k1 * 4 + 3] <= 0.0)) {
for(int m1 = k1 + 1; m1 < j1; ++m1) {
if(!(ds[m1 * 4 + 3] <= 0.0)) {
double d1 = ds[k1 * 4] - ds[m1 * 4];
double e1 = ds[k1 * 4 + 1] - ds[m1 * 4 + 1];
double g1 = ds[k1 * 4 + 2] - ds[m1 * 4 + 2];
double h1 = ds[k1 * 4 + 3] - ds[m1 * 4 + 3];
if(h1 * h1 > d1 * d1 + e1 * e1 + g1 * g1) {
if(h1 > 0.0) {
ds[m1 * 4 + 3] = -1.0;
} else {
ds[k1 * 4 + 3] = -1.0;
}
}
}
}
}
}
for(int m1 = 0; m1 < j1; ++m1) {
double d1 = ds[m1 * 4 + 3];
if(!(d1 < 0.0)) {
double e1 = ds[m1 * 4];
double g1 = ds[m1 * 4 + 1];
double h1 = ds[m1 * 4 + 2];
int n1 = (int) FastMath.max(FastMath.floor(e1 - d1), x);
int o1 = (int) FastMath.max(FastMath.floor(g1 - d1), y);
int p1 = (int) FastMath.max(FastMath.floor(h1 - d1), z);
int q1 = (int) FastMath.max(FastMath.floor(e1 + d1), n1);
int r1 = (int) FastMath.max(FastMath.floor(g1 + d1), o1);
int s1 = (int) FastMath.max(FastMath.floor(h1 + d1), p1);
for(int t1 = n1; t1 <= q1; ++t1) {
double u1 = ((double) t1 + 0.5 - e1) / d1;
if(u1 * u1 < 1.0) {
for(int v1 = o1; v1 <= r1; ++v1) {
double w1 = ((double) v1 + 0.5 - g1) / d1;
if(u1 * u1 + w1 * w1 < 1.0) {
for(int aa = p1; aa <= s1; ++aa) {
double ab = ((double) aa + 0.5 - h1) / d1;
if(u1 * u1 + w1 * w1 + ab * ab < 1.0 && !(v1 < world.getMinHeight() || v1 >= world.getMaxHeight())) {
int ac = t1 - x + (v1 - y) * horizontalSize + (aa - z) * horizontalSize * verticalSize;
if(!bitSet.get(ac)) {
bitSet.set(ac);
BlockType block = world.getBlockState(x, y, z).getBlockType();
if(shouldPlace(block, random, world, t1, v1, aa)) {
world.setBlockState(t1, v1, aa, getMaterial(block), isApplyGravity());
++i1;
break;
}
}
}
}
}
}
}
}
}
}
return i1 > 0;
}
public boolean shouldPlace(BlockType type, Random random, WritableWorld world, int x, int y, int z) {
if(!getReplaceable().contains(type)) {
return false;
} else if(shouldNotDiscard(random, exposed)) {
return true;
} else {
return !(world.getBlockState(x, y, z - 1).isAir() ||
world.getBlockState(x, y, z + 1).isAir() ||
world.getBlockState(x, y - 1, z).isAir() ||
world.getBlockState(x, y + 1, z).isAir() ||
world.getBlockState(x - 1, y, z).isAir() ||
world.getBlockState(x + 1, y, z).isAir());
}
}
public BlockState getMaterial(BlockType replace) {
return materials.getOrDefault(replace, material);
}
public MaterialSet getReplaceable() {
return replaceable;
}
public boolean isApplyGravity() {
return applyGravity;
}
}
@@ -0,0 +1,12 @@
schema-version: 1
contributors:
- Terra contributors
id: config-ore-v2
version: @VERSION@
entrypoints:
- "com.dfsek.terra.addons.ore.v2.OreAddon"
website:
issues: https://github.com/PolyhedralDev/Terra/issues
source: https://github.com/PolyhedralDev/Terra
docs: https://terra.polydev.org
license: MIT License
@@ -14,8 +14,13 @@ import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OreAddon implements AddonInitializer {
private static final Logger logger = LoggerFactory.getLogger(OreAddon.class);
@Inject
private Platform platform;
@@ -29,5 +34,7 @@ public class OreAddon implements AddonInitializer {
.register(addon, ConfigPackPreLoadEvent.class)
.then(event -> event.getPack().registerConfigType(new OreConfigType(), addon.key("ORE"), 1))
.failThrough();
logger.warn("The ore-config addon is deprecated and scheduled for removal in Terra 7.0. It is recommended to use the ore-config-v2 addon for future pack development instead.");
}
}
@@ -0,0 +1,8 @@
version = version("1.0.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
implementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
testImplementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
}

Some files were not shown because too many files have changed in this diff Show More