Compare commits

...

557 Commits

Author SHA1 Message Date
James Seibel d5740c5d7f Update core 2021-11-28 18:14:37 -06:00
James Seibel 816249acdb Update core 2021-11-28 16:54:23 -06:00
James Seibel 23d9cf10d3 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-11-28 16:28:49 -06:00
James Seibel eeea070fd1 Update core 2021-11-28 16:28:47 -06:00
James Seibel e47cdaa2e4 update the version number to a1.5.3 2021-11-28 16:09:25 -06:00
cola98765 db074a6a5b Fix Starlight (mod) once more 2021-11-28 11:28:40 +01:00
James Seibel f2f0e291d8 Add a missing comment 2021-11-27 23:28:28 -06:00
James Seibel db123072bd Add Legacy OpenGL vanilla fog removal 2021-11-27 23:24:58 -06:00
James Seibel 6bbb99ff39 Update the version number to a1.5.3 2021-11-27 21:14:50 -06:00
James Seibel 88c5fc8fde Fix gradle issues (compiling works again) 2021-11-27 21:12:42 -06:00
James Seibel a40417bbd6 Remove logo and IDE files (they are in core now) 2021-11-27 18:05:04 -06:00
James Seibel cf7373a303 Improve the config descriptions 2021-11-27 16:17:14 -06:00
James Seibel 49c0581b1d rename DistantHorizons_Server_Data -> Distant_Horizons_server_data 2021-11-27 14:48:41 -06:00
Ran 72d3b50077 Add core resources to the jar (Isn't tested yet) 2021-11-28 01:10:12 +06:00
Ran aaedc8e384 Fix NPE when loading up the world 2021-11-28 00:21:01 +06:00
James Seibel b63f24c92e rename the server data folder 2021-11-27 12:03:38 -06:00
Ran 76154aada0 Fix shadow 2021-11-27 23:41:40 +06:00
James Seibel 6b029da899 Add setUniform(color) 2021-11-27 10:32:18 -06:00
James Seibel 818c4243e0 Add FogColorMode 2021-11-27 10:10:21 -06:00
James Seibel a41a4fe77f remove a debug command 2021-11-26 21:20:41 -06:00
James Seibel 9a03a45cd7 Clean up Fog, remove Fast fog, close issue #77 (near-far incorrect center) 2021-11-26 21:14:06 -06:00
James Seibel e2eaca7869 Add fog as a fragment shader 2021-11-26 19:25:05 -06:00
James Seibel 0bfef6b3d3 change the submodule path to https instead of ssh 2021-11-26 09:43:21 -06:00
James Seibel 03345924f3 Update core 2021-11-25 11:51:45 -06:00
James Seibel 37f72980ee Downgrade Gradle to 7.1 (from 7.2) This should be the latest available to MC 1.16.5 2021-11-25 08:15:43 -06:00
James Seibel 7fd43deaca Update gradle to 7.2 (from 4.10.3) and fix the build.gradle file 2021-11-22 19:23:49 -06:00
James Seibel 05a3d4002f Add support for disabling rendering 2021-11-21 22:15:10 -06:00
James Seibel 8a574074c5 Merge branch '1.16.5_Core' into '1.16.5'
1.16.5 core

See merge request jeseibel/minecraft-lod-mod!10
2021-11-22 00:28:09 +00:00
James Seibel f801567301 Update core 2021-11-21 18:26:48 -06:00
James Seibel 632947c8ce minor spelling fix 2021-11-21 18:26:12 -06:00
James Seibel 5cb32d6181 Delete 1.5 release notes.txt 2021-11-21 16:16:26 -06:00
James Seibel 505f48cebc Add DH-Core's source folders to Gradle 2021-11-21 15:53:35 -06:00
James Seibel e2bdfcc2da Initial implementation of Core 2021-11-21 15:28:59 -06:00
cola98765 cd72edf9a6 Merge branch 'CodeF53-1.16.5-patch-42921' into '1.16.5'
Add icon for catalogue mod & Update logo displayed in configured

Closes #97

See merge request jeseibel/minecraft-lod-mod!9
2021-11-07 11:07:43 +00:00
cola98765 5172fec97f changed back how sky/block light is accessed for Starlight (mod) compatibility 2021-11-06 23:04:14 +01:00
cola98765 b1a39ce74b disabled try catch in LodGenWorker. It made logs useless 2021-11-06 22:55:26 +01:00
cola98765 563fea608e WorldWrapper -> LevelWrapper 2021-11-05 12:19:36 +01:00
cola98765 be6e52ded0 changed world to level in minecraft wrapper for consistency with other versions 2021-11-05 12:09:07 +01:00
cola98765 6d54edd74c removed client world dependency in buffer builder; minor changes 2021-11-05 11:10:56 +01:00
CodeF53 492c634cc4 Add icon for catalogue mod
Update logo displayed in configured to match new name
2021-11-05 00:01:51 +00:00
cola98765 d92cb6016f cleaned out some code 2021-11-04 11:56:43 +01:00
cola98765 0250eba715 cleaned out some code 2021-11-04 11:45:02 +01:00
cola98765 cbf1bf698d cleaned out imports 2021-11-04 11:43:13 +01:00
cola98765 2aed897b9b pulled some changes around wrappers from 1.17.1_fabric 2021-11-04 11:30:33 +01:00
cola98765 7402ad6be0 added getHashFromFile for future use with servers 2021-11-03 19:03:42 +01:00
Leonardo d4556e7f84 Biome colors is calculated using the block color wrapper 2021-11-01 22:34:39 +01:00
Leonardo 7b910ba4fd Changed waterlogged check, the biome getter and other small stuff 2021-11-01 22:34:17 +01:00
cola98765 5762c41f1a protect full regions against being overwritten by a partial one 2021-11-01 10:18:49 +01:00
cola98765 dee9fa793d cleanup 2021-10-30 17:45:32 +02:00
cola98765 1962053a2f improved adj lighting 2021-10-30 16:38:22 +02:00
cola98765 bb16cfa4fe when there are too many gaps, remove the bottom one of the smallest size first. 2021-10-30 15:52:16 +02:00
cola98765 73ddbd66b5 fixed EXCEPTION_ACCESS_VIOLATION when stitching lods on and off using F6 2021-10-30 15:43:55 +02:00
cola98765 8e727011de now recreate buffers when you reenable rendering. Doing it using debug keys can sometimes lead to crash tho 2021-10-30 13:36:00 +02:00
cola98765 717e189107 made "Enable Rendering" only affect buffer building to enable generation with no rendering 2021-10-30 12:49:58 +02:00
cola98765 cff8326810 Fixed "generate at max quality" TODO make generation work with disabled rendering at world join 2021-10-30 11:37:52 +02:00
James Seibel fd0604c7e7 Fix a few potential null pointers when on a server world 2021-10-29 19:56:03 -05:00
James Seibel f822aed285 make the fog disabler config experimental and disabled by default 2021-10-29 19:50:50 -05:00
cola98765 26535ef81c remove debug message 2021-10-29 16:03:06 +02:00
cola98765 2b0c0f89a2 cleaned up some code 2021-10-29 15:13:37 +02:00
cola98765 51e7e318ac removed weird math.ceil that was used on int 2021-10-29 14:21:23 +02:00
cola98765 e5ef7e7026 fixed index out of bounds and void air appearing purple bugs 2021-10-29 13:22:28 +02:00
cola98765 62794766c6 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-10-29 12:27:14 +02:00
cola98765 e32addfd06 actually fixed non-square non-animated textures 2021-10-29 12:26:54 +02:00
cola98765 42678a27a4 clean out previous 2021-10-29 12:21:16 +02:00
cola98765 1488747075 fixed non-square non-animated textures 2021-10-29 12:07:41 +02:00
cola98765 a03ddf7339 applied autoformat to that vivecraft commit 2021-10-29 09:18:22 +02:00
cola98765 c537e0cf49 Merge branch '1.16.5' into '1.16.5'
Add vivecraft support

See merge request jeseibel/minecraft-lod-mod!7
2021-10-29 07:02:51 +00:00
Eric 853c706b77 Add vivecraft support 2021-10-28 22:16:19 -06:00
James Seibel 65c091bb63 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-27 21:25:09 -05:00
James Seibel 9b389332be update the version number to a1.5.2 2021-10-26 19:26:23 -05:00
Leonardo 1093001841 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-10-26 23:11:16 +02:00
cola98765 a3db8e50f5 removed NativeImage dependency outside of wrappers. Now LodRenderer only triggers regen without storing lightmap 2021-10-26 23:09:31 +02:00
Leonardo db15e8684c Another small fix to light 2021-10-26 22:44:21 +02:00
Leonardo 00ac368bba fixed light being wrong 2021-10-26 22:38:14 +02:00
Leonardo f2face9782 small change to light value getters in lod builde. Added WorldLightWrapper for future implementation 2021-10-26 21:59:35 +02:00
Leonardo 923d57b377 Fixed the colors in the lodBuilder 2021-10-26 21:46:06 +02:00
Leonardo 447325e5a1 Fixed the colors for now 2021-10-26 21:43:11 +02:00
Leonardo bf712e893e removed default color use from the BlockColorWrapper 2021-10-26 21:28:18 +02:00
Leonardo 27f9a527bc Fixed part of the colors problems 2021-10-26 21:19:09 +02:00
Leonardo bde14012d9 Divided the Block Wrapper in two different class 2021-10-26 12:31:06 +02:00
Leonardo 12a32b9fb4 Implemented the wrappers in the lodBuilder 2021-10-26 11:58:33 +02:00
James Seibel 4ad081e0c6 Add a advanced graphics option to use a extended near clip plain
This prevents some overdraw issues but causes LODs in the ocean to render incorrectly.
2021-10-25 22:20:00 -05:00
James Seibel 936a3a7ece Fix #84 (misaligned LODs in third person) 2021-10-25 21:56:22 -05:00
Leonardo 98f36936d0 smallFix 2021-10-25 22:57:37 +02:00
Leonardo f6f012c42c Added some other classes/methods to the wrappers 2021-10-25 22:51:01 +02:00
Leonardo a3e6c09268 Added more methods to the wrapper 2021-10-25 22:31:43 +02:00
Leonardo 10cb46c9f9 Added biomeWrapper 2021-10-25 21:30:44 +02:00
Leonardo 95aa9cb9ab Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-10-25 20:20:47 +02:00
Leonardo 638a0ddae1 BlockPosWrapper and BlockWrapper almost completed 2021-10-25 20:20:40 +02:00
James Seibel d321833335 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-25 07:21:26 -05:00
James Seibel b8cba0dc4c increase the near clip plane to 1/5 MC's vanilla render distance 2021-10-25 07:21:17 -05:00
cola98765 bd8ccb4a05 reverted las change, fixed vanillaRenderedChunksChanged at hight altitudes 2021-10-25 13:20:13 +02:00
cola98765 895895da04 update buffers only when lightmap changes. 2021-10-25 12:43:59 +02:00
Leonardo 09d5df2856 Added the main wrapper classes (empty) 2021-10-25 11:14:17 +02:00
James Seibel 668f225528 Fix the blindness potion effect 2021-10-24 23:02:30 -05:00
James Seibel 1cd71a6b50 Re-arange Vanilla Overdraw config options 2021-10-24 22:18:09 -05:00
James Seibel f041f79ae3 Close #78 (Add a config to disable vanilla MC's fog)
And change the config to use FAR fog and disable MC's fog by default.
2021-10-24 22:17:57 -05:00
James Seibel ef3ac96b2c Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-24 21:30:35 -05:00
James Seibel 59f527e6de Update 1.5 release notes.txt 2021-10-24 15:19:59 -05:00
James Seibel 16a082b17f rename "Disable Drawing" to "Enable Rendering" in the config
The config name was incorrect.
2021-10-24 15:19:54 -05:00
jas35484 a24d28b0e2 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-24 14:57:56 -05:00
jas35484 910f11f688 Fix #63 (OpenGL errors)
NVFogDistance doesn't work on low end GPUs which causes the OpenGL error I was seeing with Optifine
2021-10-24 14:57:53 -05:00
cola98765 e00de99e31 Found place where addData was used for adding whole vertical array. We have addVerticalData for that. 2021-10-24 12:11:43 +02:00
cola98765 f80af39e0e halving every array related to worldHeight, as you can't get worldHeight lods, as half of that needs to be a gaps 2021-10-24 11:57:08 +02:00
cola98765 5bba3cb3eb minor cleanup 2021-10-24 11:35:45 +02:00
cola98765 d4261d4ccf a little improvement to 'pow' changes 2021-10-24 11:01:02 +02:00
cola98765 8b854e3abd removed debug message 2021-10-24 10:33:02 +02:00
cola98765 4064155567 changed Math.pow usages with simpler (and possibly faster) x*x and 1 << x. reduced repetitions 2021-10-24 10:28:35 +02:00
cola98765 6243201f2d removed couple Math.floorDiv and Mod as for positive inputs they are slower and give the same result as standard operands 2021-10-24 10:08:12 +02:00
James Seibel c5a2944d68 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-23 12:04:26 -05:00
James Seibel 1405b7a433 Add support for OpenGL 1.5 and 3.0 2021-10-23 12:04:16 -05:00
cola98765 be00670b7e Merge branch 'CodeF53-1.16.5-patch-77785' into '1.16.5'
Update youtube video

See merge request jeseibel/minecraft-lod-mod!5
2021-10-23 13:29:09 +00:00
CodeF53 6f2c02d283 Make link open in new tab 2021-10-23 05:11:10 +00:00
CodeF53 309526e7b9 Update youtube video 2021-10-23 05:01:37 +00:00
James Seibel d5466e0fda Fix a potential null pointer exception when leaving the world
The thread dealing with these should probably be killed and recreated when leaving the world instead of just catching the problem here.
2021-10-22 20:46:26 -05:00
James Seibel 7a0b95a105 minor refactor in LodGenWorker 2021-10-22 20:45:37 -05:00
James Seibel 7c59e33aee remove some commented out code 2021-10-22 20:42:02 -05:00
James Seibel 9907d2ddd5 Temporary null pointer fix 2021-10-22 18:48:32 -05:00
cola98765 1a838d4bd8 pushed FAR fog out by *1.6. I don't know why this number, but it works ok. 2021-10-22 20:07:02 +02:00
cola98765 dce3227bf1 Fixed compression on retail. Don't change your gradle tasks 2021-10-22 08:28:30 +02:00
cola98765 ea6f3e9881 now it's game crashes on launch when build using file made by "shadowJar" option 2021-10-21 16:42:30 +02:00
Leonardo 2f57b67bdc fixed underwater problem with always overdraw 2021-10-21 13:19:23 +02:00
Leonardo be024f524d Fixed regions wall not showing (which would cause holes in the rendering) and added border as possible overdraw config 2021-10-21 12:57:40 +02:00
cola98765 e0a176c0c4 tried to make XZ actually part of the jar 2021-10-21 12:16:39 +02:00
James Seibel b00795a0ab Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-20 22:34:26 -05:00
James Seibel 8bfa8e70a1 Create 1.5 release notes.txt 2021-10-20 22:34:14 -05:00
James Seibel 2a0653419f Update Readme.md
Ah so there is a way to preview this online, that's nice.
2021-10-21 02:21:28 +00:00
James Seibel cc1b9ea28e change the version to a1.5.0 and fix a line in the readme 2021-10-20 21:19:40 -05:00
James Seibel b32bdb3807 Add a acknowledgement for the use of the XZ data compression project 2021-10-20 21:14:15 -05:00
James Seibel 42bac2bd57 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-20 21:07:21 -05:00
James Seibel 9bd89accd3 re-add dynamic overdraw 2021-10-20 21:07:18 -05:00
Leonardo 5493ef9033 removed a print 2021-10-21 02:36:57 +02:00
Leonardo 7d1df26cc5 Shading is only disabled for lod that should be transparent 2021-10-21 02:28:37 +02:00
Leonardo 63f6d4cc56 Fixed chorus plant 2021-10-21 02:17:57 +02:00
James Seibel 6066a572d9 finish two incomplete comments 2021-10-20 18:09:08 -05:00
Leonardo ea679992e8 Small change 2021-10-21 00:40:21 +02:00
Leonardo fedce10917 Overdraw should now be fixed 2021-10-21 00:39:37 +02:00
Leonardo 80448ce990 Small change to the buffer builder 2021-10-20 23:03:13 +02:00
Leonardo 89fac88b7e Added method to check water logged block 2021-10-20 23:02:35 +02:00
Leonardo 032b63208c Added IWaterLoggable blocks to block to use in the generation 2021-10-20 21:23:53 +02:00
Leonardo 9faed3a2c8 Commented the buffer builder and added a method 2021-10-20 20:18:42 +02:00
Leonardo 05800089cb Changed fog enum name 2021-10-20 19:30:42 +02:00
James Seibel 8585511007 Update two of the config overrides 2021-10-19 22:11:11 -05:00
James Seibel 890de58c72 Fix regions culled incorrectly if far from the buffer's center
This would happen if the buffer builder died or was slow
2021-10-19 22:10:28 -05:00
James Seibel 8def361f63 rename LodNodeGenWorker to LodGenWorker 2021-10-19 21:16:58 -05:00
James Seibel 58d5a8beda Update the License header to match the new mod name
LOD -> Distance Horizons
2021-10-19 21:14:57 -05:00
James Seibel 443bb64df0 Improve the wording of the config file 2021-10-19 20:43:43 -05:00
James Seibel 400e00f572 auto-indent 2021-10-19 20:11:31 -05:00
Leonardo 5c36a3df16 Small fix to push and pop of configs 2021-10-19 23:11:39 +02:00
Leonardo 8578a833b4 Fixed crash caused by config 2021-10-19 22:36:01 +02:00
Leonardo 974a9b4452 Small change to configs name + comments 2021-10-19 22:30:23 +02:00
Leonardo d35fbb0bb3 New configs 2021-10-19 22:24:21 +02:00
Leonardo 0551fbb2d4 Small fix to config 2021-10-19 19:15:07 +02:00
Leonardo fe8a847637 Reversed resolution enum order 2021-10-19 19:09:32 +02:00
Leonardo b04eec91bc Fully disabled pre-gen code 2021-10-19 18:50:11 +02:00
Leonardo 6eddef2fb0 simplified block to avoid config 2021-10-19 18:10:59 +02:00
Leonardo 10382342d8 Added some more pre-gen testing code (disabled) 2021-10-19 13:35:22 +02:00
Leonardo 9d80b81378 Now the game doesn't use Biome class to calculate colors 2021-10-19 01:21:25 +02:00
Leonardo 12abc4f018 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-10-19 01:20:35 +02:00
Leonardo 21e774489c Added the ability to use pregenerated chunks, (bugged still to fix) 2021-10-19 01:20:29 +02:00
cola98765 f6f7f1043e cleaned out code after save compression refactor 2021-10-18 14:32:32 +02:00
cola98765 30e9dd1aac Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-10-18 11:51:14 +02:00
cola98765 219f3eba17 actually read old files as compressed 2021-10-18 11:50:50 +02:00
Leonardo d737500d95 Added a new section to the LodRegion which may be useful to identify pre-generated chunk in the future 2021-10-18 11:50:38 +02:00
Leonardo 2a402ef1d6 fully disabled the pregen code 2021-10-18 11:47:56 +02:00
Leonardo 84c0fd994d Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-10-18 11:46:33 +02:00
Leonardo cf782c5b6f changed default thread number 2021-10-18 11:46:23 +02:00
cola98765 96c19620cb now compressing using proper algorithm 2021-10-18 11:00:58 +02:00
cola98765 d0f93a2c89 Fixed light on predicted chunks in caveworld. 2021-10-17 22:15:06 +02:00
Leonardo 85157d0da0 Disabled pre-gen loading for now + small fix 2021-10-17 20:33:28 +02:00
Leonardo 33d150b090 Disabled pre-gen loading for now + small fix 2021-10-17 19:56:29 +02:00
Leonardo e6949dcd14 Fixed shadow with no tree bug 2021-10-17 19:37:06 +02:00
Leonardo b411f083e2 Added config for pre-generated chunk loading 2021-10-17 19:21:56 +02:00
Leonardo d3cd714f48 Added pre-generated chunk loading 2021-10-17 19:17:44 +02:00
Leonardo df0d1483c7 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-10-17 18:52:40 +02:00
Leonardo 0e82afd6e1 Disabled config override 2021-10-17 18:52:34 +02:00
cola98765 d80253546f fixed chunks resetting after loading save 2021-10-17 16:37:48 +02:00
Leonardo 441e4ff1f2 SmallFix to generation 2021-10-17 14:41:28 +02:00
Leonardo cbdfabeaeb Real chunks are always converted in max quality (could make it a config) 2021-10-17 14:34:39 +02:00
cola98765 bdf7fb3466 fixed blocklight one more time 2021-10-17 13:34:15 +02:00
cola98765 7595248406 cleaned a bit of code 2021-10-17 12:13:17 +02:00
cola98765 b5bcab36cd cleaned a bit of code 2021-10-17 11:53:47 +02:00
cola98765 5ed1e6135b now blocklight also uses server world when applicable 2021-10-17 11:33:07 +02:00
James Seibel 636d4129bd Rename HorizontalQuality LINEAR to LOWEST 2021-10-16 21:38:28 -05:00
James Seibel a90dfb695d Make the config file more easily readable 2021-10-16 21:07:07 -05:00
James Seibel 15c1a64cfd Update the version number to b1.5.0 2021-10-16 20:58:10 -05:00
James Seibel c5f823c54e Add generation Resolution checking to Dynamic vanilla overdraw 2021-10-16 20:56:00 -05:00
James Seibel a7c2e8a6ed Update the config file to use mod name (Distant Horizons) vs the mod id (lod) 2021-10-16 18:48:25 -05:00
James Seibel cb496208f5 If avoiding non full/solid blocks use the above block's color
This fixes snow and flowers not being used.
2021-10-16 18:08:17 -05:00
James Seibel 4ff1815436 Clean up a few compiler warnings 2021-10-16 16:28:57 -05:00
cola98765 31ada4e5d2 No longer removing 1 block gaps as with new lighting it shouldn't look worse 2021-10-16 22:43:25 +02:00
cola98765 accf082309 No longer removing 1 block gaps as with new lighting it shouldn't look worse 2021-10-16 22:42:58 +02:00
cola98765 e1b1f26f37 made project actually build after removing experimental light option 2021-10-16 22:18:24 +02:00
James Seibel a0c14c86ca Improve the lighting on local worlds
ClientWorld apparently doesn't actually know what the lighting is anywhere, so we will have to find another way to determine shadows on servers.
2021-10-16 13:45:31 -05:00
Leonardo a122015f6d Experimental light system is a config now 2021-10-16 15:52:39 +02:00
Leonardo 164d407153 Experimental light system is a config now 2021-10-16 15:51:35 +02:00
cola98765 893bfa096f made new light optional with internal boolean 2021-10-16 11:25:13 +02:00
cola98765 37995af19e fixed flag setting, and changed code back as if isLightCorrect and getBrightness actually worked 2021-10-16 10:39:06 +02:00
James Seibel c637d23121 Merge branch 'CodeF53/minecraft-lod-mod-1.16.5' into 1.16.5 2021-10-15 21:00:54 -05:00
James Seibel d762508188 Only return Minecraft Chunks from the chunkLoadEvent 2021-10-15 20:26:56 -05:00
James Seibel f5437f00a2 slight refactoring 2021-10-15 20:25:33 -05:00
CodeF53 dec20962b2 Switch readme to markdown format, adding headers and misc formatting. 2021-10-15 21:24:32 +00:00
cola98765 30e796f5bf fixed side lighting 2021-10-15 23:20:54 +02:00
cola98765 4ab4dc59e7 now renders default Skylight as 0 when player is in Skylight 0 situation 2021-10-15 18:41:58 +02:00
cola98765 839a76c594 minor adjustment that was annoying me 2021-10-15 17:24:04 +02:00
Leonardo 3fb4d16e41 New sky light handling system (not complete) 2021-10-15 16:23:20 +02:00
Leonardo 002d86af9f Added comment + small refactor + removed TODO 2021-10-15 16:22:41 +02:00
cola98765 e1877f9149 Now using flag bit in data as "is light default" 2021-10-15 15:37:51 +02:00
cola98765 4da1fcc118 since new method of getting skyLight is objectively better, I've commented out old method and 'if' used to switch between those methods 2021-10-14 23:36:27 +02:00
cola98765 af123a8c80 added fast track when we want to add whole column of data, so it doesn't call 5 different methods for each lod block. 2021-10-14 23:33:14 +02:00
cola98765 94bc7fd011 now ignoring isLightCorrect completely, old method is still available 2021-10-14 19:29:50 +02:00
cola98765 905d8bddf3 made new sky lighting calculation optional with internal boolean 2021-10-14 18:56:51 +02:00
cola98765 5ee762cc78 now actually remove garbage vertical lods 2021-10-14 18:51:39 +02:00
cola98765 7c07a88b45 small fix to that light fix 2021-10-14 18:02:58 +02:00
cola98765 d8981af7aa possible fix to skylight... revert if worse than before 2021-10-14 15:11:36 +02:00
Leonardo 9906eb75f4 Changed the Far fog on fancy. Now fog start after the real game render distance 2021-10-14 14:06:11 +02:00
Leonardo d94fedc61e Changed the Far fog on fancy. Now fog start after the real game render distance 2021-10-14 14:02:47 +02:00
cola98765 3e3b979530 indents in LodConfig 2021-10-14 07:49:02 +02:00
James Seibel 6ad94d3362 Update the version number to 1.5.0 and replace the LOD name with Distant Horizons 2021-10-13 23:28:43 -05:00
James Seibel 31f8cecc92 Add the minimum OpenGL version to the readme
BufferStorage technically only requires OpenGL 4.4, but I already had 4.5 written everywhere. If it becomes a problem that going to 4.4 fixes I could probably roll back to that.
2021-10-13 22:04:41 -05:00
James Seibel b4106a8c3c Improve the buffer rebuild logic related to light change 2021-10-13 21:59:46 -05:00
James Seibel b6390c57ef auto-indent 2021-10-13 21:03:03 -05:00
James Seibel 5172c66b9b Merge branch 'CodeF53-1.16.5-patch-87033' into '1.16.5'
Update demo video to latest

See merge request jeseibel/minecraft-lod-mod!3
2021-10-14 01:59:33 +00:00
James Seibel 067fa92419 Improve the logic for vanilla overdraw to work better on different horizontal resolutions 2021-10-13 20:44:38 -05:00
Leonardo 15e80ed063 ceiled dimension now have 64 max render distance + various small changes 2021-10-14 02:29:42 +02:00
Leonardo 2c6549e815 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5
# Conflicts:
#	src/main/java/com/seibel/lod/builders/lodBuilding/LodBuilder.java
#	src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java
2021-10-14 02:28:24 +02:00
CodeF53 32423c07eb Update demo video to latest 2021-10-13 15:03:27 +00:00
cola98765 4a0640a219 cleaned out some code. 2021-10-13 14:27:27 +02:00
cola98765 5f9f20a064 cleaned out some code. 2021-10-13 13:45:23 +02:00
cola98765 ef4ba7208e cleaned out some code. 2021-10-13 13:31:47 +02:00
cola98765 80edf19b33 cleaned out some code. 2021-10-13 13:24:07 +02:00
cola98765 4baa649972 cleaned out some code. Applied autoformat everywhere 2021-10-13 12:45:21 +02:00
cola98765 c7dbc28a9a cleaned out some code 2021-10-13 12:08:47 +02:00
cola98765 707c55653a removed single level container 2021-10-13 12:01:56 +02:00
Leonardo 40cb7f4efc Merge remote-tracking branch 'origin/1.16.5' into 1.16.5
# Conflicts:
#	src/main/java/com/seibel/lod/objects/SingleLevelContainer.java
2021-10-13 11:35:24 +02:00
Leonardo df22a082a1 Added option to avoid block with no collision + removed SingleLevelContainer class use 2021-10-13 11:34:59 +02:00
cola98765 3a7f411aa4 cleaned java ocs in DataPointUtil 2021-10-13 10:36:29 +02:00
cola98765 11c419e503 updated some TODO's 2021-10-13 10:15:48 +02:00
James Seibel 30b82e8009 update dynamic vanilla overdraw in LodUtil 2021-10-12 22:31:51 -05:00
James Seibel 7df4a161dd Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-12 22:20:53 -05:00
James Seibel 56160db7ec comment out the buffer builder expanding console logging 2021-10-12 22:20:44 -05:00
James Seibel ac84c3707a Remove a illegal import 2021-10-12 22:20:27 -05:00
Leonardo d16f571467 Small changed to border shading + changed overdraw ratio 2021-10-12 17:12:31 +02:00
cola98765 d7bd0498a0 When there are no files for current gen mode, try to load more detailed ones instead. 2021-10-12 11:58:21 +02:00
James Seibel 57a2b956dd Improve the VanillaOverdraw config to work better in roofed dimensions 2021-10-11 23:05:49 -05:00
James Seibel 51aadc8d39 Maybe fix a OpenGL error? 2021-10-11 23:00:28 -05:00
James Seibel 70e2f7f3e6 prevent a potential out of bounds exception 2021-10-11 23:00:11 -05:00
James Seibel 0ef8615a98 Extract the main work of the buffer builder into a method for easier hot swapping 2021-10-11 22:12:35 -05:00
James Seibel 4c6412d09f comment out a debug override 2021-10-11 20:55:02 -05:00
Leonardo 27b8cfea09 Fixed the purple color bug 2021-10-11 23:17:20 +02:00
cola98765 b5ab06bfb6 resolved warning 2021-10-11 22:14:56 +02:00
James Seibel 732476b454 Add the overdraw
Overdraw renders LODs on top of vanilla chunks based with  three different settings.
2021-10-10 21:31:45 -05:00
James Seibel 9d1a9eb9f3 Remove the memory estimation code.
Buffers will now be purposely created smaller than expected and then expanded to fit the need.
2021-10-10 14:01:13 -05:00
James Seibel 6018449cbe Quick formatting change and remove a completed TODO 2021-10-10 08:10:05 -05:00
cola98765 8f43695a47 did someone say warnings and typos? 2021-10-10 13:07:23 +02:00
cola98765 30913a0c29 more typos 2021-10-10 11:36:10 +02:00
cola98765 d8cee2b10c Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-10-10 11:19:09 +02:00
cola98765 33a706f660 autoformat 2021-10-10 11:19:02 +02:00
cola98765 6c2b707c02 autoformat 2021-10-10 11:16:54 +02:00
cola98765 ecb01c731e Resolved "ALOT" of typos. 2021-10-10 11:09:15 +02:00
cola98765 72737a56c1 Resolved more warnings. 2021-10-10 10:38:00 +02:00
cola98765 1d1b5f6115 Resolved more warnings. Applied auto format in affected files. 2021-10-10 10:34:34 +02:00
cola98765 b67ab59a89 Resolved more warnings. Applied auto format in affected files. 2021-10-10 09:50:44 +02:00
cola98765 ca4597e3d9 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-10-10 08:38:00 +02:00
cola98765 efd9a5a51b merge all 1 block gaps for better look o trees 2021-10-10 08:37:47 +02:00
cola98765 4c3d19a6c6 merge all 1 block gaps for better look o trees 2021-10-10 08:36:45 +02:00
cola98765 6c4faa9103 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-10-10 08:24:05 +02:00
James Seibel 6066957584 Update LodConfig.java 2021-10-09 20:14:51 -05:00
James Seibel c3f8b0b677 Remove the experimental useFovSetting setting 2021-10-09 19:47:26 -05:00
James Seibel e2cdf1549a auto-indent 2021-10-09 19:42:02 -05:00
James Seibel fe9bc5e77e Delete Reference.java 2021-10-09 19:41:22 -05:00
cola98765 ea0ef9e394 boosted skylight prediction to 13 so most trees look better 2021-10-09 23:22:39 +02:00
James Seibel 27d74958bf Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-09 14:48:53 -05:00
James Seibel dc8db97f33 Fix the region at 0,0 (-X,-Z) rendering incorrectly 2021-10-09 14:48:47 -05:00
cola98765 3ec30d49be put guts of mergeSingleData into mergeMultiData and make dataPoint smaller to make it faster 2021-10-09 17:47:58 +02:00
cola98765 627327140e reversed logic in mergeMultiData to improve performance of initial compression to maxVerticalData 2021-10-09 17:07:38 +02:00
James Seibel 2ebbb6f591 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-09 09:46:33 -05:00
James Seibel 126b581e97 Add BufferStorage recreation, improving memory usage
I still want the buffers to be created the right size at the start, but this will work for now.
2021-10-09 09:46:24 -05:00
cola98765 f625e714d4 made mergeMultiData a bit faster 2021-10-09 11:00:59 +02:00
cola98765 5db7e349b9 in mergeMultiData skip empty data 2021-10-09 08:16:58 +02:00
James Seibel 272546af4d Improve the buffer expansion console logging 2021-10-08 22:16:12 -05:00
James Seibel c0a9e3061c reformatting / commenting 2021-10-08 22:13:20 -05:00
James Seibel 5872dc3be5 Fix a memory leak when changing worlds 2021-10-07 22:47:16 -05:00
James Seibel 8fe6eff36c improve the buffer expanding logging 2021-10-07 22:45:48 -05:00
James Seibel d89f2dc9b8 Refactoring / reformatting 2021-10-07 21:34:34 -05:00
James Seibel e0ac03db6c Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-07 21:25:05 -05:00
James Seibel ee58f3d9dc Use OpenGL BufferStorage to improve upload performance
I need to change how the memory is determined, right now they fill to the maximum amount; which is very bad and only for testing.
2021-10-07 20:59:45 -05:00
cola98765 b02d58227c reworked mergeMultiData 2021-10-07 20:17:37 +02:00
Leonardo 61cb27020c small change to a print 2021-10-07 18:03:09 +02:00
Leonardo 443da5eede other config change, set roofed dimension max distance to 64 2021-10-07 17:37:02 +02:00
Leonardo 78ab4b8598 Changed configs 2021-10-07 17:20:39 +02:00
cola98765 028aed53f8 resolved some more warnings and applied autoformat in affected files 2021-10-07 11:17:02 +02:00
cola98765 1ed36ba6c4 resolved some more warnings and applied autoformat in affected files 2021-10-07 11:07:10 +02:00
cola98765 6deca8e235 resolved many warnings and applied autoformat in affected files 2021-10-07 10:36:30 +02:00
Leonardo a6f3c2478e Some refactor + fixed cpu problem with near_first 2021-10-07 00:19:32 +02:00
cola98765 a66554f2dd resolved todo 2021-10-06 11:45:36 +02:00
cola98765 28cf2eb450 removed debug message when saving 2021-10-06 10:37:36 +02:00
cola98765 e154f552c7 fixed bug where only some regions were saved when player was not moving 2021-10-06 10:36:37 +02:00
Leonardo 0c275e1495 Added back the toString in the PosToGenerateContainer 2021-10-05 19:06:51 +02:00
Leonardo 2b15c089f4 Added back the toString in the PosToGenerateContainer 2021-10-05 18:59:40 +02:00
Leonardo fca926bc8f Added some comment, fixed the thread conflict problem 2021-10-05 11:25:06 +02:00
James Seibel f6ea990ab0 Reduce buffer uploading stuttering
I also included commented out versions of several other buffer uploading options and comments on how well they perform.
2021-10-04 22:52:11 -05:00
James Seibel ee6c8d597c Hopefully prevent the buffer builder from dying if an exception is thrown 2021-10-03 14:45:38 -05:00
James Seibel 1e819bd555 temporarily disable transparency in the AbstractLodTemplate 2021-10-03 14:08:55 -05:00
James Seibel a0358dd298 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-03 13:57:34 -05:00
James Seibel 90987fc389 Add a constant to LodUtil and remove a few unused ones 2021-10-03 13:57:24 -05:00
cola98765 f72c846fe5 removed old commented code 2021-10-03 09:42:50 +02:00
James Seibel 2ca8576ad7 auto-indent 2021-10-02 21:33:09 -05:00
James Seibel 0194cffaba auto indent 2021-10-02 21:28:52 -05:00
James Seibel cc52815315 update a override option in the clientProxy 2021-10-02 21:24:19 -05:00
James Seibel 900b67920c re-add transparency rendering 2021-10-02 21:24:04 -05:00
James Seibel 9c1e5c33a5 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-10-02 21:08:22 -05:00
James Seibel d7a1d330d9 Re-add Linux and (hopefully) Mac support
I have tested Windows and Ubuntu (Linux). I didn't test Mac but I am reasonably confident that it should work.
2021-10-02 21:08:15 -05:00
cola98765 3ac05c6a2b fixed multiplyRGBcolors to not make off by 1 error 2021-10-02 15:34:02 +02:00
James Seibel eeff5437b5 Hopefully add Mac and Linux support 2021-10-01 23:25:51 -05:00
James Seibel ee89d6f512 Update the Readme to include IntelliJ install instructions 2021-10-01 07:19:55 -05:00
James Seibel a577851f6b Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-09-30 21:42:00 -05:00
James Seibel 536bff2fe7 Hopefully fix the OpenGL context switching error 2021-09-30 21:41:49 -05:00
James Seibel b1995445e2 auto-format 2021-09-30 19:24:30 -05:00
Leonardo 5d4d483b16 re-added the avoidNonFullBlock and avoidSmallBlock options 2021-10-01 01:26:11 +02:00
Leonardo c13abc3b7d Added thread data for the buffer builder 2021-09-30 19:50:05 +02:00
Leonardo 1cbb15045f Added a reset when unloading and loading world 2021-09-30 19:47:04 +02:00
Leonardo 48ea096ff1 Added comment and changed some small variables 2021-09-30 17:52:51 +02:00
cola98765 2e08b61672 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-09-30 17:46:59 +02:00
cola98765 b0bb01fde4 reworked ThreadMapUtil to actually save arrays between uses. 2021-09-30 17:46:03 +02:00
Leonardo cc666e917e Now the border chunk only work for render distance higher than 4 2021-09-30 15:26:28 +02:00
Leonardo 9e0abd06fb Better fake chunk/ real chunk blending, less holes 2021-09-30 14:48:52 +02:00
cola98765 0f56a98499 attempt to fix grass colors by setting biome opacity to 255 2021-09-30 13:04:43 +02:00
cola98765 529c52b93f getBuilderArray wasn't initialised correctly causing a lot of new long[] 2021-09-30 12:28:04 +02:00
cola98765 8020b298fb fixed saving transparency and calculating it around biome colors 2021-09-30 11:11:23 +02:00
Leonardo 37db05d18f added a borderChunk finder function 2021-09-30 01:38:39 +02:00
Leonardo 555e5a78b5 simplified the culling (still disabled) 2021-09-30 01:37:48 +02:00
Leonardo 32d492f5f6 Changed some package and added a dimension to the buffer builders 2021-09-29 14:57:43 +02:00
James Seibel d9ee9135d7 revert the config override message 2021-09-29 07:55:46 -05:00
James Seibel d59f9dc4c6 Create pre-release a1.5.1
I made this version so hopefully someone could test if Vivecraft is just ignoring Minecraft's FOV when creating their projection matrices or if they are doing something more complicated.
2021-09-28 22:56:17 -05:00
James Seibel 8f329ac785 auto-format 2021-09-28 22:18:38 -05:00
James Seibel 9865b23e5b Move MAX_ALLOCATEABLE_DIRECT_MEMORY to LodUtil 2021-09-28 21:29:25 -05:00
James Seibel a987149aa3 Create Eclipse Auto Formatting V1.xml 2021-09-28 21:06:15 -05:00
Leonardo 22b7e46d39 small fixes 2021-09-29 02:38:19 +02:00
Leonardo e5c1e67369 Changed various names, changed the memory calculator 2021-09-29 02:16:23 +02:00
James Seibel 328792cf4e Start commenting and refactoring SingleLevelContainer 2021-09-28 07:50:07 -05:00
James Seibel 344901aad5 Comment RegionPos 2021-09-28 07:41:13 -05:00
James Seibel 5f89bd8e7d comment and refactor PosToGenerateContainer 2021-09-27 22:12:55 -05:00
James Seibel 7c5713fac2 Refactor and comment LodWorld 2021-09-27 21:59:51 -05:00
James Seibel d222854717 auto-indent 2021-09-27 21:49:11 -05:00
James Seibel 6b9be10635 Add a few missing comments and refactors to LodRegion 2021-09-27 21:48:19 -05:00
James Seibel 69a72312e4 Refactor and comment LodRegion 2021-09-27 21:43:16 -05:00
James Seibel 948b58d8e9 auto-indent and move a reminder comment 2021-09-27 20:20:26 -05:00
James Seibel 86406fd12b Rename MULTI_LOD to VOXEL 2021-09-27 20:17:33 -05:00
James Seibel cc3e1d1b12 Fix a null pointer and finish commenting/refactoring LodDimension 2021-09-27 20:13:16 -05:00
James Seibel 85a0110af6 comments and regactor LodDimension 2021-09-27 07:59:55 -05:00
James Seibel a76436b73d Bring back the drawLODs config 2021-09-26 08:20:37 -05:00
James Seibel 391c99fd21 comment and refactor LodDimensionFildHandler and ReflectionHandler 2021-09-25 23:51:14 -05:00
James Seibel fb5aceb44d auto-indent 2021-09-25 23:20:05 -05:00
James Seibel 4c76a48e84 Update comments for many enums 2021-09-25 23:17:17 -05:00
James Seibel d7a42d3a51 auto-indent 2021-09-25 20:35:12 -05:00
James Seibel e0a946673e refactor LodBuilderConfig 2021-09-25 20:30:44 -05:00
James Seibel ad793fe17e Refactor and comment LodBufferBuilder and LodBuilder 2021-09-25 18:52:53 -05:00
James Seibel 209e2579c3 refactor and comment LodWorldGenerator 2021-09-25 16:09:29 -05:00
James Seibel 8627b87aa4 auto-indent 2021-09-25 16:09:12 -05:00
James Seibel 5f855dd573 CubicLodTemplate refactoring and commenting 2021-09-25 15:34:39 -05:00
James Seibel d6c918427b have the LodRenderer recreated only when leaving a server 2021-09-25 15:34:25 -05:00
James Seibel a9bdbc4300 Potentially fix a false buffer IndexOutOfBoundsException 2021-09-25 15:32:34 -05:00
James Seibel 58a150f8de fix the ThreadMapUtil not creating arrays correctly 2021-09-25 13:22:31 -05:00
James Seibel 1a3e1dfa8c add get fresh methods to TheadMapUtil
the getFresh versions create/clear the data for the given map.
2021-09-25 11:48:05 -05:00
James Seibel 430a908829 refactor clientProxy 2021-09-25 11:42:49 -05:00
James Seibel 0ca0cd9213 auto-indent 2021-09-25 09:07:07 -05:00
James Seibel 2f392375fe Refactor and Comment Box 2021-09-25 09:04:26 -05:00
James Seibel ff43118976 Update the version number to a1.5.0-pre 2021-09-24 18:06:57 -05:00
James Seibel 3b4e28d74a Add a warning chat message for the 1.5.0 pre-release 2021-09-24 18:05:05 -05:00
James Seibel 13abed4eb8 comment out the shadingMode config since it isn't currently used 2021-09-24 07:59:26 -05:00
James Seibel ccb2c601a6 add a temporary fix to color debug mode not having shading 2021-09-24 07:59:07 -05:00
James Seibel f6816d9974 auto-indent 2021-09-24 07:45:00 -05:00
James Seibel 5156ae8e63 auto-format 2021-09-24 07:43:22 -05:00
James Seibel 55c0c95339 Improve config wording 2021-09-24 07:37:14 -05:00
James Seibel 0b8503f1fa move applyConfigOverrides 2021-09-24 07:27:07 -05:00
James Seibel 79d9ac5d56 auto-indent 2021-09-24 07:26:50 -05:00
James Seibel 6cf4e663fb Fix a potential null pointer exception and improve clientProxy error logging 2021-09-23 22:07:46 -05:00
cola98765 bf9c3ce567 fixed saving multidata in void dimentions 2021-09-23 23:52:34 +02:00
Leonardo 5bbdd22b58 light works again, changed the region_fast option, fixed a indexout of buond, added a TODO for the future for better memery use 2021-09-23 16:02:32 +02:00
Leonardo 6c1c04bb5f Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-09-23 15:18:39 +02:00
Leonardo 66728cc1eb fixed the memory crash problem.
Added config to draw always in generation quality (if true the game will crash with  render distance>256)
2021-09-23 15:18:33 +02:00
James Seibel d4739b9bed Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-09-23 07:55:44 -05:00
James Seibel a93bb63c44 Fix an index out of bounds exception for vanillaRenderedChunks 2021-09-23 07:55:33 -05:00
Leonardo a2e18de9f3 small fix to generation 2021-09-23 14:38:35 +02:00
Leonardo 6183152d99 small fix to culling 2021-09-23 14:37:57 +02:00
James Seibel 5d9f36bb5f Hopefully fix the openGL errors 2021-09-22 22:26:23 -05:00
James Seibel d330083d7b auto-format 2021-09-22 22:20:03 -05:00
James Seibel 6d489b498e add additional error checking to BufferBuilder vboUpload 2021-09-22 22:16:10 -05:00
James Seibel d36d836bb3 prevent a null point in LodRegion addData 2021-09-22 22:15:42 -05:00
James Seibel 29d2d1cff1 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into
1.16.5

# Conflicts:
#	src/main/java/com/seibel/lod/objects/LodDimension.java
2021-09-22 21:52:03 -05:00
James Seibel 0a9ea1dfce improve the error reporting in GlProxy 2021-09-22 21:50:05 -05:00
James Seibel b9424cbe9f Fix a index out of bounds error in ThreadMapUtil 2021-09-22 21:04:11 -05:00
Leonardo 593a014dfc fixed far and near generation, fixed holes in the rendering in near gen 2021-09-23 02:03:02 +02:00
James Seibel 83ac04dca4 auto-format 2021-09-22 18:33:20 -05:00
Leonardo c9aed389ae various changes to the generation + some small fixes 2021-09-23 01:05:54 +02:00
Leonardo 00da0e0520 Fixed tall grass and flower colors 2021-09-22 21:32:08 +02:00
Leonardo 5124739348 removed reset of bufferBuilder and added better color creation for grass, leaves, water and flowers 2021-09-22 20:47:31 +02:00
Leonardo d65bfd408e Fixed the artifacts (was caused by wrong array initialisation) and added the reset to while changing dimension 2021-09-22 19:38:04 +02:00
Leonardo da413f594e Added different detail drop off option 2021-09-22 17:17:17 +02:00
cola98765 1fcef4be96 some warnings taken care of 2021-09-22 15:10:20 +02:00
cola98765 d7cddd9b39 fixed null check 2021-09-22 15:02:08 +02:00
Leonardo fb480c2695 fix to memory use 2021-09-22 13:17:56 +02:00
James Seibel 265abb64b3 Improve the wording for a few config options 2021-09-21 21:15:02 -05:00
James Seibel 1cf4852788 auto-format 2021-09-21 20:27:55 -05:00
Leonardo 90d0fbe45b various fix to the generation 2021-09-22 01:26:55 +02:00
cola98765 ea9eac89ec podzol netherrack and other blocks now use proper top color without biome color (nether plants might still be broken didn't test it) 2021-09-22 00:26:11 +02:00
cola98765 017cc201b1 enabled save loading for vertical lods 2021-09-21 23:09:48 +02:00
Leonardo bda963036d Introduced correctly spiral generation 2021-09-21 18:35:48 +02:00
Leonardo 46bdf5763f Added spiral generation, changed how some arrays are created 2021-09-21 18:12:15 +02:00
Leonardo 8228a3b7a6 fixed a thread problem 2021-09-21 16:30:53 +02:00
cola98765 e3df6c99da added void and !exist compression to VerticalLevelContainer 2021-09-21 16:18:58 +02:00
Leonardo a4e9e22d2c fixed end bug 2021-09-21 16:09:24 +02:00
Leonardo 435c5ee73a fixed nether roof bug 2021-09-21 15:09:28 +02:00
cola98765 c6b063a380 in SingleLevelContainer made void chunks save as 1 byte instead of 8 2021-09-21 11:34:07 +02:00
cola98765 1f5c3f5bd8 in SingleLevelContainer made void chunks save as 1 byte instead of 8 2021-09-21 11:33:01 +02:00
Leonardo 688cb3f89a New memory getter system 2021-09-20 18:58:02 +02:00
Leonardo af80ff5267 Refactoring 2021-09-20 16:33:24 +02:00
Leonardo 2bc5c43b19 small fix 2021-09-20 15:51:34 +02:00
Leonardo e7c4827d08 Now the single merge use min and max depth and height and no more the mean to make it more consistent with the multi-lod 2021-09-20 15:49:22 +02:00
Leonardo 482dfb918e Refactoring + added the ability to disable lods with f6 2021-09-20 15:44:18 +02:00
Leonardo 5aaf6e0185 removed redundant toString() 2021-09-20 15:23:56 +02:00
Leonardo ab014af15d more refactoring 2021-09-20 15:15:10 +02:00
Leonardo 772de1b869 Refactoring, removed unused class/enum, fixed a small array bug 2021-09-20 14:59:42 +02:00
Leonardo cb42683774 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-09-20 14:01:28 +02:00
Leonardo a00698ccab added the dataPoint to the threadMaputil 2021-09-20 14:01:18 +02:00
cola98765 29ed26f023 More warnings addressed 2021-09-20 13:31:16 +02:00
cola98765 af3c4ab801 More warnings addressed 2021-09-20 13:23:22 +02:00
cola98765 1156b7dd28 now using ThreadMapUtil for temp array during saving 2021-09-20 12:50:49 +02:00
cola98765 25fd29b97e replaced short[][] with short[] in mergeMultiData hotfix 2021-09-20 12:16:47 +02:00
cola98765 a11ff5b493 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-09-20 12:13:32 +02:00
cola98765 7655ae03b0 replaced short[][] with short[] in mergeMultiData 2021-09-20 12:13:24 +02:00
Leonardo 075e29c617 small fixes for better gc use 2021-09-20 12:09:45 +02:00
cola98765 e482bf02a5 fixed negative numbers in mergeMultiData 2021-09-20 11:57:32 +02:00
cola98765 0dab4a2274 addressed couple warnings 2021-09-20 11:54:01 +02:00
cola98765 001335ab47 I'm stupid 2021-09-20 11:46:47 +02:00
Leonardo faedb85f41 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-09-20 11:35:31 +02:00
Leonardo 01b9a3f3b0 Fixed culling 2021-09-20 11:35:20 +02:00
cola98765 cfd9bd903d + 1 will not hurt anyone 2021-09-20 11:34:25 +02:00
cola98765 3e634c082a addressed couple warnings 2021-09-20 11:30:56 +02:00
cola98765 6e86a808a5 addressed couple warnings 2021-09-20 11:12:54 +02:00
cola98765 e615674246 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-09-20 11:03:48 +02:00
cola98765 86b5fc48a2 addressed couple warnings 2021-09-20 11:03:24 +02:00
Leonardo 2fab33fa78 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-09-20 10:57:28 +02:00
Leonardo b56b581bb6 Refactoring 2021-09-20 10:57:20 +02:00
cola98765 2ee5289881 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-09-20 10:56:36 +02:00
cola98765 3da4e4818c replaced arraycopy in mergeMultiData in favor of simpler method for debugging 2021-09-20 10:56:28 +02:00
Leonardo 686062f39c Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-09-20 10:46:25 +02:00
Leonardo ae857dfeae fixed a small bug in the datapoint
Simplified the code in the LodBufferBuilder
2021-09-20 10:46:14 +02:00
cola98765 686592effb fixed bug in loading vertical data 2021-09-20 10:43:12 +02:00
James Seibel 34b92d1053 Fix #67 (LODs not rotating with camera) and #68 (LODs culled based on player direction, not camera) 2021-09-19 19:25:01 -05:00
James Seibel 01a850eb9d Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-09-19 18:14:09 -05:00
James Seibel 7d6f7f35ff Fix the buffer builder failing on LodQualityMode.HEIGHTMAP 2021-09-19 18:13:48 -05:00
James Seibel 28764bc16c auto-format 2021-09-19 17:58:22 -05:00
Leonardo ee1657a798 fixed a small bug 2021-09-19 23:58:24 +02:00
cola98765 c7bf60b4e0 fixed byte cast 2021-09-19 23:34:55 +02:00
cola98765 031af5f25c Merge branch '1.16.5' of https://gitlab.com/jeseibel/minecraft-lod-mod into vertical_merging
 Conflicts:
	src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java
	src/main/java/com/seibel/lod/util/DataPointUtil.java
2021-09-19 23:31:16 +02:00
Leonardo d8200422b2 disabled the clear + small change 2021-09-19 23:18:31 +02:00
cola98765 c89cd54429 implemented multi level saving and loading. they use new long[] system instead of long[][][] 2021-09-19 23:16:03 +02:00
cola98765 ca940d5a36 reworked mergeMultiData to take in long[] instead of long[][] 2021-09-19 22:59:15 +02:00
Leonardo 2241492bc5 there is a thunderstorm i don't want to loose progress 2021-09-19 22:08:30 +02:00
Leonardo 7b807bcea2 small changes 2021-09-19 21:32:13 +02:00
Leonardo ebe2c22a28 Changed how the VerticalLevelContainer contains the data 2021-09-19 20:48:07 +02:00
cola98765 cffb17aeb1 reversed grass brightness boost as it's better that way at 4+ view distance 2021-09-19 16:35:47 +02:00
cola98765 c67a5a5e29 removed commented and now unused code 2021-09-19 16:25:53 +02:00
cola98765 1d687fd1d4 new mergeMultiData. Now actually working 2021-09-19 14:44:55 +02:00
cola98765 783d4086c6 some fixes to new mergeMultiData 2021-09-19 14:40:06 +02:00
cola98765 d5243b7d16 some fixes to new mergeMultiData 2021-09-19 14:18:42 +02:00
cola98765 43f3854068 some fixes to new mergeMultiData 2021-09-19 11:41:29 +02:00
cola98765 0025f43a2e Merge branch '1.16.5' of https://gitlab.com/jeseibel/minecraft-lod-mod into vertical_merging
 Conflicts:
	src/main/java/com/seibel/lod/util/ThreadMapUtil.java
2021-09-19 10:36:33 +02:00
James Seibel 07d792979c downgrade to frogeGradle 3.+ from 5.1.+
mixins weren't compiling correctly on the newer version
2021-09-18 23:34:15 -05:00
James Seibel a939d29357 hopefully prevent a index out of bounds exception 2021-09-18 17:44:52 -05:00
James Seibel ba1d096a48 Add error catching / logging 2021-09-18 17:44:37 -05:00
James Seibel 1c9c72cfe3 Clean up a lot of warnings related to: type casting, unused variables, and unused imports 2021-09-18 17:06:41 -05:00
James Seibel aa95361866 Refactor LodDimension and change some error reporting in PosToRenderContainer 2021-09-18 16:58:23 -05:00
James Seibel ff5139d403 Remove debug code and fix a potential OpenGL error 2021-09-18 14:52:14 -05:00
James Seibel 16fc4914fb Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-09-18 14:13:05 -05:00
James Seibel c110524c11 fix vanillaRenderedChunks having index out of bounds exceptions 2021-09-18 14:12:54 -05:00
James Seibel 400b263059 Try to fix GlProxy.setGlContext failing in the BufferBuilder 2021-09-18 14:12:33 -05:00
Leonardo dadda4bdc9 Disabled the culling until it's fixed 2021-09-18 17:32:34 +02:00
Leonardo 1888ac7adc Fixed the culling coordinate 2021-09-18 17:09:13 +02:00
James Seibel 5c59ba7a80 Prevent a error related to loading textures on a null clientWorld 2021-09-18 09:39:53 -05:00
James Seibel f3dd99e792 Improve error message in GlProxy 2021-09-18 09:38:51 -05:00
Leonardo 103ec128ac small fix 2021-09-18 14:46:18 +02:00
Leonardo b4226ec9ec small fix 2021-09-18 14:46:06 +02:00
Leonardo 7c28dd70d3 Fixed the culling 2021-09-18 13:58:19 +02:00
Leonardo d8c9a41314 fixed face culling 2021-09-18 10:09:35 +02:00
Leonardo 5a7f77479f Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-09-18 10:09:14 +02:00
James Seibel d200a5ac24 Add the ability to disable direction based culling
This is a band aid fix for #68, hopefully a better solution can be found in the future.
2021-09-17 23:33:21 -05:00
James Seibel 5099f85da6 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into
1.16.5

# Conflicts:
#	src/main/java/com/seibel/lod/render/LodRenderer.java
2021-09-17 23:11:28 -05:00
James Seibel 6e150fe378 Update to ForgeGradlew from 3.+ to 5.1.+ 2021-09-17 22:58:09 -05:00
James Seibel fd67d1ac66 Change setupProjectionMatrix to be mc version agnostic
Now the LOD projection matrix's zoom, transformation, distortion, etc. are all gotten dynamically from Minecraft's projection matrix.
Hopefully this should prevent camera mods from breaking the mod.
2021-09-17 22:57:20 -05:00
cola98765 7161bd52de reworked mergeVerticalData... not actually working 2021-09-18 00:09:22 +02:00
Leonardo 82cf25c341 fixed colors 2021-09-17 23:37:16 +02:00
Leonardo 68d279807f small fix to the vertex order 2021-09-17 22:50:48 +02:00
Leonardo 3530158def Added new culling system 2021-09-17 22:37:33 +02:00
Leonardo 62223480e2 Fixed nether roof 2021-09-17 16:58:59 +02:00
Leonardo 50ab424497 added regen for the lightmap 2021-09-17 15:57:01 +02:00
Leonardo 900467cef2 System uses lightmap now 2021-09-17 13:02:05 +02:00
cola98765 fa1d950ff9 grass is actually using texture now 2021-09-17 12:55:14 +02:00
Leonardo 83571951be Added the ability to get the top texture 2021-09-17 12:29:57 +02:00
Leonardo c31b1f4039 added the ability to avoid not full block or small block 2021-09-17 11:14:23 +02:00
James Seibel 8fcd428194 improve error handling and error messages related to contexts 2021-09-16 20:32:32 -05:00
James Seibel 87c275f768 Add improved error reporting and commenting 2021-09-16 20:32:03 -05:00
James Seibel 8df6e972cb Fix a out of memory error when changing worlds 2021-09-16 20:28:07 -05:00
James Seibel d7d88d61ee Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into
1.16.5

# Conflicts:
#	src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java
2021-09-16 19:05:05 -05:00
James Seibel 9b1c0a1125 Add the ability to access the lightmap from the MinecraftWrapper 2021-09-16 19:03:43 -05:00
cola98765 b008b70255 colors are better than it were... again 2021-09-17 00:05:29 +02:00
cola98765 b6c350f667 colors are better than it were 2021-09-16 22:21:04 +02:00
Leonardo e23bbfcd91 small changes 2021-09-16 18:53:26 +02:00
cola98765 6bc14fbb2d brightness fix with real block colors TODO foliage 2021-09-16 18:12:22 +02:00
Leonardo d911017112 new color for grass, leaf and water 2021-09-16 17:11:08 +02:00
Leonardo fc4546538f Reintroduced manual color for nether blocks 2021-09-16 16:17:38 +02:00
Leonardo a90b3e9d37 Merge remote-tracking branch 'origin/1.16.5_new_file_handling' into 1.16.5 2021-09-16 16:15:29 +02:00
Leonardo b2aca27615 Added texture mean in lod builder 2021-09-16 15:44:28 +02:00
cola98765 6bf9e187e0 this breaks old file version system by simply ignoring the old files. 2021-09-16 11:41:53 +02:00
James Seibel b5f32705e8 remove commented out code 2021-09-15 23:12:10 -05:00
James Seibel 8bc72af63f Improve rendering performance and improve buffer building stability
Specifically improve the rendering speed so we can get over 100 fps again.
2021-09-15 22:01:52 -05:00
Leonardo 0e4cf8e882 added a TODO in data util 2021-09-15 17:02:44 +02:00
Leonardo 5ede5fa202 Added fake lighting system. Improved some point 2021-09-15 16:17:26 +02:00
Leonardo cdeba2616c Re-Introduced HEIGHMAP mode correctly.
Added single get and add to the LevelContainer to avoid using temp arrays
2021-09-15 13:23:41 +02:00
Leonardo 6dc94b0cc2 Introduced lodQualityMode in config to change from 3d to 2d 2021-09-15 10:42:09 +02:00
James Seibel 7f9c7d8722 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into
1.16.5

# Conflicts:
#	src/main/java/com/seibel/lod/builders/LodBufferBuilder.java
2021-09-14 23:46:01 -05:00
James Seibel aebbeb6ade Simplify the multi-context uploading, improve stability, and refactor 2021-09-14 22:49:08 -05:00
James Seibel a945eb4579 Fully add multi-context uploading 2021-09-14 21:27:36 -05:00
Leonardo f9cf27a2c7 Changed the dataPoint, started the introduction of the lightmap use 2021-09-14 23:21:48 +02:00
Leonardo e03e09a243 Fixed various bugs and introduced more maps in the ThreadMapUtil 2021-09-14 19:02:37 +02:00
Leonardo fe02813d17 Disabled a section in the getNearbyLodChunkPosToSkip 2021-09-14 13:45:08 +02:00
Leonardo 145479267d Fixed Nether color 2021-09-14 13:15:36 +02:00
Leonardo 77ccd9eec3 changed how the valid block is determined, and added a mask and a shift for future use 2021-09-14 12:58:09 +02:00
Leonardo 9b216fedad Vertical lod now works (optimization required) 2021-09-14 01:05:41 +02:00
Leonardo e9798ace13 kinda working version 2021-09-13 23:23:55 +02:00
Leonardo 75e78d9000 First vertical data working 2021-09-13 17:34:37 +02:00
Leonardo 34776074fd added the vertical merge 2021-09-13 13:16:52 +02:00
James Seibel 8822e2d8a1 partially add different context rendering 2021-09-12 23:14:43 -05:00
Leonardo 32de70b4f0 Removed the normals 2021-09-13 01:48:11 +02:00
Leonardo 76a7baeb32 Saving works again 2021-09-13 01:47:04 +02:00
Leonardo 95d9c17e49 Added new colors for the torches 2021-09-13 01:19:39 +02:00
Leonardo a6544d3bb6 Introduced light and added normal in the template for future shading improvement 2021-09-13 00:37:23 +02:00
Leonardo c3115caa8f Re-organized the LodBuilder code 2021-09-12 16:29:41 +02:00
Leonardo 4e1e5b24ee Changed the lodBuilder to use a single function 2021-09-11 20:05:11 +02:00
Leonardo 7b73445f4d Changed the lodBuilder to use a single function 2021-09-11 19:59:30 +02:00
Leonardo 5f1f5f0948 Fixed some smalls bugs 2021-09-11 16:29:20 +02:00
Leonardo bdcc4c7755 Introduced map util and now the Loading does not create new String object. 2021-09-11 15:56:46 +02:00
Leonardo 1395e32a50 introduced the mergeSingleData function 2021-09-11 13:49:51 +02:00
Leonardo 99f7d70613 disabled the class until it's ready 2021-09-10 18:22:13 +02:00
Leonardo 59bfd739f4 Added max detail at FULL 2021-09-10 18:21:46 +02:00
Leonardo 3f1cf6c305 Added some new temporal data storing 2021-09-10 18:21:09 +02:00
Leonardo 5c05df0361 changed the thread names 2021-09-10 18:20:33 +02:00
Leonardo feebc3564a Created the single level container 2021-09-10 16:42:21 +02:00
Leonardo a9aa630aff Started convertion for vertical data 2021-09-10 15:47:43 +02:00
James Seibel 6ec146ba60 re-add depreciation warning suppressions 2021-09-09 19:06:09 -05:00
Leonardo 21140593e2 Started convertion for vertical data 2021-09-10 01:05:12 +02:00
Leonardo 878714dae3 Fixed the lod not showing at screen corner bug 2021-09-09 15:51:14 +02:00
Leonardo e876d7bec6 Fixed the lod not showing at screen corner bug 2021-09-09 14:43:04 +02:00
Leonardo 4f06395557 Fixed glass color 2021-09-09 14:36:05 +02:00
Leonardo aa3dbe8f32 Fixed the allocation of long[] 2021-09-09 14:10:45 +02:00
90 changed files with 3935 additions and 8959 deletions
+1
View File
@@ -20,6 +20,7 @@ build
# other
eclipse
run
**/src/generated/
# Files from Forge MDK
logs
+3
View File
@@ -0,0 +1,3 @@
[submodule "core"]
path = core
url = https://gitlab.com/jeseibel/distant-horizons-core.git
+68
View File
@@ -0,0 +1,68 @@
# Distant Horizons
This mod adds a Level Of Detail (LOD) system to Minecraft.\
This implementation renders simplified chunks outside the normal render distance\
allowing for an increased view distance without harming performance.
Or in other words: this mod lets you see farther without turning your game into a slide show.\
If you want to see a quick demo, check out a video covering the mod here:
<a href="https://www.youtube.com/watch?v=H2tnvEVbO1c" target="_blank">![Minecraft Level Of Detail (LOD) mod - Alpha 1.4](https://i.ytimg.com/vi_webp/H2tnvEVbO1c/mqdefault.webp)</a>
Forge version: 1.16.5-36.1.0
Notes:\
This version has been confirmed to work in Eclipse and Retail Minecraft.\
(Retail running forge version 1.16.5-36.1.0)
## source code installation
See the Forge Documentation online for more detailed instructions:\
http://mcforge.readthedocs.io/en/latest/gettingstarted/
1. Create a system variable called "JAVA_MC_HOME" with the location of the JDK 1.8.0_251 (This is needed for gradle to work correctly)
2. replace JAVA_HOME with JAVA_MC_HOME in gradle.bat
3. open a command line in the project folder
**If using Ecplise:**
1. run the command: `./gradlew geneclipseruns`
2. run the command: `./gradlew eclipse`
3. Make sure eclipse has the JDK 1.8.0_251 installed. (This is needed so that eclipse can run minecraft)
4. Import the project into eclipse
**If using IntelliJ:**
1. open IDEA and import the build.gradle
2. run the command: `./gradlew genIntellijRuns`
3. refresh the Gradle project in IDEA if required
## Compiling
1. open a command line in the project folder
2. run the command: `./gradlew build`
3. the compiled jar file will be in the folder `build\libs`
## Other commands
`./gradlew --refresh-dependencies` to refresh local dependencies.
`./gradlew clean` to reset everything (this does not affect your code) and then start the process again.
## Note to self
The Minecraft source code is NOT added to your workspace in an editable way. Minecraft is treated like a normal Library. Sources are there for documentation and research purposes only.
Source code uses Mojang mappings.
The source code can be 'created' with the `./eclipse` command and can be found in the following path:\
`minecraft-lod-mod\build\fg_cache\mcp\ VERSION \joined\ RANDOM_STRING \patch\output.jar`
## Open Source Acknowledgements
XZ for Java (data compression)\
https://tukaani.org/xz/java.html
-70
View File
@@ -1,70 +0,0 @@
This mod adds a Level Of Detail (LOD) system to Minecraft.
This implementation renders simplified chunks outside the normal render distance
allowing for an increased view distance without harming performance.
Or in other words: this mod let's you see farther without turning your game into a slide show.
If you want to see a quick demo, check out the video I made here:
https://youtu.be/CCT-3s02tYA
Forge version: 1.16.5-36.1.0
Notes:
This version has been confirmed to work in Eclipse and Retail Minecraft.
(Retail running forge version 1.16.5-36.1.0)
========================
source code installation
========================
See the Forge Documentation online for more detailed instructions:
http://mcforge.readthedocs.io/en/latest/gettingstarted/
Step 1: Create a system variable called "JAVA_MC_HOME" with the location of the JDK 1.8.0_251 (This is needed for gradle to work correctly)
Step 2: replace JAVA_HOME with JAVA_MC_HOME in gradle.bat
Step 3: open a command line in the project folder
Step 4: run the command: "./gradlew geneclipseruns"
Step 5: run the command: "./gradlew eclipse"
Step 6: Make sure the eclipse has the JDK 1.8.0_251 installed. (This is needed so that eclipse can run minecraft)
Step 7: Import the project into eclipse
=========
compiling
=========
Step 1: open a command line in the project folder
Step 2: run the command: "./gradlew build"
Step 3: the compiled jar file will be in the folder "build\libs"
==============
Other commands
==============
"./gradlew --refresh-dependencies" to refresh local dependencies.
"./gradlew clean" to reset everything (this does not affect your code) and then start the process again.
============
Note to self
============
The Minecraft source code is NOT added to your workspace in a editable way. Minecraft is treated like a normal Library. Sources are there for documentation and research purposes only.
Source code uses mcp mappings not Mojangs.
The source code can be 'created' with the ./eclipse command and can be found in the following path:
minecraft-lod-mod\build\fg_cache\mcp\ VERSION \joined\ RANDOM_STRING \patch\output.jar
Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

+81 -42
View File
@@ -1,39 +1,39 @@
buildscript {
repositories {
maven { url = 'https://files.minecraftforge.net/maven' }
jcenter()
maven { url = 'https://files.minecraftforge.net' }
mavenCentral()
// potential replacement in case of problems:
// https://dist.creeper.host/Sponge/maven
// potential replacement in case of problems: https://dist.creeper.host/Sponge/maven
maven { url = 'https://repo.spongepowered.org/maven/' }
// used to download and compile dependencies from git repos
// used to download and compile dependencies from git repos
maven { url 'https://jitpack.io' }
gradlePluginPortal()
}
dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
// Note to self: Don't try to update ForgeGradle!
// As of 11-27-2021 Mixins don't work on Gradle 7,
// which the newer versions of ForgeGradle use
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3+', changing: true
classpath group: 'org.spongepowered', name: 'mixingradle', version: '0.7-SNAPSHOT'
}
}
plugins {
id "com.github.johnrengelman.shadow" version "4.0.4"
}
apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'org.spongepowered.mixin'
// Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
apply plugin: 'eclipse'
apply plugin: 'maven-publish'
apply plugin: 'java' // needed for compileJava
version = 'a1.4.1'
version = 'a1.5.3'
group = 'com.seibel.lod'
archivesBaseName = 'lod_1.16.5'
archivesBaseName = 'Distant-Horizons_1.16.5'
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
compileJava {
// release 8 is needed because otherwise FloatBuffer.flip() will crash
// on some machines
// example thread: https://github.com/eclipse/jetty.project/issues/3244
options.compilerArgs.addAll(['--release', '8', '-Xlint:unchecked', '-Xlint:deprecation'])
}
println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch'))
minecraft {
// The mappings can be changed at any time, and must be in the following format.
@@ -42,7 +42,7 @@ minecraft {
// Use non-default mappings at your own risk. they may not always work.
// Simply re-run your setup task after changing the mappings to update your workspace.
mappings channel: 'official', version: '1.16.5'
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
@@ -123,8 +123,19 @@ minecraft {
}
}
// Include resources generated by data generators.
sourceSets.main.resources { srcDir 'src/generated/resources' }
sourceSets {
main {
java {
// add Distant Horizon's Core source folders
srcDirs("core/src/main/java");
srcDirs("core/src/main/resources")
}
resources {
// Include resources generated by data generators.
srcDir("src/generated/resources")
}
}
}
// this is required so that we can use
// jitpack in the dependencies section below
@@ -134,32 +145,25 @@ repositories {
maven { url 'https://jitpack.io' }
}
configurations {
shadowMe
compileOnly.extendsFrom(embed)
}
dependencies {
// Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed
// that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied.
// The userdev artifact is a special name and will get all sorts of transformations applied to it.
minecraft 'net.minecraftforge:forge:1.16.5-36.1.0'
// these aren't needed right now
//implementation ('com.github.KaptainWutax:TerrainUtils:1.0.0'){
// transitive = false
//}
//implementation ('com.github.KaptainWutax:BiomeUtils:1.0.0'){
// transitive = false
//}
//implementation ('com.github.KaptainWutax:SeedUtils:-SNAPSHOT'){
// transitive = false
//}
//implementation ('com.github.KaptainWutax:MCUtils:1.0.0'){
// transitive = false
//}
//implementation ('com.github.KaptainWutax:NoiseUtils:1.0.0'){
// transitive = false
//}
//implementation ('com.github.KaptainWutax:MathUtils:-SNAPSHOT'){
// transitive = false
//}
// required to generate the mixin refmap
annotationProcessor 'org.spongepowered:mixin:0.8:processor'
implementation 'org.tukaani:xz:1.9'
shadowMe 'org.tukaani:xz:1.9'
implementation 'org.apache.commons:commons-compress:1.21'
shadowMe 'org.apache.commons:commons-compress:1.21'
// these were added to hopefully allow for cloning
// configuredFeatures to allow for safe
// multi threaded feature generation. Sadly I couldn't find
@@ -190,7 +194,41 @@ dependencies {
// For more info...
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
// http://www.gradle.org/docs/current/userguide/dependency_management.html
}
task copyCoreResources(type: Copy) {
from file("core/src/main/resources/shaders")
into file("src/generated/resources/shaders")
}
task copyCoreResourcesAtRuntime(type: Copy) {
from file("core/src/main/resources/shaders")
into file("build/resources/main/shaders")
}
compileJava.dependsOn(copyCoreResources).dependsOn(copyCoreResourcesAtRuntime)
jar {
classifier 'withoutDependencies'
}
shadowJar {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
configurations = [project.configurations.getByName("shadowMe")]
relocate 'org.tukaani', 'shaded.tukaani'
relocate 'org.apache.commons.compress', 'shaded.apache.commons.compress'
classifier ''
}
reobf {
shadowJar {
dependsOn tasks.createMcpToSrg
mappings = tasks.createMcpToSrg.outputs.files.singleFile
}
}
artifacts {
archives tasks.shadowJar
}
// Example for how to get properties into the manifest for reading by the runtime..
@@ -200,16 +238,15 @@ jar {
"Specification-Title": "LOD",
"Specification-Version": "1", // We are version 1 of ourselves
"Implementation-Title": project.name,
"Implementation-Version": "{version}",
"Implementation-Version": "1",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
"MixinConfigs": "lod.mixins.json",
])
}
}
// Example configuration to allow publishing using the maven-publish task
// This is the preferred method to reobfuscate your jar file
jar.finalizedBy('reobfJar')
jar.finalizedBy('reobfJar')
// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing
//publish.dependsOn('reobfJar')
@@ -228,4 +265,6 @@ publishing {
mixin {
add sourceSets.main, "lod.refmap.json"
config "lod.mixins.json"
}
Submodule
+1
Submodule core added at 73c041e02f
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip
Binary file not shown.
-32
View File
@@ -1,32 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod;
/**
* This file is similar to mcmod.info
*
* @author James Seibel
* @version 08-29-2021
*/
public final class ModInfo
{
public static final String MODID = "lod";
public static final String MODNAME = "LOD";
public static final String MODAPI = "LodAPI";
public static final String VERSION = "a1.4";
}
@@ -1,524 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.builders;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.locks.ReentrantLock;
import org.lwjgl.opengl.GL11;
import com.seibel.lod.builders.lodTemplates.Box;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.objects.DataPoint;
import com.seibel.lod.objects.LevelPosUtil;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodRegion;
import com.seibel.lod.objects.PosToRenderContainer;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.render.LodRenderer;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
/**
* This object is used to create NearFarBuffer objects.
*
* @author James Seibel
* @version 8-24-2021
*/
public class LodBufferBuilder
{
/**
* This holds the thread used to generate new LODs off the main thread.
*/
private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - main"));
/**
* This holds the threads used to generate buffers.
*/
private ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfBufferBuilderThreads.get(), new LodThreadFactory(this.getClass().getSimpleName() + " - builder"));
/**
* The buffers that are used to create LODs using far fog
*/
public volatile BufferBuilder[][] buildableBuffers;
/**
* Used when building new VBOs
*/
public volatile VertexBuffer[][] buildableVbos;
/**
* VBOs that are sent over to the LodNodeRenderer
*/
public volatile VertexBuffer[][] drawableVbos;
/**
* if this is true the LOD buffers are currently being
* regenerated.
*/
public boolean generatingBuffers = false;
/**
* if this is true new LOD buffers have been generated
* and are waiting to be swapped with the drawable buffers
*/
private boolean switchVbos = false;
/**
* Size of the buffer builders in bytes last time we created them
*/
public int previousBufferSize = 0;
/**
* Width of the dimension in regions last time we created the buffers
*/
public int previousRegionWidth = 0;
/**
* this is used to prevent multiple threads creating, destroying, or using the buffers at the same time
*/
private ReentrantLock bufferLock = new ReentrantLock();
private static final int NUMBER_OF_DIRECTION = 4;
//in order -x, +x, -z, +z
private static final int[][] ADJ_DIRECTION = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
private volatile Box[][] boxCache;
private volatile PosToRenderContainer[][] setsToRender;
private volatile RegionPos center;
/** This is the ChunkPos the player was at the last time the buffers were built.
* IE the center of the buffers last time they were built */
private volatile ChunkPos drawableCenterChunkPos = new ChunkPos(0,0);
private volatile ChunkPos buildableCenterChunkPos = new ChunkPos(0,0);
public LodBufferBuilder()
{
}
/**
* Create a thread to asynchronously generate LOD buffers
* centered around the given camera X and Z.
* <br>
* This method will write to the drawable near and far buffers.
* <br>
* After the buildable buffers have been generated they must be
* swapped with the drawable buffers in the LodRenderer to be drawn.
*/
public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim,
BlockPos playerBlockPos, boolean fullRegen)
{
// only allow one generation process to happen at a time
if (generatingBuffers)
return;
if (buildableBuffers == null)
// setupBuffers hasn't been called yet
return;
generatingBuffers = true;
// round the player's block position down to the nearest chunk BlockPos
ChunkPos playerChunkPos = new ChunkPos(playerBlockPos);
BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition();
Thread thread = new Thread(() ->
{
bufferLock.lock();
try
{
long treeStart = System.currentTimeMillis();
long treeEnd = System.currentTimeMillis();
long startTime = System.currentTimeMillis();
ArrayList<Callable<Boolean>> nodeToRenderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length);
startBuffers(fullRegen, lodDim);
// =====================//
// RENDERING PART //
// =====================//
RegionPos playerRegionPos = new RegionPos(playerChunkPos);
if (center == null)
center = playerRegionPos;
if (setsToRender == null)
setsToRender = new PosToRenderContainer[lodDim.regions.length][lodDim.regions.length];
if (setsToRender.length != lodDim.regions.length)
setsToRender = new PosToRenderContainer[lodDim.regions.length][lodDim.regions.length];
if (boxCache == null)
boxCache = new Box[lodDim.regions.length][lodDim.regions.length];
if (boxCache.length != lodDim.regions.length)
boxCache = new Box[lodDim.regions.length][lodDim.regions.length];
// this will be the center of the VBOs once they have been built
buildableCenterChunkPos = playerChunkPos;
for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++)
{
for (int zRegion = 0; zRegion < lodDim.regions.length; zRegion++)
{
if (lodDim.regen[xRegion][zRegion] || fullRegen)
{
RegionPos regionPos = new RegionPos(
xRegion + lodDim.getCenterX() - Math.floorDiv(lodDim.getWidth(), 2),
zRegion + lodDim.getCenterZ() - Math.floorDiv(lodDim.getWidth(), 2));
// local position in the vbo and bufferBuilder arrays
BufferBuilder currentBuffer = buildableBuffers[xRegion][zRegion];
LodRegion region = lodDim.getRegion(regionPos.x, regionPos.z);
if (region == null) continue;
byte minDetail = region.getMinDetailLevel();
// make sure the buffers weren't
// changed while we were running this method
if (currentBuffer == null || (currentBuffer != null && !currentBuffer.building()))
return;
//previous setToRender chache
final int xR = xRegion;
final int zR = zRegion;
Callable<Boolean> dataToRenderThread = () ->
{
//previous setToRender chache
if (setsToRender[xR][zR] == null)
{
setsToRender[xR][zR] = new PosToRenderContainer(minDetail, regionPos.x, regionPos.z);
}
if (boxCache[xR][zR] == null)
{
boxCache[xR][zR] = new Box();
}
PosToRenderContainer posToRender = setsToRender[xR][zR];
posToRender.clear(minDetail, regionPos.x, regionPos.z);
lodDim.getDataToRender(
posToRender,
regionPos,
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ());
byte detailLevel;
int posX;
int posZ;
int xAdj;
int zAdj;
int chunkXdist;
int chunkZdist;
short gameChunkRenderDistance = (short) (renderer.vanillaRenderedChunks.length / 2 - 1);
long dataPoint;
long[] adjData;
for (int index = 0; index < posToRender.getNumberOfPos(); index++)
{
detailLevel = posToRender.getNthDetailLevel(index);
posX = posToRender.getNthPosX(index);
posZ = posToRender.getNthPosZ(index);
// skip any chunks that Minecraft is going to render
chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.x;
chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.z;
if (gameChunkRenderDistance >= Math.abs(chunkXdist)
&& gameChunkRenderDistance >= Math.abs(chunkZdist)
&& detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL
&& renderer.vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1])
{
continue;
}
// skip any chunks that Minecraft is going to render
try
{
if (lodDim.doesDataExist(detailLevel, posX, posZ))
{
dataPoint = lodDim.getData(detailLevel, posX, posZ);
if(DataPoint.getHeight(dataPoint) == LodBuilder.DEFAULT_HEIGHT && DataPoint.getDepth(dataPoint) == LodBuilder.DEFAULT_DEPTH)
continue;
adjData = new long[NUMBER_OF_DIRECTION];
for (int direction = 0; direction < NUMBER_OF_DIRECTION; direction++)
{
xAdj = posX + ADJ_DIRECTION[direction][0];
zAdj = posZ + ADJ_DIRECTION[direction][1];
chunkXdist = LevelPosUtil.getChunkPos(detailLevel,xAdj) - playerChunkPos.x;
chunkZdist = LevelPosUtil.getChunkPos(detailLevel,zAdj) - playerChunkPos.z;
if (gameChunkRenderDistance >= Math.abs(chunkXdist) && gameChunkRenderDistance >= Math.abs(chunkZdist))
{
if (!renderer.vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1]
&& posToRender.contains(detailLevel, xAdj, zAdj))
{
adjData[direction]= lodDim.getData(detailLevel, xAdj, zAdj);
}
} else
{
if (posToRender.contains(detailLevel, xAdj, zAdj))
{
adjData[direction] = lodDim.getData(detailLevel, xAdj, zAdj);
}
}
}
LodConfig.CLIENT.graphics.lodTemplate.get().template.addLodToBuffer(currentBuffer, playerBlockPosRounded, dataPoint, adjData,
detailLevel, posX, posZ, boxCache[xR][zR],renderer.previousDebugMode);
}
} catch (ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
return false;
}
}// for pos to in list to render
// the thread executed successfully
return true;
};
nodeToRenderThreads.add(dataToRenderThread);
}
}// region z
}// region z
long renderStart = System.currentTimeMillis();
// wait for all threads to finish
List<Future<Boolean>> futuresBuffer = bufferBuilderThreads.invokeAll(nodeToRenderThreads);
for (Future<Boolean> future : futuresBuffer)
{
// the future will be false if its thread failed
if (!future.get())
{
ClientProxy.LOGGER.warn("LodBufferBuilder ran into trouble and had to start over.");
closeBuffers(fullRegen, lodDim);
return;
}
}
long renderEnd = System.currentTimeMillis();
long endTime = System.currentTimeMillis();
@SuppressWarnings("unused")
long buildTime = endTime - startTime;
@SuppressWarnings("unused")
long treeTime = treeEnd - treeStart;
@SuppressWarnings("unused")
long renderingTime = renderEnd - renderStart;
// ClientProxy.LOGGER.info("Buffer Build time: " + buildTime + " ms" + '\n' +
// "Tree cutting time: " + treeTime + " ms" + '\n' +
// "Rendering time: " + renderingTime + " ms");
// mark that the buildable buffers as ready to swap
switchVbos = true;
} catch (Exception e)
{
ClientProxy.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: ");
e.printStackTrace();
} finally
{
// regardless of if we successfully created the buffers
// we are done generating.
generatingBuffers = false;
// clean up any potentially open resources
if (buildableBuffers != null)
closeBuffers(fullRegen, lodDim);
// upload the new buffers
uploadBuffers(fullRegen, lodDim);
bufferLock.unlock();
}
});
mainGenThread.execute(thread);
return;
}
//===============================//
// BufferBuilder related methods //
//===============================//
/**
* Called from the LodRenderer to create the
* BufferBuilders. <br><br>
* <p>
* May have to wait for the bufferLock to open.
*/
public void setupBuffers(int numbRegionsWide, int bufferMaxCapacity)
{
bufferLock.lock();
previousRegionWidth = numbRegionsWide;
previousBufferSize = bufferMaxCapacity;
buildableBuffers = new BufferBuilder[numbRegionsWide][numbRegionsWide];
buildableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide];
drawableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide];
for (int x = 0; x < numbRegionsWide; x++)
{
for (int z = 0; z < numbRegionsWide; z++)
{
buildableBuffers[x][z] = new BufferBuilder(bufferMaxCapacity);
buildableVbos[x][z] = new VertexBuffer(LodRenderer.LOD_VERTEX_FORMAT);
drawableVbos[x][z] = new VertexBuffer(LodRenderer.LOD_VERTEX_FORMAT);
}
}
bufferLock.unlock();
}
/**
* sets the buffers and Vbos to null, forcing them to be recreated. <br><br>
* <p>
* May have to wait for the bufferLock to open.
*/
public void destroyBuffers()
{
bufferLock.lock();
buildableBuffers = null;
buildableVbos = null;
drawableVbos = null;
bufferLock.unlock();
}
/**
* Calls begin on each of the buildable BufferBuilders.
*/
private void startBuffers(boolean fullRegen, LodDimension lodDim)
{
for (int x = 0; x < buildableBuffers.length; x++)
for (int z = 0; z < buildableBuffers.length; z++)
{
if (fullRegen || lodDim.regen[x][z])
{
buildableBuffers[x][z].begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
}
}
}
/**
* Calls end on each of the buildable BufferBuilders.
*/
private void closeBuffers(boolean fullRegen, LodDimension lodDim)
{
for (int x = 0; x < buildableBuffers.length; x++)
for (int z = 0; z < buildableBuffers.length; z++)
if (buildableBuffers[x][z] != null && buildableBuffers[x][z].building() && (fullRegen || lodDim.regen[x][z]))
{
buildableBuffers[x][z].end();
}
}
/**
* Called from the LodRenderer to create the
* BufferBuilders at the right size.
*/
private void uploadBuffers(boolean fullRegen, LodDimension lodDim)
{
for (int x = 0; x < buildableVbos.length; x++)
{
for (int z = 0; z < buildableVbos.length; z++)
{
if (fullRegen || lodDim.regen[x][z])
{
buildableVbos[x][z].upload(buildableBuffers[x][z]);
lodDim.regen[x][z] = false;
}
}
}
}
/**
* Get the newly created VBOs
*/
public VertexBuffersAndOffset getVertexBuffers()
{
// don't wait for the lock to open
// since this is called on the main render thread
if (bufferLock.tryLock())
{
VertexBuffer[][] tmp = drawableVbos;
drawableVbos = buildableVbos;
buildableVbos = tmp;
drawableCenterChunkPos = buildableCenterChunkPos;
// the vbos have been swapped
switchVbos = false;
bufferLock.unlock();
}
return new VertexBuffersAndOffset(drawableVbos, drawableCenterChunkPos);
}
/**
* A simple container to pass multiple objects back in the getVertexBuffers method.
*/
public class VertexBuffersAndOffset
{
public VertexBuffer[][] vbos;
public ChunkPos drawableCenterChunkPos;
public VertexBuffersAndOffset(VertexBuffer[][] newVbos, ChunkPos newDrawableCenterChunkPos)
{
vbos = newVbos;
drawableCenterChunkPos = newDrawableCenterChunkPos;
}
}
/**
* If this is true the buildable near and far
* buffers have been generated and are ready to be
* sent to the LodRenderer.
*/
public boolean newBuffersAvaliable()
{
return switchVbos;
}
}
@@ -1,575 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.builders;
import java.awt.Color;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.LodDetail;
import com.seibel.lod.objects.DataPoint;
import com.seibel.lod.objects.LevelPosUtil;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodRegion;
import com.seibel.lod.objects.LodWorld;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.block.AbstractPlantBlock;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.BushBlock;
import net.minecraft.block.GrassBlock;
import net.minecraft.block.IGrowable;
import net.minecraft.block.LeavesBlock;
import net.minecraft.block.material.MaterialColor;
import net.minecraft.world.DimensionType;
import net.minecraft.world.IWorld;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.Heightmap;
/**
* This object is in charge of creating Lod related objects. (specifically: Lod
* World, Dimension, and Region objects)
*
* @author Leonardo Amato
* @author James Seibel
* @version 9-7-2021
*/
public class LodBuilder
{
private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH;
public static final int CHUNK_SECTION_HEIGHT = CHUNK_DATA_WIDTH;
public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;
/**
* If no blocks are found in the area in determineBottomPointForArea return this
*/
public static final short DEFAULT_DEPTH = 0;
/**
* If no blocks are found in the area in determineHeightPointForArea return this
*/
public static final short DEFAULT_HEIGHT = 0;
/**
* How wide LodDimensions should be in regions
*/
public int defaultDimensionWidthInRegions = 5;
public LodBuilder()
{
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world)
{
generateLodNodeAsync(chunk, lodWorld, world, DistanceGenerationMode.SERVER);
}
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world, DistanceGenerationMode generationMode)
{
if (lodWorld == null || !lodWorld.getIsWorldLoaded())
return;
// don't try to create an LOD object
// if for some reason we aren't
// given a valid chunk object
if (chunk == null)
return;
Thread thread = new Thread(() ->
{
try
{
DimensionType dim = world.dimensionType();
LodDimension lodDim;
int playerPosX;
int playerPosZ;
if (MinecraftWrapper.INSTANCE.getPlayer() == null)
{
playerPosX = chunk.getPos().getMinBlockX();
playerPosZ = chunk.getPos().getMinBlockZ();
} else
{
playerPosX = (int) world.players().get(0).getX();
playerPosZ = (int) world.players().get(0).getZ();
}
if (lodWorld.getLodDimension(dim) == null)
{
lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions);
lodWorld.addLodDimension(lodDim);
lodDim.treeGenerator(playerPosX, playerPosZ);
} else
{
lodDim = lodWorld.getLodDimension(dim);
}
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode));
} catch (IllegalArgumentException | NullPointerException e)
{
e.printStackTrace();
// if the world changes while LODs are being generated
// they will throw errors as they try to access things that no longer
// exist.
}
});
lodGenThreadPool.execute(thread);
return;
}
/**
* Creates a LodChunk for a chunk in the given world.
*
* @throws IllegalArgumentException thrown if either the chunk or world is null.
*/
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk) throws IllegalArgumentException
{
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig());
}
/**
* Creates a LodChunk for a chunk in the given world.
*
* @throws IllegalArgumentException thrown if either the chunk or world is null.
*/
public void generateLodNodeFromChunk(LodDimension lodDim, IChunk chunk, LodBuilderConfig config)
throws IllegalArgumentException
{
if (chunk == null)
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
int startX;
int startZ;
int endX;
int endZ;
int color;
short height;
short depth;
long data;
try
{
LodDetail detail;
LodRegion region = lodDim.getRegion(chunk.getPos().getRegionX(), chunk.getPos().getRegionZ());
if (region == null)
return;
byte minDetailLevel = region.getMinDetailLevel();
detail = DetailDistanceUtil.getLodGenDetail(minDetailLevel);
byte detailLevel = detail.detailLevel;
int posX;
int posZ;
for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
{
startX = detail.startX[i];
startZ = detail.startZ[i];
endX = detail.endX[i];
endZ = detail.endZ[i];
color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ);
if (!config.useHeightmap)
{
height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
} else
{
height = determineHeightPoint(chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP), startX,
startZ, endX, endZ);
depth = 0;
}
posX = LevelPosUtil.convert((byte) 0, chunk.getPos().x * 16 + startX, detail.detailLevel);
posZ = LevelPosUtil.convert((byte) 0, chunk.getPos().z * 16 + startZ, detail.detailLevel);
boolean isServer = config.distanceGenerationMode == DistanceGenerationMode.SERVER;
data = DataPoint.createDataPoint(height, depth, ColorUtil.getRed(color), ColorUtil.getGreen(color), ColorUtil.getBlue(color));
lodDim.addData(detailLevel,
posX,
posZ,
data,
false,
isServer);
}
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z);
} catch (Exception e)
{
e.printStackTrace();
}
}
// =====================//
// constructor helpers //
// =====================//
/**
* Find the lowest valid point from the bottom.
*/
private short determineBottomPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ)
{
int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2);
// search from the bottom up
for (int section = 0; section < CHUNK_DATA_WIDTH; section++)
{
for (int y = 0; y < CHUNK_SECTION_HEIGHT; y++)
{
int numberOfBlocksFound = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
if (isLayerValidLodPoint(chunkSections, section, y, x, z))
{
numberOfBlocksFound++;
if (numberOfBlocksFound >= numberOfBlocksRequired)
{
// we found
// enough blocks in this
// layer to count as an
// LOD point
return (short) (y + (section * CHUNK_SECTION_HEIGHT));
}
}
}
}
}
}
// we never found a valid LOD point
return DEFAULT_DEPTH;
}
/**
* Find the lowest valid point from the bottom.
*/
@SuppressWarnings("unused")
private short determineBottomPoint(Heightmap heightmap)
{
// the heightmap only shows how high the blocks go, it
// doesn't have any info about how low they go
return 0;
}
/**
* Find the highest valid point from the Top
*/
private short determineHeightPointForArea(ChunkSection[] chunkSections, int startX, int startZ, int endX, int endZ)
{
int numberOfBlocksRequired = ((endX - startX) * (endZ - startZ) / 2);
// search from the top down
for (int section = chunkSections.length - 1; section >= 0; section--)
{
for (int y = CHUNK_DATA_WIDTH - 1; y >= 0; y--)
{
int numberOfBlocksFound = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
if (isLayerValidLodPoint(chunkSections, section, y, x, z))
{
numberOfBlocksFound++;
if (numberOfBlocksFound >= numberOfBlocksRequired)
{
// we found
// enough blocks in this
// layer to count as an
// LOD point
return (short) (y + 1 + (section * CHUNK_SECTION_HEIGHT));
}
}
}
}
}
}
// we never found a valid LOD point
return DEFAULT_HEIGHT;
}
/**
* Find the highest point from the Top
*/
private short determineHeightPoint(Heightmap heightmap, int startX, int startZ, int endX, int endZ)
{
short highest = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
short newHeight = (short) heightmap.getFirstAvailable(x, z);
if (newHeight > highest)
highest = newHeight;
}
}
return highest;
}
/**
* Generate the color for the given chunk using biome water color, foliage
* color, and grass color.
*
* @param config_useSolidBlocksInColorGen <br>
* If true we look down from the top of
* the <br>
* chunk until we find a non-invisible
* block, and then use <br>
* its color. If false we generate the
* color immediately for <br>
* each x and z.
* @param config_useBiomeColors <br>
* If true use biome foliage, water, and
* grass colors, <br>
* otherwise only use the block's
* material color
*/
private int generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX,
int endZ)
{
ChunkSection[] chunkSections = chunk.getSections();
int numbOfBlocks = 0;
int red = 0;
int green = 0;
int blue = 0;
for (int x = startX; x < endX; x++)
{
for (int z = startZ; z < endZ; z++)
{
boolean foundBlock = false;
// go top down
for (int i = chunkSections.length - 1; !foundBlock && i >= 0; i--)
{
if (!foundBlock && (chunkSections[i] != null || !config.useSolidBlocksInColorGen))
{
for (int y = CHUNK_SECTION_HEIGHT - 1; !foundBlock && y >= 0; y--)
{
int colorInt = 0;
BlockState blockState = null;
if (chunkSections[i] != null)
{
blockState = chunkSections[i].getBlockState(x, y, z);
colorInt = blockState.materialColor.col;
}
if (colorInt == 0 && config.useSolidBlocksInColorGen)
{
// skip air or invisible blocks
continue;
}
if (config.useBiomeColors)
{
// I have no idea why I need to bit shift to the right, but
// if I don't the biomes don't show up correctly.
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + 1 * chunkSections.length >> 2,
z >> 2);
colorInt = getColorForBiome(x, z, biome);
} else
{
// the bit shift is equivalent to dividing by 4
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, y + i * chunkSections.length >> 2,
z >> 2);
colorInt = getColorForBlock(x, z, blockState, biome);
}
red += ColorUtil.getRed(colorInt);
green += ColorUtil.getGreen(colorInt);
blue += ColorUtil.getBlue(colorInt);
numbOfBlocks++;
// we found a valid block, skip to the
// next x and z
foundBlock = true;
}
}
}
}
}
if (numbOfBlocks == 0)
numbOfBlocks = 1;
red /= numbOfBlocks;
green /= numbOfBlocks;
blue /= numbOfBlocks;
return ColorUtil.rgbToInt(red,green,blue);
}
/**
* Returns a color int for the given block.
*/
private int getColorForBlock(int x, int z, BlockState blockState, Biome biome)
{
int colorInt = 0;
// block special cases
if (blockState == Blocks.AIR.defaultBlockState()
|| blockState == Blocks.CAVE_AIR.defaultBlockState()
|| blockState == Blocks.BARRIER.defaultBlockState())
{
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
} else if (blockState == Blocks.STONE.defaultBlockState())
{
colorInt = LodUtil.STONE_COLOR_INT;
} else if (blockState == Blocks.MYCELIUM.defaultBlockState())
{
colorInt = LodUtil.MYCELIUM_COLOR_INT;
}
// plant life
else if (blockState.getBlock() instanceof LeavesBlock || blockState.getBlock() == Blocks.VINE)
{
Color leafColor = LodUtil.intToColor(biome.getFoliageColor()).darker();
leafColor = leafColor.darker();
colorInt = LodUtil.colorToInt(leafColor);
} else if ((blockState.getBlock() instanceof GrassBlock || blockState.getBlock() instanceof AbstractPlantBlock
|| blockState.getBlock() instanceof BushBlock || blockState.getBlock() instanceof IGrowable)
&& !(blockState.getBlock() == Blocks.BROWN_MUSHROOM || blockState.getBlock() == Blocks.RED_MUSHROOM))
{
Color plantColor = LodUtil.intToColor(biome.getGrassColor(x, z));
plantColor = plantColor.darker();
colorInt = LodUtil.colorToInt(plantColor);
}
// water
else if (blockState.getBlock() == Blocks.WATER)
{
colorInt = biome.getWaterColor();
}
// everything else
else
{
colorInt = blockState.materialColor.col;
}
return colorInt;
}
/**
* Returns a color int for the given biome.
*/
private int getColorForBiome(int x, int z, Biome biome)
{
int colorInt = 0;
switch (biome.getBiomeCategory())
{
case NETHER:
colorInt = Blocks.BEDROCK.defaultBlockState().materialColor.col;
break;
case THEEND:
colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col;
break;
case BEACH:
case DESERT:
colorInt = Blocks.SAND.defaultBlockState().materialColor.col;
break;
case EXTREME_HILLS:
colorInt = Blocks.STONE.defaultMaterialColor().col;
break;
case MUSHROOM:
colorInt = MaterialColor.COLOR_LIGHT_GRAY.col;
break;
case ICY:
colorInt = Blocks.SNOW.defaultMaterialColor().col;
break;
case MESA:
colorInt = Blocks.RED_SAND.defaultMaterialColor().col;
break;
case OCEAN:
case RIVER:
colorInt = biome.getWaterColor();
break;
case NONE:
case FOREST:
case TAIGA:
case JUNGLE:
case PLAINS:
case SAVANNA:
case SWAMP:
default:
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
break;
}
return colorInt;
}
/**
* Is the layer between the given X, Z, and dataIndex values a valid LOD point?
*/
private boolean isLayerValidLodPoint(ChunkSection[] chunkSections, int sectionIndex, int y, int x, int z)
{
if (chunkSections[sectionIndex] == null)
{
// this section doesn't have any blocks,
// it is not a valid section
return false;
} else
{
if (chunkSections[sectionIndex].getBlockState(x, y, z) != null
&& chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR
&& chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.CAVE_AIR
&& chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.BARRIER)
{
return true;
}
}
return false;
}
}
@@ -1,112 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.builders;
import com.seibel.lod.enums.DistanceGenerationMode;
/**
* This is used to easily configure how LodChunks are generated.
* Generally this will only be used if we want to generate a
* LodChunk using a incomplete Chunk, otherwise the defaults
* work best for a fully generated chunk (IE has correct surface blocks).
*
* @author James Seibel
* @version 8-14-2021
*/
public class LodBuilderConfig
{
/**
* default false
*/
public boolean useHeightmap;
/**
* default false
*/
public boolean useBiomeColors;
/**
* default true
*/
public boolean useSolidBlocksInColorGen;
/**
* default server
*/
public DistanceGenerationMode distanceGenerationMode;
/**
* default settings for a normal chunk <br>
* useHeightmap = false <br>
* useBiomeColors = false <br>
* useSolidBlocksInColorGen = true <br>
* generationMode = Server <br>
*/
public LodBuilderConfig()
{
useHeightmap = false;
useBiomeColors = false;
useSolidBlocksInColorGen = true;
distanceGenerationMode = DistanceGenerationMode.SERVER;
}
/**
* @param newUseHeightmap default = false
* @param newUseBiomeColors default = false
* @param newUseSolidBlocksInBiomeColor default = true
* @param newDistanceGenerationMode default = Server
*/
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors,
boolean newUseSolidBlocksInBiomeColor, DistanceGenerationMode newDistanceGenerationMode)
{
useHeightmap = newUseHeightmap;
useBiomeColors = newUseBiomeColors;
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
distanceGenerationMode = newDistanceGenerationMode;
}
/**
* @param newUseHeightmap default = false
* @param newUseBiomeColors default = false
* @param newUseSolidBlocksInBiomeColor default = true
* @param newDistanceGenerationMode default = Server
*/
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, boolean newUseSolidBlocksInBiomeColor)
{
this();
useHeightmap = newUseHeightmap;
useBiomeColors = newUseBiomeColors;
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
if (newUseHeightmap)
{
distanceGenerationMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
} else
{
distanceGenerationMode = DistanceGenerationMode.BIOME_ONLY;
}
}
/**
* @param newUseHeightmap default = false
* @param newUseBiomeColors default = false
* @param newUseSolidBlocksInBiomeColor default = true
* @param newDistanceGenerationMode default = Server
*/
public LodBuilderConfig(DistanceGenerationMode newDistanceGenerationMode)
{
this();
distanceGenerationMode = newDistanceGenerationMode;
}
}
@@ -1,52 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.builders.lodTemplates;
import com.seibel.lod.enums.DebugMode;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.BlockPos;
/**
* This is the abstract class used to create different
* BufferBuilders.
*
* @author James Seibel
* @version 8-8-2021
*/
public abstract class AbstractLodTemplate
{
public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, long[] adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging);
/**
* add the given position and color to the buffer
*/
protected void addPosAndColor(BufferBuilder buffer,
double x, double y, double z,
int red, int green, int blue, int alpha)
{
buffer.vertex(x, y, z).color(red, green, blue, alpha).endVertex();
}
/**
* Returns in bytes how much buffer memory is required
* for one LOD object
*/
public abstract int getBufferMemoryForSingleNode();
}
@@ -1,64 +0,0 @@
package com.seibel.lod.builders.lodTemplates;
public class Box
{
public static final int DOWN = 0;
public static final int UP = 1;
public static final int EAST = 2;
public static final int WEST = 3;
public static final int SOUTH = 4;
public static final int NORTH = 5;
public static final int OFFSET = 0;
public static final int WIDTH = 1;
public static final int X = 0;
public static final int Y = 1;
public static final int Z = 2;
public int[][] box;
public Box(){
box = new int[2][3];
}
public void set(int xWidth, int yWidth, int zWidth){
box[OFFSET][X] = 0;
box[OFFSET][Y] = 0;
box[OFFSET][Z] = 0;
box[WIDTH][X] = xWidth;
box[WIDTH][Y] = yWidth;
box[WIDTH][Z] = zWidth;
}
public void move(int xOffset, int yOffset, int zOffset){
box[OFFSET][X] = xOffset;
box[OFFSET][Y] = yOffset;
box[OFFSET][Z] = zOffset;
}
public int getMinX(){
return box[OFFSET][X];
}
public int getMaxX(){
return box[OFFSET][X] + box[WIDTH][X];
}
public int getMinY(){
return box[OFFSET][Y];
}
public int getMaxY(){
return box[OFFSET][Y] + box[WIDTH][Y];
}
public int getMinZ(){
return box[OFFSET][Z];
}
public int getMaxZ(){
return box[OFFSET][Z] + box[WIDTH][Z];
}
}
@@ -1,382 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.builders.lodTemplates;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.enums.ShadingMode;
import com.seibel.lod.objects.DataPoint;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.LodUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
/**
* Builds LODs as rectangular prisms.
*
* @author James Seibel
* @version 8-10-2021
*/
public class CubicLodTemplate extends AbstractLodTemplate
{
private final int CULL_OFFSET = 16;
public CubicLodTemplate()
{
}
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, long[] adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging)
{
int width = 1 << detailLevel;
// add each LOD for the detail level
generateBoundingBox(
box,
DataPoint.getHeight(data),
DataPoint.getDepth(data),
width,
posX * width,
0,
posZ * width,
bufferCenterBlockPos);
int color = DataPoint.getColor(data);
if (debugging != DebugMode.OFF)
{
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB();
}
if (box != null)
{
addBoundingBoxToBuffer(buffer, box, color, bufferCenterBlockPos, adjData);
}
}
private void generateBoundingBox(Box box, int height, int depth, int width, double xOffset, double yOffset, double zOffset, BlockPos bufferCenterBlockPos)
{
// don't add an LOD if it is empty
if (height == -1 && depth == -1)
return;
if (depth == height)
{
// if the top and bottom points are at the same height
// render this LOD as 1 block thick
height++;
}
// offset the AABB by it's x/z position in the world since
// it uses doubles to specify its location, unlike the model view matrix
// which only uses floats
double x = -bufferCenterBlockPos.getX();
double z = -bufferCenterBlockPos.getZ();;
box.set(width, height - depth, width);
box.move((int) (xOffset + x), (int) (yOffset + depth), (int) (zOffset + z));
}
private void addBoundingBoxToBuffer(BufferBuilder buffer, Box box, int c, BlockPos playerBlockPos, long[] adjData)
{
int topColor = c;
int bottomColor = c;
int northColor = c;
int southColor = c;
int westColor = c;
int eastColor = c;
// darken the bottom and side colors if requested
if (LodConfig.CLIENT.graphics.shadingMode.get() == ShadingMode.DARKEN_SIDES)
{
// the side colors are different because
// when using fast lighting in Minecraft the north/south
// and east/west sides are different in a similar way
/**TODO OPTIMIZE THIS STEP*/
Minecraft mc = Minecraft.getInstance();
topColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.UP, true));
bottomColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.DOWN, true));
northColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.NORTH, true));
southColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.SOUTH, true));
westColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.WEST, true));
eastColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.EAST, true));
}
// apply the user specified saturation and brightness
float saturationMultiplier = LodConfig.CLIENT.graphics.saturationMultiplier.get().floatValue();
float brightnessMultiplier = LodConfig.CLIENT.graphics.brightnessMultiplier.get().floatValue();
if (saturationMultiplier != 1 || brightnessMultiplier != 1)
{
topColor = ColorUtil.applySaturationAndBrightnessMultipliers(topColor, saturationMultiplier, brightnessMultiplier);
bottomColor = ColorUtil.applySaturationAndBrightnessMultipliers(bottomColor, saturationMultiplier, brightnessMultiplier);
northColor = ColorUtil.applySaturationAndBrightnessMultipliers(northColor, saturationMultiplier, brightnessMultiplier);
southColor = ColorUtil.applySaturationAndBrightnessMultipliers(southColor, saturationMultiplier, brightnessMultiplier);
westColor = ColorUtil.applySaturationAndBrightnessMultipliers(westColor, saturationMultiplier, brightnessMultiplier);
eastColor = ColorUtil.applySaturationAndBrightnessMultipliers(eastColor, saturationMultiplier, brightnessMultiplier);
}
int minX;
int maxX;
int minY;
int maxY;
int minZ;
int maxZ;
long data;
int tempMinY;
int tempMaxY;
int red;
int green;
int blue;
int alpha;
boolean disableCulling = true;
/**TODO make all of this more automatic if possible*/
if (playerBlockPos.getY() > box.getMaxY() - CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(topColor);
green = ColorUtil.getGreen(topColor);
blue = ColorUtil.getBlue(topColor);
alpha = ColorUtil.getAlpha(topColor);
// top (facing up)
minX = box.getMinX();
maxX = box.getMaxX();
minY = box.getMinY();
maxY = box.getMaxY();
minZ = box.getMinZ();
maxZ = box.getMaxZ();
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
}
if (playerBlockPos.getY() < box.getMinY() + CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(bottomColor);
green = ColorUtil.getGreen(bottomColor);
blue = ColorUtil.getBlue(bottomColor);
alpha = ColorUtil.getAlpha(bottomColor);
// bottom (facing down)
minX = box.getMinX();
maxX = box.getMaxX();
minY = box.getMinY();
maxY = box.getMaxY();
minZ = box.getMinZ();
maxZ = box.getMaxZ();
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
}
if (playerBlockPos.getX() < box.getMaxX() + CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(westColor);
green = ColorUtil.getGreen(westColor);
blue = ColorUtil.getBlue(westColor);
alpha = ColorUtil.getAlpha(westColor);
// west (facing -X)
data = adjData[0];
minX = box.getMinX();
maxX = box.getMaxX();
minY = box.getMinY();
maxY = box.getMaxY();
minZ = box.getMinZ();
maxZ = box.getMaxZ();
if (data == 0)
{
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
}else
{
maxY = box.getMaxY();
tempMaxY = DataPoint.getHeight(data);
if (tempMaxY < maxY)
{
minY = Math.max(tempMaxY, minY);
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
}
tempMinY = DataPoint.getDepth(data);
minY = box.getMinY();
if (tempMinY > minY)
{
maxY = Math.min(tempMinY, maxY);
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
}
}
}
if (playerBlockPos.getX() > box.getMinX() - CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(eastColor);
green = ColorUtil.getGreen(eastColor);
blue = ColorUtil.getBlue(eastColor);
alpha = ColorUtil.getAlpha(eastColor);
// east (facing +X)
data = adjData[1];
minX = box.getMinX();
maxX = box.getMaxX();
minY = box.getMinY();
maxY = box.getMaxY();
minZ = box.getMinZ();
maxZ = box.getMaxZ();
if (data == 0)
{
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
}
else
{
maxY = box.getMaxY();
tempMaxY = DataPoint.getHeight(data);
if (tempMaxY < maxY)
{
minY = Math.max(tempMaxY, minY);
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
}
tempMinY = DataPoint.getDepth(data);
minY = box.getMinY();
if (tempMinY > minY)
{
maxY = Math.min(tempMinY, maxY);
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
}
}
}
if (playerBlockPos.getZ() > box.getMinZ() - CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(northColor);
green = ColorUtil.getGreen(northColor);
blue = ColorUtil.getBlue(northColor);
alpha = ColorUtil.getAlpha(northColor);
data = adjData[3];
minX = box.getMinX();
maxX = box.getMaxX();
minY = box.getMinY();
maxY = box.getMaxY();
minZ = box.getMinZ();
maxZ = box.getMaxZ();
// north (facing +Z)
if (data == 0)
{
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
}
else
{
maxY = box.getMaxY();
tempMaxY = DataPoint.getHeight(data);
if (tempMaxY < maxY)
{
minY = Math.max(tempMaxY, minY);
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
}
tempMinY = DataPoint.getDepth(data);
minY = box.getMinY();
if (tempMinY > minY)
{
maxY = Math.min(tempMinY, maxY);
addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha);
}
}
}
if (playerBlockPos.getZ() < box.getMaxZ() + CULL_OFFSET || disableCulling)
{
red = ColorUtil.getRed(southColor);
green = ColorUtil.getGreen(southColor);
blue = ColorUtil.getBlue(southColor);
alpha = ColorUtil.getAlpha(southColor);
data = adjData[2];
minX = box.getMinX();
maxX = box.getMaxX();
minY = box.getMinY();
maxY = box.getMaxY();
minZ = box.getMinZ();
maxZ = box.getMaxZ();
// south (facing -Z)
if (data == 0)
{
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
}
else
{
maxY = box.getMaxY();
tempMaxY = DataPoint.getHeight(data);
if (tempMaxY < maxY)
{
minY = Math.max(tempMaxY, minY);
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
}
tempMinY = DataPoint.getDepth(data);
minY = box.getMinY();
if (tempMinY > minY)
{
maxY = Math.min(tempMinY, maxY);
addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha);
addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha);
}
}
}
}
@Override
public int getBufferMemoryForSingleNode()
{
// (sidesOnACube * pointsInASquare * (positionPoints + colorPoints)))
return (6 * 4 * (3 + 4));
}
}
@@ -1,48 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.builders.lodTemplates;
import com.seibel.lod.enums.DebugMode;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.BlockPos;
/**
* TODO DynamicLodTemplate
* Chunks smoothly transition between
* each other, unless a neighboring chunk
* is at a significantly different height.
*
* @author James Seibel
* @version 06-16-2021
*/
public class DynamicLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, long[] adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging)
{
System.err.println("DynamicLodTemplate not implemented!");
}
@Override
public int getBufferMemoryForSingleNode()
{
return 0;
}
}
@@ -1,46 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.builders.lodTemplates;
import com.seibel.lod.enums.DebugMode;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.BlockPos;
/**
* TODO #21 TriangularLodTemplate
* Builds each LOD chunk as a singular rectangular prism.
*
* @author James Seibel
* @version 06-16-2021
*/
public class TriangularLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, long[] adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging)
{
System.err.println("DynamicLodTemplate not implemented!");
}
@Override
public int getBufferMemoryForSingleNode()
{
return 0;
}
}
@@ -1,663 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.builders.worldGeneration;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
import com.seibel.lod.builders.LodBuilder;
import com.seibel.lod.builders.LodBuilderConfig;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.WeightedList.Entry;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.palette.UpgradeData;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.chunk.ChunkPrimer;
import net.minecraft.world.chunk.ChunkStatus;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.Heightmap;
import net.minecraft.world.gen.blockstateprovider.WeightedBlockStateProvider;
import net.minecraft.world.gen.feature.BlockClusterFeatureConfig;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.DecoratedFeatureConfig;
import net.minecraft.world.gen.feature.FeatureSpread;
import net.minecraft.world.gen.feature.FeatureSpreadConfig;
import net.minecraft.world.gen.feature.IceAndSnowFeature;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraft.world.gen.placement.ConfiguredPlacement;
import net.minecraft.world.gen.placement.DecoratedPlacementConfig;
import net.minecraft.world.gen.placement.IPlacementConfig;
import net.minecraft.world.gen.placement.NoiseDependant;
import net.minecraft.world.server.ServerChunkProvider;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.server.ServerWorldLightManager;
import net.minecraftforge.common.WorldWorkerManager.IWorker;
/**
* This is used to generate a LodChunk at a given ChunkPos.
*
* @author James Seibel
* @version 9-7-2021
*/
public class LodNodeGenWorker implements IWorker
{
public static ExecutorService genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfWorldGenerationThreads.get(), new LodThreadFactory(LodNodeGenWorker.class.getSimpleName()));
private boolean threadStarted = false;
private LodChunkGenThread thread;
/** If a configured feature fails for whatever reason,
* add it to this list, this is to hopefully remove any
* features that could cause issues down the line. */
private static ConcurrentHashMap<Integer, ConfiguredFeature<?, ?>> configuredFeaturesToAvoid = new ConcurrentHashMap<>();
public LodNodeGenWorker(ChunkPos newPos, DistanceGenerationMode newGenerationMode,
LodBuilder newLodBuilder,
LodDimension newLodDimension, ServerWorld newServerWorld)
{
// just a few sanity checks
if (newPos == null)
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ChunkPos");
if (newLodBuilder == null)
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodChunkBuilder");
if (newLodDimension == null)
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodDimension");
if (newServerWorld == null)
throw new IllegalArgumentException("LodChunkGenThread requires a non-null ServerWorld");
thread = new LodChunkGenThread(newPos, newGenerationMode,
newLodBuilder,
newLodDimension, newServerWorld);
}
@Override
public boolean doWork()
{
if (!threadStarted)
{
if (LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get() == DistanceGenerationMode.SERVER)
{
// if we are using SERVER generation that has to be done
// synchronously to prevent crashing and harmful
// interactions with the normal world generator
thread.run();
}
else
{
// Every other method can
// be done asynchronously
Thread newThread = new Thread(thread);
newThread.setPriority(3);
genThreads.execute(newThread);
}
threadStarted = true;
// useful for debugging
// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods());
// ClientProxy.LOGGER.info(genThreads.toString());
}
return false;
}
@Override
public boolean hasWork()
{
return !threadStarted;
}
private class LodChunkGenThread implements Runnable
{
public final ServerWorld serverWorld;
public final LodDimension lodDim;
public final DistanceGenerationMode generationMode;
public final LodBuilder lodBuilder;
private ChunkPos pos;
public LodChunkGenThread(ChunkPos newPos, DistanceGenerationMode newGenerationMode,
LodBuilder newLodBuilder,
LodDimension newLodDimension, ServerWorld newServerWorld)
{
pos = newPos;
generationMode = newGenerationMode;
lodBuilder = newLodBuilder;
lodDim = newLodDimension;
serverWorld = newServerWorld;
}
@Override
public void run()
{
try
{
// only generate LodChunks if they can
// be added to the current LodDimension
/**TODO i must disable this if, i will find a way to replace it*/
if (lodDim.regionIsInRange(pos.x / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.z / LodUtil.REGION_WIDTH_IN_CHUNKS))
{
// long startTime = System.currentTimeMillis();
switch(generationMode)
{
case NONE:
// don't generate
break;
case BIOME_ONLY:
case BIOME_ONLY_SIMULATE_HEIGHT:
// fastest
generateUsingBiomesOnly();
break;
case SURFACE:
// faster
generateUsingSurface();
break;
case FEATURES:
// fast
generateUsingFeatures();
break;
case SERVER:
// very slow
generateWithServer();
break;
}
//lodRenderer.regenerateLODsNextFrame();
/*
boolean dataExistence = lodDim.doesDataExist(new LevelPos((byte) 3, pos.x, pos.z));
if (dataExistence)
ClientProxy.LOGGER.info(pos.x + " " + pos.z + " Success!");
else
ClientProxy.LOGGER.info(pos.x + " " + pos.z);
*/
// shows the pool size, active threads, queued tasks and completed tasks
// ClientProxy.LOGGER.info(genThreads.toString());
// long endTime = System.currentTimeMillis();
// System.out.println(endTime - startTime);
}// if in range
else{
}
}
catch (Exception e)
{
//e.printStackTrace();
}
finally
{
// decrement how many threads are running
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.addAndGet(-1);
// this position is no longer being generated
LodWorldGenerator.INSTANCE.positionWaitingToBeGenerated.remove(pos);
}
}// run
/**
* takes about 2-5 ms
*/
private void generateUsingBiomesOnly()
{
List<IChunk> chunkList = new LinkedList<>();
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
chunkList.add(chunk);
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
ChunkGenerator chunkGen = chunkSource.generator;
// generate the terrain (this is thread safe)
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
// override the chunk status so we can run the next generator stage
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
// generate fake height data for this LOD
int seaLevel = serverWorld.getSeaLevel();
boolean simulateHeight = generationMode == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
boolean inTheEnd = false;
// add fake heightmap data so our LODs aren't at height 0
Heightmap heightmap = new Heightmap(chunk, LodUtil.DEFAULT_HEIGHTMAP);
for(int x = 0; x < LodUtil.CHUNK_WIDTH && !inTheEnd; x++)
{
for(int z = 0; z < LodUtil.CHUNK_WIDTH && !inTheEnd; z++)
{
if (simulateHeight)
{
// these heights are of course aren't super accurate,
// they are just to simulate height data where there isn't any
switch(chunk.getBiomes().getNoiseBiome(x >> 2, seaLevel >> 2, z >> 2).getBiomeCategory())
{
case NETHER:
heightmap.setHeight(x, z, serverWorld.getHeight() / 2);
break;
case EXTREME_HILLS:
heightmap.setHeight(x, z, seaLevel + 30);
break;
case MESA:
heightmap.setHeight(x, z, seaLevel + 20);
break;
case JUNGLE:
heightmap.setHeight(x, z, seaLevel + 20);
break;
case BEACH:
heightmap.setHeight(x, z, seaLevel + 5);
break;
case NONE:
heightmap.setHeight(x, z, 0);
break;
case OCEAN:
case RIVER:
heightmap.setHeight(x, z, seaLevel);
break;
case THEEND:
inTheEnd = true;
break;
// DESERT
// FOREST
// ICY
// MUSHROOM
// SAVANNA
// SWAMP
// TAIGA
// PLAINS
default:
heightmap.setHeight(x, z, seaLevel + 10);
break;
}// heightmap switch
}
else
{
// we aren't simulating height
// always use sea level
heightmap.setHeight(x, z, seaLevel);
}
}// z
}// x
chunk.setHeightmap(LodUtil.DEFAULT_HEIGHTMAP, heightmap.getRawData());
if (!inTheEnd)
{
lodBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(true, true, false));
}
else
{
// if we are in the end, don't generate any chunks.
// Since we don't know where the islands are, everything
// generates the same and it looks really bad.
lodBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(true, true, false));
}
// long startTime = System.currentTimeMillis();
// long endTime = System.currentTimeMillis();
// System.out.println(endTime - startTime);
}
/**
* takes about 10 - 20 ms
*/
private void generateUsingSurface()
{
List<IChunk> chunkList = new LinkedList<>();
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
chunkList.add(chunk);
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
ChunkGenerator chunkGen = chunkSource.generator;
// generate the terrain (this is thread safe)
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
// override the chunk status so we can run the next generator stage
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
ChunkStatus.NOISE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
// this feature has been proven to be thread safe
// so we will add it
IceAndSnowFeature snowFeature = new IceAndSnowFeature(NoFeatureConfig.CODEC);
snowFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition(), null);
lodBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(DistanceGenerationMode.SURFACE));
/**TODO if we want to use Biome utils and terrain utils for overworld
* lodBuilder.generateLodNodeFromChunk(lodDim, pos ,detailLevel, serverWorld.getSeed());*/
}
/**
* takes about 15 - 20 ms
*
* Causes concurrentModification Exceptions,
* which could cause instability or world generation bugs
*/
private void generateUsingFeatures()
{
List<IChunk> chunkList = new LinkedList<>();
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
chunkList.add(chunk);
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
ChunkGenerator chunkGen = chunkSource.generator;
// generate the terrain (this is thread safe)
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
// override the chunk status so we can run the next generator stage
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
ChunkStatus.NOISE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
// get all the biomes in the chunk
HashSet<Biome> biomes = new HashSet<>();
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
{
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
{
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, serverWorld.getSeaLevel() >> 2, z >> 2);
// Issue #35
// For some reason Jungle biomes cause incredible lag
// the features here must be interacting with each other
// in unpredictable ways (specifically tree feature generation).
// When generating Features my CPU usage generally hovers around 30 - 40%
// when generating Jungles it spikes to 100%.
if (biome.getBiomeCategory() != Biome.Category.JUNGLE)
{
// should probably use the heightmap here instead of seaLevel,
// but this seems to get the job done well enough
biomes.add(biome);
}
}
}
boolean allowUnstableFeatures = LodConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.get();
// generate all the features related to this chunk.
// this may or may not be thread safe
for (Biome biome : biomes)
{
List<List<Supplier<ConfiguredFeature<?, ?>>>> featuresForState = biome.generationSettings.features();
for(int featureStateToGenerate = 0; featureStateToGenerate < featuresForState.size(); featureStateToGenerate++)
{
for(Supplier<ConfiguredFeature<?, ?>> featureSupplier : featuresForState.get(featureStateToGenerate))
{
ConfiguredFeature<?, ?> configuredFeature = featureSupplier.get();
if (!allowUnstableFeatures &&
configuredFeaturesToAvoid.containsKey(configuredFeature.hashCode()))
continue;
try
{
configuredFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition());
}
catch(ConcurrentModificationException e)
{
// This will happen. I'm not sure what to do about it
// except pray that it doesn't effect the normal world generation
// in any harmful way.
// Update: this can cause crashes and high CPU usage.
// Issue #35
// I tried cloning the config for each feature, but that
// path was blocked since I can't clone lambda methods.
// I tried using a deep cloning library and discovered
// the problem there.
// ( https://github.com/kostaskougios/cloning
// and
// https://github.com/EsotericSoftware/kryo )
if (!allowUnstableFeatures)
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
}
catch(UnsupportedOperationException e)
{
// This will happen when the LodServerWorld
// isn't able to return something that a feature
// generator needs
if (!allowUnstableFeatures)
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
}
catch(Exception e)
{
// I'm not sure what happened, print to the log
System.out.println();
System.out.println();
e.printStackTrace();
System.out.println();
System.out.println();
if (!allowUnstableFeatures)
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
}
}
}
}
// generate a Lod like normal
lodBuilder.generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(DistanceGenerationMode.FEATURES));
}
/**
* on pre generated chunks 0 - 1 ms
* on un generated chunks 0 - 50 ms
* with the median seeming to hover around 15 - 30 ms
* and outliers in the 100 - 200 ms range
*
* Note this should not be multithreaded and does cause server/simulation lag
* (Higher lag for generating than loading)
*/
private void generateWithServer()
{
lodBuilder.generateLodNodeAsync(serverWorld.getChunk(pos.x, pos.z, ChunkStatus.FEATURES), ClientProxy.getLodWorld(), serverWorld);
}
//================//
// Unused methods //
//================//
// Sadly I wasn't able to get these to work,
// they are here for documentation purposes
@SuppressWarnings({ "rawtypes", "unchecked", "unused" })
private DecoratedFeatureConfig cloneDecoratedFeatureConfig(DecoratedFeatureConfig config)
{
IPlacementConfig placementConfig = null;
Class oldConfigClass = config.decorator.config().getClass();
if (oldConfigClass == FeatureSpreadConfig.class)
{
FeatureSpreadConfig oldPlacementConfig = (FeatureSpreadConfig) config.decorator.config();
FeatureSpread oldSpread = oldPlacementConfig.count();
placementConfig = new FeatureSpreadConfig(oldSpread);
}
else if(oldConfigClass == DecoratedPlacementConfig.class)
{
DecoratedPlacementConfig oldPlacementConfig = (DecoratedPlacementConfig) config.decorator.config();
placementConfig = new DecoratedPlacementConfig(oldPlacementConfig.inner(), oldPlacementConfig.outer());
}
else if(oldConfigClass == NoiseDependant.class)
{
NoiseDependant oldPlacementConfig = (NoiseDependant) config.decorator.config();
placementConfig = new NoiseDependant(oldPlacementConfig.noiseLevel, oldPlacementConfig.belowNoise, oldPlacementConfig.aboveNoise);
}
else
{
// ClientProxy.LOGGER.debug("unkown decorated placement config: \"" + config.decorator.config().getClass() + "\"");
return config;
}
ConfiguredPlacement<?> newPlacement = new ConfiguredPlacement(config.decorator.decorator, placementConfig);
return new DecoratedFeatureConfig(config.feature, newPlacement);
}
@SuppressWarnings("unused")
private BlockClusterFeatureConfig cloneBlockClusterFeatureConfig(BlockClusterFeatureConfig config)
{
WeightedBlockStateProvider provider = new WeightedBlockStateProvider();
for(Entry<BlockState> state : ((WeightedBlockStateProvider) config.stateProvider).weightedList.entries)
provider.weightedList.entries.add(state);
HashSet<Block> whitelist = new HashSet<>();
for(Block block : config.whitelist)
whitelist.add(block);
HashSet<BlockState> blacklist = new HashSet<>();
for(BlockState state : config.blacklist)
blacklist.add(state);
BlockClusterFeatureConfig.Builder builder = new BlockClusterFeatureConfig.Builder(provider, config.blockPlacer);
builder.whitelist(whitelist);
builder.blacklist(blacklist);
builder.xspread(config.xspread);
builder.yspread(config.yspread);
builder.zspread(config.zspread);
if(config.canReplace) { builder.canReplace(); }
if(config.needWater) { builder.needWater(); }
if(config.project) { builder.noProjection(); }
builder.tries(config.tries);
return builder.build();
}
}
/**
* Stops the current genThreads if they are running
* and then recreates the Executer service. <br><br>
*
* This is done to clear any outstanding tasks
* that may exist after the player leaves their current world.
* If this isn't done unfinished tasks may be left in the queue
* preventing new LodChunks form being generated.
*/
public static void restartExecuterService()
{
if (genThreads != null && !genThreads.isShutdown())
{
genThreads.shutdownNow();
}
genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfWorldGenerationThreads.get(), new LodThreadFactory(LodNodeGenWorker.class.getSimpleName()));
}
/*
* performance/generation tests related to
* serverWorld.getChunk(x, z, ChunkStatus. *** )
true/false is whether they generated blocks or not
the time is how long it took to generate
ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P)
ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks)
ChunkStatus.BIOMES 1 - 10 ms false (no height)
ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone)
ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass)
ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass)
ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass)
ChunkStatus.FEATURES 7 - 25 ms true
ChunkStatus.HEIGHTMAPS 20 - 40 ms true
ChunkStatus.LIGHT 20 - 40 ms true
ChunkStatus.FULL 30 - 50 ms true
ChunkStatus.SPAWN 50 - 80 ms true
At this point I would suggest using FEATURES, as it generates snow and trees
(and any other object that is needed to make biomes distinct)
Otherwise if snow/trees aren't necessary SURFACE is the next fastest (although not by much)
*/
}
@@ -1,169 +0,0 @@
package com.seibel.lod.builders.worldGeneration;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import com.seibel.lod.builders.LodBuilder;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.objects.LevelPosUtil;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.PosToGenerateContainer;
import com.seibel.lod.render.LodRenderer;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.WorldWorkerManager;
/**
* A singleton that handles all long distance LOD world generation.
*
* @author James Seibel
* @version 8-24-2021
*/
public class LodWorldGenerator
{
public MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/**
* This holds the thread used to generate new LODs off the main thread.
*/
private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " world generator"));
/**
* we only want to queue up one generator thread at a time
*/
private boolean generatorThreadRunning = false;
/**
* how many chunks to generate outside of the player's view distance at one
* time. (or more specifically how many requests to make at one time). I
* multiply by 8 to make sure there is always a buffer of chunk requests, to
* make sure the CPU is always busy and we can generate LODs as quickly as
* possible.
*/
public int maxChunkGenRequests;
/**
* This keeps track of how many chunk generation requests are on going. This is
* to limit how many chunks are queued at once. To prevent chunks from being
* generated for a long time in an area the player is no longer in.
*/
public AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0);
public Set<ChunkPos> positionWaitingToBeGenerated = new HashSet<>();
/**
* Singleton copy of this object
*/
public static final LodWorldGenerator INSTANCE = new LodWorldGenerator();
private LodWorldGenerator()
{
}
/**
* Queues up LodNodeGenWorkers for the given lodDimension.
*
* @param renderer needed so the LodNodeGenWorkers can flag that the
* buffers need to be rebuilt.
*/
public void queueGenerationRequests(LodDimension lodDim, LodRenderer renderer, LodBuilder lodBuilder)
{
if (LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get() != DistanceGenerationMode.NONE
&& !generatorThreadRunning
&& mc.hasSingleplayerServer())
{
// the thread is now running, don't queue up another thread
generatorThreadRunning = true;
// just in case the config changed
maxChunkGenRequests = LodConfig.CLIENT.threading.numberOfWorldGenerationThreads.get() * 8;
Thread generatorThread = new Thread(() ->
{
try
{
// round the player's block position down to the nearest chunk BlockPos
int playerPosX = mc.getPlayer().blockPosition().getX();
int playerPosZ = mc.getPlayer().blockPosition().getZ();
// if we don't have a full number of chunks to generate in chunksToGen
// we can top it off from this reserve
//=======================================//
// create the generation Request objects //
//=======================================//
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
byte farDetail = (byte) 8;
PosToGenerateContainer posToGenerate = lodDim.getDataToGenerate(
farDetail,
maxChunkGenRequests,
0.25,
playerPosX,
playerPosZ);
//System.out.println(posToGenerate);
//here we prepare two sorted set
//the first contains the near pos to render
//the second contain the far pos to render
byte detailLevel;
int posX;
int posZ;
int[] levelPos;
for (int index = 0; index < posToGenerate.getNumberOfPos(); index++)
{
levelPos = posToGenerate.getNthPos(index);
if(levelPos[0] == 0)
continue;
detailLevel = (byte) (levelPos[0] -1);
posX = levelPos[1];
posZ = levelPos[2];
ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel,posX), LevelPosUtil.getChunkPos(detailLevel,posZ));
if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
{
// prevent generating the same chunk multiple times
if (positionWaitingToBeGenerated.contains(chunkPos))
{
continue;
}
}
// don't add null chunkPos (which shouldn't happen anyway)
// or add more to the generation queue
if (chunkPos == null || numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
continue;
positionWaitingToBeGenerated.add(chunkPos);
numberOfChunksWaitingToGenerate.addAndGet(1);
LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
WorldWorkerManager.addWorker(genWorker);
}
} catch (Exception e)
{
// this shouldn't ever happen, but just in case
e.printStackTrace();
} finally
{
generatorThreadRunning = false;
}
});
mainGenThread.execute(generatorThread);
} // if distanceGenerationMode != DistanceGenerationMode.NONE && !generatorThreadRunning
}
}
@@ -1,440 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.config;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.electronwill.nightconfig.core.io.WritingMode;
import com.seibel.lod.ModInfo;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.enums.DistanceCalculatorType;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.FogDistance;
import com.seibel.lod.enums.FogDrawOverride;
import com.seibel.lod.enums.LodDetail;
import com.seibel.lod.enums.LodTemplate;
import com.seibel.lod.enums.ShadingMode;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
/**
* This handles any configuration the user has access to.
*
* @author James Seibel
* @version 9-1-2021
*/
@Mod.EventBusSubscriber
public class LodConfig
{
public static class Client
{
public final Graphics graphics;
public final WorldGenerator worldGenerator;
public final Threading threading;
public final Debugging debugging;
public final Buffers buffers;
public Client(ForgeConfigSpec.Builder builder)
{
builder.push("client");
{
graphics = new Graphics(builder);
worldGenerator = new WorldGenerator(builder);
threading = new Threading(builder);
debugging = new Debugging(builder);
buffers = new Buffers(builder);
}
builder.pop();
}
}
//================//
// Client Configs //
//================//
public static class Graphics
{
public ForgeConfigSpec.BooleanValue drawLODs;
public ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
public ForgeConfigSpec.EnumValue<FogDrawOverride> fogDrawOverride;
public ForgeConfigSpec.EnumValue<LodTemplate> lodTemplate;
public ForgeConfigSpec.EnumValue<LodDetail> maxDrawDetail;
public ForgeConfigSpec.EnumValue<ShadingMode> shadingMode;
public ForgeConfigSpec.IntValue lodQuality;
public ForgeConfigSpec.IntValue lodChunkRenderDistance;
public ForgeConfigSpec.DoubleValue brightnessMultiplier;
public ForgeConfigSpec.DoubleValue saturationMultiplier;
Graphics(ForgeConfigSpec.Builder builder)
{
builder.comment("These settings control how the LODs look.").push(this.getClass().getSimpleName());
drawLODs = builder
.comment("\n\n"
+ " If false LODs will not be drawn, \n"
+ " however they will still be generated \n"
+ " and saved to file for later use. \n")
.define("drawLODs", true);
fogDistance = builder
.comment("\n\n"
+ " At what distance should Fog be drawn on the LODs? \n"
+ " If the fog cuts off ubruptly or you are using Optifine's \"fast\" fog option \n"
+ " set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ". \n")
.defineEnum("fogDistance", FogDistance.NEAR_AND_FAR);
fogDrawOverride = builder
.comment("\n\n"
+ " When should fog be drawn? \n"
+ " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING.toString() + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ". \n"
+ " " + FogDrawOverride.NEVER_DRAW_FOG.toString() + ": Never draw fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST.toString() + ": Always draw fast fog on the LODs \n"
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
.defineEnum("fogDrawOverride", FogDrawOverride.USE_OPTIFINE_FOG_SETTING);
lodTemplate = builder
.comment("\n\n"
+ " How should the LODs be drawn? \n"
+ " NOTE: Currently only " + LodTemplate.CUBIC.toString() + " is implemented! \n"
+ " \n"
+ " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
+ " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n"
+ " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n"
+ " " + " unless a neighboring chunk is at a significantly different height. \n")
.defineEnum("lodTemplate", LodTemplate.CUBIC);
maxDrawDetail = builder
.comment("\n\n"
+ " What is the maximum detail level that LODs should be drawn at? \n"
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
.defineEnum("lodDrawQuality", LodDetail.FULL);
lodQuality = builder
.comment("\n\n"
+ " this value is multiplied by 128 and determine \n"
+ " how much the quality decrease over distance \n")
.defineInRange("lodQuality", 1, 1, 4);
lodChunkRenderDistance = builder
.comment("\n\n"
+ " This is the render distance of the mod \n")
.defineInRange("lodChunkRenderDistance", 64, 32, 512);
shadingMode = builder
.comment("\n\n"
+ " What kind of shading should the LODs have? \n"
+ " \n"
+ " " + ShadingMode.NONE.toString() + " \n"
+ " " + "LODs will have the same lighting on every side. \n"
+ " " + "Can make large similarly colored areas hard to differentiate. \n"
+ "\n"
+ " " + ShadingMode.DARKEN_SIDES.toString() + " \n"
+ " " + "LODs will have darker sides and bottoms to simulate Minecraft's flat lighting.")
.defineEnum("lightingMode", ShadingMode.DARKEN_SIDES);
brightnessMultiplier = builder
.comment("\n\n"
+ " Change how bright LOD colors are. \n"
+ " 0 = black \n"
+ " 1 = normal color value \n"
+ " 2 = washed out colors \n")
.defineInRange("brightnessMultiplier", 1.0, 0, 2);
saturationMultiplier = builder
.comment("\n\n"
+ " Change how saturated LOD colors are. \n"
+ " 0 = black and white \n"
+ " 1 = normal saturation \n"
+ " 2 = very saturated \n")
.defineInRange("saturationMultiplier", 1.0, 0, 2);
builder.pop();
}
}
public static class WorldGenerator
{
public ForgeConfigSpec.EnumValue<LodDetail> maxGenerationDetail;
public ForgeConfigSpec.EnumValue<DistanceGenerationMode> distanceGenerationMode;
public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration;
public ForgeConfigSpec.EnumValue<DistanceCalculatorType> lodDistanceCalculatorType;
WorldGenerator(ForgeConfigSpec.Builder builder)
{
builder.comment("These settings control how LODs outside your normal view range are generated.").push(this.getClass().getSimpleName());
maxGenerationDetail = builder
.comment("\n\n"
+ " What is the maximum detail level that LODs should be generated at? \n"
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
.defineEnum("lodGenerationQuality", LodDetail.HALF);
lodDistanceCalculatorType = builder
.comment("\n\n"
+ " " + DistanceCalculatorType.LINEAR + " \n"
+ " with LINEAR calculator the quality of block decrease \n"
+ " linearly to the distance of the player \n"
+ "\n"
+ " " + DistanceCalculatorType.QUADRATIC + " \n"
+ " with LINEAR calculator the quality of block decrease \n"
+ " quadratically to the distance of the player \n"
+ "\n"
+ " " + DistanceCalculatorType.RENDER_DEPENDANT + " \n"
+ " with LINEAR calculator the quality of block decrease \n"
+ " quadratically to the distance of the player \n")
.defineEnum("lodDistanceComputation", DistanceCalculatorType.LINEAR);
distanceGenerationMode = builder
.comment("\n\n"
+ " Note: The times listed here are the amount of time it took \n"
+ " the developer's PC to generate 1 chunk, \n"
+ " and are included so you can compare the \n"
+ " different generation options. Your mileage may vary. \n"
+ "\n"
+ " " + DistanceGenerationMode.NONE.toString() + " \n"
+ " Don't run the distance generator. \n"
+ " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n"
+ " Only generate the biomes and use biome \n"
+ " grass/foliage color, water color, or snow color \n"
+ " to generate the color. \n"
+ " Doesn't generate height, everything is shown at sea level. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n"
+ " Same as BIOME_ONLY, except instead \n"
+ " of always using sea level as the LOD height \n"
+ " different biome types (mountain, ocean, forest, etc.) \n"
+ " use predetermined heights to simulate having height data. \n"
+ " Multithreaded - Fastest (2-5 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.SURFACE.toString() + " \n"
+ " Generate the world surface, \n"
+ " this does NOT include caves, trees, \n"
+ " or structures. \n"
+ " Multithreaded - Faster (10-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.FEATURES.toString() + " \n"
+ " Generate everything except structures. \n"
+ " WARNING: This may cause world generation bugs or instability! \n"
+ " Multithreaded - Fast (15-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.SERVER.toString() + " \n"
+ " Ask the server to generate/load each chunk. \n"
+ " This is the most compatible, but causes server/simulation lag. \n"
+ " This will also show player made structures if you \n"
+ " are adding the mod to a pre-existing world. \n"
+ " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n")
.defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE);
allowUnstableFeatureGeneration = builder
.comment("\n\n"
+ " When using the " + DistanceGenerationMode.FEATURES.toString() + " generation mode \n"
+ " some features may not be thread safe, which could \n"
+ " cause instability and crashes. \n"
+ " By default (false) those features are skipped, \n"
+ " improving stability, but decreasing how many features are \n"
+ " actually generated. \n"
+ " (for example: some tree generation is unstable, \n"
+ " so some trees may not be generated.) \n"
+ " By setting this to true, all features will be generated, \n"
+ " but your game will be more unstable and crashes may occur. \n"
+ " \n"
+ " I would love to remove this option and always generate everything, \n"
+ " but I'm not sure how to do that. \n"
+ " If you are a Java wizard, check out the git issue here: \n"
+ " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n")
.define("allowUnstableFeatureGeneration", false);
builder.pop();
}
}
public static class Threading
{
public ForgeConfigSpec.IntValue numberOfWorldGenerationThreads;
public ForgeConfigSpec.IntValue numberOfBufferBuilderThreads;
Threading(ForgeConfigSpec.Builder builder)
{
builder.comment("These settings control how many CPU threads the mod uses for different tasks.").push(this.getClass().getSimpleName());
numberOfWorldGenerationThreads = builder
.comment("\n\n"
+ " This is how many threads are used when generating LODs outside \n"
+ " the normal render distance. \n"
+ " If you experience stuttering when generating distant LODs, decrease \n"
+ " this number. If you want to increase LOD generation speed, \n"
+ " increase this number. \n"
+ " \n"
+ " The maximum value is the number of processors on your CPU. \n"
+ " Requires a restart to take effect. \n")
.defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors() / 2, 1, Runtime.getRuntime().availableProcessors());
numberOfBufferBuilderThreads = builder
.comment("\n\n"
+ " This is how many threads are used when building vertex buffers \n"
+ " (The things sent to your GPU to draw the LODs). \n"
+ " If you experience high CPU useage when NOT generating distant \n"
+ " LODs, lower this number. \n"
+ " \n"
+ " The maximum value is the number of processors on your CPU. \n"
+ " Requires a restart to take effect. \n")
.defineInRange("numberOfBufferBuilderThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors());
builder.pop();
}
}
public static class Debugging
{
public ForgeConfigSpec.EnumValue<DebugMode> debugMode;
public ForgeConfigSpec.BooleanValue enableDebugKeybinding;
Debugging(ForgeConfigSpec.Builder builder)
{
builder.comment("These settings can be used by to look for bugs, or see how certain parts of the mod are working.").push(this.getClass().getSimpleName());
debugMode = builder
.comment("\n\n"
+ " " + DebugMode.OFF.toString() + ": LODs will draw with their normal colors. \n"
+ " " + DebugMode.SHOW_DETAIL.toString() + ": LOD colors will be based on their detail. \n"
+ " " + DebugMode.SHOW_DETAIL_WIREFRAME.toString() + ": LOD colors will be based on their detail, drawn with wireframe. \n")
.defineEnum("debugMode", DebugMode.OFF);
enableDebugKeybinding = builder
.comment("\n\n"
+ " If true the F4 key can be used to cycle through the different debug modes. \n")
.define("enableDebugKeybinding", false);
builder.pop();
}
}
public static class Buffers
{
public ForgeConfigSpec.IntValue bufferRebuildPlayerMoveTimeout;
public ForgeConfigSpec.IntValue bufferRebuildChunkChangeTimeout;
public ForgeConfigSpec.IntValue bufferRebuildLodChangeTimeout;
Buffers(ForgeConfigSpec.Builder builder)
{
builder.comment("These settings affect when Vertex Buffers are built.").push(this.getClass().getSimpleName());
bufferRebuildPlayerMoveTimeout = builder
.comment("\n\n"
+ " How long in milliseconds should we wait to \n"
+ " rebuild the vertex buffers when the player moves \n"
+ " a chunk or more? \n")
.defineInRange("bufferRebuildPlayerMoveTimeout", 2000, 1, 60000);
bufferRebuildChunkChangeTimeout = builder
.comment("\n\n"
+ " How long in milliseconds should we wait to \n"
+ " rebuild the vertex buffers when the vanilla rendered \n"
+ " chunks change? \n")
.defineInRange("bufferRebuildChunkChangeTimeout", 1000, 1, 60000);
bufferRebuildLodChangeTimeout = builder
.comment("\n\n"
+ " How long in milliseconds should we wait to \n"
+ " rebuild the vertex buffers when the LOD regions change? \n")
.defineInRange("bufferRebuildLodChangeTimeout", 5000, 1, 60000);
builder.pop();
}
}
/**
* {@link Path} to the configuration file of this mod
*/
private static final Path CONFIG_PATH = Paths.get("config", ModInfo.MODID + ".toml");
public static final ForgeConfigSpec CLIENT_SPEC;
public static final Client CLIENT;
static
{
final Pair<Client, ForgeConfigSpec> specPair = new ForgeConfigSpec.Builder().configure(Client::new);
CLIENT_SPEC = specPair.getRight();
CLIENT = specPair.getLeft();
CommentedFileConfig clientConfig = CommentedFileConfig.builder(CONFIG_PATH)
.writingMode(WritingMode.REPLACE)
.build();
clientConfig.load();
clientConfig.save();
CLIENT_SPEC.setConfig(clientConfig);
}
@SubscribeEvent
public static void onLoad(final ModConfig.Loading configEvent)
{
LogManager.getLogger().debug(ModInfo.MODNAME, "Loaded forge config file {}", configEvent.getConfig().getFileName());
}
@SubscribeEvent
public static void onFileChange(final ModConfig.Reloading configEvent)
{
LogManager.getLogger().debug(ModInfo.MODNAME, "Forge config just got changed on the file system!");
}
}
@@ -1,51 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.enums;
/**
* TOP, NORTH, SOUTH, EAST, WEST, BOTTOM
*
* @author James Seibel
* @version 10-17-2020
*/
public enum ColorDirection
{
// used for colors
/** +Y */
TOP(0),
/** -Z */
NORTH(1),
/** +Z */
SOUTH(2),
/** +X */
EAST(3),
/** -X */
WEST(4),
/** -Y */
BOTTOM(5);
public final int value;
private ColorDirection(int newValue)
{
value = newValue;
}
}
@@ -1,50 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.enums;
/**
* off, detail, detail wireframe
*
* @author James Seibel
* @version 8-28-2021
*/
public enum DebugMode
{
OFF,
/** LOD colors are based on their detail */
SHOW_DETAIL,
/** LOD colors are based on their detail, and draws in wireframe. */
SHOW_DETAIL_WIREFRAME;
/** used when cycling through the different modes */
private DebugMode next;
static
{
OFF.next = SHOW_DETAIL;
SHOW_DETAIL.next = SHOW_DETAIL_WIREFRAME;
SHOW_DETAIL_WIREFRAME.next = OFF;
}
/** returns the next debug mode */
public DebugMode getNext()
{
return this.next;
}
}
@@ -1,23 +0,0 @@
package com.seibel.lod.enums;
/**
* @author Leonardo Amato
* @version 22-08-2021
*/
public enum DistanceCalculatorType
{
/**
* different Lod detail render and generate linearly to the distance
*/
LINEAR,
/**
* different Lod detail render and generate quadratically to the distance
*/
QUADRATIC,
/**
* we calculate the distance based on game render distance and mod render distance
*/
RENDER_DEPENDANT;
}
@@ -1,93 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.enums;
/**
* NONE <br>
* BIOME_ONLY <br>
* BIOME_ONLY_SIMULATE_HEIGHT <br>
* SURFACE <br>
* FEATURES <br>
* SERVER <br><br>
* <p>
* In order of fastest to slowest.
*
* @author James Seibel
* @author Leonardo Amato
* @version 8-7-2021
*/
public enum DistanceGenerationMode
{
/**
* Don't generate anything
*/
NONE((byte) 0),
/**
* Only generate the biomes and use biome
* grass/foliage color, water color, or ice color
* to generate the color.
* Doesn't generate height, everything is shown at sea level.
* Multithreaded - Fastest (2-5 ms)
*/
BIOME_ONLY((byte) 1),
/**
* Same as BIOME_ONLY, except instead
* of always using sea level as the LOD height
* different biome types (mountain, ocean, forest, etc.)
* use predetermined heights to simulate having height data.
*/
BIOME_ONLY_SIMULATE_HEIGHT((byte) 2),
/**
* Generate the world surface,
* this does NOT include caves, trees,
* or structures.
* Multithreaded - Faster (10-20 ms)
*/
SURFACE((byte) 3),
/**
* Generate everything except structures.
* NOTE: This may cause world generation bugs or instability,
* since some features cause concurrentModification exceptions.
* Multithreaded - Fast (15-20 ms)
*/
FEATURES((byte) 4),
/**
* Ask the server to generate/load each chunk.
* This is the most compatible, but causes server/simulation lag.
* This will also show player made structures if you
* are adding the mod to a pre-existing world.
* Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms)
*/
SERVER((byte) 5);
/**
* The higher the number the more complete the generation is.
*/
public final byte complexity;
DistanceGenerationMode(byte complexity)
{
this.complexity = complexity;
}
}
@@ -1,36 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.enums;
/**
* NEAR, FAR, or NEAR_AND_FAR.
*
* @author James Seibel
* @version 02-14-2021
*/
public enum FogDistance
{
/** good for fast or fancy fog qualities. */
NEAR,
/** good for fast or fancy fog qualities. */
FAR,
/** only looks good if the fog quality is set to Fancy. */
NEAR_AND_FAR;
}
@@ -1,43 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.enums;
/**
* USE_OPTIFINE_FOG_SETTING, <br>
* NEVER_DRAW_FOG, <br>
* ALWAYS_DRAW_FOG_FAST, <br>
* ALWAYS_DRAW_FOG_FANCY <br>
*
* @author James Seibel
* @version 7-03-2021
*/
public enum FogDrawOverride
{
/** Use whatever Fog setting optifine is using.
* If optifine isn't installed this defaults to ALWAYS_DRAW_FOG. */
USE_OPTIFINE_FOG_SETTING,
/** Never draw fog on the LODs */
NEVER_DRAW_FOG,
/** Always draw fog on the LODs */
ALWAYS_DRAW_FOG_FAST,
/** Always draw fog on the LODs */
ALWAYS_DRAW_FOG_FANCY;
}
@@ -1,43 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.enums;
/**
* NE, SE, SW, NW
*
* @author James Seibel
* @version 1-20-2020
*/
public enum LodCorner
{
/** -Z, +X */
NE(0),
/** +Z, +X */
SE(1),
/** +Z, -X */
SW(2),
/** -Z, -X */
NW(3);
public final int value;
private LodCorner(int newValue)
{
value = newValue;
}
}
@@ -1,159 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.enums;
import java.util.ArrayList;
import java.util.Collections;
import com.seibel.lod.util.LodUtil;
/**
* single, double, quad, half, full
*
* @author James Seibel
* @version 8-11-2021
*/
public enum LodDetail
{
/** render 1 LOD for each chunk */
SINGLE(1, 4),
/** render 4 LODs for each chunk */
DOUBLE(2, 3),
/** render 16 LODs for each chunk */
QUAD(4, 2),
/** render 64 LODs for each chunk */
HALF(8, 1),
/** render 256 LODs for each chunk */
FULL(16, 0);
/** How many DataPoints should
* be drawn per side per LodChunk */
public final int dataPointLengthCount;
/** How wide each LOD DataPoint is */
public final int dataPointWidth;
/** This is the same as detailLevel in LodQuadTreeNode,
* lowest is 0 highest is 9 */
public final byte detailLevel;
/* Start/End X/Z give the block positions
* for each individual dataPoint in a LodChunk */
public final int[] startX;
public final int[] startZ;
public final int[] endX;
public final int[] endZ;
/**
* 1st dimension: LodDetail.detailLevel <br>
* 2nd dimension: An array of all LodDetails that are less than or <br>
* equal to that detailLevel
*/
private static LodDetail[][] lowerDetailArrays;
private LodDetail(int newLengthCount, int newDetailLevel)
{
detailLevel = (byte) newDetailLevel;
dataPointLengthCount = newLengthCount;
dataPointWidth = 16 / dataPointLengthCount;
startX = new int[dataPointLengthCount * dataPointLengthCount];
endX = new int[dataPointLengthCount * dataPointLengthCount];
startZ = new int[dataPointLengthCount * dataPointLengthCount];
endZ = new int[dataPointLengthCount * dataPointLengthCount];
int index = 0;
for(int x = 0; x < newLengthCount; x++)
{
for(int z = 0; z < newLengthCount; z++)
{
startX[index] = x * dataPointWidth;
startZ[index] = z * dataPointWidth;
endX[index] = (x*dataPointWidth) + dataPointWidth;
endZ[index] = (z*dataPointWidth) + dataPointWidth;
index++;
}
}
}// constructor
/**
* Returns an array of all LodDetails that have a detail level
* that is less than or equal to the given LodDetail
*/
public static LodDetail[] getSelfAndLowerDetails(LodDetail detail)
{
if (lowerDetailArrays == null)
{
// run first time setup
lowerDetailArrays = new LodDetail[LodDetail.values().length][];
// go through each LodDetail
for(LodDetail currentDetail : LodDetail.values())
{
ArrayList<LodDetail> lowerDetails = new ArrayList<>();
// find the details lower than currentDetail
for(LodDetail compareDetail : LodDetail.values())
{
if (currentDetail.detailLevel <= compareDetail.detailLevel)
{
lowerDetails.add(compareDetail);
}
}
// have the highest detail item first in the list
Collections.sort(lowerDetails);
Collections.reverse(lowerDetails);
lowerDetailArrays[currentDetail.detailLevel] = lowerDetails.toArray(new LodDetail[lowerDetails.size()]);
}
}
return lowerDetailArrays[detail.detailLevel];
}
/** Returns what detail level should be used at a given distance and maxDistance. */
public static LodDetail getDetailForDistance(LodDetail maxDetailLevel, int distance, int maxDistance)
{
LodDetail[] lowerDetails = getSelfAndLowerDetails(maxDetailLevel);
int distaneBetweenDetails = maxDistance / lowerDetails.length;
int index = LodUtil.clamp(0, distance / distaneBetweenDetails, lowerDetails.length - 1);
return lowerDetails[index];
}
}
@@ -1,50 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.enums;
/**
* NONE <br>
* BIOME_ONLY <br>
* BIOME_ONLY_SIMULATE_HEIGHT <br>
* SURFACE <br>
* FEATURES <br>
* SERVER <br><br>
*
* In order of fastest to slowest.
*
* @author James Seibel
* @author Leonardo Amato
* @version 8-7-2021
*/
public enum LodRenderDistance
{
SHORT(32),
MEDIUM(64),
FAR(128);
/** The higher the number the more complete the generation is. */
public final int renderDistance;
LodRenderDistance(int complexity)
{
this.renderDistance = complexity;
}
}
@@ -1,61 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.enums;
import com.seibel.lod.builders.lodTemplates.AbstractLodTemplate;
import com.seibel.lod.builders.lodTemplates.CubicLodTemplate;
import com.seibel.lod.builders.lodTemplates.DynamicLodTemplate;
import com.seibel.lod.builders.lodTemplates.TriangularLodTemplate;
/**
* Cubic, Triangular, Dynamic
*
* @author James Seibel
* @version 8-4-2021
*/
public enum LodTemplate
{
// used for position
/** Chunks are rendered as
* rectangular prisms. */
CUBIC(new CubicLodTemplate()),
/** Chunks smoothly transition between
* each other. */
TRIANGULAR(new TriangularLodTemplate()),
/** Chunks smoothly transition between
* each other, unless a neighboring chunk
* is at a significantly different height. */
DYNAMIC(new DynamicLodTemplate());
public final AbstractLodTemplate template;
private LodTemplate(AbstractLodTemplate newTemplate)
{
template = newTemplate;
}
public int getBufferMemoryForSingleLod()
{
return template.getBufferMemoryForSingleNode();
}
}
@@ -1,86 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.enums;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
/**
* NE, SE, SW, NW <br>
* NORTH, SOUTH, EAST, WEST, <br>
* CENTER
*
* @author James Seibel
* @version 05-30-2021
*/
public enum RelativeChunkPos
{
/** +X, -Z */
NE(0, 1,-1),
/** +X, +Z */
SE(1, 1,1),
/** -X, +Z */
SW(2, -1,1),
/** -X, -Z */
NW(3, -1,-1),
/** -Z */
NORTH(4, 0,-1),
/** +Z */
SOUTH(5, 0,1),
/** +X */
EAST(6, 1,0),
/** -X */
WEST(7, -1,0),
CENTER(8, 0,0);
/** index used when referencing objects in an array */
public final int index;
/** position relative to X CENTER */
public final int x;
/** position relative to Z CENTER */
public final int z;
/** NORTH, SOUTH, EAST, WEST */
public static EnumSet<RelativeChunkPos> ADJACENT = EnumSet.of(NORTH, SOUTH, EAST, WEST);
/** NE, NW, SE, SW */
public static EnumSet<RelativeChunkPos> DIAGONAL = EnumSet.of(NE, NW, SE, SW);
public static EnumSet<RelativeChunkPos> NW_CORNER = EnumSet.of(WEST, NW, NORTH);
public static EnumSet<RelativeChunkPos> NE_CORNER = EnumSet.of(EAST, NE, NORTH);
public static EnumSet<RelativeChunkPos> SW_CORNER = EnumSet.of(WEST, SW, SOUTH);
public static EnumSet<RelativeChunkPos> SE_CORNER = EnumSet.of(EAST, SE, SOUTH);
/** NW_CORNER, NE_CORNER, SW_CORNER, SE_CORNER <br>
* Contains the 3 points surrounding a corner. */
public static ArrayList<EnumSet<RelativeChunkPos>> CORNERS = new ArrayList<EnumSet<RelativeChunkPos>>( Arrays.asList(NW_CORNER, NE_CORNER, SW_CORNER, SE_CORNER) );
private RelativeChunkPos(int newIndex, int newX, int newZ)
{
index = newIndex;
x = newX;
z = newZ;
}
}
@@ -1,18 +0,0 @@
package com.seibel.lod.enums;
/**
* NONE, DARKEN_SIDES
*
* @author James Seibel
* @version 7-25-2020
*/
public enum ShadingMode
{
/** LODs will have the same lighting on every side.
Fastest, but can make large similarly colored areas hard to differentiate */
NONE,
/** LODs will have darker sides and bottoms to simulate top down lighting.
Fastest */
DARKEN_SIDES;
}
@@ -0,0 +1,105 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge;
import com.seibel.lod.core.api.EventApi;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.forge.wrappers.chunk.ChunkWrapper;
import com.seibel.lod.forge.wrappers.world.DimensionTypeWrapper;
import com.seibel.lod.forge.wrappers.world.WorldWrapper;
import net.minecraftforge.client.event.InputEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
/**
* This handles all events sent to the client,
* and is the starting point for most of the mod.
*
* @author James_Seibel
* @version 11-12-2021
*/
public class ForgeClientProxy
{
private final EventApi eventApi = EventApi.INSTANCE;
@SubscribeEvent
public void serverTickEvent(TickEvent.ServerTickEvent event)
{
eventApi.serverTickEvent();
}
@SubscribeEvent
public void chunkLoadEvent(ChunkEvent.Load event)
{
eventApi.chunkLoadEvent(new ChunkWrapper(event.getChunk()), DimensionTypeWrapper.getDimensionTypeWrapper(event.getWorld().dimensionType()));
}
@SubscribeEvent
public void worldSaveEvent(WorldEvent.Save event)
{
eventApi.worldSaveEvent();
}
/** This is also called when a new dimension loads */
@SubscribeEvent
public void worldLoadEvent(WorldEvent.Load event)
{
eventApi.worldLoadEvent(WorldWrapper.getWorldWrapper(event.getWorld()));
}
@SubscribeEvent
public void worldUnloadEvent(WorldEvent.Unload event)
{
eventApi.worldUnloadEvent();
}
@SubscribeEvent
public void blockChangeEvent(BlockEvent event)
{
// we only care about certain block events
if (event.getClass() == BlockEvent.BreakEvent.class ||
event.getClass() == BlockEvent.EntityPlaceEvent.class ||
event.getClass() == BlockEvent.EntityMultiPlaceEvent.class ||
event.getClass() == BlockEvent.FluidPlaceBlockEvent.class ||
event.getClass() == BlockEvent.PortalSpawnEvent.class)
{
IChunkWrapper chunk = new ChunkWrapper(event.getWorld().getChunk(event.getPos()));
DimensionTypeWrapper dimType = DimensionTypeWrapper.getDimensionTypeWrapper(event.getWorld().dimensionType());
// recreate the LOD where the blocks were changed
eventApi.blockChangeEvent(chunk, dimType);
}
}
@SubscribeEvent
public void onKeyInput(InputEvent.KeyInputEvent event)
{
eventApi.onKeyInput(event.getKey(), event.getAction());
}
}
@@ -0,0 +1,444 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.electronwill.nightconfig.core.io.WritingMode;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.enums.config.BlocksToAvoid;
import com.seibel.lod.core.enums.config.BufferRebuildTimes;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.enums.config.GenerationPriority;
import com.seibel.lod.core.enums.config.GpuUploadMethod;
import com.seibel.lod.core.enums.config.HorizontalQuality;
import com.seibel.lod.core.enums.config.HorizontalResolution;
import com.seibel.lod.core.enums.config.HorizontalScale;
import com.seibel.lod.core.enums.config.LodTemplate;
import com.seibel.lod.core.enums.config.VanillaOverdraw;
import com.seibel.lod.core.enums.config.VerticalQuality;
import com.seibel.lod.core.enums.rendering.DebugMode;
import com.seibel.lod.core.enums.rendering.FogColorMode;
import com.seibel.lod.core.enums.rendering.FogDistance;
import com.seibel.lod.core.enums.rendering.FogDrawMode;
import com.seibel.lod.core.objects.MinDefaultMax;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced.IBuffers;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced.IDebugging;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced.IThreading;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.IAdvancedGraphics;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.IFogQuality;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.IQuality;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IWorldGenerator;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
/**
* This handles any configuration the user has access to.
* @author Leonardo Amato
* @author James Seibel
* @version 11-16-2021
*/
@Mod.EventBusSubscriber
public class ForgeConfig
{
// CONFIG STRUCTURE
// -> Client
// |
// |-> Graphics
// | |-> QualityOption
// | |-> FogQualityOption
// | |-> AdvancedGraphicsOption
// |
// |-> World Generation
// |
// |-> Advanced Mod Option
// |-> Threads
// |-> Buffers
// |-> Debugging
public static class Client
{
public final Graphics graphics;
public final WorldGenerator worldGenerator;
public final AdvancedModOptions advancedModOptions;
//================//
// Client Configs //
//================//
public Client(ForgeConfigSpec.Builder builder)
{
builder.push(this.getClass().getSimpleName());
{
graphics = new Graphics(builder);
worldGenerator = new WorldGenerator(builder);
advancedModOptions = new AdvancedModOptions(builder);
}
builder.pop();
}
//==================//
// Graphics Configs //
//==================//
public static class Graphics
{
public final QualityOption qualityOption;
public final FogQualityOption fogQuality;
public final AdvancedGraphicsOption advancedGraphicsOption;
Graphics(ForgeConfigSpec.Builder builder)
{
builder.comment(IGraphics.DESC).push("Graphics");
{
qualityOption = new QualityOption(builder);
advancedGraphicsOption = new AdvancedGraphicsOption(builder);
fogQuality = new FogQualityOption(builder);
}
builder.pop();
}
public static class QualityOption
{
public final ForgeConfigSpec.EnumValue<HorizontalResolution> drawResolution;
public final ForgeConfigSpec.IntValue lodChunkRenderDistance;
public final ForgeConfigSpec.EnumValue<VerticalQuality> verticalQuality;
public final ForgeConfigSpec.EnumValue<HorizontalScale> horizontalScale;
public final ForgeConfigSpec.EnumValue<HorizontalQuality> horizontalQuality;
QualityOption(ForgeConfigSpec.Builder builder)
{
builder.comment(IQuality.DESC).push(this.getClass().getSimpleName());
verticalQuality = builder
.comment("\n\n"
+ IQuality.VERTICAL_QUALITY_DESC)
.defineEnum("Vertical Quality", IQuality.VERTICAL_QUALITY_DEFAULT);
horizontalScale = builder
.comment("\n\n"
+ IQuality.HORIZONTAL_SCALE_DESC)
.defineEnum("Horizontal Scale", IQuality.HORIZONTAL_SCALE_DEFAULT);
horizontalQuality = builder
.comment("\n\n"
+ IQuality.HORIZONTAL_QUALITY_DESC)
.defineEnum("Horizontal Quality", IQuality.HORIZONTAL_QUALITY_DEFAULT);
drawResolution = builder
.comment("\n\n"
+ IQuality.DRAW_RESOLUTION_DESC)
.defineEnum("Block size", IQuality.DRAW_RESOLUTION_DEFAULT);
MinDefaultMax<Integer> minDefaultMax = IQuality.LOD_CHUNK_RENDER_DISTANCE_MIN_DEFAULT_MAX;
lodChunkRenderDistance = builder
.comment("\n\n"
+ IQuality.LOD_CHUNK_RENDER_DISTANCE_DESC)
.defineInRange("Lod Render Distance", minDefaultMax.defaultValue, minDefaultMax.minValue, minDefaultMax.maxValue);
builder.pop();
}
}
public static class FogQualityOption
{
public final ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
public final ForgeConfigSpec.EnumValue<FogDrawMode> fogDrawMode;
public final ForgeConfigSpec.EnumValue<FogColorMode> fogColorMode;
public final ForgeConfigSpec.BooleanValue disableVanillaFog;
FogQualityOption(ForgeConfigSpec.Builder builder)
{
builder.comment(IFogQuality.DESC).push(this.getClass().getSimpleName());
fogDistance = builder
.comment("\n\n"
+ IFogQuality.FOG_DISTANCE_DESC)
.defineEnum("Fog Distance", IFogQuality.FOG_DISTANCE_DEFAULT);
fogDrawMode = builder
.comment("\n\n"
+ IFogQuality.FOG_DRAW_MODE_DESC)
.defineEnum("Fog Draw Mode", IFogQuality.FOG_DRAW_MODE_DEFAULT);
fogColorMode = builder
.comment("\n\n"
+ IFogQuality.FOG_COLOR_MODE_DESC)
.defineEnum("Fog Color Mode", IFogQuality.FOG_COLOR_MODE_DEFAULT);
disableVanillaFog = builder
.comment("\n\n"
+ IFogQuality.DISABLE_VANILLA_FOG_DESC)
.define("Experimental Disable Vanilla Fog", IFogQuality.DISABLE_VANILLA_FOG_DEFAULT);
builder.pop();
}
}
public static class AdvancedGraphicsOption
{
public final ForgeConfigSpec.EnumValue<LodTemplate> lodTemplate;
public final ForgeConfigSpec.BooleanValue disableDirectionalCulling;
public final ForgeConfigSpec.BooleanValue alwaysDrawAtMaxQuality;
public final ForgeConfigSpec.EnumValue<VanillaOverdraw> vanillaOverdraw;
public final ForgeConfigSpec.EnumValue<GpuUploadMethod> gpuUploadMethod;
public final ForgeConfigSpec.BooleanValue useExtendedNearClipPlane;
AdvancedGraphicsOption(ForgeConfigSpec.Builder builder)
{
builder.comment(IAdvancedGraphics.DESC).push(this.getClass().getSimpleName());
lodTemplate = builder
.comment("\n\n"
+ IAdvancedGraphics.LOD_TEMPLATE_DESC)
.defineEnum("LOD Template", IAdvancedGraphics.LOD_TEMPLATE_DEFAULT);
disableDirectionalCulling = builder
.comment("\n\n"
+ IAdvancedGraphics.DISABLE_DIRECTIONAL_CULLING_DESC)
.define("Disable Directional Culling", IAdvancedGraphics.DISABLE_DIRECTIONAL_CULLING_DEFAULT);
alwaysDrawAtMaxQuality = builder
.comment("\n\n"
+ IAdvancedGraphics.ALWAYS_DRAW_AT_MAD_QUALITY_DESC)
.define("Always Use Max Quality", IAdvancedGraphics.ALWAYS_DRAW_AT_MAD_QUALITY_DEFAULT);
vanillaOverdraw = builder
.comment("\n\n"
+ IAdvancedGraphics.VANILLA_OVERDRAW_DESC)
.defineEnum("Vanilla Overdraw", IAdvancedGraphics.VANILLA_OVERDRAW_DEFAULT);
gpuUploadMethod = builder
.comment("\n\n"
+ IAdvancedGraphics.GPU_UPLOAD_METHOD_DESC)
.defineEnum("GPU Upload Method", IAdvancedGraphics.GPU_UPLOAD_METHOD_DEFAULT);
// This is a temporary fix (like vanilla overdraw)
// hopefully we can remove both once we get individual chunk rendering figured out
useExtendedNearClipPlane = builder
.comment("\n\n"
+ IAdvancedGraphics.USE_EXTENDED_NEAR_CLIP_PLANE_DESC)
.define("Use Extended Near Clip Plane", IAdvancedGraphics.USE_EXTENDED_NEAR_CLIP_PLANE_DEFAULT);
builder.pop();
}
}
}
//========================//
// WorldGenerator Configs //
//========================//
public static class WorldGenerator
{
public final ForgeConfigSpec.EnumValue<GenerationPriority> generationPriority;
public final ForgeConfigSpec.EnumValue<DistanceGenerationMode> distanceGenerationMode;
public final ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration;
public final ForgeConfigSpec.EnumValue<BlocksToAvoid> blocksToAvoid;
//public final ForgeConfigSpec.BooleanValue useExperimentalPreGenLoading;
WorldGenerator(ForgeConfigSpec.Builder builder)
{
builder.comment(IWorldGenerator.DESC).push("Generation");
generationPriority = builder
.comment("\n\n"
+ IWorldGenerator.GENERATION_PRIORITY_DESC)
.defineEnum("Generation Priority", IWorldGenerator.GENERATION_PRIORITY_DEFAULT);
distanceGenerationMode = builder
.comment("\n\n"
+ IWorldGenerator.DISTANCE_GENERATION_MODE_DESC)
.defineEnum("Distance Generation Mode", IWorldGenerator.DISTANCE_GENERATION_MODE_DEFAULT);
allowUnstableFeatureGeneration = builder
.comment("\n\n"
+ IWorldGenerator.ALLOW_UNSTABLE_FEATURE_GENERATION_DESC)
.define("Allow Unstable Feature Generation", IWorldGenerator.ALLOW_UNSTABLE_FEATURE_GENERATION_DEFAULT);
blocksToAvoid = builder
.comment("\n\n"
+ IWorldGenerator.BLOCKS_TO_AVOID_DESC)
.defineEnum("Blocks to avoid", IWorldGenerator.BLOCKS_TO_AVOID_DEFAULT);
/*useExperimentalPreGenLoading = builder
.comment("\n\n"
+ " if a chunk has been pre-generated, then the mod would use the real chunk for the \n"
+ "fake chunk creation. May require a deletion of the lod file to see the result. \n")
.define("Use pre-generated chunks", false);*/
builder.pop();
}
}
//============================//
// AdvancedModOptions Configs //
//============================//
public static class AdvancedModOptions
{
public final Threading threading;
public final Debugging debugging;
public final Buffers buffers;
public AdvancedModOptions(ForgeConfigSpec.Builder builder)
{
builder.comment(IAdvanced.DESC).push(this.getClass().getSimpleName());
{
threading = new Threading(builder);
debugging = new Debugging(builder);
buffers = new Buffers(builder);
}
builder.pop();
}
public static class Threading
{
public final ForgeConfigSpec.IntValue numberOfWorldGenerationThreads;
public final ForgeConfigSpec.IntValue numberOfBufferBuilderThreads;
Threading(ForgeConfigSpec.Builder builder)
{
builder.comment(IThreading.DESC).push(this.getClass().getSimpleName());
MinDefaultMax<Integer> minDefaultMax = IThreading.NUMBER_OF_WORLD_GENERATION_THREADS_DEFAULT;
numberOfWorldGenerationThreads = builder
.comment("\n\n"
+ IThreading.NUMBER_OF_WORLD_GENERATION_THREADS_DESC)
.defineInRange("numberOfWorldGenerationThreads", minDefaultMax.defaultValue, minDefaultMax.minValue, minDefaultMax.maxValue);
minDefaultMax = IThreading.NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX;
numberOfBufferBuilderThreads = builder
.comment("\n\n"
+ IThreading.NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX)
.defineInRange("numberOfBufferBuilderThreads", minDefaultMax.defaultValue, minDefaultMax.minValue, minDefaultMax.maxValue);
builder.pop();
}
}
//===============//
// Debug Options //
//===============//
public static class Debugging
{
public final ForgeConfigSpec.BooleanValue drawLods;
public final ForgeConfigSpec.EnumValue<DebugMode> debugMode;
public final ForgeConfigSpec.BooleanValue enableDebugKeybindings;
Debugging(ForgeConfigSpec.Builder builder)
{
builder.comment(IDebugging.DESC).push(this.getClass().getSimpleName());
drawLods = builder
.comment("\n\n"
+ IDebugging.DRAW_LODS_DESC)
.define("Enable Rendering", IDebugging.DRAW_LODS_DEFAULT);
debugMode = builder
.comment("\n\n"
+ IDebugging.DEBUG_MODE_DESC)
.defineEnum("Debug Mode", IDebugging.DEBUG_MODE_DEFAULT);
enableDebugKeybindings = builder
.comment("\n\n"
+ IDebugging.DEBUG_KEYBINDINGS_ENABLED_DESC)
.define("Enable Debug Keybinding", IDebugging.DEBUG_KEYBINDINGS_ENABLED_DEFAULT);
builder.pop();
}
}
public static class Buffers
{
public final ForgeConfigSpec.EnumValue<BufferRebuildTimes> rebuildTimes;
Buffers(ForgeConfigSpec.Builder builder)
{
builder.comment(IBuffers.DESC).push(this.getClass().getSimpleName());
rebuildTimes = builder
.comment("\n\n"
+ IBuffers.REBUILD_TIMES_DESC)
.defineEnum("rebuildFrequency", IBuffers.REBUILD_TIMES_DEFAULT);
builder.pop();
}
}
}
}
/** {@link Path} to the configuration file of this mod */
private static final Path CONFIG_PATH = Paths.get("config", ModInfo.NAME + ".toml");
public static final ForgeConfigSpec CLIENT_SPEC;
public static final Client CLIENT;
static
{
final Pair<Client, ForgeConfigSpec> specPair = new ForgeConfigSpec.Builder().configure(Client::new);
CLIENT_SPEC = specPair.getRight();
CLIENT = specPair.getLeft();
CommentedFileConfig clientConfig = CommentedFileConfig.builder(CONFIG_PATH)
.writingMode(WritingMode.REPLACE)
.build();
clientConfig.load();
clientConfig.save();
CLIENT_SPEC.setConfig(clientConfig);
}
@SubscribeEvent
public static void onLoad(final ModConfig.Loading configEvent)
{
LogManager.getLogger().debug(ModInfo.NAME, "Loaded forge config file {}", configEvent.getConfig().getFileName());
}
@SubscribeEvent
public static void onFileChange(final ModConfig.Reloading configEvent)
{
LogManager.getLogger().debug(ModInfo.NAME, "Forge config just got changed on the file system!");
}
}
@@ -1,5 +1,6 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
@@ -15,10 +16,11 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.proxy.ClientProxy;
package com.seibel.lod.forge;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.forge.wrappers.ForgeDependencySetup;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.SubscribeEvent;
@@ -31,50 +33,48 @@ import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
/**
* Initialize and setup the Mod.
* <br>
* Initialize and setup the Mod. <br>
* If you are looking for the real start of the mod
* check out the ClientProxy.
*
* @author James Seibel
* @version 7-3-2021
* @version 11-16-2021
*/
@Mod(ModInfo.MODID)
public class LodMain
@Mod(ModInfo.ID)
public class ForgeMain
{
public static LodMain instance;
public static ClientProxy client_proxy;
public static ForgeClientProxy forgeClientProxy;
private void init(final FMLCommonSetupEvent event)
{
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, LodConfig.CLIENT_SPEC);
// make sure the dependencies are set up before the mod needs them
ForgeDependencySetup.createInitialBindings();
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ForgeConfig.CLIENT_SPEC);
}
public LodMain()
{
// Register the methods
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::init);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientStart);
// Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this);
}
private void onClientStart(final FMLClientSetupEvent event)
{
client_proxy = new ClientProxy();
MinecraftForge.EVENT_BUS.register(client_proxy);
}
@SubscribeEvent
public void onServerStarting(FMLServerStartingEvent event)
{
// this is called when the server starts
}
public ForgeMain()
{
// Register the methods
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::init);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientStart);
// Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this);
}
private void onClientStart(final FMLClientSetupEvent event)
{
forgeClientProxy = new ForgeClientProxy();
MinecraftForge.EVENT_BUS.register(forgeClientProxy);
}
@SubscribeEvent
public void onServerStarting(FMLServerStartingEvent event)
{
// this is called when the server starts
}
}
@@ -1,5 +1,6 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
@@ -15,16 +16,19 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.mixin;
package com.seibel.lod.forge.mixins;
import org.lwjgl.opengl.GL15;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.seibel.lod.LodMain;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.objects.math.Mat4f;
import com.seibel.lod.forge.wrappers.McObjectConverter;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.WorldRenderer;
@@ -32,18 +36,19 @@ import net.minecraft.client.renderer.WorldRenderer;
/**
* This class is used to mix in my rendering code
* before Minecraft starts rendering blocks.
* If this wasn't done the LODs would render on top
* If this wasn't done, and we used Forge's
* render last event, the LODs would render on top
* of the normal terrain.
*
* @author James Seibel
* @version 05-29-2021
* @version 9-19-2021
*/
@Mixin(WorldRenderer.class)
public class MixinWorldRenderer
{
private static float previousPartialTicks = 0;
@Inject(at = @At("RETURN"), method = "renderSky(Lcom/mojang/blaze3d/matrix/MatrixStack;F)V", cancellable = false)
@Inject(at = @At("RETURN"), method = "renderSky(Lcom/mojang/blaze3d/matrix/MatrixStack;F)V")
private void renderSky(MatrixStack matrixStackIn, float partialTicks, CallbackInfo callback)
{
// get the partial ticks since renderBlockLayer doesn't
@@ -51,12 +56,24 @@ public class MixinWorldRenderer
previousPartialTicks = partialTicks;
}
@Inject(at = @At("HEAD"), method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/matrix/MatrixStack;DDD)V", cancellable = false)
@Inject(at = @At("HEAD"), method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/matrix/MatrixStack;DDD)V")
private void renderChunkLayer(RenderType renderType, MatrixStack matrixStackIn, double xIn, double yIn, double zIn, CallbackInfo callback)
{
// only render if LODs are enabled and
// only render before solid blocks
if (LodConfig.CLIENT.graphics.drawLODs.get() && renderType.equals(RenderType.solid()))
LodMain.client_proxy.renderLods(previousPartialTicks);
if (renderType.equals(RenderType.solid()))
{
// get MC's current projection matrix
float[] mcProjMatrixRaw = new float[16];
GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
Mat4f mcProjectionMatrix = new Mat4f(mcProjMatrixRaw);
// OpenGl outputs their matrices in col,row form instead of row,col
// (or maybe vice versa I have no idea :P)
mcProjectionMatrix.transpose();
Mat4f mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose());
ClientApi.INSTANCE.renderLods(mcModelViewMatrix, mcProjectionMatrix, previousPartialTicks);
}
}
}
@@ -0,0 +1,37 @@
package com.seibel.lod.forge.wrappers;
import com.seibel.lod.core.handlers.IReflectionHandler;
import com.seibel.lod.core.handlers.ReflectionHandler;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
import com.seibel.lod.forge.wrappers.block.BlockColorSingletonWrapper;
import com.seibel.lod.forge.wrappers.config.LodConfigWrapperSingleton;
import com.seibel.lod.forge.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.lod.forge.wrappers.minecraft.MinecraftWrapper;
/**
* Binds all necessary dependencies so we
* can access them in Core. <br>
* This needs to be called before any Core classes
* are loaded.
*
* @author James Seibel
* @version 11-20-2021
*/
public class ForgeDependencySetup
{
public static void createInitialBindings()
{
SingletonHandler.bind(ILodConfigWrapperSingleton.class, LodConfigWrapperSingleton.INSTANCE);
SingletonHandler.bind(IBlockColorSingletonWrapper.class, BlockColorSingletonWrapper.INSTANCE);
SingletonHandler.bind(IMinecraftWrapper.class, MinecraftWrapper.INSTANCE);
SingletonHandler.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
SingletonHandler.bind(IWrapperFactory.class, WrapperFactory.INSTANCE);
SingletonHandler.bind(IReflectionHandler.class, ReflectionHandler.createSingleton(MinecraftWrapper.INSTANCE.getOptions().getClass().getDeclaredFields(), MinecraftWrapper.INSTANCE.getOptions()));
}
}
@@ -0,0 +1,56 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge.wrappers;
import java.nio.FloatBuffer;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.objects.math.Mat4f;
import net.minecraft.util.Direction;
import net.minecraft.util.math.vector.Matrix4f;
/**
* This class converts to and from Minecraft objects (Ex: Matrix4f)
* and objects we created (Ex: Mat4f).
*
* @author James Seibel
* @version 11-20-2021
*/
public class McObjectConverter
{
/** 4x4 float matrix converter */
public static Mat4f Convert(Matrix4f mcMatrix)
{
FloatBuffer buffer = FloatBuffer.allocate(16);
mcMatrix.store(buffer);
Mat4f matrix = new Mat4f(buffer);
matrix.transpose();
return matrix;
}
public static Direction Convert(LodDirection lodDirection)
{
return Direction.byName(lodDirection.name());
}
}
@@ -0,0 +1,91 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge.wrappers;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import com.seibel.lod.forge.wrappers.chunk.ChunkPosWrapper;
import com.seibel.lod.forge.wrappers.worldGeneration.WorldGeneratorWrapper;
/**
* This handles creating abstract wrapper objects.
*
* @author James Seibel
* @version 11-20-2021
*/
public class WrapperFactory implements IWrapperFactory
{
public static final WrapperFactory INSTANCE = new WrapperFactory();
@Override
public AbstractBlockPosWrapper createBlockPos()
{
return new BlockPosWrapper();
}
@Override
public AbstractBlockPosWrapper createBlockPos(int x, int y, int z)
{
return new BlockPosWrapper(x,y,z);
}
@Override
public AbstractChunkPosWrapper createChunkPos()
{
return new ChunkPosWrapper();
}
@Override
public AbstractChunkPosWrapper createChunkPos(int x, int z)
{
return new ChunkPosWrapper(x, z);
}
@Override
public AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos)
{
return new ChunkPosWrapper(newChunkPos);
}
@Override
public AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos)
{
return new ChunkPosWrapper(blockPos);
}
@Override
public AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper)
{
return new WorldGeneratorWrapper(newLodBuilder, newLodDimension, worldWrapper);
}
}
@@ -1,5 +1,6 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
@@ -15,17 +16,20 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.enums;
package com.seibel.lod.forge.wrappers;
import net.minecraft.world.gen.Heightmap;
/**
* fast, fancy, or off
* Stores any variables or code that
* may be shared between wrapper objects.
*
* @author James Seibel
* @version 02-14-2021
* @version 11-20-2021
*/
public enum FogQuality
public class WrapperUtil
{
FAST,
FANCY,
OFF;
/** If we ever need to use a heightmap for any reason, use this one. */
public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;
}
@@ -0,0 +1,46 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge.wrappers.block;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import net.minecraft.block.Blocks;
/**
* Contains methods that would have been static in BlockColorWrapper.
* Since interfaces can't create/implement static methods we have
* to split the object up in two.
*
* @author James Seibel
* @version 11-17-2021
*/
public class BlockColorSingletonWrapper implements IBlockColorSingletonWrapper
{
public static final BlockColorSingletonWrapper INSTANCE = new BlockColorSingletonWrapper();
@Override
public IBlockColorWrapper getWaterColor()
{
return BlockColorWrapper.getBlockColorWrapper(Blocks.WATER);
}
}
@@ -0,0 +1,332 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge.wrappers.block;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import com.seibel.lod.forge.wrappers.minecraft.MinecraftWrapper;
import net.minecraft.block.AbstractPlantBlock;
import net.minecraft.block.AbstractTopPlantBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.BushBlock;
import net.minecraft.block.FlowerBlock;
import net.minecraft.block.GrassBlock;
import net.minecraft.block.IGrowable;
import net.minecraft.block.LeavesBlock;
import net.minecraft.block.TallGrassBlock;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.client.model.data.ModelDataMap;
/**
* @author ??
* @version 11-17-2021
*/
public class BlockColorWrapper implements IBlockColorWrapper
{
//set of block which require tint
public static final ConcurrentMap<Block, BlockColorWrapper> blockColorWrapperMap = new ConcurrentHashMap<>();
public static final ModelDataMap dataMap = new ModelDataMap.Builder().build();
public static final BlockPos blockPos = new BlockPos(0, 0, 0);
public static final Random random = new Random(0);
//public static BlockColourWrapper WATER_COLOR = getBlockColorWrapper(Blocks.WATER);
public static final Direction[] directions = new Direction[] { Direction.UP, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.DOWN };
private final Block block;
private int color;
private boolean isColored;
private boolean toTint;
private boolean foliageTint;
private boolean grassTint;
private boolean waterTint;
/**Constructor only require for the block instance we are wrapping**/
public BlockColorWrapper(Block block)
{
this.block = block;
this.color = 0;
this.isColored = true;
this.toTint = false;
this.foliageTint = false;
this.grassTint = false;
this.waterTint = false;
setupColorAndTint();
/*StringBuilder s = new StringBuilder();
s.append(block + "\n"
+ Integer.toHexString(
Minecraft.getInstance().getBlockColors().createDefault().getColor(
block.defaultBlockState(),
(World) MinecraftWrapper.INSTANCE.getWrappedServerLevel().getLevel(),
blockPosWrapper.getBlockPos())) + "\n"
);
for(Property x : Minecraft.getInstance().getBlockColors().getColoringProperties(block))
s.append(x.getName() + " " + x.getPossibleValues() + '\n');
System.out.println(s);*/
//System.out.println(block + " color " + Integer.toHexString(color) + " to tint " + toTint + " folliageTint " + folliageTint + " grassTint " + grassTint + " waterTint " + waterTint);
}
/**
* this return a wrapper of the block in input
* @param block object of the block to wrap
*/
public static IBlockColorWrapper getBlockColorWrapper(Block block)
{
//first we check if the block has already been wrapped
if (blockColorWrapperMap.containsKey(block) && blockColorWrapperMap.get(block) != null)
return blockColorWrapperMap.get(block);
//if it hasn't been created yet, we create it and save it in the map
BlockColorWrapper blockWrapper = new BlockColorWrapper(block);
blockColorWrapperMap.put(block, blockWrapper);
//we return the newly created wrapper
return blockWrapper;
}
/**
* Generate the color of the given block from its texture
* and store it for later use.
*/
private void setupColorAndTint()
{
BlockState blockState = block.defaultBlockState();
BlockPosWrapper blockPosWrapper = new BlockPosWrapper();
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
TextureAtlasSprite texture;
List<BakedQuad> quads = null;
boolean isTinted = false;
int listSize = 0;
// first step is to check if this block has a tinted face
for (Direction direction : directions)
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap);
listSize = Math.max(listSize, quads.size());
for (BakedQuad bakedQuad : quads)
{
isTinted |= bakedQuad.isTinted();
}
}
//if it contains a tinted face then we store this block in the toTint set
if (isTinted)
this.toTint = true;
//now we get the first non empty face
for (Direction direction : directions)
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap);
if (!quads.isEmpty())
break;
}
//the quads list is not empty we extract the first one
if (!quads.isEmpty())
{
isColored = true;
texture = quads.get(0).getSprite();
}
else
{
isColored = true;
texture = mc.getModelManager().getBlockModelShaper().getTexture(block.defaultBlockState(), mc.getClientWorld(), blockPosWrapper.getBlockPos());
}
int count = 0;
int alpha = 0;
int red = 0;
int green = 0;
int blue = 0;
int numberOfGreyPixel = 0;
int tempColor;
int colorMultiplier;
// generate the block's color
for (int frameIndex = 0; frameIndex < texture.getFrameCount(); frameIndex++)
{
// textures normally use u and v instead of x and y
for (int u = 0; u < texture.getWidth(); u++)
{
for (int v = 0; v < texture.getHeight(); v++)
{
tempColor = texture.getPixelRGBA(frameIndex, u, v);
if (ColorUtil.getAlpha(texture.getPixelRGBA(frameIndex, u, v)) == 0)
continue;
// determine if this pixel is gray
int colorMax = Math.max(Math.max(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
int colorMin = 4 + Math.min(Math.min(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
boolean isGray = colorMax < colorMin;
if (isGray)
numberOfGreyPixel++;
// for flowers, weight their non-green color higher
if (block instanceof FlowerBlock && (!(ColorUtil.getGreen(tempColor) > (ColorUtil.getBlue(tempColor) + 30)) || !(ColorUtil.getGreen(tempColor) > (ColorUtil.getRed(tempColor) + 30))))
colorMultiplier = 5;
else
colorMultiplier = 1;
// add to the running averages
count += colorMultiplier;
alpha += ColorUtil.getAlpha(tempColor) * colorMultiplier;
red += ColorUtil.getBlue(tempColor) * colorMultiplier;
green += ColorUtil.getGreen(tempColor) * colorMultiplier;
blue += ColorUtil.getRed(tempColor) * colorMultiplier;
}
}
}
if (count == 0)
// this block is entirely transparent
tempColor = 0;
else
{
// determine the average color
alpha /= count;
red /= count;
green /= count;
blue /= count;
tempColor = ColorUtil.rgbToInt(alpha, red, green, blue);
}
// determine if this block should use the biome color tint
if ((grassInstance() || leavesInstance() || waterIstance()) && (float) numberOfGreyPixel / count > 0.75f)
this.toTint = true;
// we check which kind of tint we need to apply
this.grassTint = grassInstance() && toTint;
this.foliageTint = leavesInstance() && toTint;
this.waterTint = waterIstance() && toTint;
color = tempColor;
}
/** determine if the given block should use the biome's grass color */
private boolean grassInstance()
{
return block instanceof GrassBlock
|| block instanceof BushBlock
|| block instanceof IGrowable
|| block instanceof AbstractPlantBlock
|| block instanceof AbstractTopPlantBlock
|| block instanceof TallGrassBlock;
}
/** determine if the given block should use the biome's foliage color */
private boolean leavesInstance()
{
return block instanceof LeavesBlock
|| block == Blocks.VINE
|| block == Blocks.SUGAR_CANE;
}
/** determine if the given block should use the biome's foliage color */
private boolean waterIstance()
{
return block == Blocks.WATER;
}
//--------------//
//Colors getters//
//--------------//
@Override
public boolean hasColor()
{
return isColored;
}
@Override
public int getColor()
{
return color;
}
//------------//
//Tint getters//
//------------//
@Override
public boolean hasTint()
{
return toTint;
}
@Override
public boolean hasGrassTint()
{
return grassTint;
}
@Override
public boolean hasFolliageTint()
{
return foliageTint;
}
@Override
public boolean hasWaterTint()
{
return waterTint;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockColorWrapper))
return false;
BlockColorWrapper that = (BlockColorWrapper) o;
return Objects.equals(block, that.block);
}
@Override public int hashCode()
{
return Objects.hash(block);
}
}
@@ -0,0 +1,102 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge.wrappers.block;
import java.util.Objects;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import net.minecraft.util.math.BlockPos;
/**
* @author James Seibel
* @version 11-20-2021
*/
public class BlockPosWrapper extends AbstractBlockPosWrapper
{
private final BlockPos.Mutable blockPos;
public BlockPosWrapper()
{
this.blockPos = new BlockPos.Mutable(0, 0, 0);
}
public BlockPosWrapper(int x, int y, int z)
{
this.blockPos = new BlockPos.Mutable(x, y, z);
}
@Override
public void set(int x, int y, int z)
{
blockPos.set(x, y, z);
}
@Override
public int getX()
{
return blockPos.getX();
}
@Override
public int getY()
{
return blockPos.getY();
}
@Override
public int getZ()
{
return blockPos.getZ();
}
@Override
public int get(LodDirection.Axis axis)
{
return axis.choose(getX(), getY(), getZ());
}
public BlockPos.Mutable getBlockPos()
{
return blockPos;
}
@Override
public boolean equals(Object o)
{
return blockPos.equals(o);
}
@Override
public int hashCode()
{
return Objects.hash(blockPos);
}
@Override
public BlockPosWrapper offset(int x, int y, int z)
{
blockPos.set(blockPos.getX() + x, blockPos.getY() + y, blockPos.getZ() + z);
return this;
}
}
@@ -0,0 +1,179 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge.wrappers.block;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.forge.wrappers.chunk.ChunkWrapper;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.SixWayBlock;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
/**
* @author ??
* @version 11-18-2021
*/
public class BlockShapeWrapper implements IBlockShapeWrapper
{
//set of block which require tint
public static final ConcurrentMap<Block, BlockShapeWrapper> blockShapeWrapperMap = new ConcurrentHashMap<>();
public static BlockShapeWrapper WATER_SHAPE = new BlockShapeWrapper();
private final Block block;
private boolean toAvoid;
private boolean nonFull;
private boolean noCollision;
/**Constructor only require for the block instance we are wrapping**/
public BlockShapeWrapper(Block block, IChunkWrapper chunkWrapper, AbstractBlockPosWrapper blockPosWrapper)
{
this.block = block;
this.nonFull = false;
this.noCollision = false;
this.toAvoid = ofBlockToAvoid();
setupShapes((ChunkWrapper) chunkWrapper, (BlockPosWrapper) blockPosWrapper);
//System.out.println(block + " non full " + nonFull + " no collision " + noCollision + " to avoid " + toAvoid);
}
private BlockShapeWrapper()
{
this.block = Blocks.WATER;
this.nonFull = false;
this.noCollision = false;
this.toAvoid = false;
}
/**
* this return a wrapper of the block in input
* @param block Block object to wrap
*/
static public BlockShapeWrapper getBlockShapeWrapper(Block block, IChunkWrapper chunkWrapper, AbstractBlockPosWrapper blockPosWrapper)
{
//first we check if the block has already been wrapped
if (blockShapeWrapperMap.containsKey(block) && blockShapeWrapperMap.get(block) != null)
return blockShapeWrapperMap.get(block);
//if it hasn't been created yet, we create it and save it in the map
BlockShapeWrapper blockWrapper = new BlockShapeWrapper(block, chunkWrapper, blockPosWrapper);
blockShapeWrapperMap.put(block, blockWrapper);
//we return the newly created wrapper
return blockWrapper;
}
private void setupShapes(ChunkWrapper chunkWrapper, BlockPosWrapper blockPosWrapper)
{
IBlockReader chunk = chunkWrapper.getChunk();
BlockPos blockPos = blockPosWrapper.getBlockPos();
boolean noCollisionSetted = false;
boolean nonFullSetted = false;
if (!block.defaultBlockState().getFluidState().isEmpty() || block instanceof SixWayBlock)
{
noCollisionSetted = true;
nonFullSetted = true;
noCollision = false;
nonFull = false;
}
if (!nonFullSetted)
{
VoxelShape voxelShape = block.defaultBlockState().getShape(chunk, blockPos);
if (!voxelShape.isEmpty())
{
AxisAlignedBB bbox = voxelShape.bounds();
double xWidth = (bbox.maxX - bbox.minX);
double yWidth = (bbox.maxY - bbox.minY);
double zWidth = (bbox.maxZ - bbox.minZ);
nonFull = xWidth < 1 && zWidth < 1 && yWidth < 1;
}
else
{
nonFull = false;
}
}
if (!noCollisionSetted)
{
VoxelShape collisionShape = block.defaultBlockState().getCollisionShape(chunk, blockPos);
noCollision = collisionShape.isEmpty();
}
}
@Override
public boolean ofBlockToAvoid()
{
return block.equals(Blocks.AIR)
|| block.equals(Blocks.CAVE_AIR)
|| block.equals(Blocks.BARRIER)
|| block.equals(Blocks.VOID_AIR);
}
//-----------------//
//Avoidance getters//
//-----------------//
@Override
public boolean isNonFull()
{
return nonFull;
}
@Override
public boolean hasNoCollision()
{
return noCollision;
}
@Override
public boolean isToAvoid()
{
return toAvoid;
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BlockShapeWrapper))
return false;
BlockShapeWrapper that = (BlockShapeWrapper) o;
return Objects.equals(block, that.block);
}
@Override public int hashCode()
{
return Objects.hash(block);
}
}
@@ -0,0 +1,135 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge.wrappers.chunk;
import java.util.Objects;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
/**
* @author James Seibel
* @version 11-18-2021
*/
public class ChunkPosWrapper extends AbstractChunkPosWrapper
{
private final ChunkPos chunkPos;
public ChunkPosWrapper(ChunkPos newChunkPos)
{
this.chunkPos = newChunkPos;
}
public ChunkPosWrapper(BlockPos blockPos)
{
this.chunkPos = new ChunkPos(blockPos);
}
public ChunkPosWrapper(AbstractChunkPosWrapper newChunkPos)
{
this.chunkPos = ((ChunkPosWrapper) newChunkPos).chunkPos;
}
public ChunkPosWrapper(AbstractBlockPosWrapper blockPos)
{
this.chunkPos = new ChunkPos(((BlockPosWrapper) blockPos).getBlockPos());
}
public ChunkPosWrapper(int chunkX, int chunkZ)
{
this.chunkPos = new ChunkPos(chunkX, chunkZ);
}
public ChunkPosWrapper()
{
this.chunkPos = new ChunkPos(0, 0);
}
@Override
public int getX()
{
return chunkPos.x;
}
@Override
public int getZ()
{
return chunkPos.z;
}
@Override
public int getMinBlockX()
{
return chunkPos.getMinBlockX();
}
@Override
public int getMinBlockZ()
{
return chunkPos.getMinBlockZ();
}
@Override
public int getRegionX()
{
return chunkPos.getRegionX();
}
@Override
public int getRegionZ()
{
return chunkPos.getRegionZ();
}
public ChunkPos getChunkPos()
{
return chunkPos;
}
@Override
public boolean equals(Object o)
{
return chunkPos.equals(o);
}
@Override
public int hashCode()
{
return Objects.hash(chunkPos);
}
@Override
public BlockPosWrapper getWorldPosition()
{
BlockPos blockPos = chunkPos.getWorldPosition();
return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ());
}
}
@@ -0,0 +1,125 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge.wrappers.chunk;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.forge.wrappers.WrapperUtil;
import com.seibel.lod.forge.wrappers.block.BlockColorWrapper;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import com.seibel.lod.forge.wrappers.block.BlockShapeWrapper;
import com.seibel.lod.forge.wrappers.world.BiomeWrapper;
import net.minecraft.block.BlockState;
import net.minecraft.block.ILiquidContainer;
import net.minecraft.block.IWaterLoggable;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.world.chunk.IChunk;
/**
* @author ??
* @version 11-17-2021
*/
public class ChunkWrapper implements IChunkWrapper
{
private final IChunk chunk;
private final ChunkPosWrapper chunkPos;
@Override
public int getHeight()
{
return chunk.getMaxBuildHeight();
}
@Override
public boolean isPositionInWater(AbstractBlockPosWrapper blockPos)
{
BlockState blockState = chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos());
//This type of block is always in water
return ((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable))
|| (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED));
}
@Override
public int getHeightMapValue(int xRel, int zRel)
{
return chunk.getOrCreateHeightmapUnprimed(WrapperUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(xRel, zRel);
}
@Override
public BiomeWrapper getBiome(int xRel, int yAbs, int zRel)
{
return BiomeWrapper.getBiomeWrapper(chunk.getBiomes().getNoiseBiome(xRel >> 2, yAbs >> 2, zRel >> 2));
}
@Override
public IBlockColorWrapper getBlockColorWrapper(AbstractBlockPosWrapper blockPos)
{
return BlockColorWrapper.getBlockColorWrapper(chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos()).getBlock());
}
@Override
public IBlockShapeWrapper getBlockShapeWrapper(AbstractBlockPosWrapper blockPos)
{
return BlockShapeWrapper.getBlockShapeWrapper(chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos()).getBlock(), this, blockPos);
}
public ChunkWrapper(IChunk chunk)
{
this.chunk = chunk;
this.chunkPos = new ChunkPosWrapper(chunk.getPos());
}
public IChunk getChunk()
{
return chunk;
}
@Override
public ChunkPosWrapper getPos()
{
return chunkPos;
}
@Override
public boolean isLightCorrect()
{
return chunk.isLightCorrect();
}
@Override
public boolean isWaterLogged(AbstractBlockPosWrapper blockPos)
{
BlockState blockState = chunk.getBlockState(((BlockPosWrapper)blockPos).getBlockPos());
//This type of block is always in water
return ((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable))
|| (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED));
}
@Override
public int getEmittedBrightness(AbstractBlockPosWrapper blockPos)
{
return chunk.getLightEmission(((BlockPosWrapper)blockPos).getBlockPos());
}
}
@@ -0,0 +1,504 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge.wrappers.config;
import com.seibel.lod.core.enums.config.BlocksToAvoid;
import com.seibel.lod.core.enums.config.BufferRebuildTimes;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.enums.config.GenerationPriority;
import com.seibel.lod.core.enums.config.GpuUploadMethod;
import com.seibel.lod.core.enums.config.HorizontalQuality;
import com.seibel.lod.core.enums.config.HorizontalResolution;
import com.seibel.lod.core.enums.config.HorizontalScale;
import com.seibel.lod.core.enums.config.LodTemplate;
import com.seibel.lod.core.enums.config.VanillaOverdraw;
import com.seibel.lod.core.enums.config.VerticalQuality;
import com.seibel.lod.core.enums.rendering.DebugMode;
import com.seibel.lod.core.enums.rendering.FogColorMode;
import com.seibel.lod.core.enums.rendering.FogDistance;
import com.seibel.lod.core.enums.rendering.FogDrawMode;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.forge.ForgeConfig;
/**
* @author James Seibel
* @version 11-16-2021
*/
public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
{
public static final LodConfigWrapperSingleton INSTANCE = new LodConfigWrapperSingleton();
private static final Client client = new Client();
@Override
public IClient client()
{
return client;
}
public static class Client implements IClient
{
public final IGraphics graphics;
public final IWorldGenerator worldGenerator;
public final IAdvanced advanced;
@Override
public IGraphics graphics()
{
return graphics;
}
@Override
public IWorldGenerator worldGenerator()
{
return worldGenerator;
}
@Override
public IAdvanced advanced()
{
return advanced;
}
//================//
// Client Configs //
//================//
public Client()
{
graphics = new Graphics();
worldGenerator = new WorldGenerator();
advanced = new Advanced();
}
//==================//
// Graphics Configs //
//==================//
public static class Graphics implements IGraphics
{
public final IQuality quality;
public final IFogQuality fogQuality;
public final IAdvancedGraphics advancedGraphics;
@Override
public IQuality quality()
{
return quality;
}
@Override
public IFogQuality fogQuality()
{
return fogQuality;
}
@Override
public IAdvancedGraphics advancedGraphics()
{
return advancedGraphics;
}
Graphics()
{
quality = new Quality();
advancedGraphics = new AdvancedGraphics();
fogQuality = new FogQuality();
}
public static class Quality implements IQuality
{
@Override
public HorizontalResolution getDrawResolution()
{
return ForgeConfig.CLIENT.graphics.qualityOption.drawResolution.get();
}
@Override
public void setDrawResolution(HorizontalResolution newHorizontalResolution)
{
ForgeConfig.CLIENT.graphics.qualityOption.drawResolution.set(newHorizontalResolution);
}
@Override
public int getLodChunkRenderDistance()
{
return ForgeConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get();
}
@Override
public void setLodChunkRenderDistance(int newLodChunkRenderDistance)
{
ForgeConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.set(newLodChunkRenderDistance);
}
@Override
public VerticalQuality getVerticalQuality()
{
return ForgeConfig.CLIENT.graphics.qualityOption.verticalQuality.get();
}
@Override
public void setVerticalQuality(VerticalQuality newVerticalQuality)
{
ForgeConfig.CLIENT.graphics.qualityOption.verticalQuality.set(newVerticalQuality);
}
@Override
public HorizontalScale getHorizontalScale()
{
return ForgeConfig.CLIENT.graphics.qualityOption.horizontalScale.get();
}
@Override
public void setHorizontalScale(HorizontalScale newHorizontalScale)
{
ForgeConfig.CLIENT.graphics.qualityOption.horizontalScale.set(newHorizontalScale);
}
@Override
public HorizontalQuality getHorizontalQuality()
{
return ForgeConfig.CLIENT.graphics.qualityOption.horizontalQuality.get();
}
@Override
public void setHorizontalQuality(HorizontalQuality newHorizontalQuality)
{
ForgeConfig.CLIENT.graphics.qualityOption.horizontalQuality.set(newHorizontalQuality);
}
}
public static class FogQuality implements IFogQuality
{
@Override
public FogDistance getFogDistance()
{
return ForgeConfig.CLIENT.graphics.fogQuality.fogDistance.get();
}
@Override
public void setFogDistance(FogDistance newFogDistance)
{
ForgeConfig.CLIENT.graphics.fogQuality.fogDistance.set(newFogDistance);
}
@Override
public FogDrawMode getFogDrawMode()
{
return ForgeConfig.CLIENT.graphics.fogQuality.fogDrawMode.get();
}
@Override
public void setFogDrawMode(FogDrawMode newFogDrawMode)
{
ForgeConfig.CLIENT.graphics.fogQuality.fogDrawMode.set(newFogDrawMode);
}
@Override
public FogColorMode getFogColorMode()
{
return ForgeConfig.CLIENT.graphics.fogQuality.fogColorMode.get();
}
@Override
public void setFogColorMode(FogColorMode newFogColorMode)
{
ForgeConfig.CLIENT.graphics.fogQuality.fogColorMode.set(newFogColorMode);
}
@Override
public boolean getDisableVanillaFog()
{
return ForgeConfig.CLIENT.graphics.fogQuality.disableVanillaFog.get();
}
@Override
public void setDisableVanillaFog(boolean newDisableVanillaFog)
{
ForgeConfig.CLIENT.graphics.fogQuality.disableVanillaFog.set(newDisableVanillaFog);
}
}
public static class AdvancedGraphics implements IAdvancedGraphics
{
@Override
public LodTemplate getLodTemplate()
{
return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.lodTemplate.get();
}
@Override
public void setLodTemplate(LodTemplate newLodTemplate)
{
ForgeConfig.CLIENT.graphics.advancedGraphicsOption.lodTemplate.set(newLodTemplate);
}
@Override
public boolean getDisableDirectionalCulling()
{
return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get();
}
@Override
public void setDisableDirectionalCulling(boolean newDisableDirectionalCulling)
{
ForgeConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.set(newDisableDirectionalCulling);
}
@Override
public boolean getAlwaysDrawAtMaxQuality()
{
return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.alwaysDrawAtMaxQuality.get();
}
@Override
public void setAlwaysDrawAtMaxQuality(boolean newAlwaysDrawAtMaxQuality)
{
ForgeConfig.CLIENT.graphics.advancedGraphicsOption.alwaysDrawAtMaxQuality.set(newAlwaysDrawAtMaxQuality);
}
@Override
public VanillaOverdraw getVanillaOverdraw()
{
return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.get();
}
@Override
public void setVanillaOverdraw(VanillaOverdraw newVanillaOverdraw)
{
ForgeConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.set(newVanillaOverdraw);
}
@Override
public GpuUploadMethod getGpuUploadMethod()
{
return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get();
}
@Override
public void setGpuUploadMethod(GpuUploadMethod newDisableVanillaFog)
{
ForgeConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(newDisableVanillaFog);
}
@Override
public boolean getUseExtendedNearClipPlane()
{
return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.useExtendedNearClipPlane.get();
}
@Override
public void setUseExtendedNearClipPlane(boolean newUseExtendedNearClipPlane)
{
ForgeConfig.CLIENT.graphics.advancedGraphicsOption.useExtendedNearClipPlane.set(newUseExtendedNearClipPlane);
}
}
}
//========================//
// WorldGenerator Configs //
//========================//
public static class WorldGenerator implements IWorldGenerator
{
@Override
public GenerationPriority getGenerationPriority()
{
return ForgeConfig.CLIENT.worldGenerator.generationPriority.get();
}
@Override
public void setGenerationPriority(GenerationPriority newGenerationPriority)
{
ForgeConfig.CLIENT.worldGenerator.generationPriority.set(newGenerationPriority);
}
@Override
public DistanceGenerationMode getDistanceGenerationMode()
{
return ForgeConfig.CLIENT.worldGenerator.distanceGenerationMode.get();
}
@Override
public void setDistanceGenerationMode(DistanceGenerationMode newDistanceGenerationMode)
{
ForgeConfig.CLIENT.worldGenerator.distanceGenerationMode.set(newDistanceGenerationMode);
}
@Override
public boolean getAllowUnstableFeatureGeneration()
{
return ForgeConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.get();
}
@Override
public void setAllowUnstableFeatureGeneration(boolean newAllowUnstableFeatureGeneration)
{
ForgeConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.set(newAllowUnstableFeatureGeneration);
}
@Override
public BlocksToAvoid getBlocksToAvoid()
{
return ForgeConfig.CLIENT.worldGenerator.blocksToAvoid.get();
}
@Override
public void setBlockToAvoid(BlocksToAvoid newBlockToAvoid)
{
ForgeConfig.CLIENT.worldGenerator.blocksToAvoid.set(newBlockToAvoid);
}
}
//============================//
// AdvancedModOptions Configs //
//============================//
public static class Advanced implements IAdvanced
{
public final IThreading threading;
public final IDebugging debugging;
public final IBuffers buffers;
@Override
public IThreading threading()
{
return threading;
}
@Override
public IDebugging debugging()
{
return debugging;
}
@Override
public IBuffers buffers()
{
return buffers;
}
public Advanced()
{
threading = new Threading();
debugging = new Debugging();
buffers = new Buffers();
}
public static class Threading implements IThreading
{
@Override
public int getNumberOfWorldGenerationThreads()
{
return ForgeConfig.CLIENT.advancedModOptions.threading.numberOfWorldGenerationThreads.get();
}
@Override
public void setNumberOfWorldGenerationThreads(int newNumberOfWorldGenerationThreads)
{
ForgeConfig.CLIENT.advancedModOptions.threading.numberOfWorldGenerationThreads.set(newNumberOfWorldGenerationThreads);
}
@Override
public int getNumberOfBufferBuilderThreads()
{
return ForgeConfig.CLIENT.advancedModOptions.threading.numberOfBufferBuilderThreads.get();
}
@Override
public void setNumberOfBufferBuilderThreads(int newNumberOfWorldBuilderThreads)
{
ForgeConfig.CLIENT.advancedModOptions.threading.numberOfBufferBuilderThreads.set(newNumberOfWorldBuilderThreads);
}
}
//===============//
// Debug Options //
//===============//
public static class Debugging implements IDebugging
{
@Override
public boolean getDrawLods()
{
return ForgeConfig.CLIENT.advancedModOptions.debugging.drawLods.get();
}
@Override
public void setDrawLods(boolean newDrawLods)
{
ForgeConfig.CLIENT.advancedModOptions.debugging.drawLods.set(newDrawLods);
}
@Override
public DebugMode getDebugMode()
{
return ForgeConfig.CLIENT.advancedModOptions.debugging.debugMode.get();
}
@Override
public void setDebugMode(DebugMode newDebugMode)
{
ForgeConfig.CLIENT.advancedModOptions.debugging.debugMode.set(newDebugMode);
}
@Override
public boolean getDebugKeybindingsEnabled()
{
return ForgeConfig.CLIENT.advancedModOptions.debugging.enableDebugKeybindings.get();
}
@Override
public void setDebugKeybindingsEnabled(boolean newEnableDebugKeybindings)
{
ForgeConfig.CLIENT.advancedModOptions.debugging.enableDebugKeybindings.set(newEnableDebugKeybindings);
}
}
public static class Buffers implements IBuffers
{
@Override
public BufferRebuildTimes getRebuildTimes()
{
return ForgeConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.get();
}
@Override
public void setRebuildTimes(BufferRebuildTimes newBufferRebuildTimes)
{
ForgeConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.set(newBufferRebuildTimes);
}
}
}
}
}
@@ -0,0 +1,165 @@
package com.seibel.lod.forge.wrappers.minecraft;
import java.awt.Color;
import java.util.HashSet;
import org.lwjgl.opengl.GL15;
import com.seibel.lod.core.objects.math.Mat4f;
import com.seibel.lod.core.objects.math.Vec3d;
import com.seibel.lod.core.objects.math.Vec3f;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.lod.forge.wrappers.McObjectConverter;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import com.seibel.lod.forge.wrappers.chunk.ChunkPosWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher.CompiledChunk;
import net.minecraft.potion.Effects;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.math.vector.Vector3f;
/**
* A singleton that contains everything
* related to rendering in Minecraft.
*
* @author James Seibel
* @version 11-26-2021
*/
public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
{
public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper();
private final GameRenderer gameRenderer = Minecraft.getInstance().gameRenderer;
private final static Minecraft mc = Minecraft.getInstance();
@Override
public Vec3f getLookAtVector()
{
ActiveRenderInfo camera = gameRenderer.getMainCamera();
Vector3f cameraDir = camera.getLookVector();
return new Vec3f(cameraDir.x(), cameraDir.y(), cameraDir.z());
}
@Override
public AbstractBlockPosWrapper getCameraBlockPosition()
{
ActiveRenderInfo camera = gameRenderer.getMainCamera();
BlockPos blockPos = camera.getBlockPosition();
return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ());
}
@Override
public boolean playerHasBlindnessEffect()
{
return mc.player.getActiveEffectsMap().get(Effects.BLINDNESS) != null;
}
@Override
public Vec3d getCameraExactPosition()
{
ActiveRenderInfo camera = gameRenderer.getMainCamera();
Vector3d projectedView = camera.getPosition();
return new Vec3d(projectedView.x, projectedView.y, projectedView.z);
}
@Override
public Mat4f getDefaultProjectionMatrix(float partialTicks)
{
return McObjectConverter.Convert(gameRenderer.getProjectionMatrix(gameRenderer.getMainCamera(), partialTicks, true));
}
@Override
public double getGamma()
{
return mc.options.gamma;
}
@Override
public Color getFogColor()
{
float[] colorValues = new float[4];
GL15.glGetFloatv(GL15.GL_FOG_COLOR, colorValues);
return new Color(colorValues[0], colorValues[1], colorValues[2], colorValues[3]);
}
@Override
public Color getSkyColor()
{
if (mc.level.dimensionType().hasSkyLight())
{
Vector3d colorValues = mc.level.getSkyColor(mc.gameRenderer.getMainCamera().getBlockPosition(), mc.getFrameTime());
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
}
else
{
return new Color(0, 0, 0);
}
}
@Override
public double getFov(float partialTicks)
{
return gameRenderer.getFov(gameRenderer.getMainCamera(), partialTicks, true);
}
/** Measured in chunks */
@Override
public int getRenderDistance()
{
return mc.options.renderDistance;
}
@Override
public int getScreenWidth()
{
return mc.getWindow().getWidth();
}
@Override
public int getScreenHeight()
{
return mc.getWindow().getHeight();
}
/**
* This method returns the ChunkPos of all chunks that Minecraft
* is going to render this frame. <br><br>
* <p>
* Note: This isn't perfect. It will return some chunks that are outside
* the clipping plane. (For example, if you are high above the ground some chunks
* will be incorrectly added, even though they are outside render range).
*/
@Override
public HashSet<AbstractChunkPosWrapper> getRenderedChunks()
{
HashSet<AbstractChunkPosWrapper> loadedPos = new HashSet<>();
// Wow, those are some long names!
// go through every RenderInfo to get the compiled chunks
WorldRenderer renderer = mc.levelRenderer;
for (WorldRenderer.LocalRenderInformationContainer worldRenderer$LocalRenderInformationContainer : renderer.renderChunks)
{
CompiledChunk compiledChunk = worldRenderer$LocalRenderInformationContainer.chunk.getCompiledChunk();
if (!compiledChunk.hasNoRenderableLayers())
{
// add the ChunkPos for every rendered chunk
BlockPos bpos = worldRenderer$LocalRenderInformationContainer.chunk.getOrigin();
loadedPos.add(new ChunkPosWrapper(bpos));
}
}
return loadedPos;
}
}
@@ -0,0 +1,397 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge.wrappers.minecraft;
import java.awt.Color;
import java.io.File;
import java.util.ArrayList;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.forge.wrappers.McObjectConverter;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import com.seibel.lod.forge.wrappers.chunk.ChunkPosWrapper;
import com.seibel.lod.forge.wrappers.misc.LightMapWrapper;
import com.seibel.lod.forge.wrappers.world.DimensionTypeWrapper;
import com.seibel.lod.forge.wrappers.world.WorldWrapper;
import net.minecraft.client.GameSettings;
import net.minecraft.client.MainWindow;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.network.play.ClientPlayNetHandler;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.model.ModelManager;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.crash.CrashReport;
import net.minecraft.entity.Entity;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.Nullable;
/**
* A singleton that wraps the Minecraft class
* to allow for easier movement between Minecraft versions.
*
* @author James Seibel
* @version 9-16-2021
*/
public class MinecraftWrapper implements IMinecraftWrapper
{
public static final MinecraftWrapper INSTANCE = new MinecraftWrapper();
private final Minecraft mc = Minecraft.getInstance();
/**
* The lightmap for the current:
* Time, dimension, brightness setting, etc.
*/
private NativeImage lightMap = null;
private ProfilerWrapper profilerWrapper;
private MinecraftWrapper()
{
}
//================//
// helper methods //
//================//
/**
* This should be called at the beginning of every frame to
* clear any Minecraft data that becomes out of date after a frame. <br> <br>
* <p>
* LightMaps and other time sensitive objects fall in this category. <br> <br>
* <p>
* This doesn't affect OpenGL objects in any way.
*/
@Override
public void clearFrameObjectCache()
{
lightMap = null;
}
//=================//
// method wrappers //
//=================//
@Override
public float getShade(LodDirection lodDirection)
{
Direction mcDir = McObjectConverter.Convert(lodDirection);
return mc.level.getShade(mcDir, true);
}
@Override
public boolean hasSinglePlayerServer()
{
return mc.hasSingleplayerServer();
}
@Override
public String getCurrentServerName()
{
return mc.getCurrentServer().name;
}
@Override
public String getCurrentServerIp()
{
return mc.getCurrentServer().ip;
}
@Override
public String getCurrentServerVersion()
{
return mc.getCurrentServer().version.getString();
}
/** Returns the dimension the player is currently in */
@Override
public IDimensionTypeWrapper getCurrentDimension()
{
return DimensionTypeWrapper.getDimensionTypeWrapper(mc.player.level.dimensionType());
}
@Override
public String getCurrentDimensionId()
{
return LodUtil.getDimensionIDFromWorld(WorldWrapper.getWorldWrapper(mc.level));
}
/** This texture changes every frame */
@Override
public ILightMapWrapper getCurrentLightMap()
{
// get the current lightMap if the cache is empty
if (lightMap == null)
{
LightTexture tex = mc.gameRenderer.lightTexture();
lightMap = tex.lightPixels;
}
return new LightMapWrapper(lightMap);
}
/**
* Returns the color int at the given pixel coordinates
* from the current lightmap.
* @param u x location in texture space
* @param v z location in texture space
*/
@Override
public int getColorIntFromLightMap(int u, int v)
{
if (lightMap == null)
{
// make sure the lightMap is up-to-date
getCurrentLightMap();
}
return lightMap.getPixelRGBA(u, v);
}
/**
* Returns the Color at the given pixel coordinates
* from the current lightmap.
* @param u x location in texture space
* @param v z location in texture space
*/
@Override
public Color getColorFromLightMap(int u, int v)
{
return LodUtil.intToColor(lightMap.getPixelRGBA(u, v));
}
//=============//
// Simple gets //
//=============//
public ClientPlayerEntity getPlayer()
{
return mc.player;
}
@Override
public boolean playerExists()
{
return mc.player != null;
}
@Override
public BlockPosWrapper getPlayerBlockPos()
{
BlockPos playerPos = getPlayer().blockPosition();
return new BlockPosWrapper(playerPos.getX(), playerPos.getY(), playerPos.getZ());
}
@Override
public ChunkPosWrapper getPlayerChunkPos()
{
return new ChunkPosWrapper(getPlayer().xChunk, getPlayer().zChunk);
}
public GameSettings getOptions()
{
return mc.options;
}
public ModelManager getModelManager()
{
return mc.getModelManager();
}
public ClientWorld getClientWorld()
{
return mc.level;
}
/**
* Attempts to get the ServerWorld for the dimension
* the user is currently in.
* @returns null if no ServerWorld is available
*/
@Override
public IWorldWrapper getWrappedServerWorld()
{
if (mc.level == null)
return null;
DimensionType dimension = mc.level.dimensionType();
IntegratedServer server = mc.getSingleplayerServer();
if (server == null)
return null;
ServerWorld serverWorld = null;
Iterable<ServerWorld> worlds = server.getAllLevels();
for (ServerWorld world : worlds)
{
if (world.dimensionType() == dimension)
{
serverWorld = world;
break;
}
}
return WorldWrapper.getWorldWrapper(serverWorld);
}
@Nullable
@Override
public IWorldWrapper getWrappedClientWorld()
{
return WorldWrapper.getWorldWrapper(mc.level);
}
@Override
public File getGameDirectory()
{
return mc.gameDirectory;
}
@Override
public IProfilerWrapper getProfiler()
{
if (profilerWrapper == null)
profilerWrapper = new ProfilerWrapper(mc.getProfiler());
else if (mc.getProfiler() != profilerWrapper.profiler)
profilerWrapper.profiler = mc.getProfiler();
return profilerWrapper;
}
public ClientPlayNetHandler getConnection()
{
return mc.getConnection();
}
public GameRenderer getGameRenderer()
{
return mc.gameRenderer;
}
public Entity getCameraEntity()
{
return mc.cameraEntity;
}
public MainWindow getWindow()
{
return mc.getWindow();
}
@Override
public float getSkyDarken(float partialTicks)
{
return mc.level.getSkyDarken(partialTicks);
}
public IntegratedServer getSinglePlayerServer()
{
return mc.getSingleplayerServer();
}
@Override
public boolean connectedToServer()
{
return mc.getCurrentServer() != null;
}
public ServerData getCurrentServer()
{
return mc.getCurrentServer();
}
public WorldRenderer getLevelRenderer()
{
return mc.levelRenderer;
}
/** Returns all worlds available to the server */
@Override
public ArrayList<IWorldWrapper> getAllServerWorlds()
{
ArrayList<IWorldWrapper> worlds = new ArrayList<IWorldWrapper>();
Iterable<ServerWorld> serverWorlds = mc.getSingleplayerServer().getAllLevels();
for (ServerWorld world : serverWorlds)
{
worlds.add(WorldWrapper.getWorldWrapper(world));
}
return worlds;
}
@Override
public void sendChatMessage(String string)
{
getPlayer().sendMessage(new StringTextComponent(string), getPlayer().getUUID());
}
/**
* Crashes Minecraft, displaying the given errorMessage <br> <br>
* In the following format: <br>
*
* The game crashed whilst <strong>errorMessage</strong> <br>
* Error: <strong>ExceptionClass: exceptionErrorMessage</strong> <br>
* Exit Code: -1 <br>
*/
@Override
public void crashMinecraft(String errorMessage, Throwable exception)
{
ClientApi.LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...");
CrashReport report = new CrashReport(errorMessage, exception);
Minecraft.crash(report);
}
}
@@ -0,0 +1,43 @@
package com.seibel.lod.forge.wrappers.minecraft;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import net.minecraft.profiler.IProfiler;
/**
*
*
* @author James Seibel
* @version 11-20-2021
*/
public class ProfilerWrapper implements IProfilerWrapper
{
public IProfiler profiler;
public ProfilerWrapper(IProfiler newProfiler)
{
profiler = newProfiler;
}
/** starts a new section inside the currently running section */
@Override
public void push(String newSection)
{
profiler.push(newSection);
}
/** ends the currently running section and starts a new one */
@Override
public void popPush(String newSection)
{
profiler.popPush(newSection);
}
/** ends the currently running section */
@Override
public void pop()
{
profiler.pop();
}
}
@@ -0,0 +1,31 @@
package com.seibel.lod.forge.wrappers.misc;
import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper;
import net.minecraft.client.renderer.texture.NativeImage;
/**
*
* @author Leonardo Amato
* @version 11-13-2021
*/
public class LightMapWrapper implements ILightMapWrapper
{
private NativeImage lightMap = null;
public LightMapWrapper(NativeImage newlightMap)
{
lightMap = newlightMap;
}
public void setLightMap(NativeImage newlightMap)
{
lightMap = newlightMap;
}
@Override
public int getLightValue(int skyLight, int blockLight)
{
return lightMap.getPixelRGBA(skyLight, blockLight);
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.forge.wrappers.world;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeColorWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import net.minecraft.world.biome.BiomeColors;
/**
* @author Cola?
* @version 11-15-2021
*/
public class BiomeColorWrapperSingleton implements IBiomeColorWrapperSingleton
{
private static final BiomeColorWrapperSingleton instance = new BiomeColorWrapperSingleton();
@Override
public IBiomeColorWrapperSingleton getInstance()
{
return instance;
}
@Override
public int getGrassColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos)
{
return BiomeColors.getAverageGrassColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos());
}
@Override
public int getWaterColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos)
{
return BiomeColors.getAverageWaterColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos());
}
@Override
public int getFoliageColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos)
{
return BiomeColors.getAverageFoliageColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos());
}
}
@@ -0,0 +1,157 @@
package com.seibel.lod.forge.wrappers.world;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.lod.forge.wrappers.block.BlockColorSingletonWrapper;
import com.seibel.lod.forge.wrappers.block.BlockColorWrapper;
import net.minecraft.block.Blocks;
import net.minecraft.world.biome.Biome;
/**
* @author James Seibel
* @version 11-15-2021
*/
public class BiomeWrapper implements IBiomeWrapper
{
public static final ConcurrentMap<Biome, BiomeWrapper> biomeWrapperMap = new ConcurrentHashMap<>();
private final Biome biome;
public BiomeWrapper(Biome biome)
{
this.biome = biome;
}
static public BiomeWrapper getBiomeWrapper(Biome biome)
{
//first we check if the biome has already been wrapped
if(biomeWrapperMap.containsKey(biome) && biomeWrapperMap.get(biome) != null)
return biomeWrapperMap.get(biome);
//if it hasn't been created yet, we create it and save it in the map
BiomeWrapper biomeWrapper = new BiomeWrapper(biome);
biomeWrapperMap.put(biome, biomeWrapper);
//we return the newly created wrapper
return biomeWrapper;
}
/** Returns a color int for the given biome. */
@Override
public int getColorForBiome(int x, int z)
{
int colorInt;
int tintValue = 0;
switch (biome.getBiomeCategory())
{
case NETHER:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.NETHERRACK).getColor();
break;
case THEEND:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.END_STONE).getColor();
break;
case BEACH:
case DESERT:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SAND).getColor();
break;
case EXTREME_HILLS:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.STONE).getColor();
break;
case MUSHROOM:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.MYCELIUM).getColor();
break;
case ICY:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SNOW).getColor();
break;
case MESA:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.RED_SAND).getColor();
break;
case OCEAN:
case RIVER:
colorInt = BlockColorSingletonWrapper.INSTANCE.getWaterColor().getColor();
tintValue = biome.getWaterColor();
break;
case PLAINS:
case SAVANNA:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.GRASS_BLOCK).getColor();
tintValue = biome.getGrassColor(x, z);
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
case TAIGA:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SPRUCE_LEAVES).getColor();
tintValue = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
case JUNGLE:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.JUNGLE_LEAVES).getColor();
tintValue = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
case NONE:
default:
case SWAMP:
case FOREST:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.OAK_LEAVES).getColor();
tintValue = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
}
return colorInt;
}
@Override
public int getGrassTint(int x, int z)
{
return biome.getGrassColor(x, z);
}
@Override
public int getFolliageTint()
{
return biome.getFoliageColor();
}
@Override
public int getWaterTint()
{
return biome.getWaterColor();
}
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof BiomeWrapper))
return false;
BiomeWrapper that = (BiomeWrapper) o;
return Objects.equals(biome, that.biome);
}
@Override public int hashCode()
{
return Objects.hash(biome);
}
}
@@ -0,0 +1,56 @@
package com.seibel.lod.forge.wrappers.world;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import net.minecraft.world.DimensionType;
/**
* @author ??
* @version 11-15-2021
*/
public class DimensionTypeWrapper implements IDimensionTypeWrapper
{
private static final ConcurrentMap<DimensionType, DimensionTypeWrapper> dimensionTypeWrapperMap = new ConcurrentHashMap<>();
private final DimensionType dimensionType;
public DimensionTypeWrapper(DimensionType dimensionType)
{
this.dimensionType = dimensionType;
}
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType)
{
//first we check if the biome has already been wrapped
if(dimensionTypeWrapperMap.containsKey(dimensionType) && dimensionTypeWrapperMap.get(dimensionType) != null)
return dimensionTypeWrapperMap.get(dimensionType);
//if it hasn't been created yet, we create it and save it in the map
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType);
dimensionTypeWrapperMap.put(dimensionType, dimensionTypeWrapper);
//we return the newly created wrapper
return dimensionTypeWrapper;
}
@Override
public String getDimensionName()
{
return dimensionType.effectsLocation().getPath();
}
@Override
public boolean hasCeiling()
{
return dimensionType.hasCeiling();
}
@Override
public boolean hasSkyLight()
{
return dimensionType.hasSkyLight();
}
}
@@ -0,0 +1,155 @@
package com.seibel.lod.forge.wrappers.world;
import java.io.File;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.core.enums.WorldType;
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.forge.wrappers.block.BlockPosWrapper;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.world.IWorld;
import net.minecraft.world.LightType;
import net.minecraft.world.server.ServerChunkProvider;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.Nullable;
/**
* @author James Seibel
* @author ??
* @version 11-20-2021
*/
public class WorldWrapper implements IWorldWrapper
{
private static final ConcurrentMap<IWorld, WorldWrapper> worldWrapperMap = new ConcurrentHashMap<>();
private final IWorld world;
public final WorldType worldType;
public WorldWrapper(IWorld newWorld)
{
world = newWorld;
if (world.getClass() == ServerWorld.class)
worldType = WorldType.ServerWorld;
else if (world.getClass() == ClientWorld.class)
worldType = WorldType.ClientWorld;
else
worldType = WorldType.Unknown;
}
@Nullable
public static WorldWrapper getWorldWrapper(IWorld world)
{
if (world == null) return null;
//first we check if the biome has already been wrapped
if(worldWrapperMap.containsKey(world) && worldWrapperMap.get(world) != null)
return worldWrapperMap.get(world);
//if it hasn't been created yet, we create it and save it in the map
WorldWrapper worldWrapper = new WorldWrapper(world);
worldWrapperMap.put(world, worldWrapper);
//we return the newly created wrapper
return worldWrapper;
}
public static void clearMap()
{
worldWrapperMap.clear();
}
@Override
public WorldType getWorldType()
{
return worldType;
}
@Override
public DimensionTypeWrapper getDimensionType()
{
return DimensionTypeWrapper.getDimensionTypeWrapper(world.dimensionType());
}
@Override
public int getBlockLight(AbstractBlockPosWrapper blockPos)
{
return world.getBrightness(LightType.BLOCK, ((BlockPosWrapper) blockPos).getBlockPos());
}
@Override
public int getSkyLight(AbstractBlockPosWrapper blockPos)
{
return world.getBrightness(LightType.SKY, ((BlockPosWrapper) blockPos).getBlockPos());
}
@Override
public BiomeWrapper getBiome(AbstractBlockPosWrapper blockPos)
{
return BiomeWrapper.getBiomeWrapper(world.getBiome(((BlockPosWrapper) blockPos).getBlockPos()));
}
public IWorld getWorld()
{
return world;
}
@Override
public boolean hasCeiling()
{
return world.dimensionType().hasCeiling();
}
@Override
public boolean hasSkyLight()
{
return world.dimensionType().hasSkyLight();
}
@Override
public boolean isEmpty()
{
return world == null;
}
@Override
public int getHeight()
{
return world.getHeight();
}
/** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */
@Override
public File getSaveFolder() throws UnsupportedOperationException
{
if (worldType != WorldType.ServerWorld)
throw new UnsupportedOperationException("getSaveFolder can only be called for ServerWorlds.");
ServerChunkProvider chunkSource = ((ServerWorld) world).getChunkSource();
return chunkSource.dataStorage.dataFolder;
}
/** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */
public ServerWorld getServerWorld() throws UnsupportedOperationException
{
if (worldType != WorldType.ServerWorld)
throw new UnsupportedOperationException("getSaveFolder can only be called for ServerWorlds.");
return (ServerWorld) world;
}
@Override
public int getSeaLevel()
{
// TODO this is depreciated, what should we use instead?
return world.getSeaLevel();
}
}
@@ -1,5 +1,6 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
@@ -15,7 +16,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.builders.worldGeneration;
package com.seibel.lod.forge.wrappers.worldGeneration;
import java.util.HashMap;
import java.util.List;
@@ -23,7 +25,8 @@ import java.util.Random;
import java.util.function.Predicate;
import java.util.stream.Stream;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.forge.wrappers.WrapperUtil;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
@@ -52,7 +55,6 @@ import net.minecraft.world.chunk.AbstractChunkProvider;
import net.minecraft.world.chunk.ChunkStatus;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.Heightmap;
import net.minecraft.world.gen.Heightmap.Type;
import net.minecraft.world.gen.feature.structure.Structure;
import net.minecraft.world.gen.feature.structure.StructureStart;
import net.minecraft.world.lighting.WorldLightManager;
@@ -62,269 +64,269 @@ import net.minecraft.world.storage.IWorldInfo;
/**
* This is a fake ServerWorld used when generating features.
* This allows us to keep each LodChunk generation independent
* of the actual ServerWorld, allowing us
* to multithread generation.
*
* It allows us to keep each LodChunk generation independent
* of the actual ServerWorld, allowing
* multithread generation.
*
* @author James Seibel
* @version 7-26-2021
*/
public class LodServerWorld implements ISeedReader
{
public HashMap<Heightmap.Type, Heightmap> heightmaps = new HashMap<>();
public IChunk chunk;
public ServerWorld serverWorld;
public final IChunk chunk;
public final ServerWorld serverWorld;
public LodServerWorld(ServerWorld newServerWorld, IChunk newChunk)
{
chunk = newChunk;
serverWorld = newServerWorld;
}
@Override
public int getHeight(Type heightmapType, int x, int z)
public int getHeight(Heightmap.Type heightmapType, int x, int z)
{
// make sure the block position is set relative to the chunk
x = x % LodUtil.CHUNK_WIDTH;
x = (x < 0) ? x + 16 : x;
z = z % LodUtil.CHUNK_WIDTH;
z = (z < 0) ? z + 16 : z;
return chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(x, z);
return chunk.getOrCreateHeightmapUnprimed(WrapperUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(x, z);
}
@Override
public Biome getBiome(BlockPos pos)
{
return chunk.getBiomes().getNoiseBiome(pos.getX() >> 2, pos.getY() >> 2, pos.getZ() >> 2);
}
@Override
public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft)
{
return chunk.setBlockState(pos, state, false) == state;
}
@Override
public BlockState getBlockState(BlockPos pos)
{
return chunk.getBlockState(pos);
}
@Override
public FluidState getFluidState(BlockPos pos)
{
return chunk.getFluidState(pos);
}
@Override
public boolean isStateAtPosition(BlockPos pos, Predicate<BlockState> state)
{
return state.test(chunk.getBlockState(pos));
}
@Override
public ITickList<Block> getBlockTicks()
{
return EmptyTickList.empty();
}
@Override
public IChunk getChunk(int x, int z, ChunkStatus requiredStatus, boolean nonnull)
{
return chunk;
}
@Override
public Stream<? extends StructureStart<?>> startsForFeature(SectionPos p_241827_1_, Structure<?> p_241827_2_)
{
return serverWorld.startsForFeature(p_241827_1_, p_241827_2_);
}
@Override
public ITickList<Fluid> getLiquidTicks()
{
return EmptyTickList.empty();
}
@Override
public WorldLightManager getLightEngine()
{
return new WorldLightManager(null, false, false);
}
@Override
public long getSeed()
{
return serverWorld.getSeed();
}
@Override
public DynamicRegistries registryAccess()
{
return serverWorld.registryAccess();
}
/**
* All methods below shouldn't be needed
* and thus have been left unimplemented.
*/
@Override
public Random getRandom()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void playSound(PlayerEntity player, BlockPos pos, SoundEvent soundIn, SoundCategory category, float volume,
float pitch)
float pitch)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void addParticle(IParticleData particleData, double x, double y, double z, double xSpeed, double ySpeed,
double zSpeed)
double zSpeed)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public BiomeManager getBiomeManager()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public int getSeaLevel()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public float getShade(Direction p_230487_1_, boolean p_230487_2_)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public WorldBorder getWorldBorder()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean removeBlock(BlockPos pos, boolean isMoving)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean destroyBlock(BlockPos pos, boolean dropBlock, Entity entity, int recursionLeft)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public ServerWorld getLevel()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public AbstractChunkProvider getChunkSource()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public DifficultyInstance getCurrentDifficultyAt(BlockPos arg0)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public IWorldInfo getLevelData()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void levelEvent(PlayerEntity arg0, int arg1, BlockPos arg2, int arg3)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public List<Entity> getEntities(Entity arg0, AxisAlignedBB arg1, Predicate<? super Entity> arg2)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public <T extends Entity> List<T> getEntitiesOfClass(Class<? extends T> arg0, AxisAlignedBB arg1,
Predicate<? super T> arg2)
Predicate<? super T> arg2)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public List<? extends PlayerEntity> players()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public int getSkyDarken()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public Biome getUncachedNoiseBiome(int p_225604_1_, int p_225604_2_, int p_225604_3_)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public boolean isClientSide()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public DimensionType dimensionType()
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public TileEntity getBlockEntity(BlockPos p_175625_1_)
{
throw new UnsupportedOperationException("Not Implemented");
}
}
@@ -0,0 +1,389 @@
package com.seibel.lod.forge.wrappers.worldGeneration;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.builders.lodBuilding.LodBuilderConfig;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
import com.seibel.lod.forge.wrappers.WrapperUtil;
import com.seibel.lod.forge.wrappers.chunk.ChunkPosWrapper;
import com.seibel.lod.forge.wrappers.chunk.ChunkWrapper;
import com.seibel.lod.forge.wrappers.world.WorldWrapper;
import net.minecraft.util.palette.UpgradeData;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.chunk.ChunkPrimer;
import net.minecraft.world.chunk.ChunkStatus;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.Heightmap;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.IceAndSnowFeature;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraft.world.gen.feature.template.TemplateManager;
import net.minecraft.world.server.ServerChunkProvider;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.server.ServerWorldLightManager;
/**
* @author James Seibel
* @version 11-13-2021
*/
public class WorldGeneratorWrapper extends AbstractWorldGeneratorWrapper
{
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
/**
* If a configured feature fails for whatever reason,
* add it to this list. This will hopefully remove any
* features that could cause issues down the line.
*/
private static final ConcurrentHashMap<Integer, ConfiguredFeature<?, ?>> FEATURES_TO_AVOID = new ConcurrentHashMap<>();
public final ServerWorld serverWorld;
public final LodDimension lodDim;
public final LodBuilder lodBuilder;
public WorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper)
{
super(newLodBuilder, newLodDimension, worldWrapper);
lodBuilder = newLodBuilder;
lodDim = newLodDimension;
serverWorld = ((WorldWrapper) worldWrapper).getServerWorld();
}
/** takes about 2-5 ms */
@Override
public void generateBiomesOnly(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode)
{
List<IChunk> chunkList = new LinkedList<>();
ChunkPrimer chunk = new ChunkPrimer(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY);
chunkList.add(chunk);
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
ChunkGenerator chunkGen = chunkSource.generator;
// generate the terrain (this is thread safe)
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
// override the chunk status, so we can run the next generator stage
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
// generate fake height data for this LOD
int seaLevel = serverWorld.getSeaLevel();
boolean simulateHeight = generationMode == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
boolean inTheEnd = false;
// add fake heightmap data so our LODs aren't at height 0
Heightmap heightmap = new Heightmap(chunk, WrapperUtil.DEFAULT_HEIGHTMAP);
for (int x = 0; x < LodUtil.CHUNK_WIDTH && !inTheEnd; x++)
{
for (int z = 0; z < LodUtil.CHUNK_WIDTH && !inTheEnd; z++)
{
if (simulateHeight)
{
// these heights are of course aren't super accurate,
// they are just to simulate height data where there isn't any
switch (chunk.getBiomes().getNoiseBiome(x >> 2, seaLevel >> 2, z >> 2).getBiomeCategory())
{
case NETHER:
heightmap.setHeight(x, z, serverWorld.getHeight() / 2);
break;
case EXTREME_HILLS:
heightmap.setHeight(x, z, seaLevel + 30);
break;
case MESA:
case JUNGLE:
heightmap.setHeight(x, z, seaLevel + 20);
break;
case BEACH:
heightmap.setHeight(x, z, seaLevel + 5);
break;
case NONE:
heightmap.setHeight(x, z, 0);
break;
case OCEAN:
case RIVER:
heightmap.setHeight(x, z, seaLevel);
break;
case THEEND:
inTheEnd = true;
break;
// DESERT
// FOREST
// ICY
// MUSHROOM
// SAVANNA
// SWAMP
// TAIGA
// PLAINS
default:
heightmap.setHeight(x, z, seaLevel + 10);
break;
}// heightmap switch
}
else
{
// we aren't simulating height
// always use sea level
heightmap.setHeight(x, z, seaLevel);
}
}// z
}// x
chunk.setHeightmap(WrapperUtil.DEFAULT_HEIGHTMAP, heightmap.getRawData());
if (!inTheEnd)
{
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false));
}
else
{
// if we are in the end, don't generate any chunks.
// Since we don't know where the islands are, everything
// generates the same, and it looks awful.
//TODO it appears that 'if' can be collapsed, but comment says that it should not be a case
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false));
}
// long startTime = System.currentTimeMillis();
// long endTime = System.currentTimeMillis();
// System.out.println(endTime - startTime);
}
/** takes about 10 - 20 ms */
@Override
public void generateSurface(AbstractChunkPosWrapper pos)
{
List<IChunk> chunkList = new LinkedList<>();
ChunkPrimer chunk = new ChunkPrimer(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY);
chunkList.add(chunk);
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
ServerWorldLightManager lightEngine = (ServerWorldLightManager) serverWorld.getLightEngine();
TemplateManager templateManager = serverWorld.getStructureManager();
ChunkGenerator chunkGen = chunkSource.generator;
// generate the terrain (this is thread safe)
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
// override the chunk status, so we can run the next generator stage
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
ChunkStatus.NOISE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
// this feature has been proven to be thread safe,
// so we will add it
IceAndSnowFeature snowFeature = new IceAndSnowFeature(NoFeatureConfig.CODEC);
snowFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition(), null);
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.SURFACE));
/*TODO if we want to use Biome utils and terrain utils for overworld
* lodBuilder.generateLodNodeFromChunk(lodDim, pos ,detailLevel, serverWorld.getSeed());*/
}
/**
* takes about 15 - 20 ms
* <p>
* Causes concurrentModification Exceptions,
* which could cause instability or world generation bugs
*/
@Override
public void generateFeatures(AbstractChunkPosWrapper pos)
{
List<IChunk> chunkList = new LinkedList<>();
ChunkPrimer chunk = new ChunkPrimer(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY);
chunkList.add(chunk);
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
ServerWorldLightManager lightEngine = (ServerWorldLightManager) serverWorld.getLightEngine();
TemplateManager templateManager = serverWorld.getStructureManager();
ChunkGenerator chunkGen = chunkSource.generator;
// generate the terrain (this is thread safe)
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
// override the chunk status, so we can run the next generator stage
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk);
ChunkStatus.NOISE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList);
// get all the biomes in the chunk
HashSet<Biome> biomes = new HashSet<>();
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
{
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
{
Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, serverWorld.getSeaLevel() >> 2, z >> 2);
// Issue #35
// For some reason Jungle biomes cause incredible lag
// the features here must be interacting with each other
// in unpredictable ways (specifically tree feature generation).
// When generating Features my CPU usage generally hovers around 30 - 40%
// when generating Jungles it spikes to 100%.
if (biome.getBiomeCategory() != Biome.Category.JUNGLE)
{
// should probably use the heightmap here instead of seaLevel,
// but this seems to get the job done well enough
biomes.add(biome);
}
}
}
boolean allowUnstableFeatures = CONFIG.client().worldGenerator().getAllowUnstableFeatureGeneration();
// generate all the features related to this chunk.
// this may or may not be thread safe
for (Biome biome : biomes)
{
List<List<Supplier<ConfiguredFeature<?, ?>>>> featuresForState = biome.generationSettings.features();
for (List<Supplier<ConfiguredFeature<?, ?>>> suppliers : featuresForState)
{
for (Supplier<ConfiguredFeature<?, ?>> featureSupplier : suppliers)
{
ConfiguredFeature<?, ?> configuredFeature = featureSupplier.get();
if (!allowUnstableFeatures &&
FEATURES_TO_AVOID.containsKey(configuredFeature.hashCode()))
continue;
try
{
configuredFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition());
}
catch (ConcurrentModificationException | UnsupportedOperationException e)
{
// This will happen. I'm not sure what to do about it
// except pray that it doesn't affect the normal world generation
// in any harmful way.
// Update: this can cause crashes and high CPU usage.
// Issue #35
// I tried cloning the config for each feature, but that
// path was blocked since I can't clone lambda methods.
// I tried using a deep cloning library and discovered
// the problem there.
// ( https://github.com/kostaskougios/cloning
// and
// https://github.com/EsotericSoftware/kryo )
if (!allowUnstableFeatures)
FEATURES_TO_AVOID.put(configuredFeature.hashCode(), configuredFeature);
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
}
// This will happen when the LodServerWorld
// isn't able to return something that a feature
// generator needs
catch (Exception e)
{
// I'm not sure what happened, print to the log
e.printStackTrace();
if (!allowUnstableFeatures)
FEATURES_TO_AVOID.put(configuredFeature.hashCode(), configuredFeature);
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
}
}
}
}
// generate a Lod like normal
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.FEATURES));
}
/**
* Generates using MC's ServerWorld.
* <p>
* on pre generated chunks 0 - 1 ms <br>
* on un generated chunks 0 - 50 ms <br>
* with the median seeming to hover around 15 - 30 ms <br>
* and outliers in the 100 - 200 ms range <br>
* <p>
* Note this should not be multithreaded and does cause server/simulation lag
* (Higher lag for generating than loading)
*/
@Override
public void generateFull(AbstractChunkPosWrapper pos)
{
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(serverWorld.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FEATURES)), new LodBuilderConfig(DistanceGenerationMode.FULL));
}
/*
* performance/generation tests related to
* serverWorld.getChunk(x, z, ChunkStatus. *** )
true/false is whether they generated blocks or not
the time is how long it took to generate
ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P)
ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks)
ChunkStatus.BIOMES 1 - 10 ms false (no height)
ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone)
ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass)
ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass)
ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass)
ChunkStatus.FEATURES 7 - 25 ms true
ChunkStatus.HEIGHTMAPS 20 - 40 ms true
ChunkStatus.LIGHT 20 - 40 ms true
ChunkStatus.FULL 30 - 50 ms true
ChunkStatus.SPAWN 50 - 80 ms true
At this point I would suggest using FEATURES, as it generates snow and trees
(and any other object that are needed to make biomes distinct)
Otherwise, if snow/trees aren't necessary SURFACE is the next fastest (although not by much)
*/
}
@@ -1,382 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.handlers;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.objects.LevelContainer;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodRegion;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
/**
* This object handles creating LodRegions
* from files and saving LodRegion objects
* to file.
*
* @author James Seibel
* @version 9-7-2021
*/
public class LodDimensionFileHandler
{
/**
* This is what separates each piece of data
*/
public static final char DATA_DELIMITER = ',';
private LodDimension loadedDimension = null;
public long regionLastWriteTime[][];
private File dimensionDataSaveFolder;
/**
* lod
*/
private static final String FILE_NAME_PREFIX = "lod";
/**
* .txt
*/
private static final String FILE_EXTENSION = ".txt";
/**
* detail-#
*/
private static final String DETAIL_FOLDER_NAME_PREFIX = "detail-";
/**
* .tmp <br>
* Added to the end of the file path when saving to prevent
* nulling a currently existing file. <br>
* After the file finishes saving it will end with
* FILE_EXTENSION.
*/
private static final String TMP_FILE_EXTENSION = ".tmp";
/**
* This is the file version currently accepted by this
* file handler, older versions (smaller numbers) will be deleted and overwritten,
* newer versions (larger numbers) will be ignored and won't be read.
*/
public static final int LOD_SAVE_FILE_VERSION = 5;
/**
* This is the string written before the file version
*/
private static final String LOD_FILE_VERSION_PREFIX = "lod_save_file_version";
/**
* Allow saving asynchronously, but never try to save multiple regions
* at a time
*/
private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension)
{
if (newSaveFolder == null)
throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to.");
dimensionDataSaveFolder = newSaveFolder;
loadedDimension = newLoadedDimension;
// these two variable are used in sync with the LodDimension
regionLastWriteTime = new long[loadedDimension.getWidth()][loadedDimension.getWidth()];
for (int i = 0; i < loadedDimension.getWidth(); i++)
for (int j = 0; j < loadedDimension.getWidth(); j++)
regionLastWriteTime[i][j] = -1;
}
//================//
// read from file //
//================//
/**
* Return the LodRegion region at the given coordinates.
* (null if the file doesn't exist)
*/
public LodRegion loadRegionFromFile(byte detailLevel, RegionPos regionPos, DistanceGenerationMode generationMode)
{
int regionX = regionPos.x;
int regionZ = regionPos.z;
LodRegion region = new LodRegion(LodUtil.REGION_DETAIL_LEVEL,regionPos, generationMode);
for (byte tempDetailLevel = LodUtil.REGION_DETAIL_LEVEL; tempDetailLevel >= detailLevel; tempDetailLevel--)
{
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, tempDetailLevel);
try
{
// if the fileName was null that means the folder is inaccessible
// for some reason
if (fileName == null)
throw new IllegalArgumentException("Unable to read region [" + regionX + ", " + regionZ + "] file, no fileName.");
File f = new File(fileName);
if (!f.exists())
{
// there wasn't a file, don't
// return anything
continue;
}
String data = "";
BufferedReader bufferedReader = new BufferedReader(new FileReader(f));
data = bufferedReader.readLine();
int fileVersion = -1;
if (data != null && !data.isEmpty())
{
// try to get the file version
try
{
fileVersion = Integer.parseInt(data.substring(data.indexOf(' ')).trim());
} catch (NumberFormatException | StringIndexOutOfBoundsException e)
{
// this file doesn't have a version
// keep the version as -1
fileVersion = -1;
}
// check if this file can be read by this file handler
if (fileVersion < LOD_SAVE_FILE_VERSION)
{
// the file we are reading is an older version,
// close the reader and delete the file.
bufferedReader.close();
f.delete();
ClientProxy.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ") version found: " + fileVersion +
", version requested: " + LOD_SAVE_FILE_VERSION +
" File was been deleted.");
break;
} else if (fileVersion > LOD_SAVE_FILE_VERSION)
{
// the file we are reading is a newer version,
// close the reader and ignore the file, we don't
// want to accidently delete anything the user may want.
bufferedReader.close();
ClientProxy.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ") version found: " + fileVersion +
", version requested: " + LOD_SAVE_FILE_VERSION +
" this region will not be written to in order to protect the newer file.");
break;
}
} else
{
// there is no data in this file
bufferedReader.close();
break;
}
// this file is a readable version, begin reading the file
data = bufferedReader.readLine();
bufferedReader.close();
region.addLevel(new LevelContainer(data));
} catch (Exception e)
{
// the buffered reader encountered a
// problem reading the file
ClientProxy.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + e.getMessage() + "]: ");
e.printStackTrace();
}
}
if (region.getMinDetailLevel() >= detailLevel)
region.expand(detailLevel);
return region;
}
//==============//
// Save to File //
//==============//
/**
* Save all dirty regions in this LodDimension to file.
*/
public void saveDirtyRegionsToFileAsync()
{
fileWritingThreadPool.execute(saveDirtyRegionsThread);
}
private Thread saveDirtyRegionsThread = new Thread(() ->
{
try
{
for (int i = 0; i < loadedDimension.getWidth(); i++)
{
for (int j = 0; j < loadedDimension.getWidth(); j++)
{
if (loadedDimension.isRegionDirty[i][j] && loadedDimension.regions[i][j] != null)
{
saveRegionToFile(loadedDimension.regions[i][j]);
loadedDimension.isRegionDirty[i][j] = false;
}
}
}
} catch (Exception e)
{
e.printStackTrace();
}
});
/**
* Save a specific region to disk.<br>
* Note: <br>
* 1. If a file already exists for a newer version
* the file won't be written.<br>
* 2. This will save to the LodDimension that this
* handler is associated with.
*/
private void saveRegionToFile(LodRegion region)
{
// convert to region coordinates
int x = region.regionPosX;
int z = region.regionPosZ;
for (byte detailLevel = region.getMinDetailLevel(); detailLevel <= LodUtil.REGION_DETAIL_LEVEL; detailLevel++)
{
String fileName = getFileNameAndPathForRegion(x, z, region.getGenerationMode(), detailLevel);
File oldFile = new File(fileName);
// if the fileName was null that means the folder is inaccessible
// for some reason
if (fileName == null)
{
ClientProxy.LOGGER.warn("Unable to save region [" + x + ", " + z + "] to file, no fileName.");
return;
}
try
{
// make sure the file and folder exists
if (!oldFile.exists())
{
// the file doesn't exist,
// create it and the folder if need be
if (!oldFile.getParentFile().exists())
oldFile.getParentFile().mkdirs();
oldFile.createNewFile();
} else
{
// the file exists, make sure it
// is the correct version.
// (to make sure we don't overwrite a newer
// version file if it exists)
BufferedReader br = new BufferedReader(new FileReader(oldFile));
String s = br.readLine();
int fileVersion = LOD_SAVE_FILE_VERSION;
if (s != null && !s.isEmpty())
{
// try to get the file version
try
{
fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim());
} catch (NumberFormatException | StringIndexOutOfBoundsException e)
{
// this file doesn't have a correctly formated version
// just overwrite the file
}
}
br.close();
// check if this file can be written to by the file handler
if (fileVersion <= LOD_SAVE_FILE_VERSION)
{
// we are good to continue and overwrite the old file
} else // if(fileVersion > LOD_SAVE_FILE_VERSION)
{
// the file we are reading is a newer version,
// don't write anything, we don't want to accidently
// delete anything the user may want.
return;
}
}
// the old file is good, now create a new save file
File newFile = new File(fileName + TMP_FILE_EXTENSION);
FileWriter fw = new FileWriter(newFile);
// add the version of this file
fw.write(LOD_FILE_VERSION_PREFIX + " " + LOD_SAVE_FILE_VERSION + "\n");
// add each LodChunk to the file
fw.write(region.getLevel(detailLevel).toString());
fw.close();
// overwrite the old file with the new one
Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e)
{
ClientProxy.LOGGER.error("LOD file write error. Unable to write to [" + fileName + "] error [" + e.getMessage() + "]: ");
e.printStackTrace();
}
}
}
//================//
// helper methods //
//================//
/**
* Return the name of the file that should contain the
* region at the given x and z. <br>
* Returns null if this object isn't ready to read and write. <br><br>
* <p>
* example: "lod.0.0.txt" <br><br>
* <p>
* Returns null if there is an IO Exception.
*/
private String getFileNameAndPathForRegion(int regionX, int regionZ, DistanceGenerationMode generationMode, byte detailLevel)
{
try
{
// saveFolder is something like
// ".\Super Flat\DIM-1\data"
// or
// ".\Super Flat\data"
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar +
generationMode.toString() + File.separatorChar +
DETAIL_FOLDER_NAME_PREFIX + detailLevel + File.separatorChar +
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION;
} catch (IOException | SecurityException e)
{
ClientProxy.LOGGER.warn("Unable to get the filename for the region [" + regionX + ", " + regionZ + "], error: [" + e.getMessage() + "], stacktrace: ");
e.printStackTrace();
return null;
}
}
}
@@ -1,118 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.handlers;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import com.seibel.lod.enums.FogQuality;
import com.seibel.lod.wrappers.MinecraftWrapper;
/**
* This object is used to get variables from methods
* where they are private. Specifically the fog setting
* in Optifine.
*
* @author James Seibel
* @version 9-7-2021
*/
public class ReflectionHandler
{
public static final ReflectionHandler INSTANCE = new ReflectionHandler();
private MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
public Field ofFogField = null;
public Method vertexBufferUploadMethod = null;
private ReflectionHandler()
{
setupFogField();
}
/**
* finds the Optifine fog type field
*/
private void setupFogField()
{
// get every variable from the entity renderer
Field[] optionFields = mc.getOptions().getClass().getDeclaredFields();
// try and find the ofFogType variable in gameSettings
for (Field field : optionFields)
{
if (field.getName().equals("ofFogType"))
{
ofFogField = field;
return;
}
}
// we didn't find the field,
// either optifine isn't installed, or
// optifine changed the name of the variable
}
/**
* Get what type of fog optifine is currently set to render.
*/
public FogQuality getFogQuality()
{
if (ofFogField == null)
{
// either optifine isn't installed,
// the variable name was changed, or
// the setup method wasn't called yet.
return FogQuality.FANCY;
}
int returnNum = 0;
try
{
returnNum = (int) ofFogField.get(mc.getOptions());
} catch (IllegalArgumentException | IllegalAccessException e)
{
e.printStackTrace();
}
switch (returnNum)
{
// optifine's "default" option,
// it should never be called in this case
case 0:
return FogQuality.FAST;
// normal options
case 1:
return FogQuality.FAST;
case 2:
return FogQuality.FANCY;
case 3:
return FogQuality.OFF;
default:
return FogQuality.FAST;
}
}
}
@@ -1,97 +0,0 @@
package com.seibel.lod.objects;
public class DataPoint
{
public final static int HEIGHT_SHIFT = 54;
public final static int DEPTH_SHIFT = 44;
public final static int RED_SHIFT = 36;
public final static int GREEN_SHIFT = 28;
public final static int BLUE_SHIFT = 20;
public final static int GEN_TYPE_SHIFT = 17;
public final static int LIGHT_SHIFT = 13;
public final static int EXISTENCE_SHIFT = 0;
public final static long HEIGHT_MASK = Long.parseUnsignedLong("1111111111", 2);
public final static long DEPTH_MASK = Long.parseUnsignedLong("1111111111", 2);
public final static long RED_MASK = Long.parseUnsignedLong("11111111", 2);
public final static long GREEN_MASK = Long.parseUnsignedLong("11111111", 2);
public final static long BLUE_MASK = Long.parseUnsignedLong("11111111", 2);
public final static long GEN_TYPE_MASK = Long.parseUnsignedLong("111", 2);
public final static long LIGHT_MASK = Long.parseUnsignedLong("1111", 2);
public final static long EXISTENCE_MASK = 1;
public static long createDataPoint(int height, int depth, int color)
{
int red = (getRed(color) << 16) & 0x00FF0000;
int green = (getGreen(color) << 8) & 0x0000FF00;
int blue = getBlue(color)& 0x000000FF;
return createDataPoint(height, depth, red, green, blue);
}
public static long createDataPoint(int height, int depth, int red, int green, int blue)
{
long dataPoint = 0;
dataPoint += (height & HEIGHT_MASK) << HEIGHT_SHIFT;
dataPoint += (depth & DEPTH_MASK) << DEPTH_SHIFT;
dataPoint += (red & RED_MASK) << RED_SHIFT;
dataPoint += (green & GREEN_MASK) << GREEN_SHIFT;
dataPoint += (blue & BLUE_MASK) << BLUE_SHIFT;
dataPoint += 1;
return dataPoint;
}
public static short getHeight(long dataPoint)
{
return (short) ((dataPoint >> HEIGHT_SHIFT) & HEIGHT_MASK);
}
public static short getDepth(long dataPoint)
{
return (short) ((dataPoint >> DEPTH_SHIFT) & DEPTH_MASK);
}
public static short getRed(long dataPoint)
{
return (short) ((dataPoint >> RED_SHIFT) & RED_MASK);
}
public static short getGreen(long dataPoint)
{
return (short) ((dataPoint >> GREEN_SHIFT) & GREEN_MASK);
}
public static short getBlue(long dataPoint)
{
return (short) ((dataPoint >> BLUE_SHIFT) & BLUE_MASK);
}
public static boolean doesItExist(long dataPoint)
{
return ((dataPoint & EXISTENCE_MASK) == 1);
}
public static int getColor(long dataPoint)
{
int R = (getRed(dataPoint) << 16) & 0x00FF0000;
int G = (getGreen(dataPoint) << 8) & 0x0000FF00;
int B = getBlue(dataPoint)& 0x000000FF;
return 0xFF000000 | R | G | B;
}
public static String toString(long dataPoint)
{
StringBuilder s = new StringBuilder();
s.append(getHeight(dataPoint));
s.append(" ");
s.append(getDepth(dataPoint));
s.append(" ");
s.append(getRed(dataPoint));
s.append(" ");
s.append(getBlue(dataPoint));
s.append(" ");
s.append(getGreen(dataPoint));
s.append('\n');
return s.toString();
}
}
@@ -1,67 +0,0 @@
package com.seibel.lod.objects;
import java.io.Serializable;
import com.seibel.lod.util.LodUtil;
public class LevelContainer implements Serializable
{
/** This is here so that Eclipse doesn't complain */
private static final long serialVersionUID = -4930855068717998385L;
public static final char DATA_DELIMITER = ',';
public final byte detailLevel;
public final long[][] data;
public LevelContainer(byte detailLevel, long[][] data)
{
this.detailLevel = detailLevel;
this.data = data;
}
public LevelContainer(String inputString)
{
int index = 0;
int lastIndex = 0;
index = inputString.indexOf(DATA_DELIMITER, 0);
this.detailLevel = (byte) Integer.parseInt(inputString.substring(0, index));
int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
this.data = new long[size][size];
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
lastIndex = index;
index = inputString.indexOf(DATA_DELIMITER, lastIndex + 1);
data[x][z] = Long.parseLong(inputString.substring(lastIndex + 1, index), 16);
}
}
}
@Override
public String toString()
{
StringBuilder stringBuilder = new StringBuilder();
int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
stringBuilder.append(detailLevel);
stringBuilder.append(DATA_DELIMITER);
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
//Converting the dataToHex
stringBuilder.append(Long.toHexString(data[x][z]));
stringBuilder.append(DATA_DELIMITER);
}
}
return stringBuilder.toString();
}
}
@@ -1,253 +0,0 @@
package com.seibel.lod.objects;
import com.seibel.lod.util.LodUtil;
public class LevelPosUtil
{
public static int[] convert(int[] levelPos, byte newDetailLevel)
{
return convert(getDetailLevel(levelPos), getPosX(levelPos), getPosZ(levelPos), newDetailLevel);
}
public static int[] convert(byte detailLevel, int posX, int posZ, byte newDetailLevel)
{
int width;
if (newDetailLevel >= detailLevel)
{
width = 1 << (newDetailLevel - detailLevel);
return createLevelPos(
newDetailLevel,
Math.floorDiv(posX, width),
Math.floorDiv(posZ, width));
} else
{
width = 1 << (detailLevel - newDetailLevel);
return createLevelPos(
newDetailLevel,
posX * width,
posZ * width);
}
}
public static int[] createLevelPos(byte detailLevel, int posX, int posZ)
{
return new int[]{detailLevel, posX, posZ};
}
public static int convert(byte detailLevel, int pos, byte newDetailLevel)
{
int width;
if (newDetailLevel >= detailLevel)
{
width = 1 << (newDetailLevel - detailLevel);
return Math.floorDiv(pos, width);
} else
{
width = 1 << (detailLevel - newDetailLevel);
return pos * width;
}
}
public static int getRegion(byte detailLevel, int pos)
{
return Math.floorDiv(pos, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel));
}
public static int getRegionModule(byte detailLevel, int pos)
{
return Math.floorMod(pos, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel));
}
public static byte getDetailLevel(int[] levelPos)
{
return (byte) levelPos[0];
}
public static int getPosX(int[] levelPos)
{
return levelPos[1];
}
public static int getPosZ(int[] levelPos)
{
return levelPos[2];
}
public static int getDistance(int[] levelPos)
{
return levelPos[3];
}
public static int[] getRegionModule(int[] levelPos)
{
return getRegionModule(getDetailLevel(levelPos), getPosX(levelPos), getPosZ(levelPos));
}
public static int[] getRegionModule(byte detailLevel, int posX, int posZ)
{
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
return createLevelPos(
detailLevel,
Math.floorMod(posX, width),
Math.floorMod(posZ, width));
}
public static int[] applyOffset(int[] levelPos, int xOffset, int zOffset)
{
return createLevelPos(
getDetailLevel(levelPos),
getPosX(levelPos) + xOffset,
getPosZ(levelPos) + zOffset);
}
public static int[] applyLevelOffset(int[] levelPos, byte detailOffset, int xOffset, int zOffset)
{
return createLevelPos(
getDetailLevel(levelPos),
getPosX(levelPos) + xOffset * (1 << detailOffset),
getPosZ(levelPos) + zOffset * (1 << detailOffset));
}
public static int getRegionPosX(int[] levelPos)
{
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - getDetailLevel(levelPos));
return Math.floorDiv(getPosX(levelPos), width);
}
public static int getRegionPosZ(int[] levelPos)
{
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - getDetailLevel(levelPos));
return Math.floorDiv(getPosZ(levelPos), width);
}
public static int getChunkPos(byte detailLevel, int pos)
{
return convert(detailLevel,pos, LodUtil.CHUNK_DETAIL_LEVEL);
}
public static int getChunkPosX(int[] levelPos)
{
levelPos = convert(levelPos, LodUtil.CHUNK_DETAIL_LEVEL);
return getPosX(levelPos);
}
public static int getChunkPosZ(int[] levelPos)
{
levelPos = convert(levelPos, LodUtil.CHUNK_DETAIL_LEVEL);
return getPosZ(levelPos);
}
public static int maxDistance(int[] levelPos, int playerPosX, int playerPosZ)
{
return maxDistance(getDetailLevel(levelPos), getPosX(levelPos), getPosZ(levelPos), playerPosX, playerPosZ);
}
public static int maxDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ)
{
int width = 1 << detailLevel;
int startPosX = posX * width;
int startPosZ = posZ * width;
int endPosX = startPosX + width;
int endPosZ = startPosZ + width;
int maxDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
return maxDistance;
}
public static int maxDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ, int xRegion, int zRegion)
{
int width = 1 << detailLevel;
int startPosX = xRegion * 512 + posX * width;
int startPosZ = zRegion * 512 + posZ * width;
int endPosX = startPosX + width;
int endPosZ = startPosZ + width;
int maxDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
return maxDistance;
}
public static int minDistance(int[] levelPos, int playerPosX, int playerPosZ)
{
return minDistance(getDetailLevel(levelPos), getPosX(levelPos), getPosZ(levelPos), playerPosX, playerPosZ);
}
public static int minDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ)
{
int width = 1 << detailLevel;
int startPosX = posX * width;
int startPosZ = posZ * width;
int endPosX = startPosX + width;
int endPosZ = startPosZ + width;
boolean inXArea = playerPosX >= startPosX && playerPosX <= endPosX;
boolean inZArea = playerPosZ >= startPosZ && playerPosZ <= endPosZ;
if (inXArea && inZArea)
{
return 0;
} else if (inXArea)
{
return Math.min(
Math.abs(playerPosZ - startPosZ),
Math.abs(playerPosZ - endPosZ)
);
} else if (inZArea)
{
return Math.min(
Math.abs(playerPosX - startPosX),
Math.abs(playerPosX - endPosX)
);
} else
{
int minDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
return minDistance;
}
}
public static int compareDistance(int firstDistance, int secondDistance)
{
return Integer.compare(
firstDistance,
secondDistance);
}
public static int compareLevelAndDistance(byte firstDetail, int firstDistance, byte secondDetail, int secondDistance)
{
int compareResult = Integer.compare(
secondDetail,
firstDetail);
// System.out.println("comparing level "+ firstDetail + " " + secondDetail + " " + compareResult);
if (compareResult == 0)
{
compareResult = compareDistance(
firstDistance,
secondDistance);
// System.out.println("Equal level "+ firstDistance + " " + secondDistance + " " + compareResult);
}
return compareResult;
}
public static String toString(int[] levelPos)
{
return (getDetailLevel(levelPos) + " " + getPosX(levelPos) + " " + getPosZ(levelPos));
}
public static String toString(byte detailLevel, int posX, int posZ)
{
return (detailLevel + " " + posX + " " + posZ);
}
}
@@ -1,678 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.objects;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.handlers.LodDimensionFileHandler;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerChunkProvider;
import net.minecraft.world.server.ServerWorld;
/**
* This object holds all loaded LOD regions
* for a given dimension.
*
* @author Leonardo Amato
* @author James Seibel
* @version 8-8-2021
*/
public class LodDimension
{
public final DimensionType dimension;
/**
* measured in regions
*/
private volatile int width;
/**
* measured in regions
*/
private volatile int halfWidth;
public volatile LodRegion regions[][];
public volatile boolean isRegionDirty[][];
public volatile boolean regen[][];
/**
* if true that means there are regions in this dimension
* that need to have their buffers rebuilt.
*/
public volatile boolean regenDimension = false;
private volatile RegionPos center;
private volatile ChunkPos lastGenChunk;
private volatile ChunkPos lastCutChunk;
private LodDimensionFileHandler fileHandler;
private ExecutorService cutAndGenThreads = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - cutAndGen"));
/**
* Creates the dimension centered at (0,0)
*
* @param newWidth in regions
*/
public LodDimension(DimensionType newDimension, LodWorld lodWorld, int newWidth)
{
lastCutChunk = null;
lastGenChunk = null;
dimension = newDimension;
width = newWidth;
halfWidth = (int) Math.floor(width / 2);
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
if (newDimension != null && lodWorld != null)
{
try
{
File saveDir;
if (mc.hasSingleplayerServer())
{
// local world
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(newDimension);
// provider needs a separate variable to prevent
// the compiler from complaining
ServerChunkProvider provider = serverWorld.getChunkSource();
saveDir = new File(provider.dataStorage.dataFolder.getCanonicalFile().getPath() + File.separatorChar + "lod");
} else
{
// connected to server
saveDir = new File(mc.getGameDirectory().getCanonicalFile().getPath() +
File.separatorChar + "lod server data" + File.separatorChar + mc.getCurrentDimensionId());
}
fileHandler = new LodDimensionFileHandler(saveDir, this);
} catch (IOException e)
{
// the file handler wasn't able to be created
// we won't be able to read or write any files
}
}
regions = new LodRegion[width][width];
isRegionDirty = new boolean[width][width];
regen = new boolean[width][width];
//treeGenerator((int) mc.player.getX(),(int) mc.player.getZ());
// populate isRegionDirty
for (int i = 0; i < width; i++)
for (int j = 0; j < width; j++)
isRegionDirty[i][j] = false;
center = new RegionPos(0, 0);
}
/**
* Move the center of this LodDimension and move all owned
* regions over by the given x and z offset. <br><br>
* <p>
* Synchronized to prevent multiple moves happening on top of each other.
*/
public synchronized void move(RegionPos regionOffset)
{
int xOffset = regionOffset.x;
int zOffset = regionOffset.z;
// if the x or z offset is equal to or greater than
// the total size, just delete the current data
// and update the centerX and/or centerZ
if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width)
{
for (int x = 0; x < width; x++)
{
for (int z = 0; z < width; z++)
{
regions[x][z] = null;
}
}
// update the new center
center.x += xOffset;
center.z += zOffset;
return;
}
// X
if (xOffset > 0)
{
// move everything over to the left (as the center moves to the right)
for (int x = 0; x < width; x++)
{
for (int z = 0; z < width; z++)
{
if (x + xOffset < width)
regions[x][z] = regions[x + xOffset][z];
else
regions[x][z] = null;
}
}
} else
{
// move everything over to the right (as the center moves to the left)
for (int x = width - 1; x >= 0; x--)
{
for (int z = 0; z < width; z++)
{
if (x + xOffset >= 0)
regions[x][z] = regions[x + xOffset][z];
else
{
regions[x][z] = null;
}
}
}
}
// Z
if (zOffset > 0)
{
// move everything up (as the center moves down)
for (int x = 0; x < width; x++)
{
for (int z = 0; z < width; z++)
{
if (z + zOffset < width)
regions[x][z] = regions[x][z + zOffset];
else
regions[x][z] = null;
}
}
} else
{
// move everything down (as the center moves up)
for (int x = 0; x < width; x++)
{
for (int z = width - 1; z >= 0; z--)
{
if (z + zOffset >= 0)
regions[x][z] = regions[x][z + zOffset];
else
regions[x][z] = null;
}
}
}
// update the new center
center.x += xOffset;
center.z += zOffset;
}
/**
* return needed memory in byte
*/
public int getMinMemoryNeeded()
{
int count = 0;
LodRegion region;
for (int x = 0; x < regions.length; x++)
{
for (int z = 0; z < regions.length; z++)
{
region = regions[x][z];
if (region != null)
{
count += region.getMinMemoryNeeded();
}
}
}
return count;
}
/**
* Gets the region at the given X and Z
* <br>
* Returns null if the region doesn't exist
* or is outside the loaded area.
*/
public LodRegion getRegion(byte detailLevel, int posX, int posZ)
{
int xRegion = LevelPosUtil.getRegion(detailLevel, posX);
int zRegion = LevelPosUtil.getRegion(detailLevel, posZ);
int xIndex = (xRegion - center.x) + halfWidth;
int zIndex = (zRegion - center.z) + halfWidth;
if (!regionIsInRange(xRegion, zRegion))
return null;
//throw new ArrayIndexOutOfBoundsException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " out of range");
else if (regions[xIndex][zIndex] == null)
return null;
//throw new InvalidParameterException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " not currently initialized");
else if (regions[xIndex][zIndex].getMinDetailLevel() > detailLevel)
return null;
//throw new InvalidParameterException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " currently only reach level " + regions[xIndex][zIndex].getMinDetailLevel());
return regions[xIndex][zIndex];
}
/**
* Gets the region at the given X and Z
* <br>
* Returns null if the region doesn't exist
* or is outside the loaded area.
*/
public LodRegion getRegion(int regionPosX, int regionPosZ)
{
int xIndex = (regionPosX - center.x) + halfWidth;
int zIndex = (regionPosZ - center.z) + halfWidth;
if (!regionIsInRange(regionPosX, regionPosZ))
return null;
//throw new ArrayIndexOutOfBoundsException("Region " + regionPosX + " " + regionPosZ + " out of range");
else if (regions[xIndex][zIndex] == null)
return null;
//throw new InvalidParameterException("Region " + regionPosX + " " + regionPosZ + " not currently initialized");
return regions[xIndex][zIndex];
}
/**
* Overwrite the LodRegion at the location of newRegion with newRegion.
*
* @throws ArrayIndexOutOfBoundsException if newRegion is outside what can be stored in this LodDimension.
*/
public synchronized void addOrOverwriteRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException
{
int xIndex = (newRegion.regionPosX - center.x) + halfWidth;
int zIndex = (newRegion.regionPosZ - center.z) + halfWidth;
if (!regionIsInRange(newRegion.regionPosX, newRegion.regionPosZ))
// out of range
throw new ArrayIndexOutOfBoundsException("Region " + newRegion.regionPosX + ", " + newRegion.regionPosZ + " out of range");
regions[xIndex][zIndex] = newRegion;
}
/**
*
*/
public void treeCutter(int playerPosX, int playerPosZ)
{
ChunkPos newPlayerChunk = new ChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ));
if (lastCutChunk == null)
lastCutChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1);
if (newPlayerChunk.x != lastCutChunk.x || newPlayerChunk.z != lastCutChunk.z)
{
lastCutChunk = newPlayerChunk;
Thread thread = new Thread(() ->
{
int regionX;
int regionZ;
int minDistance;
byte detail;
byte levelToCut;
for (int x = 0; x < regions.length; x++)
{
for (int z = 0; z < regions.length; z++)
{
regionX = (x + center.x) - halfWidth;
regionZ = (z + center.z) - halfWidth;
//we start checking from the first circle. If the whole region is in the circle
//we proceed to cut all the level lower than the level of circle 1 and we break
//if this is not the case w
if (regions[x][z] != null)
{
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
detail = DetailDistanceUtil.getDistanceTreeCutInverse(minDistance);
levelToCut = DetailDistanceUtil.getCutLodDetail(detail);
if (regions[x][z].getMinDetailLevel() > levelToCut)
{
regions[x][z].cutTree(levelToCut);
}
}
}// region z
}// region z
});
cutAndGenThreads.execute(thread);
}
}
/**
*
*/
public void treeGenerator(int playerPosX, int playerPosZ)
{
DistanceGenerationMode generationMode = LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get();
ChunkPos newPlayerChunk = new ChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ));
if (lastGenChunk == null)
lastGenChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1);
if (newPlayerChunk.x != lastGenChunk.x || newPlayerChunk.z != lastGenChunk.z)
{
lastGenChunk = newPlayerChunk;
Thread thread = new Thread(() ->
{
int regionX;
int regionZ;
LodRegion region;
int minDistance;
byte detail;
byte levelToGen;
for (int x = 0; x < regions.length; x++)
{
for (int z = 0; z < regions.length; z++)
{
regionX = (x + center.x) - halfWidth;
regionZ = (z + center.z) - halfWidth;
final RegionPos regionPos = new RegionPos(regionX, regionZ);
region = regions[x][z];
//We require that the region we are checking is loaded with at least this level
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
detail = DetailDistanceUtil.getDistanceTreeGenInverse(minDistance);
levelToGen = DetailDistanceUtil.getLodGenDetail(detail).detailLevel;
if (region == null || region.getGenerationMode() != generationMode)
{
//First case, region has to be initialized
//We check if there is a file at the target level
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode);
//if there is no file we initialize the region
if (regions[x][z] == null)
{
regions[x][z] = new LodRegion(levelToGen, regionPos, generationMode);
}
regen[x][z] = true;
regenDimension = true;
} else if (region.getMinDetailLevel() > levelToGen)
{
//Second case, region has been initialized but at a higher level
//We expand the region by introducing the missing layer
region.expand(levelToGen);
}
}
}
});
cutAndGenThreads.execute(thread);
}
}
/**
* Add the given LOD to this dimension at the coordinate
* stored in the LOD. If an LOD already exists at the given
* coordinates it will be overwritten.
*/
public synchronized Boolean addData(byte detailLevel, int posX, int posZ, long lodDataPoint, boolean dontSave, boolean serverQuality)
{
// don't continue if the region can't be saved
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
LodRegion region = getRegion(regionPosX, regionPosZ);
if (region == null)
return false;
boolean nodeAdded = region.addData(detailLevel, posX, posZ, lodDataPoint, serverQuality);
// only save valid LODs to disk
if (!dontSave && fileHandler != null)
{
try
{
// mark the region as dirty so it will be saved to disk
int xIndex = (regionPosX - center.x) + halfWidth;
int zIndex = (regionPosZ - center.z) + halfWidth;
isRegionDirty[xIndex][zIndex] = true;
regen[xIndex][zIndex] = true;
regenDimension = true;
} catch (ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
// This method was probably called when the dimension was changing size.
// Hopefully this shouldn't be an issue.
}
}
return nodeAdded;
}
public void setToRegen(int xRegion, int zRegion)
{
int xIndex = (xRegion - center.x) + halfWidth;
int zIndex = (zRegion - center.z) + halfWidth;
regen[xIndex][zIndex] = true;
}
/**
* method to get all the quadtree level that have to be generated based on the position of the player
*
* @return list of quadTrees
*/
public PosToGenerateContainer getDataToGenerate(byte farDetail, int maxDataToGenerate, double farRatio, int playerPosX, int playerPosZ)
{
PosToGenerateContainer posToGenerate = new PosToGenerateContainer(farDetail, maxDataToGenerate, (int) (maxDataToGenerate * farRatio), playerPosX, playerPosZ);
int n = regions.length;
int xIndex;
int zIndex;
LodRegion region;
for (int xRegion = 0; xRegion < n; xRegion++)
{
for (int zRegion = 0; zRegion < n; zRegion++)
{
xIndex = (xRegion + center.x) - halfWidth;
zIndex = (zRegion + center.z) - halfWidth;
region = getRegion(xIndex, zIndex);
if (region != null)
region.getDataToGenerate(posToGenerate, playerPosX, playerPosZ);
}
}
return posToGenerate;
}
/**
* method to get all the nodes that have to be rendered based on the position of the player
*
* @return list of nodes
*/
public void getDataToRender(PosToRenderContainer posToRender, RegionPos regionPos, int playerPosX, int playerPosZ)
{
LodRegion region = getRegion(regionPos.x, regionPos.z);
if (region != null)
region.getDataToRender(posToRender, playerPosX, playerPosZ);
}
/**
* Get the data point at the given X and Z coordinates
* in this dimension.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public long getData(byte detailLevel, int posX, int posZ)
{
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
{
return 0;
}
return region.getData(detailLevel, posX, posZ);
}
/**
* Get the data point at the given X and Z coordinates
* in this dimension.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public void updateData(byte detailLevel, int posX, int posZ)
{
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
{
return;
}
region.updateArea(detailLevel, posX, posZ);
}
/**
* return true if and only if the node at that position exist
*/
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
{
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
{
return false;
}
return region.doesDataExist(detailLevel, posX, posZ);
}
/**
* Get the region at the given X and Z coordinates from the
* RegionFileHandler.
*/
public LodRegion getRegionFromFile(RegionPos regionPos, byte detailLevel, DistanceGenerationMode generationMode)
{
if (fileHandler != null)
return fileHandler.loadRegionFromFile(detailLevel, regionPos, generationMode);
else
return null;
}
/**
* Save all dirty regions in this LodDimension to file.
*/
public void saveDirtyRegionsToFileAsync()
{
fileHandler.saveDirtyRegionsToFileAsync();
}
/**
* Returns whether the region at the given X and Z coordinates
* is within the loaded range.
*/
public boolean regionIsInRange(int regionX, int regionZ)
{
int xIndex = (regionX - center.x) + halfWidth;
int zIndex = (regionZ - center.z) + halfWidth;
return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width;
}
public int getCenterX()
{
return center.x;
}
public int getCenterZ()
{
return center.z;
}
public int getWidth()
{
if (regions != null)
{
// we want to get the length directly from the
// source to make sure it is in sync with region
// and isRegionDirty
return regions.length;
} else
{
return width;
}
}
public void setRegionWidth(int newWidth)
{
width = newWidth;
halfWidth = Math.floorDiv(width, 2);
regions = new LodRegion[width][width];
isRegionDirty = new boolean[width][width];
regen = new boolean[width][width];
// populate isRegionDirty
for (int i = 0; i < width; i++)
for (int j = 0; j < width; j++)
isRegionDirty[i][j] = false;
}
@Override
public String toString()
{
LodRegion region;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Dimension : \n");
for (int x = 0; x < regions.length; x++)
{
for (int z = 0; z < regions.length; z++)
{
region = regions[x][z];
if (region == null)
{
stringBuilder.append("n");
stringBuilder.append("\t");
} else
{
stringBuilder.append(region.getMinDetailLevel());
stringBuilder.append("\t");
}
}
stringBuilder.append("\n");
}
return stringBuilder.toString();
}
}
@@ -1,463 +0,0 @@
package com.seibel.lod.objects;
import com.seibel.lod.builders.LodBuilder;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodUtil;
/**
* STANDARD TO FOLLOW
* every coordinate called posX or posZ is a relative coordinate and not and absolute coordinate
* if an array contain coordinate the order is the following
* 0 for x, 1 for z in 2D
* 0 for x, 1 for y, 2 for z in 3D
*/
public class LodRegion
{
//x coord,
private byte minDetailLevel;
private static final byte POSSIBLE_LOD = 10;
//private int numberOfPoints;
private DistanceGenerationMode generationMode;
//For each of the following field the first slot is for the level of detail
//Important: byte have a [-128, 127] range. When converting from or to int a 128 should be added or removed
//If there is a bug with color then it's probably caused by this.
//in the future other fields like transparency and light level could be added
private long[][][] data;
public final int regionPosX;
public final int regionPosZ;
public LodRegion(LevelContainer levelContainer, RegionPos regionPos, DistanceGenerationMode generationMode)
{
this.generationMode = generationMode;
this.regionPosX = regionPos.x;
this.regionPosZ = regionPos.z;
this.minDetailLevel = levelContainer.detailLevel;
//Arrays of matrices
data = new long[POSSIBLE_LOD][][];
data[minDetailLevel] = levelContainer.data;
//Initialize all the different matrices
for (byte lod = (byte) (minDetailLevel + 1); lod <= LodUtil.REGION_DETAIL_LEVEL; lod++)
{
int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod);
data[lod] = new long[size][size];
}
updateArea(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ);
}
public LodRegion(byte minDetailLevel, RegionPos regionPos, DistanceGenerationMode generationMode)
{
this.generationMode = generationMode;
this.minDetailLevel = minDetailLevel;
this.regionPosX = regionPos.x;
this.regionPosZ = regionPos.z;
data = new long[POSSIBLE_LOD][][];
//Initialize all the different matrices
for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++)
{
int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod);
data[lod] = new long[size][size];
}
}
/**
* This method can be used to insert data into the LodRegion
*
* @param dataPoint
* @return
*/
public boolean addData(byte detailLevel, int posX, int posZ, long dataPoint, boolean serverQuality)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
if (!doesDataExist(detailLevel, posX, posZ) || serverQuality)
{
//update the number of node present
//if (!doesDataExist(detailLevel, posX, posZ)) numberOfPoints++;
//add the node data
this.data[detailLevel][posX][posZ] = dataPoint;
return true;
} else
{
return false;
}
}
/**
* This method will return the data in the position relative to the level of detail
*
* @return the data at the relative pos and level
*/
public long getData(byte detailLevel, int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return data[detailLevel][posX][posZ];
}
/**
* This method will return all the levelPos that are renderable according to the requisite given in input
*
* @return
*/
public void getDataToGenerate(PosToGenerateContainer posToGenerate, int playerPosX, int playerPosZ)
{
getDataToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ);
}
private void getDataToGenerate(PosToGenerateContainer posToGenerate, byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ)
{
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
//here i calculate the the LevelPos is in range
//This is important to avoid any kind of hole in the generation
//nt minDistance = LevelPosUtil.minDistance(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size, playerPosX, playerPosZ);
int maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
byte childDetailLevel = (byte) (detailLevel - 1);
int childPosX = posX * 2;
int childPosZ = posZ * 2;
int childSize = 1 << (LodUtil.REGION_DETAIL_LEVEL - childDetailLevel);
//we have reached the target detail level
if (DetailDistanceUtil.getDistanceGenerationInverse(maxDistance) > detailLevel)
{
return;
} else if (DetailDistanceUtil.getDistanceGenerationInverse(maxDistance) == detailLevel)
{
if (!doesDataExist(detailLevel, posX, posZ))
{
posToGenerate.addPosToGenerate(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size);
}
} else
{
//we want max a request per chunk. So for lod smaller than chunk we explore only the top rigth child
if (detailLevel > LodUtil.CHUNK_DETAIL_LEVEL)
{
int num = 0;
//We take all the children that are not generated to at least the generation level taken in input
for (int x = 0; x <= 1; x++)
{
for (int z = 0; z <= 1; z++)
{
if (!doesDataExist(childDetailLevel, childPosX + x, childPosZ + z))
{
num++;
posToGenerate.addPosToGenerate(childDetailLevel, childPosX + x + regionPosX * childSize, childPosZ + z + regionPosZ * childSize);
}
}
}
//only if all the children are correctly generated we go deeper
if (num == 0)
{
for (int x = 0; x <= 1; x++)
{
for (int z = 0; z <= 1; z++)
{
getDataToGenerate(posToGenerate, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ);
}
}
}
} else
//now we keep exploring the top right child
{
if (DetailDistanceUtil.getLodGenDetail(childDetailLevel).detailLevel <= (childDetailLevel))
{
if (!doesDataExist(childDetailLevel, childPosX, childPosZ))
{
posToGenerate.addPosToGenerate(childDetailLevel, childPosX + regionPosX * childSize, childPosZ + regionPosZ * childSize);
} else
{
getDataToGenerate(posToGenerate, childDetailLevel, childPosX, childPosZ, playerPosX, playerPosZ);
}
}
}
}
}
/**
* @return
*/
public void getDataToRender(PosToRenderContainer posToRender, int playerPosX, int playerPosZ)
{
getDataToRender(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ);
}
/**
* @return
*/
private void getDataToRender(PosToRenderContainer posToRender, byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ)
{
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
//here i calculate the the LevelPos is in range
//This is important to avoid any kind of hole in the rendering
int maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
byte supposedLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDistanceRenderingInverse(maxDistance));
if (supposedLevel > detailLevel)
return;
else if (supposedLevel == detailLevel)
{
posToRender.addPosToRender(detailLevel,
posX + regionPosX * size,
posZ + regionPosZ * size);
} else //case where (detailLevel > supposedLevel)
{
int childPosX = posX * 2;
int childPosZ = posZ * 2;
byte childDetailLevel = (byte) (detailLevel - 1);
int childrenCount = 0;
for (int x = 0; x <= 1; x++)
{
for (int z = 0; z <= 1; z++)
{
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z)) childrenCount++;
}
}
//If all the four children exist we go deeper
if (childrenCount == 4)
{
for (int x = 0; x <= 1; x++)
{
for (int z = 0; z <= 1; z++)
{
getDataToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ);
}
}
} else
{
posToRender.addPosToRender(detailLevel,
posX + regionPosX * size,
posZ + regionPosZ * size);
}
}
}
/**
*/
public void updateArea(byte detailLevel, int posX, int posZ)
{
int width;
int startX;
int startZ;
for (byte bottom = (byte) (minDetailLevel + 1); bottom <= detailLevel; bottom++)
{
startX = LevelPosUtil.convert(detailLevel, posX, bottom);
startZ = LevelPosUtil.convert(detailLevel, posZ, bottom);
width = 1 << (detailLevel - bottom);
for (int x = 0; x < width; x++)
{
for (int z = 0; z < width; z++)
{
update(bottom, startX + x, startZ + z);
}
}
}
for (byte up = (byte) (detailLevel + 1); up <= LodUtil.REGION_DETAIL_LEVEL; up++)
{
update(up,
LevelPosUtil.convert(detailLevel, posX, up),
LevelPosUtil.convert(detailLevel, posZ, up));
}
}
/**
*/
private void update(byte detailLevel, int posX, int posZ)
{
int numberOfChildren = 0;
int numberOfVoidChildren = 0;
int tempRed = 0;
int tempGreen = 0;
int tempBlue = 0;
int tempHeight = 0;
int tempDepth = 0;
int childPosX;
int childPosZ;
byte childDetailLevel;
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
for (int x = 0; x <= 1; x++)
{
for (int z = 0; z <= 1; z++)
{
childPosX = 2 * posX + x;
childPosZ = 2 * posZ + z;
childDetailLevel = (byte) (detailLevel - 1);
if (doesDataExist(childDetailLevel, childPosX, childPosZ))
{
if (!(DataPoint.getHeight(data[childDetailLevel][childPosX][childPosZ]) == LodBuilder.DEFAULT_HEIGHT
&& DataPoint.getDepth(data[childDetailLevel][childPosX][childPosZ]) == LodBuilder.DEFAULT_DEPTH))
{
numberOfChildren++;
tempRed += DataPoint.getRed(data[childDetailLevel][childPosX][childPosZ]);
tempGreen += DataPoint.getGreen(data[childDetailLevel][childPosX][childPosZ]);
tempBlue += DataPoint.getBlue(data[childDetailLevel][childPosX][childPosZ]);
tempHeight += DataPoint.getHeight(data[childDetailLevel][childPosX][childPosZ]);
tempDepth += DataPoint.getDepth(data[childDetailLevel][childPosX][childPosZ]);
} else
{
// void children have the default height (most likely -1)
// and represent a LOD with no blocks in it
numberOfVoidChildren++;
}
}
}
}
if (numberOfChildren > 0)
{
tempRed = tempRed / numberOfChildren;
tempGreen = tempGreen / numberOfChildren;
tempBlue = tempBlue / numberOfChildren;
tempHeight = tempHeight / numberOfChildren;
tempDepth = tempDepth / numberOfChildren;
} else if (numberOfVoidChildren > 0)
{
tempRed = (byte) 0;
tempGreen = (byte) 0;
tempBlue = (byte) 0;
tempHeight = LodBuilder.DEFAULT_HEIGHT;
tempDepth = LodBuilder.DEFAULT_DEPTH;
}
data[detailLevel][posX][posZ] = DataPoint.createDataPoint(tempHeight, tempDepth, tempRed, tempGreen, tempBlue);
}
/**
* @return
*/
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
{
if(detailLevel < minDetailLevel) return false;
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return DataPoint.doesItExist(data[detailLevel][posX][posZ]);
}
/**
* @return
*/
public DistanceGenerationMode getGenerationMode()
{
return generationMode;
}
public byte getMinDetailLevel()
{
return minDetailLevel;
}
/**
* This will be used to save a level
*
* @param detailLevel
* @return
*/
public LevelContainer getLevel(byte detailLevel)
{
if (detailLevel < minDetailLevel)
{
throw new IllegalArgumentException("getLevel asked for a level that does not exist: minimum " + minDetailLevel + " level requested " + detailLevel);
}
return new LevelContainer(detailLevel, data[detailLevel]);
}
/**
* @param levelContainer
*/
public void addLevel(LevelContainer levelContainer)
{
if (levelContainer.detailLevel < minDetailLevel - 1)
{
throw new IllegalArgumentException("addLevel requires a level that is at least the minimum level of the region -1 ");
}
if (levelContainer.detailLevel == minDetailLevel - 1) minDetailLevel = levelContainer.detailLevel;
data[levelContainer.detailLevel] = levelContainer.data;
}
/**
* @param detailLevel
*/
public void cutTree(byte detailLevel)
{
if (minDetailLevel < detailLevel)
{
for (byte tempLod = 0; tempLod < detailLevel; tempLod++)
{
data[tempLod] = new long[0][0];
}
minDetailLevel = detailLevel;
}
}
/**
* @param detailLevel
*/
public void expand(byte detailLevel)
{
if (detailLevel < minDetailLevel)
{
for (byte tempLod = detailLevel; tempLod < minDetailLevel; tempLod++)
{
int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod);
data[tempLod] = new long[size][size];
}
minDetailLevel = detailLevel;
}
}
/**
* return RegionPos of this lod region
*/
public RegionPos getRegionPos()
{
return new RegionPos(regionPosX, regionPosZ);
}
/**
* return needed memory in byte
*/
public int getMinMemoryNeeded()
{
int count = 0;
for (byte tempLod = LodUtil.REGION_DETAIL_LEVEL; tempLod > minDetailLevel; tempLod--)
{
//i'm doing a upper limit of the minimum
//Color should be just 3 byte but i'm gonna calculate as 12 byte
//Height and depth should be just 4 byte but i'm gonna calculate as 8 byte
count += Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod) * (8 + 3 + 2 + 2 + 1);
//count += Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod) * (24 + 8 + 8 + 8 + 8);
}
return count;
}
@Override
public String toString()
{
return getLevel(LodUtil.REGION_DETAIL_LEVEL).toString();
}
}
@@ -1,159 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.objects;
import java.util.Hashtable;
import java.util.Map;
import com.seibel.lod.proxy.ClientProxy;
import net.minecraft.world.DimensionType;
/**
* This stores all LODs for a given world.
*
* @author James Seibel
* @author Leonardo Amato
* @version 8-17-2021
*/
public class LodWorld
{
private String worldName;
private Map<DimensionType, LodDimension> lodDimensions;
/**
* If true then the LOD world is setup and ready to use
*/
private boolean isWorldLoaded = false;
public static final String NO_WORLD_LOADED = "No world loaded";
public LodWorld()
{
worldName = NO_WORLD_LOADED;
}
/**
* Set up the LodQuadTreeWorld with the given newWorldName. <br>
* This should be done whenever loading a new world.
*
* @param newWorldName name of the world
*/
public void selectWorld(String newWorldName)
{
if (newWorldName.isEmpty())
{
deselectWorld();
return;
}
if (worldName.equals(newWorldName))
// don't recreate everything if we
// didn't actually change worlds
return;
worldName = newWorldName;
lodDimensions = new Hashtable<DimensionType, LodDimension>();
isWorldLoaded = true;
}
/**
* Set the worldName to "No world loaded"
* and clear the lodDimensions Map. <br>
* This should be done whenever unloaded a world.
*/
public void deselectWorld()
{
worldName = NO_WORLD_LOADED;
lodDimensions = null;
isWorldLoaded = false;
}
/**
* Adds newStorage to this world, if a LodQuadTreeDimension
* already exists for the given dimension it is replaced.
*/
public void addLodDimension(LodDimension newStorage)
{
if (lodDimensions == null)
return;
lodDimensions.put(newStorage.dimension, newStorage);
}
/**
* Returns null if no LodQuadTreeDimension exists for the given dimension
*/
public LodDimension getLodDimension(DimensionType dimension)
{
if (lodDimensions == null)
return null;
return lodDimensions.get(dimension);
}
/**
* Resizes the max width in regions that each LodDimension
* should use.
*/
public void resizeDimensionRegionWidth(int newWidth)
{
if (lodDimensions == null)
return;
saveAllDimensions();
for (DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).setRegionWidth(newWidth);
}
/**
* Requests all dimensions save any dirty regions they may have.
*/
public void saveAllDimensions()
{
if (lodDimensions == null)
return;
ClientProxy.LOGGER.info("Saving LODs");
for (DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).saveDirtyRegionsToFileAsync();
}
public boolean getIsWorldLoaded()
{
return isWorldLoaded;
}
public String getWorldName()
{
return worldName;
}
@Override
public String toString()
{
return "World name: " + worldName;
}
}
@@ -1,63 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.objects;
import com.seibel.lod.enums.FogDistance;
import com.seibel.lod.enums.FogQuality;
/**
* This object is just a replacement for an array
* to make things easier to understand in the LodRenderer.
*
* @author James Seibel
* @version 7-03-2021
*/
public class NearFarFogSettings
{
public NearOrFarSetting near = new NearOrFarSetting(FogDistance.NEAR);
public NearOrFarSetting far = new NearOrFarSetting(FogDistance.FAR);
/** If true that means Minecraft is
* rendering fog along side us */
public boolean vanillaIsRenderingFog = true;
public NearFarFogSettings()
{
}
/**
* This holds all relevant data to rendering fog at either
* near or far distances.
*/
public class NearOrFarSetting
{
public FogQuality quality = FogQuality.FANCY;
public FogDistance distance = FogDistance.FAR;
/** If true this section should render with fog */
public boolean enabled = true;
public NearOrFarSetting(FogDistance newFogDistance)
{
distance = newFogDistance;
}
}
}
@@ -1,148 +0,0 @@
package com.seibel.lod.objects;
public class PosToGenerateContainer
{
private int playerPosX;
private int playerPosZ;
private byte farMinDetail;
private int maxSize;
private int maxNearSize;
private int maxFarSize;
private int nearSize;
private int farSize;
private int[][] posToGenerate;
public PosToGenerateContainer(byte farMinDetail, int maxDataToGenerate, int maxFarDataToGenerate, int playerPosX, int playerPosZ)
{
this.playerPosX = playerPosX;
this.playerPosZ = playerPosZ;
this.farMinDetail = farMinDetail;
maxNearSize = maxDataToGenerate;
maxFarSize = maxFarDataToGenerate;
maxSize = maxDataToGenerate;
nearSize = 0;
farSize = 0;
posToGenerate = new int[maxDataToGenerate][4];
}
public void addPosToGenerate(byte detailLevel, int posX, int posZ)
{
int distance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ);
int index;
if (detailLevel >= farMinDetail)
{//We are introducing a position in the far array
if (farSize < maxFarSize)
{
farSize++;
if (nearSize == maxNearSize)
{
nearSize--;
}
maxNearSize--;
}
index = posToGenerate.length - farSize;
while (index < posToGenerate.length - 1 && LevelPosUtil.compareLevelAndDistance(detailLevel, distance, (byte) (posToGenerate[index + 1][0] - 1), posToGenerate[index + 1][3]) <= 0)
{
posToGenerate[index][0] = posToGenerate[index + 1][0];
posToGenerate[index][1] = posToGenerate[index + 1][1];
posToGenerate[index][2] = posToGenerate[index + 1][2];
posToGenerate[index][3] = posToGenerate[index + 1][3];
index++;
}
if (index <= posToGenerate.length - 1)
{
posToGenerate[index][0] = detailLevel + 1;
posToGenerate[index][1] = posX;
posToGenerate[index][2] = posZ;
posToGenerate[index][3] = distance;
}
} else
{//We are introducing a position in the near array
if (nearSize < maxNearSize)
nearSize++;
index = nearSize - 1;
while (index > 0 && LevelPosUtil.compareDistance(distance, posToGenerate[index - 1][3]) <= 0)
{
posToGenerate[index][0] = posToGenerate[index - 1][0];
posToGenerate[index][1] = posToGenerate[index - 1][1];
posToGenerate[index][2] = posToGenerate[index - 1][2];
posToGenerate[index][3] = posToGenerate[index - 1][3];
index--;
}
if (index >= 0)
{
posToGenerate[index][0] = detailLevel + 1;
posToGenerate[index][1] = posX;
posToGenerate[index][2] = posZ;
posToGenerate[index][3] = distance;
}
}
}
public int getNumberOfPos()
{
return farSize + nearSize;
}
public int[] getNthPos(int n)
{
/*if(n < farSize)
return posToGenerate[maxSize - n - 1];
else
return posToGenerate[n - farSize];*/
int index;
if (n > farSize * 2)
index = n - farSize;
else if (n % 2 == 0)
index = n / 2;
else
index = posToGenerate.length - n / 2 - 1;
return posToGenerate[index];
}
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("Number of pos to generate ");
builder.append(farSize + nearSize);
builder.append('\n');
builder.append("Number of near pos to generate ");
builder.append(nearSize);
builder.append('\n');
builder.append("Number of far pos to generate ");
builder.append(farSize);
builder.append('\n');
builder.append('\n');
builder.append("near pos to generate");
builder.append('\n');
for (int i = 0; i < nearSize; i++)
{
builder.append(posToGenerate[i][0]-1);
builder.append(" ");
builder.append(posToGenerate[i][1]);
builder.append(" ");
builder.append(posToGenerate[i][2]);
builder.append(" ");
builder.append(posToGenerate[i][3]);
builder.append('\n');
}
builder.append('\n');
builder.append("far pos to generate");
builder.append('\n');
for (int i = maxSize - 1; i >= maxSize - farSize; i--)
{
builder.append(posToGenerate[i][0]-1);
builder.append(" ");
builder.append(posToGenerate[i][1]);
builder.append(" ");
builder.append(posToGenerate[i][2]);
builder.append(" ");
builder.append(posToGenerate[i][3]);
builder.append('\n');
}
builder.append('\n');
return builder.toString();
}
}
@@ -1,117 +0,0 @@
package com.seibel.lod.objects;
import com.seibel.lod.util.LodUtil;
public class PosToRenderContainer
{
public byte minDetail;
private int regionPosX;
private int regionPosZ;
private int numberOfPosToRender;
private int[][] posToRender;
/*TODO this population matrix could be converted to boolean to improve memory use*/
private byte[][] population;
public PosToRenderContainer(byte minDetail, int regionPosX, int regionPosZ)
{
this.minDetail = minDetail;
this.numberOfPosToRender = 0;
this.regionPosX = regionPosX;
this.regionPosZ = regionPosZ;
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail);
posToRender = new int[size*size][3];
population = new byte[size][size];
}
public void addPosToRender(byte detailLevel, int posX, int posZ)
{
//if(numberOfPosToRender >= posToRender.length)
// posToRender = Arrays.copyOf(posToRender, posToRender.length*2);
posToRender[numberOfPosToRender][0] = detailLevel;
posToRender[numberOfPosToRender][1] = posX;
posToRender[numberOfPosToRender][2] = posZ;
numberOfPosToRender++;
population[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel,posX,minDetail))]
[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel,posZ,minDetail))] = (byte) (detailLevel + 1);
}
public boolean contains(byte detailLevel, int posX, int posZ)
{
if(LevelPosUtil.getRegion(detailLevel, posX) == regionPosX && LevelPosUtil.getRegion(detailLevel, posZ) == regionPosZ)
{
return (population[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel,posX,minDetail))]
[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel,posZ,minDetail))] == (detailLevel + 1));
}else
{
return false;
}
}
public void clear(byte minDetail, int regionPosX, int regionPosZ){
this.numberOfPosToRender = 0;
this.regionPosX = regionPosX;
this.regionPosZ = regionPosZ;
if(this.minDetail == minDetail)
{
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail);
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
posToRender[0][0] = 0;
posToRender[0][1] = 0;
posToRender[0][2] = 0;
population[x][z] = 0;
}
}
}else{
this.minDetail = minDetail;
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail);
posToRender = new int[size*size][3];
population = new byte[size][size];
}
}
public int getNumberOfPos()
{
return numberOfPosToRender;
}
public byte getNthDetailLevel(int n)
{
return (byte) posToRender[n][0];
}
public int getNthPosX(int n)
{
return posToRender[n][1];
}
public int getNthPosZ(int n)
{
return posToRender[n][2];
}
public int[] getNthPos(int n)
{
return posToRender[n];
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("To render ");
builder.append(numberOfPosToRender);
builder.append('\n');
for(int i = 0; i < numberOfPosToRender; i++)
{
builder.append(posToRender[i][0]);
builder.append(" ");
builder.append(posToRender[i][1]);
builder.append(" ");
builder.append(posToRender[i][2]);
builder.append('\n');
}
builder.append('\n');
return builder.toString();
}
}
@@ -1,87 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.objects;
import com.seibel.lod.util.LodUtil;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
/**
* This object is similar to ChunkPos or BlockPos.
*
* @author James Seibel
* @version 8-21-2021
*/
public class RegionPos
{
public int x;
public int z;
/**
* Default Constructor <br>
* <p>
* Sets x and z to 0
*/
public RegionPos()
{
x = 0;
z = 0;
}
public RegionPos(int newX, int newZ)
{
x = newX;
z = newZ;
}
public RegionPos(BlockPos pos)
{
this(new ChunkPos(pos));
}
public RegionPos(ChunkPos pos)
{
x = Math.floorDiv(pos.x, LodUtil.REGION_WIDTH_IN_CHUNKS);
z = Math.floorDiv(pos.z, LodUtil.REGION_WIDTH_IN_CHUNKS);
}
/**
* Returns the ChunkPos at the center of this region
*/
public ChunkPos chunkPos()
{
return new ChunkPos((x * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2, (z * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2);
}
/**
* Returns the BlockPos at the center of this region
*/
public BlockPos blockPos()
{
return chunkPos().getWorldPosition().offset(LodUtil.CHUNK_WIDTH / 2, 0, LodUtil.CHUNK_WIDTH / 2);
}
@Override
public String toString()
{
return "(" + x + "," + z + ")";
}
}
@@ -1,335 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.proxy;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;
import com.seibel.lod.builders.LodBufferBuilder;
import com.seibel.lod.builders.LodBuilder;
import com.seibel.lod.builders.worldGeneration.LodNodeGenWorker;
import com.seibel.lod.builders.worldGeneration.LodWorldGenerator;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.LodDetail;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodWorld;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.render.LodRenderer;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.profiler.IProfiler;
import net.minecraft.util.text.StringTextComponent;
import net.minecraftforge.client.event.InputEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
/**
* This handles all events sent to the client,
* and is the starting point for most of this program.
*
* @author James_Seibel
* @version 8-24-2021
*/
public class ClientProxy
{
public static final Logger LOGGER = LogManager.getLogger("LOD");
private static LodWorld lodWorld = new LodWorld();
private static LodBuilder lodBuilder = new LodBuilder();
private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder();
private static LodRenderer renderer = new LodRenderer(lodBufferBuilder);
private static LodWorldGenerator lodWorldGenerator = LodWorldGenerator.INSTANCE;
private boolean configOverrideReminderPrinted = false;
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/**
* This is used to determine if the LODs should be regenerated
*/
public static int previousChunkRenderDistance = 0;
/**
* This is used to determine if the LODs should be regenerated
*/
public static int previousLodRenderDistance = 0;
/**
* can be set if we want to recalculate variables related
* to the LOD view distance
*/
private boolean recalculateWidths = false;
public ClientProxy()
{
}
//==============//
// render event //
//==============//
/**
* Do any setup that is required to draw LODs
* and then tell the LodRenderer to draw.
*/
public void renderLods(float partialTicks)
{
DetailDistanceUtil.updateSettings();
if (mc == null || mc.getPlayer() == null || !lodWorld.getIsWorldLoaded())
return;
viewDistanceChangedEvent();
LodDimension lodDim = lodWorld.getLodDimension(mc.getCurrentDimension());
if (lodDim == null)
return;
playerMoveEvent(lodDim);
//System.out.println("memory needed " + lodDim.getMinMemoryNeeded() + " byte");
//System.out.println(lodDim);
lodDim.treeCutter((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ());
lodDim.treeGenerator((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ());
// comment out when creating a release
//applyConfigOverrides();
// Note to self:
// if "unspecified" shows up in the pie chart, it is
// possibly because the amount of time between sections
// is too small for the profiler to measure
IProfiler profiler = mc.getProfiler();
profiler.pop(); // get out of "terrain"
profiler.push("LOD");
renderer.drawLODs(lodDim, partialTicks, mc.getProfiler());
profiler.pop(); // end LOD
profiler.push("terrain"); // restart "terrain"
// these can't be set until after the buffers are built (in renderer.drawLODs)
// otherwise the buffers may be set to the wrong size, or not changed at all
previousChunkRenderDistance = mc.getRenderDistance();
previousLodRenderDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get();
}
private void applyConfigOverrides()
{
// remind the developer(s). that config override is active
if (!configOverrideReminderPrinted)
{
mc.getPlayer().sendMessage(new StringTextComponent("Debug settings enabled!"), mc.getPlayer().getUUID());
configOverrideReminderPrinted = true;
}
//LodConfig.CLIENT.drawLODs.set(true);
//LodConfig.CLIENT.debugMode.set(true);
LodConfig.CLIENT.graphics.maxDrawDetail.set(LodDetail.FULL);
LodConfig.CLIENT.worldGenerator.maxGenerationDetail.set(LodDetail.FULL);
// LodConfig.CLIENT.graphics.fogDistance.set(FogDistance.FAR);
// LodConfig.CLIENT.graphics.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
// LodConfig.CLIENT.graphics.shadingMode.set(ShadingMode.DARKEN_SIDES);
// LodConfig.CLIENT.graphics.brightnessMultiplier.set(1.0);
// LodConfig.CLIENT.graphics.saturationMultiplier.set(1.0);
// LodConfig.CLIENT.worldGenerator.distanceGenerationMode.set(DistanceGenerationMode.SURFACE);
// LodConfig.CLIENT.graphics.lodChunkRenderDistance.set(256);
// LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR);
// LodConfig.CLIENT.graphics.lodQuality.set(3);
// LodConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.set(false);
// LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.set(2000); // 2000
// LodConfig.CLIENT.buffers.bufferRebuildChunkChangeTimeout.set(1000); // 1000
// LodConfig.CLIENT.buffers.bufferRebuildLodChangeTimeout.set(50); // 5000
LodConfig.CLIENT.debugging.enableDebugKeybinding.set(true);
}
//==============//
// forge events //
//==============//
@SubscribeEvent
public void serverTickEvent(TickEvent.ServerTickEvent event)
{
if (mc == null || mc.getPlayer() == null || !lodWorld.getIsWorldLoaded())
return;
LodDimension lodDim = lodWorld.getLodDimension(mc.getPlayer().level.dimensionType());
if (lodDim == null)
return;
lodWorldGenerator.queueGenerationRequests(lodDim, renderer, lodBuilder);
}
@SubscribeEvent
public void chunkLoadEvent(ChunkEvent.Load event)
{
lodBuilder.generateLodNodeAsync(event.getChunk(), lodWorld, event.getWorld(), DistanceGenerationMode.SERVER);
}
@SubscribeEvent
public void worldSaveEvent(WorldEvent.Save event)
{
if (lodWorld != null)
lodWorld.saveAllDimensions();
}
@SubscribeEvent
public void worldLoadEvent(WorldEvent.Load event)
{
// the player just loaded a new world/dimension
lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld()));
// make sure the correct LODs are being rendered
// (if this isn't done the previous world's LODs may be drawn)
renderer.regenerateLODsNextFrame();
}
@SubscribeEvent
public void worldUnloadEvent(WorldEvent.Unload event)
{
// the player just unloaded a world/dimension
if (mc.getConnection().getLevel() == null)
{
// if this isn't done unfinished tasks may be left in the queue
// preventing new LodChunks form being generated
LodNodeGenWorker.restartExecuterService();
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0);
// the player has disconnected from a server
lodWorld.deselectWorld();
// hopefully this should reduce issues related to the buffer builder
// breaking when changing worlds.
renderer.destroyBuffers();
recalculateWidths = true;
}
}
@SubscribeEvent
public void blockChangeEvent(BlockEvent event)
{
if (event.getClass() == BlockEvent.BreakEvent.class ||
event.getClass() == BlockEvent.EntityPlaceEvent.class ||
event.getClass() == BlockEvent.EntityMultiPlaceEvent.class ||
event.getClass() == BlockEvent.FluidPlaceBlockEvent.class ||
event.getClass() == BlockEvent.PortalSpawnEvent.class)
{
// recreate the LOD where the blocks were changed
lodBuilder.generateLodNodeAsync(event.getWorld().getChunk(event.getPos()), lodWorld, event.getWorld());
}
}
@SubscribeEvent
public void onKeyInput(InputEvent.KeyInputEvent event)
{
if(LodConfig.CLIENT.debugging.enableDebugKeybinding.get()
&& event.getKey() == GLFW.GLFW_KEY_F4 && event.getAction() == GLFW.GLFW_PRESS)
{
LodConfig.CLIENT.debugging.debugMode.set(LodConfig.CLIENT.debugging.debugMode.get().getNext());
}
}
//==================//
// frame LOD events //
//==================//
/**
* Re-centers the given LodDimension if it needs to be.
*/
private void playerMoveEvent(LodDimension lodDim)
{
// make sure the dimension is centered
RegionPos playerRegionPos = new RegionPos(mc.getPlayer().blockPosition());
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterX(), playerRegionPos.z - lodDim.getCenterZ());
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
{
lodWorld.saveAllDimensions();
lodDim.move(worldRegionOffset);
//LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ());
}
}
/**
* Re-sizes all LodDimensions if they needs to be.
*/
private void viewDistanceChangedEvent()
{
// calculate how wide the dimension(s) should be in regions
int chunksWide = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * 2 + 1;
int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS);
newWidth = (newWidth % 2 == 0) ? (newWidth += 1) : (newWidth += 2); // make sure we have a odd number of regions
// do the dimensions need to change in size?
if (lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths)
{
lodWorld.saveAllDimensions();
// update the dimensions to fit the new width
lodWorld.resizeDimensionRegionWidth(newWidth);
lodBuilder.defaultDimensionWidthInRegions = newWidth;
renderer.setupBuffers(newWidth);
recalculateWidths = false;
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
}
DetailDistanceUtil.updateSettings();
}
//================//
// public getters //
//================//
public static LodWorld getLodWorld()
{
return lodWorld;
}
public static LodBuilder getLodBuilder()
{
return lodBuilder;
}
public static LodRenderer getRenderer()
{
return renderer;
}
}
@@ -1,913 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.render;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.HashSet;
import java.util.Iterator;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.NVFogDistance;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.lod.builders.LodBufferBuilder;
import com.seibel.lod.builders.LodBufferBuilder.VertexBuffersAndOffset;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.enums.FogDistance;
import com.seibel.lod.enums.FogDrawOverride;
import com.seibel.lod.enums.FogQuality;
import com.seibel.lod.handlers.ReflectionHandler;
import com.seibel.lod.objects.LevelPosUtil;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.NearFarFogSettings;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.entity.Entity;
import net.minecraft.potion.EffectInstance;
import net.minecraft.potion.Effects;
import net.minecraft.profiler.IProfiler;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.math.vector.Vector3f;
/**
* This is where all the magic happens. <br>
* This is where LODs are draw to the world.
*
* @author James Seibel
* @version 9-7-2021
*/
public class LodRenderer
{
/**
* this is the light used when rendering the LODs,
* it should be something different than what is used by Minecraft
*/
private static final int LOD_GL_LIGHT_NUMBER = GL11.GL_LIGHT2;
/**
* 64 MB by default is the maximum amount of memory that
* can be directly allocated. <br><br>
* <p>
* I know there are commands to change that amount
* (specifically "-XX:MaxDirectMemorySize"), but
* I have no idea how to access that amount. <br>
* So I guess this will be the hard limit for now. <br><br>
* <p>
* https://stackoverflow.com/questions/50499238/bytebuffer-allocatedirect-and-xmx
*/
public static final int MAX_ALOCATEABLE_DIRECT_MEMORY = 64 * 1024 * 1024;
/**
* Does this computer's GPU support fancy fog?
*/
private static Boolean fancyFogAvailable = null;
/**
* If true the LODs colors will be replaced with
* a checkerboard, this can be used for debugging.
*/
public DebugMode previousDebugMode = DebugMode.OFF;
private MinecraftWrapper mc;
private GameRenderer gameRender;
private IProfiler profiler;
private int farPlaneBlockDistance;
/**
* This is used to generate the buildable buffers
*/
private LodBufferBuilder lodBufferBuilder;
/**
* Each VertexBuffer represents 1 region
*/
private VertexBuffer[][] vbos;
public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR;
private ChunkPos vbosCenter = new ChunkPos(0,0);
/**
* This is used to determine if the LODs should be regenerated
*/
private int[] previousPos = new int[]{0,0,0};
private int prevRenderDistance = 0;
private long prevPlayerPosTime = 0;
private long prevVanillaChunkTime = 0;
private long prevChunkTime = 0;
/**
* This is used to determine if the LODs should be regenerated
*/
private FogDistance prevFogDistance = FogDistance.NEAR_AND_FAR;
/**
* if this is true the LOD buffers should be regenerated,
* provided they aren't already being regenerated.
*/
private volatile boolean partialRegen = false;
private volatile boolean fullRegen = true;
/**
* This HashSet contains every chunk that Vanilla Minecraft
* is going to render
*/
public boolean[][] vanillaRenderedChunks;
public boolean vanillaRenderedChunksChanged;
public LodRenderer(LodBufferBuilder newLodNodeBufferBuilder)
{
mc = MinecraftWrapper.INSTANCE;
gameRender = mc.getGameRenderer();
lodBufferBuilder = newLodNodeBufferBuilder;
}
/**
* Besides drawing the LODs this method also starts
* the async process of generating the Buffers that hold those LODs.
*
* @param lodDim The dimension to draw, if null doesn't replace the current dimension.
* @param partialTicks how far into the current tick this method was called.
*/
public void drawLODs(LodDimension lodDim, float partialTicks, IProfiler newProfiler)
{
if (lodDim == null)
{
// if there aren't any loaded LodChunks
// don't try drawing anything
return;
}
//===============//
// initial setup //
//===============//
profiler = newProfiler;
profiler.push("LOD setup");
// only check the GPU capability's once
if (fancyFogAvailable == null)
{
// see if this GPU can run fancy fog
fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance;
if (!fancyFogAvailable)
{
ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that fancy fog options will not be available.");
}
}
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
// starting here...
determineIfLodsShouldRegenerate(lodDim);
//=================//
// create the LODs //
//=================//
// only regenerate the LODs if:
// 1. we want to regenerate LODs
// 2. we aren't already regenerating the LODs
// 3. we aren't waiting for the build and draw buffers to swap
// (this is to prevent thread conflicts)
if ((partialRegen || fullRegen) && !lodBufferBuilder.generatingBuffers && !lodBufferBuilder.newBuffersAvaliable())
{
// generate the LODs on a separate thread to prevent stuttering or freezing
lodBufferBuilder.generateLodBuffersAsync(this, lodDim, mc.getPlayer().blockPosition(), true);
// the regen process has been started,
// it will be done when lodBufferBuilder.newBuffersAvaliable()
// is true
fullRegen = false;
partialRegen = false;
}
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
// ...ending here
//===========================//
// GL settings for rendering //
//===========================//
// set the required open GL settings
if (LodConfig.CLIENT.debugging.debugMode.get() == DebugMode.SHOW_DETAIL_WIREFRAME)
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
else
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
GL11.glEnable(GL11.GL_DEPTH_TEST);
// disable the lights Minecraft uses
GL11.glDisable(GL11.GL_LIGHT0);
GL11.glDisable(GL11.GL_LIGHT1);
// get the default projection matrix so we can
// reset it after drawing the LODs
float[] defaultProjMatrix = new float[16];
GL11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, defaultProjMatrix);
Matrix4f modelViewMatrix = generateModelViewMatrix(partialTicks);
// required for setupFog and setupProjectionMatrix
farPlaneBlockDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH;
setupProjectionMatrix(partialTicks);
setupLighting(lodDim, partialTicks);
NearFarFogSettings fogSettings = determineFogSettings();
// determine the current fog settings so they can be
// reset after drawing the LODs
float defaultFogStartDist = GL11.glGetFloat(GL11.GL_FOG_START);
float defaultFogEndDist = GL11.glGetFloat(GL11.GL_FOG_END);
int defaultFogMode = GL11.glGetInteger(GL11.GL_FOG_MODE);
int defaultFogDistance = GL11.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV);
//===========//
// rendering //
//===========//
profiler.popPush("LOD draw");
if (vbos != null)
{
Entity cameraEntity = mc.getCameraEntity();
Vector3d cameraDir = cameraEntity.getLookAngle().normalize();
cameraDir = mc.getOptions().getCameraType().isMirrored() ? cameraDir.reverse() : cameraDir;
// used to determine what type of fog to render
int halfWidth = vbos.length / 2;
int quarterWidth = vbos.length / 4;
for (int i = 0; i < vbos.length; i++)
{
for (int j = 0; j < vbos.length; j++)
{
RegionPos vboPos = new RegionPos(i + lodDim.getCenterX() - lodDim.getWidth() / 2, j + lodDim.getCenterZ() - lodDim.getWidth() / 2);
if (RenderUtil.isRegionInViewFrustum(cameraEntity.blockPosition(), cameraDir, vboPos.blockPos()))
{
if ((i > halfWidth - quarterWidth && i < halfWidth + quarterWidth) && (j > halfWidth - quarterWidth && j < halfWidth + quarterWidth))
setupFog(fogSettings.near.distance, fogSettings.near.quality);
else
setupFog(fogSettings.far.distance, fogSettings.far.quality);
sendLodsToGpuAndDraw(vbos[i][j], modelViewMatrix);
}
}
}
}
//=========//
// cleanup //
//=========//
profiler.popPush("LOD cleanup");
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glDisable(LOD_GL_LIGHT_NUMBER);
// re-enable the lights Minecraft uses
GL11.glEnable(GL11.GL_LIGHT0);
GL11.glEnable(GL11.GL_LIGHT1);
RenderSystem.disableLighting();
// reset the fog settings so the normal chunks
// will be drawn correctly
cleanupFog(fogSettings, defaultFogStartDist, defaultFogEndDist, defaultFogMode, defaultFogDistance);
// reset the projection matrix so anything drawn after
// the LODs will use the correct projection matrix
Matrix4f mvm = new Matrix4f(defaultProjMatrix);
mvm.transpose();
gameRender.resetProjectionMatrix(mvm);
// clear the depth buffer so anything drawn is drawn
// over the LODs
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
// replace the buffers used to draw and build,
// this is only done when the createLodBufferGenerationThread
// has finished executing on a parallel thread.
if (lodBufferBuilder.newBuffersAvaliable())
{
// this has to be called after the VBOs have been drawn
// otherwise rubber banding may occur
swapBuffers();
}
// end of internal LOD profiling
profiler.pop();
}
/**
* This is where the actual drawing happens.
*/
private void sendLodsToGpuAndDraw(VertexBuffer vbo, Matrix4f modelViewMatrix)
{
if (vbo == null)
return;
vbo.bind();
// 0L is the starting pointer
LOD_VERTEX_FORMAT.setupBufferState(0L);
vbo.draw(modelViewMatrix, GL11.GL_QUADS);
VertexBuffer.unbind();
LOD_VERTEX_FORMAT.clearBufferState();
}
//=================//
// Setup Functions //
//=================//
@SuppressWarnings("deprecation")
private void setupFog(FogDistance fogDistance, FogQuality fogQuality)
{
if (fogQuality == FogQuality.OFF)
{
FogRenderer.setupNoFog();
RenderSystem.disableFog();
return;
}
if (fogDistance == FogDistance.NEAR_AND_FAR)
{
throw new IllegalArgumentException("setupFog doesn't accept the NEAR_AND_FAR fog distance.");
}
// determine the fog distance mode to use
int glFogDistanceMode;
if (fogQuality == FogQuality.FANCY)
{
// fancy fog (fragment distance based fog)
glFogDistanceMode = NVFogDistance.GL_EYE_RADIAL_NV;
}
else
{
// fast fog (frustum distance based fog)
glFogDistanceMode = NVFogDistance.GL_EYE_PLANE_ABSOLUTE_NV;
}
// the multipliers are percentages
// of the regular view distance.
if (fogDistance == FogDistance.FAR)
{
// the reason that I wrote fogEnd then fogStart backwards
// is because we are using fog backwards to how
// it is normally used, with it hiding near objects
// instead of far objects.
if (fogQuality == FogQuality.FANCY)
{
// for more realistic fog when using FAR
if(LodConfig.CLIENT.graphics.fogDistance.get() == FogDistance.NEAR_AND_FAR)
{
RenderSystem.fogStart(farPlaneBlockDistance * 0.9f);
RenderSystem.fogEnd(farPlaneBlockDistance * 1.0f);
}else{
RenderSystem.fogStart(farPlaneBlockDistance * 0.1f);
RenderSystem.fogEnd(farPlaneBlockDistance * 1.0f);
}
}
else if (fogQuality == FogQuality.FAST)
{
// for the far fog of the normal chunks
// to start right where the LODs' end use:
// end = 0.8f, start = 1.5f
RenderSystem.fogStart(farPlaneBlockDistance * 1.5f);
RenderSystem.fogEnd(farPlaneBlockDistance * 2.0f);
}
}
else if (fogDistance == FogDistance.NEAR)
{
if (fogQuality == FogQuality.FANCY)
{
RenderSystem.fogEnd(mc.getRenderDistance() * 16 * 1.41f);
RenderSystem.fogStart(mc.getRenderDistance() * 16 * 1.6f);
}
else if (fogQuality == FogQuality.FAST)
{
RenderSystem.fogEnd(mc.getRenderDistance() * 16 * 1.0f);
RenderSystem.fogStart(mc.getRenderDistance() * 16 * 1.5f);
}
}
GL11.glEnable(GL11.GL_FOG);
RenderSystem.enableFog();
RenderSystem.setupNvFogDistance();
RenderSystem.fogMode(GlStateManager.FogMode.LINEAR);
GL11.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, glFogDistanceMode);
}
/**
* Revert any changes that were made to the fog.
*/
private void cleanupFog(NearFarFogSettings fogSettings,
float defaultFogStartDist, float defaultFogEndDist,
int defaultFogMode, int defaultFogDistance)
{
RenderSystem.fogStart(defaultFogStartDist);
RenderSystem.fogEnd(defaultFogEndDist);
RenderSystem.fogMode(defaultFogMode);
GL11.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, defaultFogDistance);
// disable fog if Minecraft wasn't rendering fog
// but we were
if (!fogSettings.vanillaIsRenderingFog &&
(fogSettings.near.quality != FogQuality.OFF ||
fogSettings.far.quality != FogQuality.OFF))
{
GL11.glDisable(GL11.GL_FOG);
}
}
/**
* Create the model view matrix to move the LODs
* from object space into world space.
*/
private Matrix4f generateModelViewMatrix(float partialTicks)
{
// get all relevant camera info
ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera();
Vector3d projectedView = renderInfo.getPosition();
// generate the model view matrix
MatrixStack matrixStack = new MatrixStack();
matrixStack.pushPose();
// rotate to the current camera's direction
matrixStack.mulPose(Vector3f.XP.rotationDegrees(renderInfo.getXRot()));
matrixStack.mulPose(Vector3f.YP.rotationDegrees(renderInfo.getYRot() + 180));
// translate the camera relative to the regions' center
// (AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
// accuracy vs the model view matrix, which only uses floats)
BlockPos bufferPos = vbosCenter.getWorldPosition();
Vector3d eyePos = mc.getPlayer().getEyePosition(partialTicks);
double xDiff = eyePos.x - bufferPos.getX();
double zDiff = eyePos.z - bufferPos.getZ();
matrixStack.translate(-xDiff, -projectedView.y, -zDiff);
return matrixStack.last().pose();
}
/**
* create a new projection matrix and send it over to the GPU
* <br><br>
* A lot of this code is copied from renderLevel (line 567)
* in the GameRender class. The code copied is anything with
* a matrixStack and is responsible for making sure the LOD
* objects distort correctly relative to the rest of the world.
* Distortions are caused by: standing in a nether portal,
* nausea potion effect, walking bobbing.
*
* @param partialTicks how many ticks into the frame we are
*/
private void setupProjectionMatrix(float partialTicks)
{
// Note: if the LOD objects don't distort correctly
// compared to regular minecraft terrain, make sure
// all the transformations in renderWorld are here too
MatrixStack matrixStack = new MatrixStack();
matrixStack.pushPose();
gameRender.bobHurt(matrixStack, partialTicks);
if (this.mc.getOptions().bobView)
{
gameRender.bobView(matrixStack, partialTicks);
}
// potion and nausea effects
float f = MathHelper.lerp(partialTicks, this.mc.getPlayer().oPortalTime, this.mc.getPlayer().portalTime) * this.mc.getOptions().screenEffectScale * this.mc.getOptions().screenEffectScale;
if (f > 0.0F)
{
int i = this.mc.getPlayer().hasEffect(Effects.CONFUSION) ? 7 : 20;
float f1 = 5.0F / (f * f + 5.0F) - f * 0.04F;
f1 = f1 * f1;
Vector3f vector3f = new Vector3f(0.0F, MathHelper.SQRT_OF_TWO / 2.0F, MathHelper.SQRT_OF_TWO / 2.0F);
matrixStack.mulPose(vector3f.rotationDegrees((gameRender.tick + partialTicks) * i));
matrixStack.scale(1.0F / f1, 1.0F, 1.0F);
float f2 = -(gameRender.tick + partialTicks) * i;
matrixStack.mulPose(vector3f.rotationDegrees(f2));
}
// this projection matrix allows us to see past the normal
// world render distance
Matrix4f projectionMatrix =
Matrix4f.perspective(
getFov(partialTicks, true),
(float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(),
// it is possible to see the near clip plane, but
// you have to be flying quickly in spectator mode through ungenerated
// terrain, so I don't think it is much of an issue.
mc.getRenderDistance(),
farPlaneBlockDistance * LodUtil.CHUNK_WIDTH * 2);
// add the screen space distortions
projectionMatrix.multiply(matrixStack.last().pose());
gameRender.resetProjectionMatrix(projectionMatrix);
return;
}
/**
* setup the lighting to be used for the LODs
*/
private void setupLighting(LodDimension lodDimension, float partialTicks)
{
// Determine if the player has night vision
boolean playerHasNightVision = false;
if (this.mc.getPlayer() != null)
{
Iterator<EffectInstance> iterator = this.mc.getPlayer().getActiveEffects().iterator();
while (iterator.hasNext())
{
EffectInstance instance = iterator.next();
if (instance.getEffect() == Effects.NIGHT_VISION)
{
playerHasNightVision = true;
break;
}
}
}
float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.getSkyDarken(partialTicks) : 0.2f;
sunBrightness = playerHasNightVision ? 1.0f : sunBrightness;
float gammaMultiplyer = (float) mc.getOptions().gamma - 0.5f;
float lightStrength = ((sunBrightness / 2f) - 0.2f) + (gammaMultiplyer * 0.3f);
float lightAmbient[] = {lightStrength, lightStrength, lightStrength, 1.0f};
// can be used for debugging
// if (partialTicks < 0.005)
// ClientProxy.LOGGER.debug(lightStrength);
ByteBuffer temp = ByteBuffer.allocateDirect(16);
temp.order(ByteOrder.nativeOrder());
GL11.glLightfv(LOD_GL_LIGHT_NUMBER, GL11.GL_AMBIENT, (FloatBuffer) temp.asFloatBuffer().put(lightAmbient).flip());
GL11.glEnable(LOD_GL_LIGHT_NUMBER); // Enable the above lighting
RenderSystem.enableLighting();
}
/**
* Create all buffers that will be used.
*/
public void setupBuffers(int numbRegionsWide)
{
// calculate the max amount of memory needed (in bytes)
int bufferMemory = RenderUtil.getBufferMemoryForRegion();
// if the required memory is greater than the
// MAX_ALOCATEABLE_DIRECT_MEMORY lower the lodChunkRadiusMultiplier
// to fit.
if (bufferMemory > MAX_ALOCATEABLE_DIRECT_MEMORY)
{
ClientProxy.LOGGER.warn("setupBuffers tried to allocate too much memory for the BufferBuilders."
+ " It tried to allocate \"" + bufferMemory + "\" bytes, when \"" + MAX_ALOCATEABLE_DIRECT_MEMORY + "\" is the max.");
}
lodBufferBuilder.setupBuffers(numbRegionsWide, bufferMemory);
}
//======================//
// Other Misc Functions //
//======================//
/**
* If this is called then the next time "drawLODs" is called
* the LODs will be regenerated; the same as if the player moved.
*/
public void regenerateLODsNextFrame()
{
fullRegen = true;
}
/**
* Replace the current Vertex Buffers with the newly
* created buffers from the lodBufferBuilder. <br><br>
*
* For some reason this has to be called after the frame has been rendered,
* otherwise visual stuttering/rubber banding may happen. I'm not sure why...
*/
private void swapBuffers()
{
// replace the drawable buffers with
// the newly created buffers from the lodBufferBuilder
VertexBuffersAndOffset result = lodBufferBuilder.getVertexBuffers();
vbos = result.vbos;
vbosCenter = result.drawableCenterChunkPos;
}
/**
* Calls the BufferBuilder's destroyBuffers method.
*/
public void destroyBuffers()
{
lodBufferBuilder.destroyBuffers();
}
private double getFov(float partialTicks, boolean useFovSetting)
{
return mc.getGameRenderer().getFov(mc.getGameRenderer().getMainCamera(), partialTicks, useFovSetting);
}
/**
* Return what fog settings should be used when rendering.
*/
private NearFarFogSettings determineFogSettings()
{
NearFarFogSettings fogSettings = new NearFarFogSettings();
FogQuality quality = ReflectionHandler.INSTANCE.getFogQuality();
FogDrawOverride override = LodConfig.CLIENT.graphics.fogDrawOverride.get();
if (quality == FogQuality.OFF)
fogSettings.vanillaIsRenderingFog = false;
else
fogSettings.vanillaIsRenderingFog = true;
// use any fog overrides the user may have set
switch (override)
{
case ALWAYS_DRAW_FOG_FANCY:
quality = FogQuality.FANCY;
break;
case NEVER_DRAW_FOG:
quality = FogQuality.OFF;
break;
case ALWAYS_DRAW_FOG_FAST:
quality = FogQuality.FAST;
break;
case USE_OPTIFINE_FOG_SETTING:
// don't override anything
break;
}
// only use fancy fog if the user's GPU can deliver
if (!fancyFogAvailable && quality == FogQuality.FANCY)
{
quality = FogQuality.FAST;
}
// how different distances are drawn depends on the quality set
switch (quality)
{
case FANCY:
fogSettings.near.quality = FogQuality.FANCY;
fogSettings.far.quality = FogQuality.FANCY;
switch (LodConfig.CLIENT.graphics.fogDistance.get())
{
case NEAR_AND_FAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.FAR;
break;
case NEAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.NEAR;
break;
case FAR:
fogSettings.near.distance = FogDistance.FAR;
fogSettings.far.distance = FogDistance.FAR;
break;
}
break;
case FAST:
fogSettings.near.quality = FogQuality.FAST;
fogSettings.far.quality = FogQuality.FAST;
// fast fog setting should only have one type of
// fog, since the LODs are separated into a near
// and far portion; and fast fog is rendered from the
// frustrum's perspective instead of the camera
switch (LodConfig.CLIENT.graphics.fogDistance.get())
{
case NEAR_AND_FAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.NEAR;
break;
case NEAR:
fogSettings.near.distance = FogDistance.NEAR;
fogSettings.far.distance = FogDistance.NEAR;
break;
case FAR:
fogSettings.near.distance = FogDistance.FAR;
fogSettings.far.distance = FogDistance.FAR;
break;
}
break;
case OFF:
fogSettings.near.quality = FogQuality.OFF;
fogSettings.far.quality = FogQuality.OFF;
break;
}
return fogSettings;
}
/**
* Determines if the LODs should have a fullRegen or partialRegen
*/
private void determineIfLodsShouldRegenerate(LodDimension lodDim)
{
short renderDistance = (short) mc.getRenderDistance();
//=============//
// full regens //
//=============//
// check if the view distance changed
if (ClientProxy.previousLodRenderDistance != LodConfig.CLIENT.graphics.lodChunkRenderDistance.get()
|| mc.getRenderDistance() != prevRenderDistance
|| prevFogDistance != LodConfig.CLIENT.graphics.fogDistance.get())
{
DetailDistanceUtil.updateSettings();
fullRegen = true;
previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk);
prevFogDistance = LodConfig.CLIENT.graphics.fogDistance.get();
prevRenderDistance = mc.getRenderDistance();
//should use this when it's ready
vanillaRenderedChunks = new boolean[renderDistance*2+2][renderDistance*2+2];
}
// did the user change the debug setting?
if (LodConfig.CLIENT.debugging.debugMode.get() != previousDebugMode)
{
previousDebugMode = LodConfig.CLIENT.debugging.debugMode.get();
fullRegen = true;
}
long newTime = System.currentTimeMillis();
// check if the player has moved
if (newTime - prevPlayerPosTime > LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.get())
{
if (LevelPosUtil.getDetailLevel(previousPos) == 0
|| mc.getPlayer().xChunk != LevelPosUtil.getPosX(previousPos)
|| mc.getPlayer().zChunk != LevelPosUtil.getPosZ(previousPos))
{
fullRegen = true;
previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk);
//should use this when it's ready
vanillaRenderedChunks = new boolean[renderDistance*2+2][renderDistance*2+2];
}
prevPlayerPosTime = newTime;
}
//================//
// partial regens //
//================//
// check if the vanilla rendered chunks changed
if (newTime - prevVanillaChunkTime > LodConfig.CLIENT.buffers.bufferRebuildChunkChangeTimeout.get())
{
if (vanillaRenderedChunksChanged)
{
partialRegen = true;
vanillaRenderedChunksChanged = false;
}
prevVanillaChunkTime = newTime;
}
// check if there is any newly generated terrain to show
if (newTime - prevChunkTime > LodConfig.CLIENT.buffers.bufferRebuildLodChangeTimeout.get())
{
if (lodDim.regenDimension)
{
partialRegen = true;
lodDim.regenDimension = false;
}
prevChunkTime = newTime;
}
//==============//
// LOD skipping //
//==============//
// determine which LODs should not be rendered close to the player
HashSet<ChunkPos> chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, mc.getPlayer().blockPosition());
int chunkX;
int chunkZ;
for (ChunkPos pos : chunkPosToSkip)
{
chunkX = pos.x - mc.getPlayer().xChunk + renderDistance + 1;
chunkZ = pos.z - mc.getPlayer().zChunk + renderDistance + 1;
try
{
if (!vanillaRenderedChunks[chunkX][chunkZ])
{
vanillaRenderedChunks[chunkX][chunkZ] = true;
vanillaRenderedChunksChanged = true;
lodDim.setToRegen(pos.getRegionX(), pos.getRegionZ());
}
}catch (Exception e){
System.out.println(vanillaRenderedChunks.length);
e.printStackTrace();
}
}
// if the player is high enough, draw all LODs
if(chunkPosToSkip.isEmpty() && mc.getPlayer().position().y > 256)
{
vanillaRenderedChunks = new boolean[renderDistance*2+2][renderDistance*2+2];
vanillaRenderedChunksChanged = true;
}
}
}
@@ -1,151 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.render;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.LodTemplate;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.vector.Vector3d;
/**
* This holds miscellaneous helper code
* to be used in the rendering process.
*
* @author James Seibel
* @version 8-21-2021
*/
public class RenderUtil
{
private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/**
* Returns if the given ChunkPos is in the loaded area of the world.
* @param centerCoordinate the center of the loaded world (probably the player's ChunkPos)
*/
public static boolean isChunkPosInLoadedArea(ChunkPos pos, ChunkPos center)
{
return (pos.x >= center.x - mc.getRenderDistance()
&& pos.x <= center.x + mc.getRenderDistance())
&&
(pos.z >= center.z - mc.getRenderDistance()
&& pos.z <= center.z + mc.getRenderDistance());
}
/**
* Returns if the given coordinate is in the loaded area of the world.
* @param centerCoordinate the center of the loaded world
*/
public static boolean isCoordinateInLoadedArea(int x, int z, int centerCoordinate)
{
return (x >= centerCoordinate - mc.getRenderDistance()
&& x <= centerCoordinate + mc.getRenderDistance())
&&
(z >= centerCoordinate - mc.getRenderDistance()
&& z <= centerCoordinate + mc.getRenderDistance());
}
/**
* Find the coordinates that are in the center half of the given
* 2D matrix, starting at (0,0) and going to (2 * lodRadius, 2 * lodRadius).
*/
public static boolean isCoordinateInNearFogArea(int i, int j, int lodRadius)
{
int halfRadius = lodRadius / 2;
return (i >= lodRadius - halfRadius
&& i <= lodRadius + halfRadius)
&&
(j >= lodRadius - halfRadius
&& j <= lodRadius + halfRadius);
}
/**
* Get how much buffer memory would be required for the given radius multiplier
*
* issue #62
* TODO check if this is actually returning the correct memory needed
* it is possible (albeit unlikely) to have a buffer indexOutOfBounds exception
* which is caused by the buffer not being big enough.
*/
public static int getBufferMemoryForRegion()
{
// calculate the max amount of buffer memory needed (in bytes)
return LodUtil.REGION_WIDTH_IN_CHUNKS * LodUtil.REGION_WIDTH_IN_CHUNKS *
LodConfig.CLIENT.graphics.lodTemplate.get().getBufferMemoryForSingleLod();
}
/**
* Returns the maxViewDistanceMultiplier for the given LodTemplate
* at the given LodDetail level.
*/
public static int getMaxRadiusMultiplierWithAvaliableMemory(LodTemplate lodTemplate, int detailLevel)
{
int maxNumberOfLods = LodRenderer.MAX_ALOCATEABLE_DIRECT_MEMORY / lodTemplate.getBufferMemoryForSingleLod();
int numbLodsWide = (int) Math.sqrt(maxNumberOfLods);
return numbLodsWide / (2 * mc.getRenderDistance());
}
/**
* Returns true if one of the region's 4 corners is in front
* of the camera.
*/
public static boolean isRegionInViewFrustum(BlockPos playerBlockPos, Vector3d cameraDir, BlockPos vboCenterPos)
{
// convert the vbo position into a direction vector
// starting from the player's position
Vector3d vboVec = new Vector3d(vboCenterPos.getX(), 0, vboCenterPos.getZ());
Vector3d playerVec = new Vector3d(playerBlockPos.getX(), playerBlockPos.getY(), playerBlockPos.getZ());
Vector3d vboCenterVec = vboVec.subtract(playerVec);
int halfRegionWidth = LodUtil.REGION_WIDTH / 2;
// calculate the 4 corners
Vector3d vboSeVec = new Vector3d(vboCenterVec.x + halfRegionWidth, vboCenterVec.y, vboCenterVec.z + halfRegionWidth);//.normalize();
Vector3d vboSwVec = new Vector3d(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z + halfRegionWidth);//.normalize();
Vector3d vboNwVec = new Vector3d(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth);//.normalize();
Vector3d vboNeVec = new Vector3d(vboCenterVec.x + halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth);//.normalize();
// if any corner is visible, this region should be rendered
return isNormalizedVectorInViewFrustum(vboSeVec, cameraDir) ||
isNormalizedVectorInViewFrustum(vboSwVec, cameraDir) ||
isNormalizedVectorInViewFrustum(vboNwVec, cameraDir) ||
isNormalizedVectorInViewFrustum(vboNeVec, cameraDir);
}
/**
* Currently takes the dot product of the two vectors,
* but in the future could do more complicated frustum culling tests.
*/
private static boolean isNormalizedVectorInViewFrustum(Vector3d objectVector, Vector3d cameraDir)
{
// the -0.1 is to offer a slight buffer so we are
// more likely to render LODs and thus, hopefully prevent
// flickering or odd disappearences
return objectVector.dot(cameraDir) > -0.1;
}
}
@@ -1,70 +0,0 @@
package com.seibel.lod.util;
import java.awt.*;
public class ColorUtil
{
public static int rgbToInt(int red, int green, int blue)
{
return (0xFF << 24) | (red << 16) | (green << 8) | blue;
}
public static int rgbToInt(int alpha, int red, int green, int blue)
{
return (alpha << 24) | (red << 16) | (green << 8) | blue;
}
public static int getAlpha(int color)
{
return (color >> 24) & 0xFF;
}
public static int getRed(int color)
{
return (color >> 16) & 0xFF;
}
public static int getGreen(int color)
{
return (color >> 8) & 0xFF;
}
public static int getBlue(int color)
{
return color & 0xFF;
}
public static int applyShade(int color, int shade)
{
if (shade < 0)
{
return (getAlpha(color) << 24) | (Math.max(getRed(color) + shade, 0) << 16) | (Math.max(getGreen(color) + shade, 0) << 8) | Math.max(getBlue(color) + shade, 0);
} else
{
return (getAlpha(color) << 24) | (Math.min(getRed(color) + shade, 255) << 16) | (Math.min(getGreen(color) + shade, 255) << 8) | Math.min(getBlue(color) + shade, 255);
}
}
public static int applyShade(int color, float shade)
{
if (shade < 1)
{
return (getAlpha(color) << 24) | ((int) Math.max(getRed(color) * shade, 0) << 16) | ((int) Math.max(getGreen(color) * shade, 0) << 8) | (int) Math.max(getBlue(color) * shade, 0);
} else
{
return (getAlpha(color) << 24) | ((int) Math.min(getRed(color) * shade, 255) << 16) | ((int) Math.min(getGreen(color) * shade, 255) << 8) | (int) Math.min(getBlue(color) * shade, 255);
}
}
/**
* Edit the given color as a HSV (Hue Saturation Value) color.
*/
public static int applySaturationAndBrightnessMultipliers(int color, float saturationMultiplier, float brightnessMultiplier)
{
float[] hsv = Color.RGBtoHSB(getRed(color), getGreen(color), getBlue(color), null);
return Color.getHSBColor(
hsv[0], // hue
LodUtil.clamp(0.0f, hsv[1] * saturationMultiplier, 1.0f),
LodUtil.clamp(0.0f, hsv[2] * brightnessMultiplier, 1.0f)).getRGB();
}
}
@@ -1,189 +0,0 @@
package com.seibel.lod.util;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.LodDetail;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.wrappers.MinecraftWrapper;
public class DetailDistanceUtil
{
private static double genMultiplier = 1.0;
private static double treeGenMultiplier = 1.0;
private static double treeCutMultiplier = 1.0;
private static int minGenDetail = LodConfig.CLIENT.worldGenerator.maxGenerationDetail.get().detailLevel;
private static int minDrawDetail = Math.max(LodConfig.CLIENT.graphics.maxDrawDetail.get().detailLevel,LodConfig.CLIENT.worldGenerator.maxGenerationDetail.get().detailLevel);
private static int maxDetail = LodUtil.REGION_DETAIL_LEVEL + 1;
private static int minDistance = 0;
private static int maxDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * 16 * 2;
private static int base = 2;
private static double logBase = Math.log(2);
private static LodDetail[] lodGenDetails = {
LodDetail.FULL,
LodDetail.HALF,
LodDetail.QUAD,
LodDetail.DOUBLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE,
LodDetail.SINGLE};
public static void updateSettings(){
minGenDetail = LodConfig.CLIENT.worldGenerator.maxGenerationDetail.get().detailLevel;
minDrawDetail = Math.max(LodConfig.CLIENT.graphics.maxDrawDetail.get().detailLevel,LodConfig.CLIENT.worldGenerator.maxGenerationDetail.get().detailLevel);
maxDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * 16 * 2;
}
public static int getDistanceRendering(int detail)
{
int initial;
int distance = 0;
if (detail <= minGenDetail)
return minDistance;
if (detail == maxDetail)
return maxDistance;
if (detail == maxDetail + 1)
return maxDistance;
switch (LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.get())
{
case LINEAR:
initial = LodConfig.CLIENT.graphics.lodQuality.get() * 128;
return (detail * initial);
case QUADRATIC:
initial = LodConfig.CLIENT.graphics.lodQuality.get() * 128;
return (int) (Math.pow(base, detail) * initial);
case RENDER_DEPENDANT:
int realRenderDistance = MinecraftWrapper.INSTANCE.getRenderDistance() * 16;
int border = 64;
byte detailAtBorder = (byte) 4;
if (detail > detailAtBorder)
{
return (detail * (border - realRenderDistance) / detailAtBorder + realRenderDistance);
} else
{
return ((maxDetail - detail) * (maxDistance - border) / (maxDetail - detailAtBorder) + border);
}
}
return distance;
}
public static byte baseInverse(int distance, int minDetail)
{
int initial;
byte detail = 0;
if (distance == 0)
detail = (byte) minDetail;
if (distance > maxDistance)
detail = (byte) (maxDetail-1);
switch (LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.get())
{
case LINEAR:
initial = LodConfig.CLIENT.graphics.lodQuality.get() * 128;
detail = (byte) Math.floorDiv(distance, initial);
break;
case QUADRATIC:
initial = LodConfig.CLIENT.graphics.lodQuality.get() * 128;
detail = (byte) (Math.log(Math.floorDiv(distance, initial))/logBase);
break;
case RENDER_DEPENDANT:
detail = (byte) 9;
break;
}
return (byte) Math.min(detail, LodUtil.REGION_DETAIL_LEVEL);
}
public static byte getDistanceRenderingInverse(int distance)
{
return baseInverse(distance, minDrawDetail);
}
public static byte getDistanceGenerationInverse(int distance)
{
return baseInverse((int) (distance * genMultiplier), minGenDetail);
}
public static byte getDistanceTreeCutInverse(int distance)
{
return baseInverse((int) (distance * treeCutMultiplier), minGenDetail);
}
public static byte getDistanceTreeGenInverse(int distance)
{
return baseInverse((int) (distance * treeGenMultiplier), minGenDetail);
}
public static int getDistanceGeneration(int detail)
{
if (detail == maxDetail)
return maxDistance;
return (int) (getDistanceRendering(detail) * genMultiplier);
}
public static int getDistanceTreeCut(int detail)
{
if (detail == maxDetail)
return maxDistance;
return (int) (getDistanceRendering(detail) * treeCutMultiplier);
}
public static int getDistanceTreeGen(int detail)
{
if (detail == maxDetail)
return maxDistance;
return (int) (getDistanceRendering(detail) * treeGenMultiplier);
}
public static DistanceGenerationMode getDistanceGenerationMode(int detail)
{
return LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get();
}
public static byte getLodDrawDetail(int detail)
{
return (byte) Math.max(detail, minDrawDetail);
}
public static LodDetail getLodGenDetail(int detail)
{
if (detail < minGenDetail)
{
return lodGenDetails[minGenDetail];
} else
{
return lodGenDetails[detail];
}
}
public static byte getCutLodDetail(int detail)
{
if (detail < minGenDetail)
{
return lodGenDetails[minGenDetail].detailLevel;
} else if (detail == maxDetail)
{
return LodUtil.REGION_DETAIL_LEVEL;
} else
{
return lodGenDetails[detail].detailLevel;
}
}
public static boolean regionInView(int playerPosX, int playerPosY, int playerPosZ, int xRot, int yRot, int fov, RegionPos regionPos)
{
//System.out.println(Math.floorMod((int) mc.player.xRot,360) + " " + Math.floorMod((int) mc.player.yRot,360) + " " + mc.options.fov);
//System.out.println(mc.player.xRotO + " " + mc.player.yRotO);
//mc.options.fov;
return false;
}
}
@@ -1,28 +0,0 @@
package com.seibel.lod.util;
import java.util.concurrent.ThreadFactory;
/**
* Just a simple ThreadFactory to name ExecutorService
* threads, which can be helpful when debugging.
*
* @author James Seibel
* @version 8-15-2021
*/
public class LodThreadFactory implements ThreadFactory
{
public String threadName;
public LodThreadFactory(String newThreadName)
{
threadName = newThreadName + " Thread";
}
@Override
public Thread newThread(Runnable r)
{
return new Thread(r, threadName);
}
}
@@ -1,390 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.util;
import java.awt.Color;
import java.io.File;
import java.util.HashSet;
import com.seibel.lod.objects.DataPoint;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.wrappers.MinecraftWrapper;
import it.unimi.dsi.fastutil.objects.ObjectList;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.WorldRenderer.LocalRenderInformationContainer;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.DimensionType;
import net.minecraft.world.IWorld;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.Heightmap;
import net.minecraft.world.server.ServerChunkProvider;
import net.minecraft.world.server.ServerWorld;
/**
* This class holds methods and constants that may be used in multiple places.
*
* @author James Seibel
* @version 9-7-2021
*/
public class LodUtil
{
private static MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/** alpha used when drawing chunks in debug mode */
public static final int DEBUG_ALPHA = 255; // 0 - 255
public static final Color COLOR_DEBUG_BLACK = new Color(0, 0, 0, DEBUG_ALPHA);
public static final Color COLOR_DEBUG_WHITE = new Color(255, 255, 255, DEBUG_ALPHA);
public static final Color COLOR_INVISIBLE = new Color(0,0,0,0);
/** a gray-purple color */
public static final int MYCELIUM_COLOR_INT = LodUtil.colorToInt(Color.decode("#6E6166"));
/** TODO, add a better way to override material colors
* and/or add a method to generate colors based on texture
* issue #64 */
public static final int STONE_COLOR_INT = LodUtil.colorToInt(new Color(150, 150, 150));
/**
* In order of nearest to farthest: <br>
* Red, Orange, Yellow, Green, Cyan, Blue, Magenta, white, gray, black
*/
public static final Color DEBUG_DETAIL_LEVEL_COLORS[] = new Color[] { Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE, Color.MAGENTA, Color.WHITE, Color.GRAY, Color.BLACK };
/** 512 blocks wide */
public static final byte REGION_DETAIL_LEVEL = 9;
/** 16 blocks wide */
public static final byte CHUNK_DETAIL_LEVEL = 4;
/** 1 block wide */
public static final byte BLOCK_DETAIL_LEVEL = 0;
public static final byte DETAIL_OPTIONS = 10;
/** measured in Blocks <br>
* detail level 9 */
public static final short REGION_WIDTH = 512;
/** measured in Blocks <br>
* detail level 4 */
public static final short CHUNK_WIDTH = 16;
/** measured in Blocks <br>
* detail level 0 */
public static final short BLOCK_WIDTH = 1;
/** number of chunks wide */
public static final int REGION_WIDTH_IN_CHUNKS = 32;
/** If we ever need to use a heightmap for any reason, use this one. */
public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;
/** This regex finds any characters that are invalid for use in a windows
* (and by extension mac and linux) file path */
public static final String INVALID_FILE_CHARACTERS_REGEX = "[\\\\\\/:*?\\\"<>|]";
/**
* Gets the first valid ServerWorld.
*
* @return null if there are no ServerWorlds
*/
public static ServerWorld getFirstValidServerWorld()
{
if (mc.hasSingleplayerServer())
return null;
Iterable<ServerWorld> worlds = mc.getSingleplayerServer().getAllLevels();
for (ServerWorld world : worlds)
return world;
return null;
}
/**
* Gets the ServerWorld for the relevant dimension.
*
* @return null if there is no ServerWorld for the given dimension
*/
public static ServerWorld getServerWorldFromDimension(DimensionType dimension)
{
IntegratedServer server = mc.getSingleplayerServer();
if (server == null)
return null;
Iterable<ServerWorld> worlds = server.getAllLevels();
ServerWorld returnWorld = null;
for (ServerWorld world : worlds)
{
if(world.dimensionType() == dimension)
{
returnWorld = world;
break;
}
}
return returnWorld;
}
/**
* Convert a 2D absolute position into a quad tree relative position.
*/
public static RegionPos convertGenericPosToRegionPos(int x, int z, int detailLevel)
{
int relativePosX = Math.floorDiv(x, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel));
int relativePosZ = Math.floorDiv(z, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel));
return new RegionPos(relativePosX, relativePosZ);
}
/**
* Convert a 2D absolute position into a quad tree relative position.
*/
public static int convertLevelPos(int pos, int currectDetailLevel, int targetDetailLevel)
{
int newPos = Math.floorDiv(pos, (int) Math.pow(2, targetDetailLevel - currectDetailLevel));
return newPos;
}
/**
* Return whether the given chunk
* has any data in it.
*/
public static boolean chunkHasBlockData(IChunk chunk)
{
ChunkSection[] blockStorage = chunk.getSections();
for(ChunkSection section : blockStorage)
{
if(section != null && !section.isEmpty())
{
return true;
}
}
return false;
}
/**
* If on single player this will return the name of the user's
* world, if in multiplayer it will return the server name, IP,
* and game version.
*/
public static String getWorldID(IWorld world)
{
if(mc.hasSingleplayerServer())
{
// chop off the dimension ID as it is not needed/wanted
String dimId = getDimensionIDFromWorld(world);
// get the world name
int saveIndex = dimId.indexOf("saves") + 1 + "saves".length();
int slashIndex = dimId.indexOf(File.separatorChar, saveIndex);
dimId = dimId.substring(saveIndex, slashIndex);
return dimId;
}
else
{
return getServerId();
}
}
/**
* If on single player this will return the name of the user's
* world and the dimensional save folder, if in multiplayer
* it will return the server name, ip, game version, and dimension.<br>
* <br>
* This can be used to determine where to save files for a given
* dimension.
*/
public static String getDimensionIDFromWorld(IWorld world)
{
if(mc.hasSingleplayerServer())
{
// this will return the world save location
// and the dimension folder
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(world.dimensionType());
if(serverWorld == null)
throw new NullPointerException("getDimensionIDFromWorld wasn't able to get the ServerWorld for the dimension " + world.dimensionType().effectsLocation().getPath());
ServerChunkProvider provider = serverWorld.getChunkSource();
if(provider == null)
throw new NullPointerException("getDimensionIDFromWorld wasn't able to get the ServerChunkProvider for the dimension " + world.dimensionType().effectsLocation().getPath());
return provider.dataStorage.dataFolder.toString();
}
else
{
return getServerId() + File.separatorChar + "dim_" + world.dimensionType().effectsLocation().getPath() + File.separatorChar;
}
}
/**
* returns the server name, IP and game version.
*/
public static String getServerId()
{
ServerData server = mc.getCurrentServer();
String serverName = server.name.replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
String serverIp = server.ip.replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
String serverMcVersion = server.version.getString().replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
String serverId = serverName + ", IP " + serverIp + ", GameVersion " + serverMcVersion;
return serverId;
}
/**
* Convert a BlockColors int into a Color object.
*/
public static Color intToColor(int num)
{
int filter = 0b11111111;
int red = (num >> 16 ) & filter;
int green = (num >> 8 ) & filter;
int blue = num & filter;
return new Color(red, green, blue);
}
/**
* Convert a Color into a BlockColors object.
*/
public static int colorToInt(Color color)
{
return color.getRGB();
}
/**
* Clamps the given value between the min and max values.
* May behave strangely if min > max.
*/
public static int clamp(int min, int value, int max)
{
return Math.min(max, Math.max(value, min));
}
/**
* Clamps the given value between the min and max values.
* May behave strangely if min > max.
*/
public static float clamp(float min, float value, float max)
{
return Math.min(max, Math.max(value, min));
}
/**
* Clamps the given value between the min and max values.
* May behave strangely if min > max.
*/
public static double clamp(double min, double value, double max)
{
return Math.min(max, Math.max(value, min));
}
/**
* Get a HashSet of all ChunkPos within the normal render distance
* that should not be rendered.
*/
public static HashSet<ChunkPos> getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPos playerPos)
{
int chunkRenderDist = mc.getRenderDistance();
ChunkPos centerChunk = new ChunkPos(playerPos);
// skip chunks that are already going to be rendered by Minecraft
HashSet<ChunkPos> posToSkip = getRenderedChunks();
// go through each chunk within the normal view distance
for (int x = centerChunk.x - chunkRenderDist; x < centerChunk.x + chunkRenderDist; x++)
{
for (int z = centerChunk.z - chunkRenderDist; z < centerChunk.z + chunkRenderDist; z++)
{
if (!lodDim.doesDataExist(LodUtil.CHUNK_DETAIL_LEVEL, x, z))
continue;
long data = lodDim.getData(LodUtil.CHUNK_DETAIL_LEVEL, x, z);
short lodAverageHeight = DataPoint.getHeight(data);
if (playerPos.getY() <= lodAverageHeight)
{
// don't draw Lod's that are taller than the player
// to prevent LODs being drawn on top of the player
posToSkip.add(new ChunkPos(x, z));
}
}
}
return posToSkip;
}
/**
* This method returns the ChunkPos of all chunks that Minecraft
* is going to render this frame. <br><br>
* <p>
* Note: This isn't perfect. It will return some chunks that are outside
* the clipping plane. (For example, if you are high above the ground some chunks
* will be incorrectly added, even though they are outside render range).
*/
public static HashSet<ChunkPos> getRenderedChunks()
{
HashSet<ChunkPos> loadedPos = new HashSet<>();
// Wow those are some long names!
// go through every RenderInfo to get the compiled chunks
WorldRenderer renderer = mc.getLevelRenderer();
ObjectList<LocalRenderInformationContainer> chunks = renderer.renderChunks;
for (WorldRenderer.LocalRenderInformationContainer worldrenderer$localrenderinformationcontainer : chunks)
{
if (!worldrenderer$localrenderinformationcontainer.chunk.getCompiledChunk().hasNoRenderableLayers())
{
// add the ChunkPos for every empty compiled chunk
BlockPos bpos = worldrenderer$localrenderinformationcontainer.chunk.getOrigin();
loadedPos.add(new ChunkPos(bpos));
}
}
return loadedPos;
}
}
@@ -1,43 +0,0 @@
/*
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.util;
/**
* This holds meta information about the mod.
*
* @author James Seibel
* @version 04-16-2020
*/
public class Reference
{
/** the mod's identifier */
public static final String MOD_ID = "lod";
/** the mod's name */
public static final String NAME = "LOD Mod";
/** the mod's version */
public static final String VERSION = "1.0";
/** the version of minecraft this mod is built for */
public static final String ACCEPTED_VERSIONS = "[1.16.4]";
/** where the client proxy class is */
public static final String CLIENT_PROXY_CLASS = "com.backsun.lod.proxy.ClientProxy";
/** where the common proxy class is*/
public static final String COMMON_PROXY_CLASS = "com.backsun.lod.proxy.CommonProxy";
}
@@ -1,137 +0,0 @@
package com.seibel.lod.wrappers;
import java.io.File;
import com.seibel.lod.util.LodUtil;
import net.minecraft.client.GameSettings;
import net.minecraft.client.MainWindow;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.network.play.ClientPlayNetHandler;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.profiler.IProfiler;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.Direction;
import net.minecraft.world.DimensionType;
/**
* A singleton that wraps the Minecraft class
* to allow for easier movement between Minecraft versions.
*
* @author James Seibel
* @version 9-6-2021
*/
public class MinecraftWrapper
{
public static MinecraftWrapper INSTANCE = new MinecraftWrapper();
private Minecraft mc = Minecraft.getInstance();
private MinecraftWrapper()
{
}
//=================//
// method wrappers //
//=================//
public float getShading(Direction direction)
{
return mc.level.getShade(Direction.UP, true);
}
public boolean hasSingleplayerServer()
{
return mc.hasSingleplayerServer();
}
public DimensionType getCurrentDimension()
{
return mc.player.level.dimensionType();
}
public String getCurrentDimensionId()
{
return LodUtil.getDimensionIDFromWorld(mc.level);
}
//=============//
// Simple gets //
//=============//
public ClientPlayerEntity getPlayer()
{
return mc.player;
}
public GameSettings getOptions()
{
return mc.options;
}
/** Measured in chunks */
public int getRenderDistance()
{
return mc.options.renderDistance;
}
public File getGameDirectory()
{
return mc.gameDirectory;
}
public IProfiler getProfiler()
{
return mc.getProfiler();
}
public ClientPlayNetHandler getConnection()
{
return mc.getConnection();
}
public GameRenderer getGameRenderer()
{
return mc.gameRenderer;
}
public Entity getCameraEntity()
{
return mc.cameraEntity;
}
public MainWindow getWindow()
{
return mc.getWindow();
}
public float getSkyDarken(float partialTicks)
{
return mc.level.getSkyDarken(partialTicks);
}
public IntegratedServer getSingleplayerServer()
{
return mc.getSingleplayerServer();
}
public ServerData getCurrentServer()
{
return mc.getCurrentServer();
}
public WorldRenderer getLevelRenderer()
{
return mc.levelRenderer;
}
}
@@ -44,6 +44,13 @@ public net.minecraft.world.lighting.WorldLightManager field_215576_a # blockEngi
public net.minecraft.world.lighting.WorldLightManager field_215577_b # skyEngine
public net.minecraft.world.gen.feature.Feature field_236290_a_ # configuredCodec
# used for uploading vertex buffers off the render thread
public net.minecraft.client.renderer.vertex.VertexBuffer field_177365_a # id
public net.minecraft.client.renderer.vertex.VertexBuffer field_177363_b # format
public net.minecraft.client.renderer.vertex.VertexBuffer field_177364_c # vertexCount
# used for accessing the lightmap
public net.minecraft.client.renderer.LightTexture field_205111_b # lightPixels
#=====================#
+7 -4
View File
@@ -24,10 +24,10 @@ modId="lod" #mandatory
#// The version number of the mod - there's a few well known ${} variables useable here or just hardcode it
#//${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata
#// see the associated build.gradle script for how to populate this completely automatically during a build
version="a1.4.1" #mandatory
version="a1.5.3" #mandatory
#// A display name for the mod
displayName="LOD" #mandatory
displayName="Distant Horizons" #mandatory
#// A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/
#//updateJSONURL="https://change.me.example.invalid/updates.json" #optional
@@ -38,11 +38,14 @@ displayURL="https://www.curseforge.com/minecraft/mc-mods/lod-level-of-detail" #o
#// A file name (in the root of the mod JAR) containing a logo for display
logoFile="logo.png" #optional
#// A file name (in the root of the mod JAR) containing a icon for display by catalogue
catalogueImageIcon="icon.png"
#// A text field displayed in the mod UI
credits="TechnoVision, Vike, and Darkhax for their modding tutorials." #optional
#// A text field displayed in the mod UI
authors="James Seibel and Leonardo Amato" #optional
authors="James Seibel, Leonardo Amato, and Cola" #optional
#// The description text for the mod (multi line!) (#mandatory)
description='''This mod generates and renders simplified terrain beyond the normal view distance, at a low performance cost.'''
description='''This mod generates and renders simplified terrain beyond the normal view distance, at a low performance cost.'''
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+1 -1
View File
@@ -1,6 +1,6 @@
{
"required": true,
"package": "com.seibel.lod.mixin",
"package": "com.seibel.lod.forge.mixins",
"compatibilityLevel": "JAVA_8",
"refmap": "lod.refmap.json",
"mixins": [
Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 191 KiB