Compare commits

..

994 Commits

Author SHA1 Message Date
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
James Seibel b1bcdb24f0 comment out the config override for release a1.4.1 2021-09-08 18:30:37 -05:00
James Seibel cbd7f4300e Improve logging wording 2021-09-08 18:17:36 -05:00
James Seibel 4d8e30121e fix a spelling mistake 2021-09-08 18:17:17 -05:00
James Seibel fbe39542fc update the version number to a1.4.1 2021-09-08 18:13:25 -05:00
James Seibel 6a1f448b41 comment out testing code 2021-09-08 18:13:06 -05:00
James Seibel e13b23832c Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-09-08 18:09:15 -05:00
James Seibel 4c9e0edf32 Add barrier blocks to the list of blocks to ignore 2021-09-08 18:08:59 -05:00
Leonardo 8664a06d3b fixed a small bug that was causing visual artifacts 2021-09-08 19:38:17 +02:00
James Seibel 18b8359f0d rename the wrapper package to wrappers 2021-09-07 21:12:58 -05:00
James Seibel 7de4c7c72a Make the reflection handler a singleton 2021-09-07 21:12:16 -05:00
James Seibel b4bbabae42 enable debugging settings in ClientProxy 2021-09-07 19:52:27 -05:00
James Seibel 40321d6e21 Remove unused variables, imports, etc. 2021-09-07 19:52:06 -05:00
James Seibel cf1702bd0c Fix file reading/writing not working, Closes #61 2021-09-07 19:38:41 -05:00
James Seibel a9b50cdb32 remove invalid imports / organize imports 2021-09-07 19:10:30 -05:00
James Seibel 7eed472029 Fix stuttering caused by a merge 2021-09-07 19:09:50 -05:00
James Seibel 0262e452c7 Improve FileHandler info message 2021-09-07 19:04:48 -05:00
James Seibel 30287d5a7d Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-09-07 18:54:41 -05:00
James Seibel 83346fed1f Improve the error messages in the LodDimensionFileHandler 2021-09-07 18:54:24 -05:00
Leonardo e897c3edba fixed generation 2021-09-08 00:51:39 +02:00
Leonardo bd9e2acaf6 Changed the default heigth and depth and small fix to the culling 2021-09-08 00:23:44 +02:00
Leonardo 1e15d372c4 Added box caching 2021-09-07 21:22:12 +02:00
Leonardo 849d563425 Added box to replace AxisAlignedBB 2021-09-07 20:07:39 +02:00
Leonardo aa5a8aa3b8 small change 2021-09-07 19:05:58 +02:00
Leonardo b096cc53aa Working version 2021-09-07 19:02:51 +02:00
Leonardo 94d994e761 re-added the commit 2021-09-07 18:52:58 +02:00
Leonardo f76fa17e25 Removed level pos and data arrays 2021-09-07 17:23:22 +02:00
James Seibel 53a66268cb Close issue #37 (z-fighting far from the origin)
I'm not certain why in LodRenderer swapBuffers has to be called after the frame has been rendered, but otherwise it causes stuttering / rubber banding.
2021-09-06 22:51:46 -05:00
James Seibel 139867d4b8 auto-indent 2021-09-06 12:17:49 -05:00
James Seibel 81cf05ba19 fix a issue where nothing renders in the compiled version 2021-09-06 11:55:44 -05:00
James Seibel 13a1e7ed56 Add the MinecraftWrapper 2021-09-06 11:20:32 -05:00
James Seibel 42bd0fbde9 Delete 1.4 release notes.txt 2021-09-05 17:25:46 -05:00
James Seibel ebe2bd97bc Remove the compiled jars (they are now handled by CurseForge) 2021-09-05 16:51:26 -05:00
James Seibel a3ac3386d0 Update 1.4 release notes.txt 2021-09-05 16:50:42 -05:00
Leonardo 69ba83d34f small change to far Fog 2021-09-05 16:15:14 +02:00
James Seibel e048d277f8 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-09-04 11:01:26 -05:00
James Seibel 0bca47a92b Update pack.mcmeta
I don't even know if we need this file
2021-09-04 11:01:05 -05:00
James Seibel ce84b30ddb Change a println to use the logger 2021-09-04 11:00:54 -05:00
Leonardo 1843f96526 various fix to the new classes 2021-09-04 12:48:22 +02:00
Leonardo e46864431b Fixing the new classes 2021-09-03 23:33:54 +02:00
Leonardo 152a3fa108 Optimized the generation memory use 2021-09-03 22:35:08 +02:00
Leonardo 3420133bd3 Added LevelPosUtil, PosToGenerateContainer and PosToRenderContainer to be used in the future 2021-09-03 19:34:04 +02:00
Leonardo c79bf7c3f7 Fixed chunk data generation during first world loading 2021-09-03 13:10:16 +02:00
James Seibel 2219da4050 revert the fog changes 2021-09-01 21:47:31 -05:00
James Seibel a1aa90cccb Split up the configs to make it easier to understand 2021-09-01 21:39:02 -05:00
Leonardo d19abaef7b small fix to a bug that was causing crash if the player went over y 256 2021-09-01 18:20:51 +02:00
Leonardo 77625c65c7 small fix 2021-09-01 18:11:25 +02:00
Leonardo 91ba48ad4a Max draw detail now works correctly and node bigger than a chunk do not disappear if you touch a border 2021-09-01 18:11:01 +02:00
Leonardo 32e587d536 Small fix to the index of vanillaRenderedChunks 2021-09-01 17:27:37 +02:00
Leonardo e3dfe658f5 Fixed chunkPos being created multiple time in the buffer 2021-09-01 17:20:52 +02:00
Leonardo 4e249e943a Revert "Added LevelPosUtil, now the node are added using it and not with the LevelPos object"
LevelPos are useful and the small stutter in the buffer builder is probably caused by the creation of ChunkPos
2021-09-01 16:33:34 +02:00
Leonardo 1360edb459 Added LevelPosUtil, now the node are added using it and not with the LevelPos object 2021-09-01 15:15:23 +02:00
James Seibel 4c7ca395c6 Add the buffer rebuild timeouts to ClientProxy.applyConfigOverrides() 2021-08-31 21:14:19 -05:00
James Seibel a1c720d588 Fix a issue where the projection matrix was given a depth of 0
This caused the depth buffer to stop working.
2021-08-31 21:05:53 -05:00
James Seibel 7b09840d02 Organize inputs 2021-08-31 19:26:17 -05:00
Leonardo e65579f346 temporary fix to the missing region bug 2021-08-31 18:08:02 +02:00
Leonardo 78d6481a49 Another small fix 2021-08-31 12:55:42 +02:00
Leonardo 1428e72d46 small fix to the generation 2021-08-31 11:47:18 +02:00
James Seibel 2c2c6d6785 Add configs for buffer building timeouts and small refactors 2021-08-30 23:16:27 -05:00
Leonardo d5d48f2448 now the vanillaRenderedChunk is reset if the player goes to high 2021-08-30 23:41:56 +02:00
Leonardo 536de1a22d fixed a small error with the timer 2021-08-30 23:25:37 +02:00
Leonardo 193c579712 changed the generation. Added re-use of levelPos 2021-08-30 23:03:22 +02:00
Leonardo cdba7b20f5 Buffer that does not change won't be rebuilt 2021-08-30 17:55:34 +02:00
Leonardo 5467d007b8 preparing the mod for future buffer caching 2021-08-30 12:36:07 +02:00
Leonardo b24d691d8a Removed a useless temp Object in the getNodeToRender 2021-08-30 10:36:15 +02:00
James Seibel f68e6a9a13 Add a config option to disable the F4 keybinding 2021-08-29 21:23:16 -05:00
James Seibel 2dcdc854e3 Improve stone color 2021-08-29 21:09:30 -05:00
James Seibel 42da213ea9 Update the mod meta info 2021-08-29 16:35:59 -05:00
James Seibel dfea2f561a Replace the old logo files with the new logo files 2021-08-29 14:32:19 -05:00
James Seibel d8b4730ee0 Remove a unneeded depreciation suppression warning 2021-08-29 14:20:18 -05:00
Leonardo 74cbf794a1 the culling in templace can be disabled using the disableCulling boolean 2021-08-29 18:22:41 +02:00
Leonardo 812fa65054 divided the time checker for the regen 2021-08-29 17:18:21 +02:00
Leonardo ba63b44288 small change 2021-08-29 16:44:36 +02:00
Leonardo b19a80f411 Optimized the regen 2021-08-29 16:42:44 +02:00
Leonardo 450f15ad36 Finally fixed the regen problem 2021-08-29 10:56:14 +02:00
James Seibel a6ed4968f5 Add a config option for the number of buffer builder threads 2021-08-28 23:38:51 -05:00
James Seibel 65dc629771 Remove a unsettable configOverride 2021-08-28 23:38:16 -05:00
James Seibel 80aa49e28b Update 1.4 release notes.txt 2021-08-28 23:37:34 -05:00
James Seibel 2c3149ef30 Fix a potential issue with compiling to the wrong Java version 2021-08-28 17:15:38 -05:00
James Seibel cd792d7045 Fix a potential crash where Setupbuffers isn't called fast enough for the BufferBuilder 2021-08-28 17:13:17 -05:00
Leonardo 3ade43651f small Optimization to the buffer 2021-08-28 20:55:18 +02:00
Leonardo f4c3ad8bb5 Fixed the garbage collector being called too much 2021-08-28 20:32:58 +02:00
James Seibel 13d2c7b421 Create the comment for issue #62 2021-08-28 12:10:19 -05:00
James Seibel c54c2c6612 Remove the unused parameter in getBufferMemoryForSingleLod() 2021-08-28 11:55:48 -05:00
James Seibel 13878b7387 auto-format 2021-08-28 11:27:20 -05:00
James Seibel 2f20fc6b77 Update the default config options 2021-08-28 11:18:19 -05:00
James Seibel d4ada067c5 auto-indent 2021-08-28 11:05:33 -05:00
James Seibel 9277b3ad38 Add different debug modes (off, detail, detail_wireframe) 2021-08-28 11:04:59 -05:00
James Seibel c48d409015 remove the unused enum LodQuality 2021-08-28 10:18:23 -05:00
James Seibel ccdebb4242 auto-indent 2021-08-28 10:18:05 -05:00
James Seibel 18c94cea46 Update known issues 2021-08-28 10:09:32 -05:00
James Seibel fe381fc207 Rearrange ClientProxy and fix a potential saving issues 2021-08-28 10:07:20 -05:00
James Seibel acc73af666 Remove some debugging 2021-08-28 10:00:11 -05:00
Leonardo 685284f131 removed some e.printStack for the release 2021-08-28 13:40:39 +02:00
Leonardo f803bd58af fixed server not overriding lower quality 2021-08-28 13:21:50 +02:00
Leonardo 26fbdfc92c fixed memory use method and put F4 as debug key 2021-08-28 12:41:54 +02:00
Leonardo e3e8ef705a geneartionType is no longer used in LodRegion. Every region has a generationMode (+ server) 2021-08-28 12:22:09 +02:00
Leonardo e1a425dbcc fixed a small generation errore,
Now cut and gen tree are automatic
2021-08-28 11:15:34 +02:00
Leonardo d8bd4e2347 Fixed a small loading bug,
made the getDataToGenerate fully automatic, added an option to update the DetailDistanceUtil values
2021-08-28 10:38:44 +02:00
James Seibel 49cdd5702f Add 1.4 release notes
This also includes bugs that need to be fixed before 1.4 can be released
2021-08-27 21:25:26 -05:00
James Seibel 32c19eab7f move a comment 2021-08-27 20:59:41 -05:00
James Seibel a32b66aaab organize imports 2021-08-27 18:05:27 -05:00
James Seibel 425f4948dc Remove unneeded imports and a unneeded cast 2021-08-27 18:05:02 -05:00
James Seibel d05908af32 Auto-formatting 2021-08-27 18:04:50 -05:00
Leonardo 822a088096 fixed loading 2021-08-28 00:36:45 +02:00
Leonardo 36079e1624 fix to the Rendering holes 2021-08-28 00:16:44 +02:00
Leonardo 8db782a406 Now nodeToRender is fully automatic 2021-08-27 23:05:37 +02:00
Leonardo dd018f90d3 Small change 2021-08-27 18:47:42 +02:00
Leonardo af4e30588f new z fix 2021-08-27 18:27:37 +02:00
Leonardo 2630d50147 temporary z fix 2021-08-27 17:43:17 +02:00
Leonardo db2410e7fe changed indent 2021-08-27 14:34:43 +02:00
Leonardo 9990132db2 small changes 2021-08-27 14:31:13 +02:00
Leonardo 21069d6479 Optimization to getLodToRender 2021-08-27 12:02:02 +02:00
Leonardo b27afbb18c Optimized the getDataToGenerate 2021-08-27 11:34:55 +02:00
Leonardo 783c0c97a1 Preparing the BufferBuilder for new system 2021-08-27 11:28:29 +02:00
Leonardo 3dfa2d778a Fixed end bug 2021-08-27 10:59:24 +02:00
James Seibel b44a641d96 Add a TODO 2021-08-26 22:59:55 -05:00
James Seibel 8139b83a01 Fix heights saving higher then they are 2021-08-26 21:01:59 -05:00
James Seibel 456ba183da Fix updating regions where LODs contain no blocks 2021-08-26 20:28:48 -05:00
James Seibel 7525c1b257 Fix the "ffffff" error when reading files 2021-08-26 19:37:38 -05:00
James Seibel 5d4d634537 Re-add the Number Of World Generation Threads config option 2021-08-26 19:31:33 -05:00
James Seibel e1149be7c1 add a missing closing comment "/" 2021-08-26 18:14:49 -05:00
Leonardo fc0aaac69f added better shading + small changes 2021-08-27 00:54:46 +02:00
Leonardo 9786a12273 Fixed a small bug with getDataToGenerate 2021-08-26 23:34:14 +02:00
Leonardo c169cb6b7f Changed how color are calculated 2021-08-26 23:02:32 +02:00
Leonardo afcdce4667 Small fix to LevelPos 2021-08-26 20:17:00 +02:00
Leonardo ab7ed9a4c5 Optimized getDataToGenerate 2021-08-26 20:13:32 +02:00
Leonardo 120e8e5f6d small fixes and changed settings 2021-08-26 19:33:59 +02:00
Leonardo 65824a6fed fixed fog 2021-08-26 19:33:32 +02:00
Leonardo 114aaf9fe4 Update doesn't use temp object anymore 2021-08-26 19:33:15 +02:00
Leonardo 347b149037 The generation is working now 2021-08-26 17:41:01 +02:00
Leonardo 70317a37cb fixed LevelPos and colors 2021-08-26 14:28:27 +02:00
Leonardo 74e4744ff7 Correctly removed LodDataPoint and optimized the LevelPos use 2021-08-26 13:18:18 +02:00
Leonardo 98cbc30709 Small changes to how number of thread is chosen and returned to old LINEAR system 2021-08-25 16:12:18 +02:00
Leonardo b44900e3f0 Added new render/generation distance calculator 2021-08-25 13:44:53 +02:00
Leonardo 1755a252b1 Fixed how request works 2021-08-25 13:13:48 +02:00
Leonardo afcedd0c6d Improved how LINEAR is determined 2021-08-25 13:13:27 +02:00
Leonardo 525005bcb6 small changes to how the node to render are chosen 2021-08-25 12:26:16 +02:00
Leonardo 360294d37c fixed the generation Request 2021-08-25 12:24:59 +02:00
Leonardo ea05e3f9e6 fixed the cull 2021-08-25 12:24:01 +02:00
Leonardo 70a75f0543 added an offset to the cull 2021-08-25 11:09:49 +02:00
Leonardo 488d520b8f Improved the template, shared faces are not rendered 2021-08-25 10:55:05 +02:00
Leonardo 30bb175ea0 small changes 2021-08-25 08:17:39 +02:00
Leonardo 500a68e0fc Merge remote-tracking branch 'origin/1.16.5' into 1.16.5
# Conflicts:
#	src/main/java/com/seibel/lod/builders/LodBufferBuilder.java
#	src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java
#	src/main/java/com/seibel/lod/proxy/ClientProxy.java
2021-08-25 07:59:57 +02:00
James Seibel 0cd9bfaef2 Put all LOD save files in the same folder 2021-08-24 21:48:09 -05:00
James Seibel a464176a25 Close #32 (make distance generation independent of the buffer builder) 2021-08-24 21:47:51 -05:00
James Seibel a0dcde48ae Remove unneeded TODOs and unused code 2021-08-24 18:31:42 -05:00
Leonardo 8ae81d3aac added some debugging comments 2021-08-24 23:11:14 +02:00
Leonardo c4f864f0e3 changed how the distance generation mode is determined and added
zFix boolean in node to render to disable the zFighting fix
2021-08-24 23:10:29 +02:00
Leonardo cddd239fc5 Introduced try and catch were needed and fixed some small bugs 2021-08-24 14:58:22 +02:00
Leonardo 189b0ec878 Now the system work stable with SINGLE 2021-08-24 02:35:25 +02:00
Leonardo bd305a0269 small fixes to tree generator new tree cutter 2021-08-24 01:54:01 +02:00
Leonardo bbf99ed145 fixed tree generator new tree cutter 2021-08-24 00:33:46 +02:00
Leonardo cac4807986 Added tree generator and new tree cutter 2021-08-23 23:44:55 +02:00
Leonardo 181539b83b small fixes 2021-08-23 16:05:21 +02:00
Leonardo 4795ddd1ff Fixed circle generation bug 2021-08-23 15:33:51 +02:00
Leonardo c1c6680d04 small changes 2021-08-23 14:20:09 +02:00
Leonardo 81cbc7a12b added the big update function 2021-08-23 12:36:32 +02:00
Leonardo 4c6185556f Small fixes 2021-08-23 12:10:55 +02:00
Leonardo b0ed460230 small fix to the tree cutter 2021-08-23 11:45:15 +02:00
Leonardo 38e323a12f small fix to the tree cutter 2021-08-23 11:24:06 +02:00
Leonardo 9840c594e6 Added the tree cutter 2021-08-23 11:12:23 +02:00
James Seibel fdd8204a46 Add multi-threaded buffer building 2021-08-22 22:54:16 -05:00
James Seibel fb188e4819 auto indentation 2021-08-22 18:46:47 -05:00
James Seibel 9b3d9cbba3 javadoc formatting 2021-08-22 18:46:22 -05:00
James Seibel a2aa1dd081 add a lock to the buffer builder when the buffers are being used
Hopefully this should prevent some crashes and errors when generating buffers
2021-08-22 18:42:27 -05:00
James Seibel cb889d7430 auto formatting 2021-08-22 18:26:59 -05:00
James Seibel 2a88b2e746 Remove a unused method 2021-08-22 18:25:58 -05:00
James Seibel 93dd441708 Close #60 (LODs rendering on top of the player) and improve get Lod pos to skip 2021-08-22 18:24:41 -05:00
James Seibel a81ce6c28a auto reformat 2021-08-22 17:55:24 -05:00
James Seibel 967627c57e Move getRenderedChunks to LodUtil 2021-08-22 17:54:10 -05:00
Leonardo 31cb684bec Added different generation detail option 2021-08-22 19:54:10 +02:00
Leonardo d5112be385 small fixes 2021-08-22 19:18:14 +02:00
Leonardo 547d54aab1 Fixed the near clipping 2021-08-22 16:54:09 +02:00
Leonardo 397573bc08 removed lodChunkRadiusMultiplier from proxy, config and renderer 2021-08-22 16:46:55 +02:00
Leonardo b52d7e4f20 small fix to the Near fog 2021-08-22 16:33:20 +02:00
Leonardo a450f44aa7 small fix to the fog 2021-08-22 16:20:27 +02:00
Leonardo a4e8e9ef60 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5
# Conflicts:
#	src/main/java/com/seibel/lod/proxy/ClientProxy.java
2021-08-22 16:09:07 +02:00
Leonardo 3c28ed5601 small fix to fog 2021-08-22 16:06:30 +02:00
James Seibel c5ec41eb2d Add buffer recreation when changing worlds 2021-08-22 08:42:55 -05:00
Leonardo d1bb96c2dc small fix 2021-08-22 14:46:16 +02:00
James Seibel d72ba5bb82 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-08-22 07:40:09 -05:00
James Seibel baba54bdb8 Add error reporting and handling when unable to access the save folder 2021-08-22 07:40:00 -05:00
Leonardo aa7ea85cde New config 2021-08-22 14:34:02 +02:00
Leonardo a1f01bef78 Fixed game chunk not overriding lod chunk 2021-08-22 11:51:26 +02:00
Leonardo 1a7655e752 Added DetailUtil and it's use in the buffer builder +
small fixes
2021-08-22 11:27:40 +02:00
James Seibel e2a983e4ac comment grammar change 2021-08-21 21:54:40 -05:00
James Seibel e2f0263643 Remove synchronized from getters 2021-08-21 21:54:23 -05:00
James Seibel b280303f8f Improve vine colors 2021-08-21 20:06:22 -05:00
James Seibel ff3b2aee6b Add error catching and improve the None distance generation mode 2021-08-21 20:02:30 -05:00
James Seibel 804f830a10 fix third person backwards culling the wrong regions 2021-08-21 19:57:03 -05:00
James Seibel 68b5978135 Change the color of Mycelium to better match its block color 2021-08-21 19:45:43 -05:00
James Seibel 9ebb84bf9c Make air and Cave air act the same when generating LODs 2021-08-21 19:25:31 -05:00
James Seibel a08e08856a 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-08-21 18:57:39 -05:00
James Seibel 8d5c8bf7b8 Finish adding simple frustum culling 2021-08-21 18:54:20 -05:00
Leonardo 168eb537cb Improved generation 2021-08-21 22:21:41 +02:00
James Seibel ca57c15d46 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-08-21 15:19:04 -05:00
James Seibel 5aea9877e0 re-organize RenderUtil 2021-08-21 15:11:13 -05:00
Leonardo c20ec4ef59 added commented use of biomeUtils and terrainUtils 2021-08-21 22:01:52 +02:00
James Seibel 32fb349621 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-08-21 13:05:35 -05:00
James Seibel c5b4e20787 Add simple partially functioning frustum culling 2021-08-21 13:05:32 -05:00
James Seibel a246dd7561 improve a few comments 2021-08-21 13:02:21 -05:00
James Seibel 615e29451a add conversions to chunk and block pos 2021-08-21 13:01:39 -05:00
James Seibel bcac2bd7ba Update a few comments 2021-08-21 13:01:24 -05:00
Leonardo a02c232f5f improved getDataToGenerate and changed how it's used in the buffer builder 2021-08-21 17:28:11 +02:00
James Seibel d27dbbb64e 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-08-21 10:06:44 -05:00
James Seibel fdfbee108a minor client proxy debug setting changes/commenting 2021-08-21 09:57:14 -05:00
James Seibel de54c37cf1 Optimize Buffer Builder chunk generator queuing 2021-08-21 09:55:04 -05:00
Leonardo d4580806d1 minor fixes, fixed z fighting 2021-08-21 16:25:09 +02:00
James Seibel 5a856c65fa Update the debug colors to work up to region size 2021-08-21 08:03:35 -05:00
James Seibel 75d6da98dc Remove invalid imports (again) 2021-08-21 07:36:49 -05:00
Leonardo 7654cac01d minor fixes 2021-08-21 14:33:11 +02:00
James Seibel cbe242cd2f Remove invalid import 2021-08-21 07:23:55 -05:00
Leonardo 7f69b11ae4 minor fixes 2021-08-21 12:43:38 +02:00
Leonardo 426c9edb05 new generation system fixed 2021-08-21 12:25:49 +02:00
James Seibel 88fb9b5b21 Add debug code to the LodNodeGenWorker 2021-08-20 21:32:57 -05:00
James Seibel 163f8ca979 Remove Node from the names of LodRenderer, BufferBuilder, LodTemplates, and LodBuilder 2021-08-20 20:28:37 -05:00
James Seibel ee416a10ec Add null checking to the BufferBuilder 2021-08-20 20:24:33 -05:00
James Seibel c5ef03aa0e Add null checks and minor formatting changes 2021-08-20 20:19:46 -05:00
James Seibel 25cb08f541 Eclipse auto-formating 2021-08-20 20:19:05 -05:00
Leonardo fcd06bde87 fixed getDataToGenerate use in buffer builder,
Fixed buffers indexing
2021-08-21 02:57:57 +02:00
Leonardo 00f50a2e60 Added getNodeToGenerate to the buffer builder 2021-08-20 19:46:46 +02:00
Leonardo 550c0f1bf2 Added getDataToRender to the bufferBuilder 2021-08-20 19:02:11 +02:00
Leonardo 0f0179bd19 Various fix 2021-08-20 17:06:41 +02:00
Leonardo 7885d14a91 getDataToRender and getDataToGenerate added to dimension 2021-08-20 16:35:38 +02:00
Leonardo bd9c3bba3f added getDataTeRender 2021-08-20 16:07:27 +02:00
Leonardo 04f8cd653a getDataToGenerate should work correctly 2021-08-20 15:03:28 +02:00
James Seibel 02bfc20ae6 Fix small mushrooms using the grass color 2021-08-20 07:34:14 -05:00
James Seibel d65017366a comment out the buffer building time console printing 2021-08-20 07:25:52 -05:00
James Seibel 05d17ba9fa Close #57 (Add support for night vision) 2021-08-20 07:25:22 -05:00
Leonardo 967aab3b3b new line brackets 2021-08-20 11:46:41 +02:00
Leonardo 9f77bf4e61 Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-08-20 11:32:21 +02:00
James Seibel 6aa096de57 Remove an incorrect import 2021-08-19 19:41:56 -05:00
Leonardo d312d60cd9 Fixed loading not working, started the creation of dataToGenerate and dataToRender 2021-08-20 00:50:08 +02:00
Leonardo 65fde550cb Small fix to the position 2021-08-19 15:49:36 +02:00
Leonardo 736b5ba625 new enum for render distance and quality of lods 2021-08-19 12:47:54 +02:00
Leonardo b2227688bc Merge remote-tracking branch 'origin/1.16.5' into 1.16.5 2021-08-19 11:31:40 +02:00
Leonardo cbe8b647b1 New fast save system 2021-08-19 11:31:26 +02:00
James Seibel 0b0f0eab2f Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-08-18 17:37:38 -05:00
James Seibel ffb63ce8ef Move the setupBuffers method to the ClientProxy 2021-08-18 17:37:23 -05:00
Leonardo 0e6f5d9805 Changed the save system
Now lod render distance and quality are not dependent on game render distance
2021-08-18 23:54:13 +02:00
Leonardo a0bd9648dc Conversion to new quad tree LodRegion 2021-08-18 19:46:56 +02:00
James Seibel aa1778cf82 Remove the direct memory Buffer limit
Now the LOD multiplier won't be lowered when the detail is increased.
2021-08-17 22:44:50 -05:00
Leonardo 10d66a4775 LodRegion now works (might still have bugs) 2021-08-16 23:03:21 +02:00
Leonardo 4082c97e0e LodRegion update (not yet complete) 2021-08-16 18:45:42 +02:00
Leonardo 665f979e82 LodRegion update (not yet complete) 2021-08-16 14:23:26 +02:00
Leonardo 911205ce0a Create the new LodRegion class that will replace the quadTrees 2021-08-16 12:24:49 +02:00
James Seibel 51078fde5b Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-08-15 19:39:55 -05:00
James Seibel adde7e8d67 Add a ThreadFactory to name executor threads 2021-08-15 19:38:51 -05:00
Leonardo 582e38eec5 Added the ability to change the save type to bin (slower method at the moment) 2021-08-16 00:05:24 +02:00
James Seibel e987e679d0 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into
1.16.5

# Conflicts:
#	src/main/java/com/seibel/lod/builders/LodNodeBufferBuilder.java
2021-08-15 15:14:43 -05:00
James Seibel 6d776407a4 Optimize the BufferBuilder 2021-08-15 14:52:17 -05:00
Leonardo 8a61a5f0e7 Slightly optimize addLodToBuffer 2021-08-15 16:40:13 +02:00
James Seibel 49a9ff8640 Re-add the render.drawLODs method to clientProxy
Ops.
2021-08-15 08:32:00 -05:00
James Seibel 47ebdb4aee Remove BiomeContainer from BufferBuilder
Having it prevents server use
2021-08-15 08:19:00 -05:00
James Seibel 2b10a9d977 Remove objectSizeCalculator Java Mission Control is much better 2021-08-15 07:50:31 -05:00
James Seibel 247b7d5633 Add toString methods 2021-08-14 21:01:27 -05:00
James Seibel ae9eba0608 Add distanceGenerationMode when generating nodes 2021-08-14 19:56:16 -05:00
James Seibel 7983681b70 Update the config 2021-08-14 18:13:08 -05:00
James Seibel 85d4106f28 Add a try catch to LodQuadTreeDimension 2021-08-14 18:08:47 -05:00
James Seibel 22d329b8aa remove illegal state exception in LodQuadTreeWorld
This fixes an exception being thrown when leaving the world.
2021-08-14 18:02:25 -05:00
James Seibel 004d36ffa7 Remove old commented out code 2021-08-14 18:01:35 -05:00
James Seibel 231340d979 Comment out debug code 2021-08-14 17:38:40 -05:00
James Seibel f55f5b881f Optimize the region distance logic 2021-08-14 17:26:47 -05:00
James Seibel 03ac090e06 Refactor and improve the comments in LodNodeBufferBuilder 2021-08-14 17:26:28 -05:00
James Seibel 3b375e9c1b minor spacing change 2021-08-14 17:22:59 -05:00
James Seibel e44157fae5 Update / Fix RegionPos 2021-08-14 17:22:44 -05:00
James Seibel 997513231c Improve variable naming in LodNodeRenderer 2021-08-14 16:00:12 -05:00
James Seibel 64c3ba297b Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-08-14 09:34:01 -05:00
James Seibel 3ae9c49de4 Improve file writing logic 2021-08-14 09:33:46 -05:00
Leonardo 5692f9fa69 Added the cutTreeAtLevel method 2021-08-14 15:52:14 +02:00
Leonardo 72ba9f5699 Added the cutTreeAtLevel method 2021-08-14 15:50:44 +02:00
James Seibel 696be6f796 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-08-14 08:20:09 -05:00
James Seibel 5198335948 Improve LOD saving logic 2021-08-14 08:19:44 -05:00
Leonardo 8102063850 Added the cutTreeAtLevel method 2021-08-14 15:17:03 +02:00
Leonardo 86bf551de8 Lod not saving fix 2021-08-14 15:04:54 +02:00
Leonardo ec2933f23c Fixed generation detail not being used 2021-08-14 14:14:26 +02:00
James Seibel f6daf62c7d Add a Size calculator class 2021-08-13 23:35:41 -05:00
James Seibel 50988db28c Use a lower level biome generation method 2021-08-13 23:34:53 -05:00
James Seibel 047cef184d Update TODO comment 2021-08-13 23:34:23 -05:00
James Seibel ee5b8662d1 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-08-13 18:01:25 -05:00
James Seibel 9470ee2507 Add KaptainWutax TerrainUtils to build.gradle 2021-08-13 18:00:06 -05:00
James Seibel 8f63e755be Rename saveRegionToDisk to saveRegionToFile 2021-08-13 17:59:33 -05:00
Leonardo b0d62da7fe Remove QuadTreeImage and remove the start, center and end field from the LodNode 2021-08-13 20:25:28 +02:00
James Seibel 7a4347b288 Fix the try catch block in LodNodeGenWorker
Try catch finally blocks need catch to work properly, who knew.
2021-08-12 21:00:35 -05:00
James Seibel bbaa583a6d Fix a config typo 2021-08-12 19:41:09 -05:00
James Seibel c3709f726c Improve how distance based quality is determined 2021-08-11 21:52:16 -05:00
James Seibel d1417069d9 Remove the old LOD objects 2021-08-11 07:33:51 -05:00
James Seibel 412f1bead0 Start removing the old LOD objects 2021-08-11 07:29:51 -05:00
James Seibel 1095f63832 Remove unneeded code in LodDetail 2021-08-10 21:19:14 -05:00
James Seibel 89d0317c7f Add full detail to the LodNodeBufferBuilder 2021-08-10 21:15:34 -05:00
James Seibel 4f489a5272 Change the distance debug colors 2021-08-10 21:14:52 -05:00
James Seibel 8c843ba4b5 Fix the Full detail mode 2021-08-10 21:14:33 -05:00
James Seibel 77401cbfa0 Auto Reformat 2021-08-10 21:14:04 -05:00
James Seibel b983692fc1 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-08-10 20:49:42 -05:00
James Seibel e1a40c6868 Add a comment to LodNodeBufferBuilder 2021-08-10 20:47:28 -05:00
James Seibel 9c2add4e3b Clean up distance based detail and change debugMode 2021-08-10 20:34:06 -05:00
Leonardo 8cede4760e reduced quality to improve performance 2021-08-11 01:46:11 +02:00
Leonardo bceab238d5 Small fix to the offset 2021-08-11 01:45:18 +02:00
James Seibel 121676e661 Remove a unneeded offset in LodNodeBufferBuilder 2021-08-10 17:44:44 -05:00
Leonardo 9ec3be8a48 Added different level 2021-08-10 17:23:43 +02:00
Leonardo bb61cdad70 Added different LodDetail support 2021-08-10 17:11:36 +02:00
Leonardo 2e32e4d768 LodNodeBuilder and LodNodeGenWorker now support correctly LodDetail 2021-08-10 13:07:59 +02:00
Leonardo d4ee641362 Fixed file saving not working 2021-08-10 12:28:32 +02:00
James Seibel 953c2b2e92 Improve color generation and fix Biome Only generation 2021-08-09 22:25:49 -05:00
James Seibel 2cc78c874b Remove comment for issue #54 2021-08-09 22:23:47 -05:00
Leonardo f6f0ecb21b Fixed negative region not showing 2021-08-09 17:54:04 +02:00
Leonardo 190fef934c Added getNodeAtPos for generic node level 2021-08-09 17:01:31 +02:00
James Seibel 52608a9f3f Create issue #54 2021-08-08 22:06:45 -05:00
James Seibel 54e5fd30ab Refactor and comment 2021-08-08 22:04:13 -05:00
James Seibel 89115fd5d5 Add a text reminder when overriding the config file 2021-08-08 21:48:13 -05:00
James Seibel 5631ae52be Refactoring and Commenting 2021-08-08 16:41:25 -05:00
James Seibel 6520cdb184 Refactoring and commenting 2021-08-08 16:30:04 -05:00
James Seibel ce5f8708cd Remove unused code in LodNodeBuilder
I don't think using a 3rd party generator is a good idea; even if it is much faster than vanilla MC.
For a couple of reasons:
1. we could run into issues where the 3rd party isn't updated fast enough
2. the 3rd party generator may not generate the same terrain/biomes/etc.
3.  we wouldn't be able to generate mod terrain
2021-08-08 16:25:42 -05:00
James Seibel 270a83ddbc Refactoring and Commenting 2021-08-08 09:58:55 -05:00
James Seibel 64b8cb3556 Refactoring, reformatting, and commenting
I removed the getters from LodQuadTreeNode since they were public variables anyway.
2021-08-08 00:09:12 -05:00
James Seibel 7f7884c130 update LodNodeBufferBuilder version date 2021-08-07 23:38:14 -05:00
James Seibel 5a95613cee spacing reformatting 2021-08-07 23:38:00 -05:00
James Seibel 9a58800499 Refactoring and commenting 2021-08-07 23:31:40 -05:00
James Seibel d68e46a2d0 Refactoring 2021-08-07 19:31:31 -05:00
James Seibel 20d1ff6d49 revert LodQuadTree region to LodQuadTree and other refactors 2021-08-07 18:30:36 -05:00
James Seibel ed188448f2 Fix LodNodeBufferBuilder queueing the same chunk for generation multiple times 2021-08-07 18:29:20 -05:00
James Seibel 87c803e4c6 Minor refactoring 2021-08-07 13:40:04 -05:00
James Seibel 58f51aae91 reword a TODO 2021-08-07 13:36:15 -05:00
James Seibel 97a773176d Refactoring fixes 2021-08-07 13:30:03 -05:00
James Seibel f6d92021ed Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-08-07 13:29:05 -05:00
James Seibel 026cbd5da3 add fix from 2d70e8eb 2021-08-07 12:33:34 -05:00
James Seibel 3538e79a6b Reformat, Refactor, and add comments 2021-08-07 12:22:49 -05:00
Leonardo d520fb8535 previus fix was not correct 2021-08-07 19:03:56 +02:00
Leonardo d7dbf8b994 Fixed the repeating region bug 2021-08-07 18:56:51 +02:00
Leonardo 2d70e8ebe2 Fixed the repeating region bug 2021-08-07 18:54:10 +02:00
James Seibel 5eafe8f818 Improve formatting and a few variable names 2021-08-05 21:42:58 -05:00
James Seibel e875c664fd fix LODs generating multiple times (at least in single threaded gen) 2021-08-05 21:41:08 -05:00
James Seibel e313a03410 Fix/improve rendering again 2021-08-05 20:40:12 -05:00
James Seibel 781aa339bc Partially fix LodNode rendering 2021-08-05 07:56:00 -05:00
James Seibel 73e318ee38 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-08-04 23:07:12 -05:00
James Seibel 337853cdfa Start merging in 1.16.5_QuadTree
The mod does compile and render, however distance LODs don't generate or render correctly and there are other problems as well.
2021-08-04 23:07:03 -05:00
James Seibel afe4e111c3 Add release version a1.3.2 2021-08-04 22:58:06 -05:00
James Seibel 3675c5da46 Add deprecation suppressions in LodRenderer 2021-07-29 07:20:22 -05:00
James Seibel a4cc39dbac Add 400 px icons 2021-07-27 22:32:18 -05:00
James Seibel e3787079b7 Add icon files 2021-07-27 21:24:11 -05:00
James Seibel b2f98e2d92 Improve grass colors (#36) 2021-07-26 22:27:20 -05:00
James Seibel e1edd01069 Implement a few small features in LodServerWorld 2021-07-26 22:20:00 -05:00
James Seibel eb3455452b Fix getting the GPU's fancy fog capabilities 2021-07-26 20:26:24 -05:00
James Seibel 34baf923b2 Fix #45 (Half and Full qualities checkerboard) 2021-07-26 07:56:00 -05:00
James Seibel 499308870d fix a typo 2021-07-25 20:55:15 -05:00
James Seibel f852a79a0e Fix LODs rendering with an offset 2021-07-25 20:52:30 -05:00
James Seibel fa98454151 Hopefully reduce the chance of index out of bounds exceptions 2021-07-25 20:51:52 -05:00
James Seibel 286fc91db6 Slightly improve gamma lighting 2021-07-25 20:51:18 -05:00
James Seibel 99209f920d Fix a few truncation and off by 1 errors 2021-07-25 18:48:25 -05:00
James Seibel 19fd1aff98 Close #36 (Improve foliage colors)
While the colors could still be improved, I'm not sure what else to do with them to do so.
2021-07-25 18:20:34 -05:00
James Seibel 9ebc5aeb32 Improve comments in CublicLodTemplate 2021-07-25 18:06:42 -05:00
James Seibel 7c890a4627 Add a config option to change LOD brightness and saturation
Related to issue #36
2021-07-25 18:00:39 -05:00
James Seibel ccc661225e Improve the DARKEN_SIDES shading mode 2021-07-25 17:58:46 -05:00
James Seibel d7b13a2e0b fix a typo 2021-07-25 15:49:18 -05:00
James Seibel c53ce15f7d Hopefully fix #38 (teleporting causing regions to save in the wrong place) 2021-07-25 14:14:07 -05:00
James Seibel 35ac6fc756 Fix a typo 2021-07-25 14:10:08 -05:00
James Seibel 6bcf58959b Fix #46 (MC crashing when joining a server) 2021-07-25 13:18:47 -05:00
James Seibel 5051508e42 Fix #48 (Lava showing up as water) 2021-07-25 12:29:40 -05:00
James Seibel 196e02e61a Partially close #43 (add SSAO)
I am surprised how much this small change improves how things look. I may still implement SSAO in the future but for the time being this is a big improvement for how things look.
2021-07-25 12:19:14 -05:00
Leonardo bc8188beb0 Added important TODOs 2021-07-21 10:43:51 +02:00
Leonardo 0fc4c5532b Minor fixes 2021-07-19 17:00:40 +02:00
Leonardo 98586d6af9 now different generation setting can be used in the same Dimension without conflict 2021-07-19 16:23:14 +02:00
Leonardo bdd1e2410a Updated the guide 2021-07-19 11:47:26 +02:00
Leonardo c0240c71bf Updated the guide 2021-07-19 11:46:22 +02:00
Leonardo 65ffc93e0f Updated the guide 2021-07-19 11:35:38 +02:00
Leonardo 01183be383 getLevelToGenerate has been renamed getNodesToGenerate. Now it return node and not the complete level 2021-07-19 11:32:20 +02:00
Leonardo 359791ea58 Fixed getLevelToGenerate. Now it works correctly 2021-07-19 11:26:05 +02:00
Leonardo 5b3aa82817 Updated the image creation 2021-07-14 15:08:10 +02:00
Leonardo d77bb48da1 Updated the guide 2021-07-14 15:07:09 +02:00
Leonardo 8b941cb95c Updated the guide 2021-07-14 14:41:03 +02:00
Leonardo b9d991db63 Updated the guide 2021-07-14 14:37:48 +02:00
Leonardo 1a8a7be494 Updated the guide 2021-07-14 14:33:58 +02:00
Leonardo d054ac91b1 Fixed all negative coords bug 2021-07-14 14:33:17 +02:00
Leonardo e40ee20460 Added GUIDE to Readme.txt 2021-07-14 11:14:44 +02:00
Leonardo 29b3e9fadc The lodQuadTree is now correctly converted to use LodDataPoint and DistanceGenerationMode 2021-07-13 20:40:55 +02:00
Leonardo 3cc78c62a0 The lodQuadTree is now correctly converted to use LodDataPoint and DistanceGenerationMode 2021-07-13 15:38:20 +02:00
Leonardo 2c719c41d9 Various change to support LodDataPoint and the DistanceGenerationMode enum 2021-07-13 11:38:08 +02:00
Leonardo cd06b42b02 changed pc 2021-07-13 00:53:24 +02:00
Leonardo e8b46a6fd2 Converting the quadTree to use the LodDataPoint class 2021-07-13 00:52:24 +02:00
James Seibel 814ac5df84 Change distanceBiomeOnlyGeneration to distanceGenerationMode in the configHandler 2021-07-11 18:41:38 -05:00
Morippi 7854f659a3 various change 2021-07-10 15:02:26 +02:00
Morippi 6cb1d2f8f4 second fix for negative coordinate 2021-07-10 13:36:15 +02:00
Morippi f95d57ab6d Several chages to converto to quadTree + first fix for negative coordinate 2021-07-10 12:43:23 +02:00
James Seibel 55f9d142e6 Add 1.16.5 a1.3.1 to the compiled Jars 2021-07-09 23:10:36 -05:00
James Seibel 58607ab1fc Update the version to a1.3.1 2021-07-09 23:09:32 -05:00
James Seibel 433ab4f92f Fix #37 (Z fighting far from the world's origin) 2021-07-09 23:08:30 -05:00
James Seibel 8840973a1e Fix #41 (leaving a world causing Lod generation to break) 2021-07-09 22:57:32 -05:00
Morippi f91479829d Several chages to converto to quadTree 2021-07-09 21:07:00 +02:00
Morippi 829c9531fa Several chages to converto to quadTree 2021-07-09 21:01:37 +02:00
Morippi f9bb248eef Several chages to converto to quadTree 2021-07-09 21:01:17 +02:00
Morippi 03ce4d9c42 Several chages to converto to quadTree 2021-07-09 19:23:14 +02:00
Morippi f181d073a3 fixed LodQuadTreeDimension 2021-07-09 17:56:44 +02:00
Morippi 63b01f3ec7 fixed LodQuadTreeDimension 2021-07-09 15:53:32 +02:00
Morippi 14f1b6db54 fixed LodQuadTreeDimension 2021-07-09 15:26:03 +02:00
Morippi 604089cfa8 fixed LodQuadTreeDimension 2021-07-09 15:08:48 +02:00
Morippi 712d9db2fa fixed LodQuadTreeDimension 2021-07-09 14:58:48 +02:00
Morippi 2b5b023472 fixed LodQuadTreeDimension 2021-07-09 13:33:11 +02:00
Morippi 4a9bc7ca74 fixed LodQuadTreeDimension 2021-07-09 13:11:25 +02:00
Morippi 804f910584 added region in image + various fix 2021-07-08 19:31:27 +02:00
Morippi d5b2512e27 various fix 2021-07-08 13:00:29 +02:00
Morippi 280d281604 various fix 2021-07-08 12:52:50 +02:00
Morippi 95bcc71d23 various fix 2021-07-08 12:40:36 +02:00
Morippi b623f1581f various fix 2021-07-07 23:12:45 +02:00
Morippi 62b6d9ea30 Modified to used getNodeToRender 2021-07-07 22:44:53 +02:00
Morippi 6c8d0f98a6 Added circular rendering,
Fixed get node to render
2021-07-07 22:43:44 +02:00
Morippi dc353cd029 Map works correctly 2021-07-07 21:16:18 +02:00
Morippi 86e4ab7e83 Map works correctly 2021-07-07 20:22:05 +02:00
Morippi c6a96ae710 Fixed QuadTreeImage 2021-07-07 19:44:10 +02:00
Morippi 9e24ee0ef7 Fixed LodQuadTree
Added QuadTreeImage to visualize how the quadTree is builded
2021-07-07 00:53:32 +02:00
James Seibel 4460789ab1 Fix a typo in the release 2021-07-05 17:17:08 -05:00
James Seibel a232f449ed change mcmod.info Details to Detail 2021-07-05 17:16:26 -05:00
James Seibel 1c5988fbfb Update the readme to say which version of retail minecraft has been confirmed to work. 2021-07-05 17:12:27 -05:00
James Seibel b386547616 Improve how the compiled jars are stored 2021-07-05 17:06:35 -05:00
James Seibel 03ea7ee460 Rename the mod internally from Levels of Detail to Level of Detail 2021-07-05 16:56:30 -05:00
James Seibel 752a2e4a69 Merge branch '1.16.5' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.5 2021-07-05 16:51:22 -05:00
James Seibel 3e3e30c4b1 Add some comments about issue #40 2021-07-05 16:51:18 -05:00
James Seibel f5cc5c2846 Improve config wording 2021-07-05 16:40:12 -05:00
James Seibel 609d471346 Update the version number to a1.3 2021-07-05 13:09:01 -05:00
James Seibel 5f921ffcef Make LICENSE.txt uppercase 2021-07-05 18:04:30 +00:00
James Seibel 4aebf36f02 Put the project under the GNU GPL v3 License 2021-07-05 12:55:42 -05:00
James Seibel 0b1fdba6ea Optimize world generation and add a config to set the number of generation threads 2021-07-05 12:41:20 -05:00
James Seibel 475324ed73 fix config typos 2021-07-05 12:34:55 -05:00
James Seibel d36f97d765 Improve javadocs in LodBuilderConfig 2021-07-05 12:00:37 -05:00
James Seibel ee5dc0a6b3 Improve plant color generation 2021-07-05 12:00:25 -05:00
James Seibel 05ed61b0a1 Add a config to enable/disable unstable Feature generation
Related to issue #35
2021-07-05 12:00:03 -05:00
James Seibel 2ef6c59dfc Add an extra comment to Issue #35 and remove a unused log 2021-07-05 09:05:34 -05:00
James Seibel fd90f761a1 Add another library comment for issue #35 2021-07-05 08:59:39 -05:00
Morippi 72bcc87de7 QuadTree version, still doesn't work 2021-07-05 14:46:51 +02:00
James Seibel 7ec7d6bdc5 Add a TODO/Issue and fix capitalization 2021-07-05 00:32:51 -05:00
James Seibel f9ed9a472c Disable Jungle Feature generation, since it contains a world gen bug
Related to #35
2021-07-04 08:31:29 -05:00
James Seibel b542738097 Fix a generation bug created when updating to 1.16.5 2021-07-04 00:09:04 -05:00
James Seibel bceefb5717 Rename LodBuilder to LodChunkBuilder 2021-07-03 21:16:56 -05:00
James Seibel f977687ad6 Add the ability for LOD fog rendering to differ from Minecraft's 2021-07-03 21:10:34 -05:00
James Seibel 31ef1d4959 Update to 1.16.5 forge 36.1.0 2021-07-03 10:25:21 -05:00
James Seibel 86d07eb62b Close #30 and 31 (LODs rendering on top of the player and distance based rendering) 2021-07-01 21:21:34 -05:00
James Seibel 39c7ae8d17 add isChunkPosInLoadedArea to LodUtil 2021-07-01 21:10:07 -05:00
James Seibel 5c369fd515 Add LodDataPoint toString for debugging 2021-07-01 21:09:40 -05:00
James Seibel 2b0926a097 Update the readme 2021-06-28 20:26:40 -05:00
James Seibel 9b6b67b178 Remove some unneeded forge text files 2021-06-28 20:26:28 -05:00
James Seibel bb07e3db9a Close #28 (Optimize Distance Lod Generation) 2021-06-27 22:00:43 -05:00
James Seibel 46e65704d7 Potentially fix #34 (Replace "\" in file paths with File.separatorChar to allow for use on Linux)
I can't currently test if this fixed the problem since I don't have a Linux install to test on.
2021-06-27 13:12:38 -05:00
James Seibel d5ec17551e Add grass and tree generation to biome only generation 2021-06-23 21:32:05 -05:00
James Seibel 8390cfc6fb Fix block positions not wrapping around correctly 2021-06-23 19:31:41 -05:00
James Seibel 2711fbf01a Partially fix 0 area bugs 2021-06-22 22:58:51 -05:00
James Seibel e4f64df660 Add snow to biome only Lod generation 2021-06-22 22:36:47 -05:00
James Seibel a6c9d0df98 Improve the Biome only generation mode 2021-06-21 22:16:46 -05:00
James Seibel 136dd5b47a Add a heightmap generation option 2021-06-21 22:16:23 -05:00
James Seibel 277def2acc Rename LodConfigHandler to LodConfig 2021-06-19 20:47:04 -05:00
James Seibel e941b553e7 rename LodRender to LodRenderer 2021-06-19 20:45:30 -05:00
James Seibel 92e651c779 Rename LodUtils to LodUtil 2021-06-19 20:42:48 -05:00
James Seibel 2b82eb88c8 Partially address #28 (improve LOD chunk generation outside normal view distance) 2021-06-19 20:36:13 -05:00
James Seibel 13fecf9d1d Add constructors and a don't save flag to LodChunk 2021-06-19 20:24:46 -05:00
James Seibel cdd1529a13 Add a int constructor to LodDataPoint 2021-06-19 20:22:31 -05:00
James Seibel 7915444713 Move intoToColor and colorToInto to LotUtils 2021-06-19 20:21:31 -05:00
James Seibel 2135ae769d Close #29 (slightly improve lighting) 2021-06-17 21:33:14 -05:00
James Seibel b98cfb9666 Fix LodChunk not having the detail level set 2021-06-17 20:58:20 -05:00
James Seibel a28da86d44 Rename and move LodConfig to LodConfigHandler 2021-06-17 20:51:13 -05:00
James Seibel 799b0e2481 Fix #4 (Prevent excessive render distances from causing out of memory errors) 2021-06-17 20:27:42 -05:00
James Seibel e380c7c069 Label a comment with its issue number 2021-06-14 21:50:33 -05:00
James Seibel 5a3b8f5baa Add vanilla rendered chunk detection to improve edge rendering
In other words, we now render LODs where ever Minecraft isn't rendering chunks itself.
2021-06-14 21:44:26 -05:00
James Seibel a1c547b72e Minor LodChunkGenWorker optimization 2021-06-13 22:56:04 -05:00
James Seibel bb5613e3fe Slight optimization to LodChunkGenWorker 2021-06-13 20:56:53 -05:00
James Seibel a29060bf4f Fix LodChunk string constructor 2021-06-13 20:18:20 -05:00
James Seibel 79ee23e0b0 Add variably detailed LodChunks 2021-06-13 19:59:25 -05:00
James Seibel d3231a480d Delete a duplicate file, how did this get here? 2021-06-13 19:37:17 -05:00
James Seibel 3694dcba4c Add variable detail LODs 2021-06-12 18:19:45 -05:00
James Seibel bf6db89a4b Add folder for compiled jars
I did this since Gitlab doesn't have an easy way to add release binaries, and I don't want to move everything over to Github right now.
2021-06-01 20:11:41 -05:00
James Seibel 38c644739a Fix the mixin using the old package name 2021-06-01 20:10:27 -05:00
James Seibel a0fe977976 comment out debug lines 2021-05-31 19:31:36 -05:00
James Seibel 739d6d5856 update the version to a1.2 2021-05-31 19:31:12 -05:00
James Seibel 84125735a1 change the packages from com.backsun.lod... to com.seibel.lod... 2021-05-31 19:30:48 -05:00
James Seibel 884f6811a1 Improve how dimension width is determined and add a TODO 2021-05-31 16:43:42 -05:00
James Seibel 402abb0963 add some depreciation supressions and improve the fog slightly 2021-05-31 16:43:21 -05:00
James Seibel 2f7f489e14 Add the ability to count the number of loaded LodChunks 2021-05-31 16:41:27 -05:00
James Seibel 4de5c287fc Close #20 (allow the LOD distance to be changed in the config) 2021-05-31 15:29:26 -05:00
James Seibel ccb58024a1 rename LodRenderer to LodRender 2021-05-31 15:21:58 -05:00
James Seibel 0381d56511 rename method to getFileNameAndPathForRegion 2021-05-31 14:36:11 -05:00
James Seibel 7bbd3fd815 Improve logging 2021-05-31 14:35:34 -05:00
James Seibel 4e243252e9 Simplify the debug render setup 2021-05-31 14:15:24 -05:00
James Seibel b488d21a14 Replace System.out with a Logger 2021-05-31 14:11:58 -05:00
James Seibel 3e8dbf7ac7 Add file versioning logic when writing to a file 2021-05-31 14:11:34 -05:00
James Seibel 695b73f9d3 Fix the CubicLodTemplate taking the average of empty chunks 2021-05-31 11:54:46 -05:00
James Seibel 6e37bce38a Improve how empty LodChunks are determined 2021-05-31 11:54:27 -05:00
James Seibel 06232f65b2 Fix empty chunks not generating
This is most useful in the end, where there are many empty chunks
2021-05-31 11:53:59 -05:00
James Seibel bebe4b7436 Organize the chunk generation modes comment by time 2021-05-31 11:09:38 -05:00
James Seibel 7d9f04d54c Improve wording in the config file 2021-05-31 11:09:03 -05:00
James Seibel 70336acc75 Add a double detail level to the CublicLodTemplate 2021-05-31 10:50:36 -05:00
James Seibel e1ef08a783 Close #24 (only add visible side colors) 2021-05-30 14:06:54 -05:00
James Seibel 648a70097a Add a comment about the LodChunk resolution change 2021-05-30 13:52:38 -05:00
James Seibel 280892f5f4 replace == with .equals 2021-05-29 16:59:10 -05:00
James Seibel 3c57b124d5 Improve profile tracking 2021-05-29 16:59:02 -05:00
James Seibel 04d6bae479 Add several TODOs 2021-05-29 13:47:18 -05:00
James Seibel 98096a5db7 part of 7e4f3a3 2021-05-29 13:23:49 -05:00
James Seibel 7e4f3a3185 Fix chunks not generating out of view range when changing worlds 2021-05-29 13:22:48 -05:00
James Seibel 9fe6be10f6 replace LodGeometryQuality with LodDetail 2021-05-29 13:02:05 -05:00
James Seibel 76b356d38e Add version handling and improve error handling 2021-05-29 13:01:47 -05:00
James Seibel 811e24ee5e Improve and simplify LodChunk 2021-05-29 13:00:43 -05:00
James Seibel 5002db15d6 Remove LodLocation since LodCorner exists 2021-05-29 12:45:46 -05:00
James Seibel a3357c1193 Replace LodGeometryQuality with LodDetail 2021-05-29 12:45:19 -05:00
James Seibel af36224a57 Remove unneeded object creation 2021-05-29 12:42:39 -05:00
James Seibel 630ff361d3 in ColorDirection replace N,S,E,W with NORTH, SOUTH, EAST, WEST 2021-05-19 12:51:23 -05:00
James Seibel 34c6f29a18 Add file versioning to the file handler 2021-05-19 12:34:02 -05:00
James Seibel 909718e491 Remove the readyToReadAndWrite method 2021-05-19 12:10:36 -05:00
James Seibel 8a3d199247 Implement Single and Quad geometry qualities for CubicLod Rendering 2021-05-19 08:47:22 -05:00
James Seibel fa13c981b7 Add a missing empty line in the config file 2021-05-18 21:56:33 -05:00
James Seibel 815b00c3ca Add multiple color modes and set up for geometry quality 2021-05-08 16:41:55 -05:00
James Seibel ae9144a6c4 Add the basis for different LOD drawing modes (aka templates) 2021-05-08 14:58:41 -05:00
James Seibel 359fde3b6b Remove an unneeded line 2021-05-08 14:37:05 -05:00
James Seibel 92fa904cc6 Setup the config and enum for multiple LOD drawing modes 2021-05-05 16:55:01 -05:00
James Seibel 2583ae34d4 Remove DrawMode since it wasn't supported anyway 2021-05-05 16:53:39 -05:00
James Seibel e36b3394f4 Remove an unneeded comment 2021-05-05 16:35:23 -05:00
James Seibel 7af38df92c Remove some outdated TODOs 2021-04-18 21:23:30 -05:00
James Seibel 51add24110 Merge branch '1.16.4' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.4 2021-04-03 12:11:32 -05:00
James Seibel 9e5aac3bf7 Close #18 (allow client use on servers) 2021-04-03 12:11:20 -05:00
James Seibel 5738a5b7cd Close #8 (allow client use on servers) 2021-04-03 12:05:16 -05:00
James Seibel e1216966a3 Add error checking 2021-04-02 22:56:13 -05:00
James Seibel 222c0de7f1 Only try generating chunks on a local server 2021-04-02 22:50:33 -05:00
James Seibel b4f1fb6d28 Improve a TODO
related to issue #13
2021-04-01 21:05:45 -05:00
James Seibel a32082ad20 Remove the need for a World object in LodBuilder
The world hasn't been needed for a while, I just never got around to removing it.
2021-04-01 13:57:47 -05:00
James Seibel eeb5fb6c3c Improve the generateLodChunkAsync method 2021-04-01 13:56:52 -05:00
James Seibel cb50f24c86 Improve how a unloaded LodWorld is handled 2021-04-01 13:13:14 -05:00
James Seibel 5ca5764c0e Improve how LodWorld, LodBuilder, and LodRenderer objects are handled to prevent multiple references
There was a problem where upon changing worlds the LodWorld wouldn't reflect the commit along with 6c515350 and fccd1db0 should fix that problem.
2021-03-31 14:22:35 -05:00
James Seibel fccd1db045 Add the ability to select and deselect worlds 2021-03-31 14:18:11 -05:00
James Seibel 6c515350bc Clean up references to LodDimension objects 2021-03-31 14:15:49 -05:00
James Seibel 50aee9dfb2 Move getCurrentWorldID into the LodUtils class 2021-03-31 10:56:50 -05:00
James Seibel 0649504770 Fix #16 (stop placeholder chunks from being saved to disk) 2021-03-30 14:55:12 -05:00
James Seibel 29068f9550 Fix the projection matrix not being reset after rendering LODs
Regressive fix for 18c08ccd.
2021-03-30 08:07:15 -05:00
James Seibel 18c08ccd88 Fix #8 and #9 (inaccurate lighting and rendering behind transparent objects)
Instead of using a stencil and rendering in the forgelastdraw event, we now you a mixin to render right before the sold block layer.

The main purpose of this was to allow for LODs to be drawn behind transparent objects; however as a happy accident it seems to have also improved the lighting, I'm not sure if it is perfect, but it is much better.
2021-03-28 22:39:58 -05:00
James Seibel b71d6a5e3f Closes issue #10 (Regen LODs on block change) 2021-03-27 21:49:23 -05:00
James Seibel 8f619f3fa1 Closes issue #12 (improve world change detection) 2021-03-27 21:28:03 -05:00
James Seibel eab16ff20a Move all buffer building into the LodBufferBuilder and improve chunk generation logic
Chunks generation requests should no longer stack exponentially (before whenever one chunk was generated the LODs would be regenerated, causing more chunks to generate so if more than one chunk was ever generated at a time they would stack).
2021-03-25 23:04:48 -05:00
James Seibel d913ed9621 Fix issue #11 (generate chunks closer to the player first) 2021-03-25 16:31:11 -05:00
James Seibel a649cf179f comment out a debug statement 2021-03-25 16:04:02 -05:00
James Seibel cf5de39250 Rename SingleLodChunkGenWorker to LodChunkGenWorker 2021-03-25 16:03:44 -05:00
James Seibel bab3cd9656 Move SingleLodChunkGenWorker into the builders package 2021-03-24 21:59:32 -05:00
James Seibel cafd4f0c47 Move all LodChunk generation code to the LodBuilder object 2021-03-24 21:59:06 -05:00
James Seibel e20833225f Slightly improve LOD generation speed and add code related to heightmap
The slight speed increase is done by only generating the chunk to the "FEATURES" status instead of "FULL".

The code related to the heightmap is currently unused, since the LOD color generation requires blocks. Although it may have some use in the future so I will keep it in, albeit commented out.
2021-03-24 19:09:30 -05:00
James Seibel 14a06c220b Move the enums into their own package 2021-03-24 17:50:17 -05:00
James Seibel e5a5ba327e Add a TODO 2021-03-19 20:36:11 -05:00
James Seibel fedc8f7b66 Add LODs generating outside the player's view distance
It isn't fast enough to keep up with flying creating or spectator players; but it does function without causing heavy server or client lag.
LODs are generated in lines starting far away from the player and moving towards them, in the future they should be generated close to the player first.

Also add a RegionPos object and a way to convert from ChunkPos objects in LodUtils
2021-03-19 20:25:23 -05:00
James Seibel 7a3497d44c Update and improve Access Transformer
Remove old unneeded transformations and add useful comments.
2021-03-04 21:55:26 -06:00
James Seibel 873034f7e4 Merge branch '1.16.4' of gitlab.com:jeseibel/minecraft-lod-mod into 1.16.4 2021-03-04 21:30:33 -06:00
James Seibel a151054b48 Fix issue #7 (screen space distortions not be applied) 2021-03-04 21:30:19 -06:00
James Seibel 14c69971f6 Fix a bug with holding grass blocks
For some reason hold grass blocks (and presumably other biome colored blocks)  would look gray if GL_COLOR_MATERIAL is disabled
2021-03-04 21:16:41 -06:00
James Seibel edc3858699 Add the youtube demo video 2021-03-03 16:16:47 +00:00
James Seibel bdaf33b80b Add a simplified description to the readme 2021-03-03 03:48:13 +00:00
109 changed files with 14113 additions and 5960 deletions
+1
View File
@@ -22,4 +22,5 @@ eclipse
run
# Files from Forge MDK
logs
forge*changelog.txt
Binary file not shown.
-65
View File
@@ -1,65 +0,0 @@
Minecraft Forge: Credits/Thank You
Forge is a set of tools and modifications to the Minecraft base game code to assist
mod developers in creating new and exciting content. It has been in development for
several years now, but I would like to take this time thank a few people who have
helped it along it's way.
First, the people who originally created the Forge projects way back in Minecraft
alpha. Eloraam of RedPower, and SpaceToad of Buildcraft, without their acceptiance
of me taking over the project, who knows what Minecraft modding would be today.
Secondly, someone who has worked with me, and developed some of the core features
that allow modding to be as functional, and as simple as it is, cpw. For developing
FML, which stabelized the client and server modding ecosystem. As well as the base
loading system that allows us to modify Minecraft's code as elegently as possible.
Mezz, who has stepped up as the issue and pull request manager. Helping to keep me
sane as well as guiding the community into creating better additions to Forge.
Searge, Bspks, Fesh0r, ProfMobious, and all the rest over on the MCP team {of which
I am a part}. For creating some of the core tools needed to make Minecraft modding
both possible, and as stable as can be.
On that note, here is some specific information of the MCP data we use:
* Minecraft Coder Pack (MCP) *
Forge Mod Loader and Minecraft Forge have permission to distribute and automatically
download components of MCP and distribute MCP data files. This permission is not
transitive and others wishing to redistribute the Minecraft Forge source independently
should seek permission of MCP or remove the MCP data files and request their users
to download MCP separately.
And lastly, the countless community members who have spent time submitting bug reports,
pull requests, and just helping out the community in general. Thank you.
--LexManos
=========================================================================
This is Forge Mod Loader.
You can find the source code at all times at https://github.com/MinecraftForge/MinecraftForge/tree/1.12.x/src/main/java/net/minecraftforge/fml
This minecraft mod is a clean open source implementation of a mod loader for minecraft servers
and minecraft clients.
The code is authored by cpw.
It began by partially implementing an API defined by the client side ModLoader, authored by Risugami.
http://www.minecraftforum.net/topic/75440-
This support has been dropped as of Minecraft release 1.7, as Risugami no longer maintains ModLoader.
It also contains suggestions and hints and generous helpings of code from LexManos, author of MinecraftForge.
http://www.minecraftforge.net/
Additionally, it contains an implementation of topological sort based on that
published at http://keithschwarz.com/interesting/code/?dir=topological-sort
It also contains code from the Maven project for performing versioned dependency
resolution. http://maven.apache.org/
It also contains a partial repackaging of the javaxdelta library from http://sourceforge.net/projects/javaxdelta/
with credit to it's authors.
Forge Mod Loader downloads components from the Minecraft Coder Pack
(http://mcp.ocean-labs.de/index.php/Main_Page) with kind permission from the MCP team.
+555 -454
View File
File diff suppressed because it is too large Load Diff
+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
-65
View File
@@ -1,65 +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.
Forge version: 1.16.4-35.1.4
Notes:
This version has been confirmed to work in Eclipse and retail Minecraft.
(retail running forge 1.16.4-35.1.37)
That being said only singleplayer is currently supported; connecting
to servers (local or otherwise) will cause no LODs to be drawn and
may cause instibility.
========================
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.
+390
View File
@@ -0,0 +1,390 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="21">
<profile kind="CodeFormatterProfile" name="Eclipse (James' Edit)" version="21">
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_with_spaces" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_record_components" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_logical_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.text_block_indentation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_annotations" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_not_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_constructor" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_string_concatenation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_method_body_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_additive_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_relational_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="99"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_declaration" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assertion_message" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="next_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_string_concatenation" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="1200"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
</profile>
</profiles>
@@ -0,0 +1,50 @@
<code_scheme name="Eclipse (James' Edit)" version="173">
<option name="RIGHT_MARGIN" value="1200" />
<JavaCodeStyleSettings>
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
<option name="JD_ADD_BLANK_AFTER_DESCRIPTION" value="false" />
<option name="JD_P_AT_EMPTY_LINES" value="false" />
<option name="JD_KEEP_INVALID_TAGS" value="false" />
<option name="JD_DO_NOT_WRAP_ONE_LINE_COMMENTS" value="true" />
<option name="JD_KEEP_EMPTY_PARAMETER" value="false" />
<option name="JD_KEEP_EMPTY_EXCEPTION" value="false" />
<option name="JD_KEEP_EMPTY_RETURN" value="false" />
</JavaCodeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="10" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="10" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="10" />
<option name="BLANK_LINES_BEFORE_PACKAGE" value="1" />
<option name="BRACE_STYLE" value="2" />
<option name="CLASS_BRACE_STYLE" value="2" />
<option name="METHOD_BRACE_STYLE" value="2" />
<option name="ELSE_ON_NEW_LINE" value="true" />
<option name="WHILE_ON_NEW_LINE" value="true" />
<option name="CATCH_ON_NEW_LINE" value="true" />
<option name="FINALLY_ON_NEW_LINE" value="true" />
<option name="INDENT_CASE_FROM_SWITCH" value="false" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_RESOURCES" value="false" />
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="RESOURCE_LIST_WRAP" value="5" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="THROWS_LIST_WRAP" value="1" />
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
<option name="THROWS_KEYWORD_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="TERNARY_OPERATION_WRAP" value="5" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="METHOD_ANNOTATION_WRAP" value="0" />
<option name="CLASS_ANNOTATION_WRAP" value="0" />
<option name="FIELD_ANNOTATION_WRAP" value="0" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

+90 -16
View File
@@ -1,13 +1,17 @@
buildscript {
repositories {
maven { url = 'https://files.minecraftforge.net/maven' }
jcenter()
maven { url = 'https://files.minecraftforge.net' }
mavenCentral()
maven { url='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
maven { url 'https://jitpack.io' }
}
dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
classpath group: 'org.spongepowered', name: 'mixingradle', version: '0.7-SNAPSHOT'
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.2'
}
}
apply plugin: 'net.minecraftforge.gradle'
@@ -15,10 +19,12 @@ 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: 'com.github.johnrengelman.shadow'
version = 'a1.5.1'
group = 'com.seibel.lod'
archivesBaseName = 'Distant-Horizons_1.16.5'
version = 'a1'
group = 'com.backsun.lod'
archivesBaseName = 'lod_1.16.4'
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
@@ -29,7 +35,7 @@ minecraft {
// stable_# Stables are built at the discretion of the MCP team.
// 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: 'snapshot', version: '20201028-1.16.3'
mappings channel: 'official', version: '1.16.5'
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
@@ -43,9 +49,15 @@ minecraft {
arg "-mixin.config=lod.mixins.json"
// Recommended logging data for a userdev environment
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
// The markers can be changed as needed.
// "SCAN": For mods scan.
// "REGISTRIES": For firing of registry events.
// "REGISTRYDUMP": For getting the contents of all registries.
property 'forge.logging.markers', 'REGISTRIES'
// Recommended logging level for the console
// You can set various levels here.
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
property 'forge.logging.console.level', 'debug'
mods {
@@ -60,9 +72,15 @@ minecraft {
arg "-mixin.config=lod.mixins.json"
// Recommended logging data for a userdev environment
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
// The markers can be changed as needed.
// "SCAN": For mods scan.
// "REGISTRIES": For firing of registry events.
// "REGISTRYDUMP": For getting the contents of all registries.
property 'forge.logging.markers', 'REGISTRIES'
// Recommended logging level for the console
// You can set various levels here.
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
property 'forge.logging.console.level', 'debug'
mods {
@@ -76,9 +94,15 @@ minecraft {
workingDirectory project.file('run')
// Recommended logging data for a userdev environment
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
// The markers can be changed as needed.
// "SCAN": For mods scan.
// "REGISTRIES": For firing of registry events.
// "REGISTRYDUMP": For getting the contents of all registries.
property 'forge.logging.markers', 'REGISTRIES'
// Recommended logging level for the console
// You can set various levels here.
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
property 'forge.logging.console.level', 'debug'
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
@@ -96,11 +120,43 @@ minecraft {
// Include resources generated by data generators.
sourceSets.main.resources { srcDir 'src/generated/resources' }
// this is required so that we can use
// jitpack in the dependencies section below
repositories {
mavenCentral()
// used to download and compile dependencies from git repos
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.4-35.1.4'
minecraft 'net.minecraftforge:forge:1.16.5-36.1.0'
compile 'org.tukaani:xz:1.9'
shadowMe 'org.tukaani:xz:1.9'
compile '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
// a way to duplicate lambda functions (which features use)
// so for now I'm not sure what to do.
//implementation 'io.github.kostaskougios:cloning:1.10.3'
//
//implementation ('com.esotericsoftware:kryo:5.1.1') {
// exclude group: "org.objenesis"
//}
//implementation 'org.objenesis:objenesis:3.2'
// You may put jars on which you depend on in ./libs or you may define them like so..
// compile "some.group:artifact:version:classifier"
@@ -122,23 +178,41 @@ dependencies {
}
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..
jar {
manifest {
attributes([
"Specification-Title": "Levels of Detail",
"Specification-Title": "LOD",
"Specification-Version": "1", // We are version 1 of ourselves
"Implementation-Title": project.name,
"Implementation-Version": "1.0",
"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')
@@ -157,4 +231,4 @@ publishing {
mixin {
add sourceSets.main, "lod.refmap.json"
}
}
-1664
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
View File
View File
@@ -1,65 +0,0 @@
package com.backsun.lod;
import com.backsun.lod.proxy.ClientProxy;
import com.backsun.lod.util.LodConfig;
import net.minecraft.client.Minecraft;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
/**
* 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 02-07-2021
*/
@Mod(ModInfo.MODID)
public class LodMain
{
public static LodMain instance;
public static ClientProxy client_proxy;
private void init(final FMLCommonSetupEvent event)
{
Minecraft.getInstance().getFramebuffer().enableStencil();
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, LodConfig.clientSpec);
}
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
}
}
@@ -1,15 +0,0 @@
package com.backsun.lod;
/**
* This file is similar to mcmod.info
*
* @author James Seibel
* @version 02-17-2021
*/
public final class ModInfo
{
public static final String MODID = "lod";
public static final String MODNAME = "Levels of Detail";
public static final String MODAPI = "LodAPI";
public static final String VERSION = "1.0";
}
@@ -1,189 +0,0 @@
package com.backsun.lod.builders;
import java.awt.Color;
import org.lwjgl.opengl.GL11;
import com.backsun.lod.objects.NearFarBuffer;
import com.backsun.lod.renderer.LodRenderer;
import com.backsun.lod.util.enums.FogDistance;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.AxisAlignedBB;
/**
* This object is used to create NearFarBuffer objects.
*
* @author James Seibel
* @version 02-27-2021
*/
public class LodBufferBuilder
{
public BufferBuilder nearBuffer;
public BufferBuilder farBuffer;
public FogDistance distanceMode;
public AxisAlignedBB[][] lods;
public Color[][] colors;
public LodBufferBuilder()
{
}
public NearFarBuffer createBuffers(
BufferBuilder newNearBufferBuilder, BufferBuilder newFarBufferBuilder,
FogDistance newDistanceMode,
AxisAlignedBB[][] newLods, Color[][] newColors)
{
nearBuffer = newNearBufferBuilder;
farBuffer = newFarBufferBuilder;
distanceMode = newDistanceMode;
lods = newLods;
colors = newColors;
nearBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
farBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
int numbChunksWide = lods.length;
BufferBuilder currentBuffer;
AxisAlignedBB bb;
int red;
int green;
int blue;
int alpha;
// x axis
for (int i = 0; i < numbChunksWide; i++)
{
// z axis
for (int j = 0; j < numbChunksWide; j++)
{
if (lods[i][j] == null || colors[i][j] == null)
continue;
bb = lods[i][j];
// get the color of this LOD object
red = colors[i][j].getRed();
green = colors[i][j].getGreen();
blue = colors[i][j].getBlue();
alpha = colors[i][j].getAlpha();
if (isCoordinateInNearFogArea(i, j, numbChunksWide / 2))
currentBuffer = nearBuffer;
else
currentBuffer = farBuffer;
if (bb.minY != bb.maxY)
{
// top (facing up)
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
// bottom (facing down)
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
// south (facing -Z)
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
// north (facing +Z)
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
// west (facing -X)
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
// east (facing +X)
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
}
else
{
// render this LOD as one block thick
// top (facing up)
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.minZ, red, green, blue, alpha);
// bottom (facing down)
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
// south (facing -Z)
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
// north (facing +Z)
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
// west (facing -X)
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.minZ, red, green, blue, alpha);
// east (facing +X)
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
}
} // z axis
} // x axis
nearBuffer.finishDrawing();
farBuffer.finishDrawing();
return new NearFarBuffer(nearBuffer, farBuffer);
}
private void addPosAndColor(BufferBuilder buffer, double x, double y, double z, int red, int green, int blue, int alpha)
{
buffer.pos(x, y, z).color(red, green, blue, alpha).endVertex();
}
/**
* 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).
*/
private static boolean isCoordinateInNearFogArea(int chunkX, int chunkZ, int lodRadius)
{
int halfRadius = lodRadius / 2;
return (chunkX >= lodRadius - halfRadius
&& chunkX <= lodRadius + halfRadius)
&&
(chunkZ >= lodRadius - halfRadius
&& chunkZ <= lodRadius + halfRadius);
}
}
@@ -1,124 +0,0 @@
package com.backsun.lod.builders;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.backsun.lod.handlers.LodDimensionFileHandler;
import com.backsun.lod.objects.LodChunk;
import com.backsun.lod.objects.LodDimension;
import com.backsun.lod.objects.LodWorld;
import com.backsun.lod.util.LodUtils;
import net.minecraft.world.DimensionType;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraft.world.server.ServerWorld;
/**
* This object is in charge of creating Lod
* related objects.
* (specifically: Lod World, Dimension, Region, and Chunk objects)
*
* @author James Seibel
* @version 2-22-2021
*/
public class LodBuilder
{
private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor();
public volatile LodWorld lodWorld;
/** Default size of any LOD regions we use */
public int regionWidth = 5;
public LodBuilder()
{
}
/**
* Returns LodWorld so that it can be passed
* to the LodRenderer.
*/
public LodWorld generateLodChunkAsync(Chunk chunk)
{
if (lodWorld != null)
// is this chunk from the same world as the lodWorld?
if (!lodWorld.worldName.equals(LodDimensionFileHandler.getCurrentWorldID()))
// we are not in the same world anymore,
// remove the old world so it can be recreated later
lodWorld = null;
// don't try to create an LOD object
// if for some reason we aren't
// given a valid chunk object
// (Minecraft often gives back empty
// or null chunks in this method)
if (chunk == null || !isValidChunk(chunk))
return lodWorld;
DimensionType dim = chunk.getWorld().getDimensionType();
ServerWorld world = LodUtils.getServerWorldFromDimension(dim);
if (world == null)
return lodWorld;
Thread thread = new Thread(() ->
{
try
{
LodChunk lod = new LodChunk(chunk, world);
LodDimension lodDim;
if (lodWorld == null)
{
lodWorld = new LodWorld(LodDimensionFileHandler.getCurrentWorldID());
}
if (lodWorld.getLodDimension(dim) == null)
{
lodDim = new LodDimension(dim, regionWidth);
lodWorld.addLodDimension(lodDim);
}
else
{
lodDim = lodWorld.getLodDimension(dim);
}
lodDim.addLod(lod);
}
catch(IllegalArgumentException | NullPointerException e)
{
// 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 lodWorld;
}
/**
* Return whether the given chunk
* has any data in it.
*/
public boolean isValidChunk(Chunk chunk)
{
ChunkSection[] blockStorage = chunk.getSections();
for(ChunkSection section : blockStorage)
{
if(section != null && !section.isEmpty())
{
return true;
}
}
return false;
}
}
@@ -1,289 +0,0 @@
package com.backsun.lod.handlers;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.backsun.lod.objects.LodChunk;
import com.backsun.lod.objects.LodDimension;
import com.backsun.lod.objects.LodRegion;
import com.backsun.lod.util.LodUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.world.server.ServerChunkProvider;
import net.minecraft.world.server.ServerWorld;
/**
* This object handles creating LodRegions
* from files and saving LodRegion objects
* to file.
*
* @author James Seibel
* @version 01-30-2021
*/
public class LodDimensionFileHandler
{
private LodDimension loadedDimension = null;
public long regionLastWriteTime[][];
private File dimensionDataSaveFolder;
private final String FILE_NAME_PREFIX = "lod";
private final String FILE_EXTENSION = ".txt";
private ExecutorService fileWritingThreadPool = Executors.newFixedThreadPool(1);
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension)
{
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 at the given coordinates.
* (null if the file doesn't exist)
*/
public LodRegion loadRegionFromFile(int regionX, int regionZ)
{
// we don't currently support reading or writing
// files when connected to a server
if (!Minecraft.getInstance().isIntegratedServerRunning())
return null;
if (!readyToReadAndWrite())
return null;
String fileName = getFileNameForRegion(regionX, regionZ);
File f = new File(fileName);
if (!f.exists())
{
// there wasn't a file, don't
// return anything
return null;
}
LodRegion region = new LodRegion(regionX, regionZ);
try
{
BufferedReader br = new BufferedReader(new FileReader(f));
String s = br.readLine();
while(s != null && !s.isEmpty())
{
try
{
// convert each line into an LOD object and add it to the region
LodChunk lod = new LodChunk(s);
region.addLod(lod);
}
catch(IllegalArgumentException e)
{
// we were unable to create this chunk
// for whatever reason.
// skip to the next chunk
}
s = br.readLine();
}
br.close();
}
catch (IOException e)
{
// File not found
// or the buffered reader encountered a
// problem reading the file
return null;
}
return region;
}
//==============//
// Save to File //
//==============//
/**
* Save all dirty regions in this LodDimension to file.
*/
public synchronized void saveDirtyRegionsToFileAsync()
{
// we don't currently support reading or writing
// files when connected to a server
if (!Minecraft.getInstance().isIntegratedServerRunning())
return;
if (!readyToReadAndWrite())
// we aren't ready to read and write yet
return;
fileWritingThreadPool.execute(saveDirtyRegionsThread);
}
private Thread saveDirtyRegionsThread = new Thread(() ->
{
for(int i = 0; i < loadedDimension.getWidth(); i++)
{
for(int j = 0; j < loadedDimension.getWidth(); j++)
{
if(loadedDimension.isRegionDirty[i][j])
{
saveRegionToDisk(loadedDimension.regions[i][j]);
loadedDimension.isRegionDirty[i][j] = false;
}
}
}
});
/**
* Save a specific region to disk.<br>
* Note: it will save to the LodDimension that this
* handler is associated with.
*/
private void saveRegionToDisk(LodRegion region)
{
if (!readyToReadAndWrite() || region == null)
return;
// convert chunk coordinates to region
// coordinates
int x = region.x;
int z = region.z;
File f = new File(getFileNameForRegion(x, z));
try
{
// make sure the file and folder exists
if (!f.exists())
if(!f.getParentFile().exists())
f.getParentFile().mkdirs();
f.createNewFile();
FileWriter fw = new FileWriter(f);
for(LodChunk[] chunkArray : region.getAllLods())
for(LodChunk chunk : chunkArray)
if(chunk != null)
fw.write(chunk.toData() + "\n");
fw.close();
}
catch(Exception e)
{
System.err.println("LOD file write error: " + e.getMessage());
}
}
//================//
// 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.
*/
private String getFileNameForRegion(int regionX, int regionZ)
{
if (!readyToReadAndWrite())
return null;
try
{
// saveFolder is something like
// ".\Super Flat\DIM-1\data"
// or
// ".\Super Flat\data"
return dimensionDataSaveFolder.getCanonicalPath() + "\\lod\\" +
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION;
}
catch(IOException e)
{
return null;
}
}
/**
* Returns if this FileHandler is ready to read
* and write files.
* <br>
* This returns true when the world save directory is known.
*/
public boolean readyToReadAndWrite()
{
return dimensionDataSaveFolder != null;
}
/**
* If on single player this will return the name of the user's
* world, if in multiplayer it will return the server name
* and game version.
*/
public static String getCurrentWorldID()
{
Minecraft mc = Minecraft.getInstance();
if(mc.isIntegratedServerRunning())
{
ServerWorld serverWorld = LodUtils.getFirstValidServerWorld();
if (serverWorld == null)
return "";
ServerChunkProvider provider = serverWorld.getChunkProvider();
if(provider != null)
return provider.getSavedData().folder.toString();
return "";
}
else
{
return mc.getCurrentServerData().serverName + "_version_" + mc.getCurrentServerData().gameVersion;
}
}
}
@@ -1,99 +0,0 @@
package com.backsun.lod.handlers;
import java.lang.reflect.Field;
import com.backsun.lod.util.enums.FogQuality;
import net.minecraft.client.Minecraft;
/**
* This object is used to get variables from methods
* where they are private. Specifically the fog setting
* in Optifine.
*
* @author James Seibel
* @version 09-21-2020
*/
public class ReflectionHandler
{
private Minecraft mc = Minecraft.getInstance();
public Field ofFogField = null;
public ReflectionHandler()
{
setupFogField();
}
/**
* Similar to setupFovMethod.
*/
private void setupFogField()
{
// get every variable from the entity renderer
Field[] vars = mc.gameSettings.getClass().getDeclaredFields();
// try and find the ofFogType variable in gameSettings
for(Field f : vars)
{
if(f.getName().equals("ofFogType"))
{
ofFogField = f;
return;
}
}
// we didn't find the field,
// either optifine isn't installed, or
// optifine changed the name of the variable
ofFogField = null;
}
/**
* 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.gameSettings);
}
catch (IllegalArgumentException | IllegalAccessException e)
{
e.printStackTrace();
}
switch (returnNum)
{
case 0:
return FogQuality.FAST;
case 1:
return FogQuality.FAST;
case 2:
return FogQuality.FANCY;
case 3:
return FogQuality.OFF;
default:
return FogQuality.FAST;
}
}
}
@@ -1,22 +0,0 @@
package com.backsun.lod.mixin;
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.backsun.lod.renderer.RenderGlobalHook;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.WorldRenderer;
@Mixin(WorldRenderer.class)
public class MixinWorldRenderer
{
@Inject(at = @At("HEAD"), method = "renderBlockLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/matrix/MatrixStack;DDD)V", cancellable = false)
private void renderBlockLayer(RenderType blockLayerIn, MatrixStack matrixStackIn, double xIn, double yIn, double zIn, CallbackInfo callback)
{
RenderGlobalHook.startRenderingStencil(blockLayerIn);
}
}
@@ -1,776 +0,0 @@
package com.backsun.lod.objects;
import java.awt.Color;
import com.backsun.lod.util.enums.ColorDirection;
import com.backsun.lod.util.enums.LodCorner;
import com.backsun.lod.util.enums.LodLocation;
import net.minecraft.block.Blocks;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.color.BlockColors;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkSection;
/**
* This object contains position
* and color data for an LOD object.
*
* @author James Seibel
* @version 02-13-2021
*/
public class LodChunk
{
/** how many different pieces of data are in one line */
private static final int DATA_DELIMITER_COUNT = 28;
/** This is what separates each piece of data in the toData method */
public static final char DATA_DELIMITER = ',';
public static final int WIDTH = 16;
private static final int CHUNK_DATA_WIDTH = WIDTH;
private static final int CHUNK_DATA_HEIGHT = WIDTH;
/**
* This is how many blocks are
* required at a specific y-value
* to constitute a LOD point
*/
private final int LOD_BLOCK_REQ = 16;
// the max number of blocks per layer = 64 (8*8)
// since each layer is 1/4 the chunk
/** The x coordinate of the chunk. */
public int x;
/** The z coordinate of the chunk. */
public int z;
// each short is the height of
// that 8th of the chunk.
public short top[];
public short bottom[];
/** The average color of each 6 cardinal directions */
public Color colors[];
//==============//
// constructors //
//==============//
/**
* Create an empty invisible LodChunk at (0,0)
*/
public LodChunk()
{
x = 0;
z = 0;
top = new short[4];
bottom = new short[4];
colors = new Color[6];
// by default have the colors invisible
for(ColorDirection dir : ColorDirection.values())
{
colors[dir.value] = new Color(0, 0, 0, 0);
}
}
/**
* Creates an LodChunk from the string
* generated by the toData method.
*
* @throws IllegalArgumentException if the data isn't valid to create a LodChunk
* @throws NumberFormatException if the data can't be converted into an int at any point
*/
public LodChunk(String data) throws IllegalArgumentException, NumberFormatException
{
/*
* data format:
* x, z, top data, bottom data, rgb color data
*
* example:
* 5,8, 4,4,4,4, 0,0,0,0, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255,
*/
// make sure there are the correct number of entries
// in the data string (28)
int count = 0;
for(int i = 0; i < data.length(); i++)
{
if(data.charAt(i) == DATA_DELIMITER)
{
count++;
}
}
if(count != DATA_DELIMITER_COUNT)
{
throw new IllegalArgumentException("LodChunk constructor givin an invalid string. The data given had " + count + " delimiters when it should have had " + DATA_DELIMITER_COUNT + ".");
}
// index we will use when going through the String
int index = 0;
int lastIndex = 0;
// x and z position
index = data.indexOf(DATA_DELIMITER, 0);
x = Integer.parseInt(data.substring(0,index));
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
z = Integer.parseInt(data.substring(lastIndex+1,index));
// top
top = new short[4];
for(LodLocation loc : LodLocation.values())
{
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
top[loc.value] = Short.parseShort(data.substring(lastIndex+1,index));
}
// bottom
bottom = new short[4];
for(LodLocation loc : LodLocation.values())
{
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
bottom[loc.value] = Short.parseShort(data.substring(lastIndex+1,index));
}
// color
colors = new Color[6];
for(ColorDirection dir : ColorDirection.values())
{
int red = 0;
int green = 0;
int blue = 0;
// get r,g,b
for(int i = 0; i < 3; i++)
{
lastIndex = index;
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
String raw = "";
switch(i)
{
case 0:
raw = data.substring(lastIndex+1,index);
red = Short.parseShort(raw);
break;
case 1:
raw = data.substring(lastIndex+1,index);
green = Short.parseShort(raw);
break;
case 2:
raw = data.substring(lastIndex+1,index);
blue = Short.parseShort(raw);
break;
}
}
colors[dir.value] = new Color(red, green, blue);
}
}
/**
* Creates a LodChunk for a chunk in the given world. <br>
* Note: The world is required to determine each block's color
*
* @throws IllegalArgumentException
* thrown if either the chunk or world is null.
*/
public LodChunk(Chunk chunk, World world) throws IllegalArgumentException
{
if(chunk == null)
{
throw new IllegalArgumentException("LodChunk constructor given a null chunk");
}
if(world == null)
{
throw new IllegalArgumentException("LodChunk constructor given a null world");
}
x = chunk.getPos().x;
z = chunk.getPos().z;
top = new short[4];
bottom = new short[4];
colors = new Color[6];
// generate the top and bottom points of this LOD
for(LodLocation loc : LodLocation.values())
{
top[loc.value] = generateLodCorner(chunk, SectionGenerationMode.GENERATE_TOP, loc);
bottom[loc.value] = generateLodCorner(chunk, SectionGenerationMode.GENERATE_BOTTOM, loc);
}
// determine the average color for each direction
for(ColorDirection dir : ColorDirection.values())
{
colors[dir.value] = generateLodColorForDirection(chunk, world, dir);
}
}
//=====================//
// constructor helpers //
//=====================//
/**
* Generate the height for the given LodLocation, either the top or bottom.
* <br><br>
* If invalid/null/empty chunks are given
* crashes may occur.
*/
public short generateLodCorner(Chunk chunk, SectionGenerationMode generationMode, LodLocation lodLoc)
{
// should have a length of 16
// (each storage is 16x16x16 and the
// world height is 256)
ChunkSection[] chunkSections = chunk.getSections();
int startX = 0;
int endX = 0;
int startZ = 0;
int endZ = 0;
// determine where we should look in this
// chunk
switch(lodLoc)
{
case NE:
// -N
startZ = 0;
endZ = (CHUNK_DATA_WIDTH / 2) - 1;
// +E
startX = CHUNK_DATA_WIDTH / 2;
endX = CHUNK_DATA_WIDTH - 1;
break;
case SE:
// +S
startZ = CHUNK_DATA_WIDTH / 2;
endZ = CHUNK_DATA_WIDTH;
// +E
startX = CHUNK_DATA_WIDTH / 2;
endX = CHUNK_DATA_WIDTH;
break;
case SW:
// +S
startZ = CHUNK_DATA_WIDTH / 2;
endZ = CHUNK_DATA_WIDTH;
// -W
startX = 0;
endX = (CHUNK_DATA_WIDTH / 2) - 1;
break;
case NW:
// -N
startZ = 0;
endZ = CHUNK_DATA_WIDTH / 2;
// -W
startX = 0;
endX = CHUNK_DATA_WIDTH / 2;
break;
}
if(generationMode == SectionGenerationMode.GENERATE_TOP)
return determineTopPoint(chunkSections, startX, endX, startZ, endZ);
else
return determineBottomPoint(chunkSections, startX, endX, startZ, endZ);
}
/** GENERATE_TOP, GENERATE_BOTTOM */
private enum SectionGenerationMode
{
GENERATE_TOP,
GENERATE_BOTTOM;
}
/**
* Find the lowest valid point from the bottom.
*/
private short determineBottomPoint(ChunkSection[] chunkSections, int startX, int endX, int startZ, int endZ)
{
// search from the bottom up
for(int i = 0; i < chunkSections.length; i++)
{
for(int y = 0; y < CHUNK_DATA_HEIGHT; y++)
{
if(isLayerValidLodPoint(chunkSections, startX, endX, startZ, endZ, i, y))
{
// we found
// enough blocks in this
// layer to count as an
// LOD point
return (short) (y + (i * CHUNK_DATA_HEIGHT));
}
}
}
// we never found a valid LOD point
return -1;
}
/**
* Find the highest valid point from the Top
*/
private short determineTopPoint(ChunkSection[] chunkSections, int startX, int endX, int startZ, int endZ)
{
// search from the top down
for(int i = chunkSections.length - 1; i >= 0; i--)
{
for(int y = CHUNK_DATA_WIDTH - 1; y >= 0; y--)
{
if(isLayerValidLodPoint(chunkSections, startX, endX, startZ, endZ, i, y))
{
// we found
// enough blocks in this
// layer to count as an
// LOD point
return (short) (y + (i * CHUNK_DATA_HEIGHT));
}
}
}
// we never found a valid LOD point
return -1;
}
/**
* Is the layer between the given X, Z, and dataIndex
* values a valid LOD point?
*/
private boolean isLayerValidLodPoint(
ChunkSection[] chunkSections,
int startX, int endX,
int startZ, int endZ,
int sectionIndex, int y)
{
// search through this layer
int layerBlocks = 0;
for(int x = startX; x < endX; x++)
{
for(int z = startZ; z < endZ; 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)
{
// we found a valid block in
// in this layer
layerBlocks++;
if(layerBlocks >= LOD_BLOCK_REQ)
{
return true;
}
}
}
} // z
} // x
return false;
}
/**
* Generate the color of the given ColorDirection at the given chunk
* in the given world.
*/
private Color generateLodColorForDirection(Chunk chunk, World world, ColorDirection colorDir)
{
Minecraft mc = Minecraft.getInstance();
BlockColors bc = mc.getBlockColors();
switch (colorDir)
{
case TOP:
return generateLodColorVertical(chunk, colorDir, world, bc);
case BOTTOM:
return generateLodColorVertical(chunk, colorDir, world, bc);
case N:
return generateLodColorHorizontal(chunk, colorDir, world, bc);
case S:
return generateLodColorHorizontal(chunk, colorDir, world, bc);
case E:
return generateLodColorHorizontal(chunk, colorDir, world, bc);
case W:
return generateLodColorHorizontal(chunk, colorDir, world, bc);
}
return new Color(0, 0, 0, 0);
}
/**
* Generates the color of the top or bottom of a given chunk in the given world.
*
* @throws IllegalArgumentException if given a ColorDirection other than TOP or BOTTOM
*/
private Color generateLodColorVertical(Chunk chunk, ColorDirection colorDir, World world, BlockColors bc)
{
if(colorDir != ColorDirection.TOP && colorDir != ColorDirection.BOTTOM)
{
throw new IllegalArgumentException("generateLodColorVertical only accepts the ColorDirection TOP or BOTTOM");
}
ChunkSection[] chunkSections = chunk.getSections();
int numbOfBlocks = 0;
int red = 0;
int green = 0;
int blue = 0;
boolean goTopDown = (colorDir == ColorDirection.TOP);
// either go top down or bottom up
int dataStart = goTopDown? chunkSections.length - 1 : 0;
int dataMax = chunkSections.length;
int dataMin = 0;
int dataIncrement = goTopDown? -1 : 1;
int topStart = goTopDown? CHUNK_DATA_HEIGHT - 1 : 0;
int topMax = CHUNK_DATA_HEIGHT;
int topMin = 0;
int topIncrement = goTopDown? -1 : 1;
for(int x = 0; x < CHUNK_DATA_WIDTH; x++)
{
for(int z = 0; z < CHUNK_DATA_WIDTH; z++)
{
boolean foundBlock = false;
for(int i = dataStart; !foundBlock && i >= dataMin && i < dataMax; i += dataIncrement)
{
if(!foundBlock && chunkSections[i] != null)
{
for(int y = topStart; !foundBlock && y >= topMin && y < topMax; y += topIncrement)
{
int ci;
ci = chunkSections[i].getBlockState(x, y, z).materialColor.colorValue;
if(ci == 0)
{
// skip air or invisible blocks
continue;
}
Color c = intToColor(ci);
red += c.getRed();
green += c.getGreen();
blue += c.getBlue();
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 new Color(red, green, blue);
}
/**
* Generates the color of the side of a given chunk in the given world for the given ColorDirection.
*
* @throws IllegalArgumentException if given a ColorDirection other than N, S, W, E (North, South, East, West)
*/
private Color generateLodColorHorizontal(Chunk chunk, ColorDirection colorDir, World world, BlockColors bc)
{
if(colorDir != ColorDirection.N && colorDir != ColorDirection.S && colorDir != ColorDirection.E && colorDir != ColorDirection.W)
{
throw new IllegalArgumentException("generateLodColorHorizontal only accepts the ColorDirection N (North), S (South), E (East), or W (West)");
}
ChunkSection[] chunkSections = chunk.getSections();
int numbOfBlocks = 0;
int red = 0;
int green = 0;
int blue = 0;
// these don't change since the over direction doesn't matter
int overStart = 0;
int overIncrement = 1;
// determine which direction is "in"
int inStart = 0;
int inIncrement = 1;
switch (colorDir)
{
case N:
inStart = 0;
inIncrement = 1;
break;
case S:
inStart = CHUNK_DATA_WIDTH - 1;
inIncrement = -1;
break;
case E:
inStart = 0;
inIncrement = 1;
break;
case W:
inStart = CHUNK_DATA_WIDTH - 1;
inIncrement = -1;
break;
default:
// we were given an invalid position, return invisible.
// this shouldn't happen and is mostly here to make the
// compiler happy
return new Color(0,0,0,0);
}
for (int i = 0; i < chunkSections.length; i++)
{
if (chunkSections[i] != null)
{
for (int y = 0; y < CHUNK_DATA_HEIGHT; y++)
{
boolean foundBlock = false;
// over moves "over" the side of the chunk
// in moves "into" the chunk until it finds a block
for (int over = overStart; !foundBlock && over >= 0 && over < CHUNK_DATA_WIDTH; over += overIncrement)
{
for (int in = inStart; !foundBlock && in >= 0 && in < CHUNK_DATA_WIDTH; in += inIncrement)
{
int x = -1;
int z = -1;
// determine which should be X and Z
switch(colorDir)
{
case N:
x = over;
z = in;
break;
case S:
x = over;
z = in;
break;
case E:
x = in;
z = over;
break;
case W:
x = in;
z = over;
break;
default:
// this will never happen, it would have
// been caught by the switch before the loops
break;
}
int ci;
ci = chunkSections[i].getBlockState(x, y, z).getMaterial().getColor().colorValue;
if (ci == 0) {
// skip air or invisible blocks
continue;
}
Color c = intToColor(ci);
red += c.getRed();
green += c.getGreen();
blue += c.getBlue();
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 new Color(red, green, blue);
}
/**
* Convert a BlockColors int into a Color object.
*/
private 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.
*/
@SuppressWarnings("unused")
private int colorToInt(Color color)
{
return color.getRGB();
}
//================//
// misc functions //
//================//
/**
* If this LOD is either invisible from every
* direction or doesn't have a valid height
* it is empty.
*/
public boolean isLodEmpty()
{
for(LodCorner corner : LodCorner.values())
if(top[corner.value] != -1 || bottom[corner.value] != -1)
// at least one corner is valid
return false;
Color invisible = new Color(0,0,0,0);
for(ColorDirection dir : ColorDirection.values())
if(!colors[dir.value].equals(invisible))
// at least one direction has a non-invisible color
return false;
return true;
}
//========//
// output //
//========//
/**
* Outputs all data in csv format
* with the given delimiter.
* <br>
* Exports data in the form:
* <br>
* x, z, top data, bottom data, rgb color data
*
* <br>
* example output:
* <br>
* 5,8, 4,4,4,4, 0,0,0,0 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255,
*/
public String toData()
{
String s = "";
s += Integer.toString(x) + DATA_DELIMITER + Integer.toString(z) + DATA_DELIMITER;
for(int i = 0; i < top.length; i++)
{
s += Short.toString(top[i]) + DATA_DELIMITER;
}
for(int i = 0; i < bottom.length; i++)
{
s += Short.toString(bottom[i]) + DATA_DELIMITER;
}
for(int i = 0; i < colors.length; i++)
{
s += Integer.toString(colors[i].getRed()) + DATA_DELIMITER + Integer.toString(colors[i].getGreen()) + DATA_DELIMITER + Integer.toString(colors[i].getBlue()) + DATA_DELIMITER;
}
return s;
}
@Override
public String toString()
{
String s = "";
s += "x: " + x + " z: " + z + "\t";
s += "(" + colors[ColorDirection.TOP.value].getRed() + ", " + colors[ColorDirection.TOP.value].getGreen() + ", " + colors[ColorDirection.TOP.value].getBlue() + ")";
return s;
}
}
@@ -1,351 +0,0 @@
package com.backsun.lod.objects;
import com.backsun.lod.handlers.LodDimensionFileHandler;
import com.backsun.lod.util.LodUtils;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerChunkProvider;
/**
* This object holds all loaded LOD regions
* for a given dimension.
*
* @author James Seibel
* @version 02-23-2021
*/
public class LodDimension
{
public final DimensionType dimension;
private volatile int width;
private volatile int halfWidth;
public LodRegion regions[][];
public boolean isRegionDirty[][];
private int centerX;
private int centerZ;
private LodDimensionFileHandler fileHandler;
public LodDimension(DimensionType newDimension, int newMaxWidth)
{
dimension = newDimension;
width = newMaxWidth;
ServerChunkProvider provider = LodUtils.getServerWorldFromDimension(newDimension).getChunkProvider();
fileHandler = new LodDimensionFileHandler(provider.getSavedData().folder, this);
regions = new LodRegion[width][width];
isRegionDirty = new boolean[width][width];
// populate isRegionDirty
for(int i = 0; i < width; i++)
for(int j = 0; j < width; j++)
isRegionDirty[i][j] = false;
centerX = 0;
centerZ = 0;
halfWidth = (int)Math.floor(width / 2);
}
/**
* Move the center of this LodDimension and move all owned
* regions over by the given x and z offset.
*/
public void move(int xOffset, int zOffset)
{
// 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
centerX += xOffset;
centerZ += 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
centerX += xOffset;
centerZ += zOffset;
}
/**
* 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 regionX, int regionZ)
{
int xIndex = (regionX - centerX) + halfWidth;
int zIndex = (regionZ - centerZ) + halfWidth;
if (!regionIsInRange(regionX, regionZ))
// out of range
return null;
if (regions[xIndex][zIndex] == null)
{
regions[xIndex][zIndex] = getRegionFromFile(regionX, regionZ);
if (regions[xIndex][zIndex] == null)
{
regions[xIndex][zIndex] = new LodRegion(regionX, regionZ);
}
}
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 void setRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException
{
int xIndex = (newRegion.x - centerX) + halfWidth;
int zIndex = (centerZ - newRegion.z) + halfWidth;
if (!regionIsInRange(newRegion.x, newRegion.z))
// out of range
throw new ArrayIndexOutOfBoundsException();
regions[xIndex][zIndex] = newRegion;
}
/**
* 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 void addLod(LodChunk lod)
{
int regionX = lod.x / LodRegion.SIZE;
int regionZ = lod.z / LodRegion.SIZE;
// prevent issues if X/Z is negative and less than 16
if (lod.x < 0)
{
regionX = (Math.abs(regionX) * -1) - 1;
}
if (lod.z < 0)
{
regionZ = (Math.abs(regionZ) * -1) - 1;
}
// don't continue if the region can't be saved
if (!regionIsInRange(regionX, regionZ))
return;
LodRegion region = getRegion(regionX, regionZ);
if (region == null)
{
// if no region exists, create it
region = new LodRegion(regionX, regionZ);
setRegion(region);
}
region.addLod(lod);
// mark the region as dirty so it will be saved to disk
int xIndex = (regionX - centerX) + halfWidth;
int zIndex = (regionZ - centerZ) + halfWidth;
isRegionDirty[xIndex][zIndex] = true;
fileHandler.saveDirtyRegionsToFileAsync();
}
/**
* Get the LodChunk 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 LodChunk getLodFromCoordinates(int chunkX, int chunkZ)
{
int regionX = chunkX / LodRegion.SIZE;
int regionZ = chunkZ / LodRegion.SIZE;
// prevent issues if chunkX/Z is negative and less than width
if (chunkX < 0)
{
regionX = (Math.abs(regionX) * -1) - 1;
}
if (chunkZ < 0)
{
regionZ = (Math.abs(regionZ) * -1) - 1;
}
LodRegion region = getRegion(regionX, regionZ);
if(region == null)
return null;
return region.getLod(chunkX, chunkZ);
}
/**
* Get the region at the given X and Z coordinates from the
* RegionFileHandler.
*/
public LodRegion getRegionFromFile(int regionX, int regionZ)
{
return fileHandler.loadRegionFromFile(regionX, regionZ);
}
/**
* Returns whether the region at the given X and Z coordinates
* is within the loaded range.
*/
private boolean regionIsInRange(int regionX, int regionZ)
{
int xIndex = (regionX - centerX) + halfWidth;
int zIndex = (regionZ - centerZ) + halfWidth;
return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width;
}
public int getCenterX()
{
return centerX;
}
public int getCenterZ()
{
return centerZ;
}
public int getWidth()
{
return width;
}
public void setRegionWidth(int newWidth)
{
width = newWidth;
halfWidth = (int)Math.floor(width / 2);
regions = new LodRegion[width][width];
isRegionDirty = 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()
{
String s = "";
s += "dim: " + dimension.toString() + "\t";
s += "(" + centerX + "," + centerZ + ")";
return s;
}
}
@@ -1,90 +0,0 @@
package com.backsun.lod.objects;
/**
* A LodRegion is the a 32x32
* 2D array of LodChunk objects.
* Each LodRegion corresponds to
* one file in the file system.
*
* @author James Seibel
* @version 1-22-2021
*/
public class LodRegion
{
/** number of chunks wide */
public static final int SIZE = 32;
/** X coordinate of this region */
public final int x;
/** Z coordinate of this region */
public final int z;
private LodChunk chunks[][];
public LodRegion(int regionX, int regionZ)
{
x = regionX;
z = regionZ;
chunks = new LodChunk[SIZE][SIZE];
}
/**
* Add the given LOD to this region at the coordinate
* stored in the LOD. If an LOD already exists at the given
* coordinates it will be overwritten.
*/
public void addLod(LodChunk lod)
{
// we use ABS since LODs can be negative, but if they are
// the region will negative first, therefore we don't have to
// store the LOD chunks at negative indexes since we search
// LOD the region first
int xIndex = Math.abs(lod.x % SIZE);
int zIndex = Math.abs(lod.z % SIZE);
chunks[xIndex][zIndex] = lod;
}
/**
* Get the LodChunk at the given X and Z coordinates
* in this region.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public LodChunk getLod(int chunkX, int chunkZ)
{
// since we add LOD's with ABS, we get them the same way
int arrayX = Math.abs(chunkX % SIZE);
int arrayZ = Math.abs(chunkZ % SIZE);
if(arrayX >= SIZE || arrayZ >= SIZE)
return null;
return chunks[arrayX][arrayZ];
}
/**
* Returns all LodChunks in this region
*/
public LodChunk[][] getAllLods()
{
return chunks;
}
@Override
public String toString()
{
String s = "";
s += "x: " + x + " z: " + z + "\t";
return s;
}
}
@@ -1,65 +0,0 @@
package com.backsun.lod.objects;
import java.util.Hashtable;
import java.util.Map;
import net.minecraft.world.DimensionType;
/**
* This stores all LODs for a given world.
*
* @author James Seibel
* @version 02-22-2021
*/
public class LodWorld
{
public String worldName;
/**
* Key = Dimension id (as an int)
*/
private Map<DimensionType, LodDimension> lodDimensions;
public LodWorld(String newWorldName)
{
worldName = newWorldName;
lodDimensions = new Hashtable<DimensionType, LodDimension>();
}
public void addLodDimension(LodDimension newStorage)
{
lodDimensions.put(newStorage.dimension, newStorage);
}
public LodDimension getLodDimension(DimensionType dimension)
{
return lodDimensions.get(dimension);
}
/**
* Resizes the max width in regions that each LodDimension
* should use.
*/
public void resizeDimensionRegionWidth(int newWidth)
{
for(DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).setRegionWidth(newWidth);
}
@Override
public String toString()
{
String s = "";
s += worldName + "\t - dimensions: ";
for(DimensionType key : lodDimensions.keySet())
s += lodDimensions.get(key).dimension.toString() + ", ";
return s;
}
}
@@ -1,25 +0,0 @@
package com.backsun.lod.objects;
import net.minecraft.client.renderer.BufferBuilder;
/**
* This object is just a replacement for an array
* to make things easier to understand in the LodRenderer
* and BuildBufferThread.
*
* @author James Seibel
* @version 02-21-2021
*/
public class NearFarBuffer
{
public BufferBuilder nearBuffer;
public BufferBuilder farBuffer;
public NearFarBuffer(BufferBuilder newNearBuffer, BufferBuilder newFarBuffer)
{
nearBuffer = newNearBuffer;
farBuffer = newFarBuffer;
}
}
@@ -1,28 +0,0 @@
package com.backsun.lod.objects;
import com.backsun.lod.util.enums.FogDistance;
/**
* This object is just a replacement for an array
* to make things easier to understand in the LodRenderer.
*
* @author James Seibel
* @version 02-27-2021
*/
public class NearFarFogSetting
{
public FogDistance nearFogSetting = FogDistance.NEAR;
public FogDistance farFogSetting = FogDistance.FAR;
public NearFarFogSetting()
{
}
public NearFarFogSetting(FogDistance newNearFogSetting, FogDistance newFarFogSetting)
{
nearFogSetting = newNearFogSetting;
farFogSetting = newFarFogSetting;
}
}
@@ -1,129 +0,0 @@
package com.backsun.lod.proxy;
import org.lwjgl.opengl.GL11;
import com.backsun.lod.builders.LodBuilder;
import com.backsun.lod.handlers.LodDimensionFileHandler;
import com.backsun.lod.objects.LodChunk;
import com.backsun.lod.objects.LodDimension;
import com.backsun.lod.objects.LodRegion;
import com.backsun.lod.objects.LodWorld;
import com.backsun.lod.renderer.LodRenderer;
import com.backsun.lod.renderer.RenderGlobalHook;
import com.backsun.lod.util.LodConfig;
import net.minecraft.client.Minecraft;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
//TODO Find a way to replace getIntegratedServer so this mod could be used on non-local worlds.
// Minecraft.getMinecraft().getIntegratedServer()
/**
* This handles all events sent to the client,
* and is the starting point for most of this program.
*
* @author James_Seibel
* @version 02-23-2021
*/
public class ClientProxy
{
private LodRenderer renderer;
private LodWorld lodWorld;
private LodBuilder lodBuilder;
Minecraft mc = Minecraft.getInstance();
public ClientProxy()
{
lodBuilder = new LodBuilder();
renderer = new LodRenderer();
}
//==============//
// render event //
//==============//
@SubscribeEvent
public void renderWorldLast(RenderWorldLastEvent event)
{
RenderGlobalHook.endRenderingStencil();
GL11.glStencilFunc(GL11.GL_EQUAL, 0, 0xFF);
if (LodConfig.CLIENT.drawLODs.get())
renderLods(event.getPartialTicks());
GL11.glDisable(GL11.GL_STENCIL_TEST);
}
/**
* Do any setup that is required to draw LODs
* and then tell the LodRenderer to draw.
*/
public void renderLods(float partialTicks)
{
// update the
int newWidth = Math.max(4, (mc.gameSettings.renderDistanceChunks * LodChunk.WIDTH * 2) / LodRegion.SIZE);
if (lodWorld != null && lodBuilder.regionWidth != newWidth)
{
lodWorld.resizeDimensionRegionWidth(newWidth);
lodBuilder.regionWidth = newWidth;
// skip this frame, hopefully the lodWorld
// should have everything set up by then
return;
}
// are we still in the same world?
if (!lodWorld.worldName.equals(LodDimensionFileHandler.getCurrentWorldID()))
// no, don't render the wrong world
return;
if (mc == null || mc.player == null || lodWorld == null)
return;
LodDimension lodDim = lodWorld.getLodDimension(mc.player.world.getDimensionType());
if (lodDim == null)
return;
// offset the regions
double playerX = mc.player.getPosX();
double playerZ = mc.player.getPosZ();
int xOffset = ((int)playerX / (LodChunk.WIDTH * LodRegion.SIZE)) - lodDim.getCenterX();
int zOffset = ((int)playerZ / (LodChunk.WIDTH * LodRegion.SIZE)) - lodDim.getCenterZ();
if (xOffset != 0 || zOffset != 0)
{
lodDim.move(xOffset, zOffset);
}
renderer.drawLODs(lodDim, partialTicks, mc.getProfiler());
}
//=====================//
// lod creation events //
//=====================//
@SubscribeEvent
public void chunkLoadEvent(ChunkEvent.Load event)
{
if (event.getChunk().getClass() == Chunk.class)
lodWorld = lodBuilder.generateLodChunkAsync((Chunk) event.getChunk());
}
}
@@ -1,810 +0,0 @@
package com.backsun.lod.renderer;
import java.awt.Color;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.lwjgl.opengl.GL11;
import com.backsun.lod.builders.LodBufferBuilder;
import com.backsun.lod.handlers.ReflectionHandler;
import com.backsun.lod.objects.LodChunk;
import com.backsun.lod.objects.LodDimension;
import com.backsun.lod.objects.NearFarBuffer;
import com.backsun.lod.objects.NearFarFogSetting;
import com.backsun.lod.util.LodConfig;
import com.backsun.lod.util.enums.ColorDirection;
import com.backsun.lod.util.enums.FogDistance;
import com.backsun.lod.util.enums.FogQuality;
import com.backsun.lod.util.enums.LodCorner;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.BufferBuilder;
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.potion.Effects;
import net.minecraft.profiler.IProfiler;
import net.minecraft.util.math.AxisAlignedBB;
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.
* This is where LODs are draw to the world.
*
* @author James Seibel
* @version 2-27-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;
/** If true the LODs colors will be replaced with
* a checkerboard, this can be used for debugging. */
public boolean debugging = false;
private Minecraft mc;
private GameRenderer gameRender;
private IProfiler profiler;
private float farPlaneDistance;
/** this is the radius of the LODs */
private static final int LOD_CHUNK_DISTANCE_RADIUS = 6;
private ReflectionHandler reflectionHandler;
public LodDimension lodDimension = null;
/** This is used to generate the buildable buffers */
private LodBufferBuilder lodBufferBuilder = null;
/** The buffers that are used to draw LODs using near fog */
private volatile BufferBuilder drawableNearBuffer = null;
/** The buffers that are used to draw LODs using far fog */
private volatile BufferBuilder drawableFarBuffer = null;
/** The buffers that are used to create LODs using near fog */
private volatile BufferBuilder buildableNearBuffer = null;
/** The buffers that are used to create LODs using far fog */
private volatile BufferBuilder buildableFarBuffer = null;
/** This is the VertexBuffer used to draw any LODs that use near fog */
private volatile VertexBuffer nearVbo = null;
/** This is the VertexBuffer used to draw any LODs that use far fog */
private volatile VertexBuffer farVbo = null;
public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR;
/** This holds the thread used to generate new LODs off the main thread. */
private ExecutorService genThread = Executors.newSingleThreadExecutor();
/** This is used to determine if the LODs should be regenerated */
private int previousChunkRenderDistance = 0;
/** This is used to determine if the LODs should be regenerated */
private int prevChunkX = 0;
/** This is used to determine if the LODs should be regenerated */
private int prevChunkZ = 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 boolean regen = false;
/** if this is true the LOD buffers are currently being
* regenerated. */
private volatile boolean regenerating = false;
/** if this is true new LOD buffers have been generated
* and are waiting to be swapped with the drawable buffers*/
private volatile boolean switchBuffers = false;
public LodRenderer()
{
mc = Minecraft.getInstance();
gameRender = mc.gameRenderer;
reflectionHandler = new ReflectionHandler();
}
/**
* Besides drawing the LODs this method also starts
* the async process of generating the Buffers that hold those LODs.
*
* @param newDimension 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 newDimension, float partialTicks, IProfiler newProfiler)
{
if (lodDimension == null && newDimension == null)
{
// if there aren't any loaded LodChunks
// don't try drawing anything
return;
}
//===============//
// initial setup //
//===============//
// used for debugging and viewing how long different processes take
profiler = newProfiler;
profiler.endSection();
profiler.startSection("LOD");
profiler.startSection("LOD setup");
ClientPlayerEntity player = mc.player;
// should LODs be regenerated?
if ((int)player.getPosX() / LodChunk.WIDTH != prevChunkX ||
(int)player.getPosZ() / LodChunk.WIDTH != prevChunkZ ||
previousChunkRenderDistance != mc.gameSettings.renderDistanceChunks ||
prevFogDistance != LodConfig.CLIENT.fogDistance.get() ||
lodDimension != newDimension)
{
// yes
regen = true;
prevChunkX = (int)player.getPosX() / LodChunk.WIDTH;
prevChunkZ = (int)player.getPosZ() / LodChunk.WIDTH;
prevFogDistance = LodConfig.CLIENT.fogDistance.get();
}
else
{
// nope, the player hasn't moved, the
// render distance hasn't changed, and
// the dimension is the same
}
lodDimension = newDimension;
if (lodDimension == null)
{
// if there aren't any loaded LodChunks
// don't try drawing anything
return;
}
if (LodConfig.CLIENT.drawCheckerBoard.get())
{
if (debugging != LodConfig.CLIENT.drawCheckerBoard.get())
regen = true;
debugging = true;
}
else
{
if (debugging != LodConfig.CLIENT.drawCheckerBoard.get())
regen = true;
debugging = false;
}
// determine how far the game's render distance is currently set
int renderDistWidth = mc.gameSettings.renderDistanceChunks;
farPlaneDistance = renderDistWidth * LodChunk.WIDTH;
// set how big the LODs will be and how far they will go
int totalLength = (int) farPlaneDistance * LOD_CHUNK_DISTANCE_RADIUS * 2;
int numbChunksWide = (totalLength / LodChunk.WIDTH);
//=================//
// 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 (regen && !regenerating && !switchBuffers)
{
regenerating = true;
if (lodBufferBuilder == null)
lodBufferBuilder = new LodBufferBuilder();
// this will mainly happen when the view distance is changed
if (drawableNearBuffer == null || drawableFarBuffer == null ||
previousChunkRenderDistance != mc.gameSettings.renderDistanceChunks)
setupBuffers(numbChunksWide);
// generate the LODs on a separate thread to prevent stuttering or freezing
generateLodBuffersAsync(player.getPosX(), player.getPosZ(), numbChunksWide);
// the regen process has been started
regen = false;
}
// replace the buffers used to draw and build,
// this is only done when the createLodBufferGenerationThread
// has finished executing on a parallel thread.
if (switchBuffers)
{
swapBuffers();
switchBuffers = false;
}
//===========================//
// GL settings for rendering //
//===========================//
// set the required open GL settings
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
GL11.glEnable(GL11.GL_DEPTH_TEST);
Matrix4f modelViewMatrix = generateModelViewMatrix(partialTicks);
setupProjectionMatrix(partialTicks);
setupLighting(partialTicks);
NearFarFogSetting fogSetting = determineFogSettings();
//===========//
// rendering //
//===========//
setupFog(fogSetting.nearFogSetting, reflectionHandler.getFogQuality());
sendLodsToGpuAndDraw(nearVbo, modelViewMatrix);
setupFog(fogSetting.farFogSetting, reflectionHandler.getFogQuality());
sendLodsToGpuAndDraw(farVbo, modelViewMatrix);
//=========//
// cleanup //
//=========//
profiler.endStartSection("LOD cleanup");
// this must be done otherwise other parts of the screen may be drawn with a fog effect
// IE the GUI
FogRenderer.resetFog();
RenderSystem.disableFog();
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glDisable(LOD_GL_LIGHT_NUMBER);
GL11.glDisable(GL11.GL_COLOR_MATERIAL);
// this can't be called until after the buffers are built
// because otherwise the buffers may be set to the wrong size
previousChunkRenderDistance = mc.gameSettings.renderDistanceChunks;
// end of profiler tracking
profiler.endSection();
}
/**
* 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.gameRenderer.getActiveRenderInfo();
Vector3d projectedView = renderInfo.getProjectedView();
double cameraX = projectedView.x;
double cameraY = projectedView.y;
double cameraZ = projectedView.z;
// generate the model view matrix
MatrixStack matrixStack = new MatrixStack();
matrixStack.push();
// translate and rotate to the current camera location
matrixStack.rotate(Vector3f.XP.rotationDegrees(renderInfo.getPitch()));
matrixStack.rotate(Vector3f.YP.rotationDegrees(renderInfo.getYaw() + 180));
matrixStack.translate(-cameraX, -cameraY, -cameraZ);
// this isn't a great solution to nausea or portal spinning
// but i'm not sure what to do otherwise
float f = 0.23f * MathHelper.lerp(partialTicks, this.mc.player.prevTimeInPortal, this.mc.player.timeInPortal) * this.mc.gameSettings.screenEffectScale * this.mc.gameSettings.screenEffectScale;
if (f > 0.0F) {
int i = this.mc.player.isPotionActive(Effects.NAUSEA) ? 7 : 20;
float f1 = 5.0F / (f * f + 5.0F) - f * 0.04F;
f1 = f1 * f1;
Vector3f vector3f = new Vector3f(0.0F, MathHelper.SQRT_2 / 2.0F, MathHelper.SQRT_2 / 2.0F);
matrixStack.rotate(vector3f.rotationDegrees((mc.gameRenderer.rendererUpdateCount + partialTicks) * i));
matrixStack.scale(1.01F / f1, 1.0F, 1.0F);
float f2 = -(mc.gameRenderer.rendererUpdateCount + partialTicks) * i;
matrixStack.rotate(vector3f.rotationDegrees(f2));
}
return matrixStack.getLast().getMatrix();
}
/**
* This is where the actual drawing happens.
*
* @param buffers the buffers sent to the GPU to draw
*/
private void sendLodsToGpuAndDraw(VertexBuffer vbo, Matrix4f modelViewMatrix)
{
if (vbo == null)
return;
profiler.endStartSection("LOD draw setup");
vbo.bindBuffer();
// 0L is the starting pointer, and the value doesn't appear to matter
LOD_VERTEX_FORMAT.setupBufferState(0L);
profiler.endStartSection("LOD draw");
vbo.draw(modelViewMatrix, GL11.GL_QUADS);
profiler.endStartSection("LOD draw cleanup");
VertexBuffer.unbindBuffer();
LOD_VERTEX_FORMAT.clearBufferState();
}
//=================//
// Setup Functions //
//=================//
@SuppressWarnings("deprecation")
private void setupFog(FogDistance fogDistance, FogQuality fogQuality)
{
if(fogQuality == FogQuality.OFF)
{
FogRenderer.resetFog();
RenderSystem.disableFog();
return;
}
if(fogDistance == FogDistance.NEAR_AND_FAR)
{
throw new IllegalArgumentException("setupFog doesn't accept the NEAR_AND_FAR fog distance.");
}
// the multipliers are percentages
// of the regular view distance.
if(fogDistance == FogDistance.NEAR)
{
// 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)
{
RenderSystem.fogEnd(farPlaneDistance * 0.3f * LOD_CHUNK_DISTANCE_RADIUS);
RenderSystem.fogStart(farPlaneDistance * 0.35f * LOD_CHUNK_DISTANCE_RADIUS);
}
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.fogEnd(farPlaneDistance * 1.5f);
RenderSystem.fogStart(farPlaneDistance * 2.0f);
}
}
else if(fogDistance == FogDistance.FAR)
{
if (fogQuality == FogQuality.FANCY)
{
RenderSystem.fogStart(farPlaneDistance * 0.78f * LOD_CHUNK_DISTANCE_RADIUS);
RenderSystem.fogEnd(farPlaneDistance * 1.0f * LOD_CHUNK_DISTANCE_RADIUS);
}
else if(fogQuality == FogQuality.FAST)
{
RenderSystem.fogStart(farPlaneDistance * 0.5f * LOD_CHUNK_DISTANCE_RADIUS);
RenderSystem.fogEnd(farPlaneDistance * 0.75f * LOD_CHUNK_DISTANCE_RADIUS);
}
}
RenderSystem.fogMode(GlStateManager.FogMode.LINEAR);
RenderSystem.enableFog();
}
/**
* create a new projection matrix and send it over to the GPU
* @param partialTicks how many ticks into the frame we are
*/
private void setupProjectionMatrix(float partialTicks)
{
Matrix4f projectionMatrix =
Matrix4f.perspective(
getFov(partialTicks, true),
(float)this.mc.getMainWindow().getFramebufferWidth() / (float)this.mc.getMainWindow().getFramebufferHeight(),
0.5F,
this.farPlaneDistance * LOD_CHUNK_DISTANCE_RADIUS * 2);
gameRender.resetProjectionMatrix(projectionMatrix);
return;
}
/**
* setup the lighting to be used for the LODs
*/
private void setupLighting(float partialTicks)
{
float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.world.getSunBrightness(partialTicks) : 0.2f;
float gammaMultiplyer = (float)mc.gameSettings.gamma - 0.5f;
float lightStrength = sunBrightness - 0.7f + (gammaMultiplyer * 0.2f);
float lightAmbient[] = {lightStrength, lightStrength, lightStrength, 1.0f};
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.
*/
private void setupBuffers(int numbChunksWide)
{
// calculate the max amount of storage needed (in bytes)
int bufferMaxCapacity = (numbChunksWide * numbChunksWide * (6 * 4 * (3 + 4)));
// (numbChunksWide * numbChunksWide *
// (sidesOnACube * pointsInASquare * (positionPoints + colorPoints)))
// TODO complain or do something when memory is too low
// currently the VM will just crash and complain there is no more memory
// issue #4
drawableNearBuffer = new BufferBuilder(bufferMaxCapacity);
drawableFarBuffer = new BufferBuilder(bufferMaxCapacity);
buildableNearBuffer = new BufferBuilder(bufferMaxCapacity);
buildableFarBuffer = new BufferBuilder(bufferMaxCapacity);
}
//======================//
// Other Misc Functions //
//======================//
/**
* @Returns -1 if there are no valid points
*/
private int getValidHeightPoint(short[] heightPoints)
{
if (heightPoints[LodCorner.NE.value] != -1)
return heightPoints[LodCorner.NE.value];
if (heightPoints[LodCorner.NW.value] != -1)
return heightPoints[LodCorner.NW.value];
if (heightPoints[LodCorner.SE.value] != -1)
return heightPoints[LodCorner.NE.value];
return heightPoints[LodCorner.NE.value];
}
/**
* Create a thread to asynchronously generate LOD buffers
* centered around the given camera X and Z.
* <br>
* This thread will write to the drawableNearBuffers and drawableFarBuffers.
* <br>
* After the buildable buffers have been generated they must be
* swapped with the drawable buffers to be drawn.
*/
private void generateLodBuffersAsync(double playerX, double playerZ,
int numbChunksWide)
{
// this is where we store the points for each LOD object
AxisAlignedBB lodArray[][] = new AxisAlignedBB[numbChunksWide][numbChunksWide];
// this is where we store the color for each LOD object
Color colorArray[][] = new Color[numbChunksWide][numbChunksWide];
int alpha = 255; // 0 - 255
Color red = new Color(255, 0, 0, alpha);
Color black = new Color(0, 0, 0, alpha);
Color white = new Color(255, 255, 255, alpha);
@SuppressWarnings("unused")
Color invisible = new Color(0,0,0,0);
@SuppressWarnings("unused")
Color error = new Color(255, 0, 225, alpha); // bright pink
// this seemingly useless math is required,
// just using (int) camera doesn't work
int playerXChunkOffset = ((int) playerX / LodChunk.WIDTH) * LodChunk.WIDTH;
int playerZChunkOffset = ((int) playerZ / LodChunk.WIDTH) * LodChunk.WIDTH;
// this is where we will start drawing squares
// (exactly half the total width)
int startX = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerXChunkOffset;
int startZ = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerZChunkOffset;
Thread t = new Thread(()->
{
// x axis
for (int i = 0; i < numbChunksWide; i++)
{
// z axis
for (int j = 0; j < numbChunksWide; j++)
{
// skip the middle
// (As the player moves some chunks will overlap or be missing,
// this is just how chunk loading/unloading works. This can hopefully
// be hidden with careful use of fog)
int middle = (numbChunksWide / 2);
if (isCoordInCenterArea(i, j, middle))
{
continue;
}
// set where this square will be drawn in the world
double xOffset = (LodChunk.WIDTH * i) + // offset by the number of LOD blocks
startX; // offset so the center LOD block is centered underneath the player
double yOffset = 0;
double zOffset = (LodChunk.WIDTH * j) + startZ;
int chunkX = i + (startX / LodChunk.WIDTH);
int chunkZ = j + (startZ / LodChunk.WIDTH);
LodChunk lod = lodDimension.getLodFromCoordinates(chunkX, chunkZ);
if (lod == null)
{
// note: for some reason if any color or lod objects are set here
// it causes the game to use 100% gpu;
// undefined in the debug menu
// and drop to ~6 fps.
colorArray[i][j] = null;
lodArray[i][j] = null;
// This is something I would like to have done someday
// but currently world generation is too slow to have it
// here, maybe it could be put in a loop
// that happens every tick for a specific number of chunks?
// LodChunk tmpLod = new LodChunk();
// tmpLod.x = chunkX;
// tmpLod.z = chunkZ;
// lodDimension.addLod(tmpLod);
//
// world.getChunkProvider().getChunk(chunkX, chunkZ, true);
// System.out.println(chunkX + "," + chunkZ);
continue;
}
Color c = new Color(
(lod.colors[ColorDirection.TOP.value].getRed()),
(lod.colors[ColorDirection.TOP.value].getGreen()),
(lod.colors[ColorDirection.TOP.value].getBlue()),
lod.colors[ColorDirection.TOP.value].getAlpha());
if (!debugging)
{
// add the color to the array
colorArray[i][j] = c;
}
else
{
// if debugging draw the squares as a black and white checker board
if ((chunkX + chunkZ) % 2 == 0)
c = white;
else
c = black;
// draw the first square as red
if (i == 0 && j == 0)
c = red;
colorArray[i][j] = c;
}
// add the new box to the array
int topPoint = getValidHeightPoint(lod.top);
int bottomPoint = getValidHeightPoint(lod.bottom);
// don't draw an LOD if it is empty
if (topPoint == -1 && bottomPoint == -1)
continue;
lodArray[i][j] = new AxisAlignedBB(0, bottomPoint, 0, LodChunk.WIDTH, topPoint, LodChunk.WIDTH).offset(xOffset, yOffset, zOffset);
}
}
// generate our new buildable buffers
NearFarBuffer nearFarBuffers = lodBufferBuilder.createBuffers(
buildableNearBuffer, buildableFarBuffer,
LodConfig.CLIENT.fogDistance.get(), lodArray, colorArray);
// update our buffers
buildableNearBuffer = nearFarBuffers.nearBuffer;
buildableFarBuffer = nearFarBuffers.farBuffer;
// mark the buildable buffers as ready to swap
regenerating = false;
switchBuffers = true;
});
genThread.execute(t);
return;
}
/**
* Swap buildable and drawable buffers.
*/
private void swapBuffers()
{
// swap the BufferBuilders
BufferBuilder tmp = buildableNearBuffer;
buildableNearBuffer = drawableNearBuffer;
drawableNearBuffer = tmp;
tmp = buildableFarBuffer;
buildableFarBuffer = drawableFarBuffer;
drawableFarBuffer = tmp;
// bind the buffers with their respective VBOs
if (nearVbo != null)
nearVbo.close();
nearVbo = new VertexBuffer(LOD_VERTEX_FORMAT);
nearVbo.upload(drawableNearBuffer);
if (farVbo != null)
farVbo.close();
farVbo = new VertexBuffer(LOD_VERTEX_FORMAT);
farVbo.upload(drawableFarBuffer);
}
/**
* Returns if the given coordinate is in the loaded area of the world.
* @param centerCoordinate the center of the loaded world
*/
private boolean isCoordInCenterArea(int i, int j, int centerCoordinate)
{
return (i >= centerCoordinate - mc.gameSettings.renderDistanceChunks
&& i <= centerCoordinate + mc.gameSettings.renderDistanceChunks)
&&
(j >= centerCoordinate - mc.gameSettings.renderDistanceChunks
&& j <= centerCoordinate + mc.gameSettings.renderDistanceChunks);
}
public double getFov(float partialTicks, boolean useFovSetting)
{
return mc.gameRenderer.getFOVModifier(mc.gameRenderer.getActiveRenderInfo(), partialTicks, useFovSetting);
}
/**
* Based on the fogDistance setting and
* optifine's fogQuality setting return what fog
* settings should be used when rendering.
*/
private NearFarFogSetting determineFogSettings()
{
NearFarFogSetting fogSetting = new NearFarFogSetting();
LodConfig.CLIENT.fogDistance.get();
switch(reflectionHandler.getFogQuality())
{
case FANCY:
switch(LodConfig.CLIENT.fogDistance.get())
{
case NEAR_AND_FAR:
fogSetting.nearFogSetting = FogDistance.NEAR;
fogSetting.farFogSetting = FogDistance.FAR;
break;
case NEAR:
fogSetting.nearFogSetting = FogDistance.NEAR;
fogSetting.farFogSetting = FogDistance.NEAR;
break;
case FAR:
fogSetting.nearFogSetting = FogDistance.FAR;
fogSetting.farFogSetting = FogDistance.FAR;
break;
}
break;
case 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.fogDistance.get())
{
case NEAR_AND_FAR:
fogSetting.nearFogSetting = FogDistance.NEAR;
fogSetting.farFogSetting = FogDistance.NEAR;
break;
case NEAR:
fogSetting.nearFogSetting = FogDistance.NEAR;
fogSetting.farFogSetting = FogDistance.NEAR;
break;
case FAR:
fogSetting.nearFogSetting = FogDistance.FAR;
fogSetting.farFogSetting = FogDistance.FAR;
break;
}
break;
case OFF:
break;
}
return fogSetting;
}
}
@@ -1,82 +0,0 @@
package com.backsun.lod.renderer;
import org.lwjgl.opengl.GL11;
import net.minecraft.client.renderer.RenderType;
/**
* This code is used for drawing
* to the stencil buffer.
*
* @author James Seibel
* @version 02-17-2021
*/
public class RenderGlobalHook
{
/**
* this variable should be the same as the method name below.
* It is used when transforming the RenderGlobal class'
* renderBlockLayer method.
*/
public static final String START_STENCIL_METHOD_NAME = "startRenderingStencil";
/**
* This method tells OpenGL to start drawing everything to the stencil.
* This is done to prevent LODs from being rendered on top of the world.
* <br><br>
* Called in the order (as of minecraft 1.16.4): <br>
* RenderType.getSolid() <br>
* RenderType.getCutoutMipped() <br>
* RenderType.getCutout() <br>
* RenderType.getTranslucent() <br>
* RenderType.getTripwire() <br>
*/
public static void startRenderingStencil(RenderType blockLayerIn)
{
// we only enable drawing to the stencil once since
// we want to skip the rendering of the out of world fog
// but catch everything else
if (blockLayerIn == RenderType.getSolid())
{
// solid is the first layer rendered
// clear the buffer so we can start fresh.
// if this isn't cleared first we will have overlap with the fog
// outside the world
GL11.glClearStencil(0);
GL11.glStencilMask(0x11111111);
GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT);
GL11.glEnable(GL11.GL_STENCIL_TEST);
GL11.glStencilFunc(GL11.GL_ALWAYS, 1, 0x11111111);
GL11.glStencilMask(0b11111111);
GL11.glStencilOp(GL11.GL_KEEP, // this doesn't mater since GL_ALWAYS is being used
GL11.GL_KEEP, // stencil test passes
GL11.GL_REPLACE); // stencil + depth pass
}
}
/**
* this variable should be the same as the method name below.
* It is used when transforming the RenderGlobal class'
* renderBlockLayer method.
*/
public static final String END_STENCIL_METHOD_NAME = "endRenderingStencil";
/**
* Currently this method isn't used in any transformations since we end
* the stencil drawing in the ClientProxy right before we draw the LODs.
*/
public static void endRenderingStencil(RenderType blockLayerIn)
{
GL11.glStencilOp(GL11.GL_KEEP, // this doesn't mater since GL_ALWAYS is being used
GL11.GL_KEEP, // stencil test passes
GL11.GL_KEEP); // stencil + depth pass
}
public static void endRenderingStencil()
{
endRenderingStencil(null);
}
}
@@ -1,45 +0,0 @@
package com.backsun.lod.renderer;
import net.minecraft.client.Minecraft;
/**
* This holds miscellaneous helper code
* to be used in the rendering process.
*
* @author James Seibel
* @version 2-13-2021
*/
public class RenderUtil
{
/**
* 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 i, int j, int centerCoordinate)
{
Minecraft mc = Minecraft.getInstance();
return (i >= centerCoordinate - mc.gameSettings.renderDistanceChunks
&& i <= centerCoordinate + mc.gameSettings.renderDistanceChunks)
&&
(j >= centerCoordinate - mc.gameSettings.renderDistanceChunks
&& j <= centerCoordinate + mc.gameSettings.renderDistanceChunks);
}
/**
* 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);
}
}
@@ -1,105 +0,0 @@
package com.backsun.lod.util;
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.backsun.lod.ModInfo;
import com.backsun.lod.util.enums.FogDistance;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.electronwill.nightconfig.core.io.WritingMode;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
/**
*
* @author James Seibel
* @version 02-27-2021
*/
@Mod.EventBusSubscriber
public class LodConfig
{
public static class Client
{
public ForgeConfigSpec.BooleanValue drawLODs;
public ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
public ForgeConfigSpec.BooleanValue drawCheckerBoard;
Client(ForgeConfigSpec.Builder builder)
{
builder.comment(ModInfo.MODNAME + " configuration settings").push("client");
drawLODs = builder
.comment("If false LODs will not be drawn, \n"
+ "however they will still be generated \n"
+ "and saved to file for later use.")
.define("drawLODs", true);
fogDistance = builder
.comment("\n"
+ "At what distance should Fog be drawn on the LODs? \n"
+ "If fog cuts off ubruptly or you are using Optifine's \"fast\" \n"
+ "fog option set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ".")
.defineEnum("fogDistance", FogDistance.NEAR_AND_FAR);
drawCheckerBoard = builder
.comment("\n"
+ "If false the LODs will draw with their normal world colors. \n"
+ "If true they will draw as a black and white checkerboard. \n"
+ "This can be used for debugging or imagining you are playing a \n"
+ "giant game of chess ;)")
.define("drawCheckerBoard", false);
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 clientSpec;
public static final Client CLIENT;
static {
final Pair<Client, ForgeConfigSpec> specPair = new ForgeConfigSpec.Builder().configure(Client::new);
clientSpec = specPair.getRight();
CLIENT = specPair.getLeft();
// setup the config file
CommentedFileConfig config = CommentedFileConfig.builder(CONFIG_PATH)
.writingMode(WritingMode.REPLACE)
.build();
config.load();
config.save();
clientSpec.setConfig(config);
}
@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,63 +0,0 @@
package com.backsun.lod.util;
import net.minecraft.client.Minecraft;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerWorld;
/**
* This class holds methods that may be used in multiple places.
*
* @author James Seibel
* @version 02-26-2021
*/
public class LodUtils
{
private static Minecraft mc = Minecraft.getInstance();
/**
* Gets the first valid ServerWorld.
*
* @return null if there are no ServerWorlds
*/
public static ServerWorld getFirstValidServerWorld()
{
if (mc.getIntegratedServer() == null)
return null;
Iterable<ServerWorld> worlds = mc.getIntegratedServer().getWorlds();
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.getIntegratedServer();
if (server == null)
return null;
Iterable<ServerWorld> worlds = server.getWorlds();
ServerWorld returnWorld = null;
for (ServerWorld world : worlds)
{
if(world.getDimensionType() == dimension)
{
returnWorld = world;
break;
}
}
return returnWorld;
}
}
@@ -1,26 +0,0 @@
package com.backsun.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,96 +0,0 @@
package com.backsun.lod.util;
import java.lang.reflect.Field;
import com.backsun.lod.util.enums.FogQuality;
import net.minecraft.client.Minecraft;
/**
* This object is used to get variables from methods
* where they are private.
*
* @author James Seibel
* @version 02-18-2021
*/
public class ReflectionHandler
{
private Minecraft mc = Minecraft.getInstance();
public Field ofFogField = null;
public ReflectionHandler()
{
setupFogField();
}
/**
* Similar to setupFovMethod.
*/
private void setupFogField()
{
// get every variable from the entity renderer
Field[] vars = mc.gameSettings.getClass().getDeclaredFields();
// try and find the ofFogType variable in gameSettings
for(Field f : vars)
{
if(f.getName().equals("ofFogType"))
{
ofFogField = f;
return;
}
}
// we didn't find the field,
// either optifine isn't installed, or
// optifine changed the name of the variable
ofFogField = null;
}
/**
* 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.OFF;
}
int returnNum = 0;
try
{
returnNum = (int)ofFogField.get(mc.gameSettings);
}
catch (IllegalArgumentException | IllegalAccessException e)
{
e.printStackTrace();
}
switch (returnNum)
{
case 0:
return FogQuality.FAST;
case 1:
return FogQuality.FAST;
case 2:
return FogQuality.FANCY;
case 3:
return FogQuality.OFF;
default:
return FogQuality.FAST;
}
}
}
@@ -1,34 +0,0 @@
package com.backsun.lod.util.enums;
/**
* TOP, N, S, E, W, BOTTOM
*
* @author James Seibel
* @version 10-17-2020
*/
public enum ColorDirection
{
// used for colors
/** +Y */
TOP(0),
/** -Z */
N(1),
/** +Z */
S(2),
/** +X */
E(3),
/** -X */
W(4),
/** -Y */
BOTTOM(5);
public final int value;
private ColorDirection(int newValue)
{
value = newValue;
}
}
@@ -1,32 +0,0 @@
package com.backsun.lod.util.enums;
/**
* @author James Seibel
* @version 1-23-2021
*/
public enum DrawMode
{
/** Draw the LOD objects in groups.
* <br>
* <br>
* Fancy fog: render the center and outside LOD
* objects in 2 different groups.
* <br>
* Fast fog: render all LOD objects at one time.
*/
BATCH(0),
/** Draw each LOD objects separately.
* <br>
* <br>
* Not suggested normally since draw calls are GPU expensive.
*/
INDIVIDUAL(5);
public final int value;
private DrawMode(int newValue)
{
value = newValue;
}
}
@@ -1,17 +0,0 @@
package com.backsun.lod.util.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,14 +0,0 @@
package com.backsun.lod.util.enums;
/**
* fast, fancy, or off
*
* @author James Seibel
* @version 02-14-2021
*/
public enum FogQuality
{
FAST,
FANCY,
OFF;
}
@@ -1,28 +0,0 @@
package com.backsun.lod.util.enums;
/**
* NE, SE, SW, NW
*
* @author James Seibel
* @version 1-20-2020
*/
public enum LodCorner
{
// used for position
/** -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,29 +0,0 @@
package com.backsun.lod.util.enums;
/**
*
* @author James Seibel
* @version 1-20-2020
*
* NE, SE, SW, NW
*/
public enum LodLocation
{
// used for position
/** -Z, +X */
NE(0),
/** +Z, +X */
SE(1),
/** +Z, -X */
SW(2),
/** -Z, -X */
NW(3);
public final int value;
private LodLocation(int newValue)
{
value = newValue;
}
}
+82
View File
@@ -0,0 +1,82 @@
/*
* 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;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.proxy.ClientProxy;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
/**
* 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
*/
@Mod(ModInfo.ID)
public class LodMain
{
public static LodMain instance;
public static ClientProxy client_proxy;
private void init(final FMLCommonSetupEvent event)
{
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, LodConfig.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
}
}
+35
View File
@@ -0,0 +1,35 @@
/*
* 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;
/**
* This file is similar to mcmod.info
* @author James Seibel
* @version 10-23-2021
*/
public final class ModInfo
{
public static final String ID = "lod";
public static final String NAME = "DistantHorizons";
/** Human readable version of MOD_NAME */
public static final String READABLE_NAME = "Distant Horizons";
public static final String API = "LodAPI";
public static final String VERSION = "a1.5.1";
}
@@ -0,0 +1,992 @@
/*
* 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.builders.bufferBuilding;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
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 org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL45;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.lod.builders.bufferBuilding.lodTemplates.Box;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.GlProxyContext;
import com.seibel.lod.enums.GpuUploadMethod;
import com.seibel.lod.enums.VanillaOverdraw;
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.proxy.GlProxy;
import com.seibel.lod.render.LodRenderer;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LevelPosUtil;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.util.ThreadMapUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.LightType;
/**
* This object is used to create NearFarBuffer objects.
* @author James Seibel
* @version 10-23-2021
*/
public class LodBufferBuilder
{
private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/** The thread used to generate new LODs off the main thread. */
public static final ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(LodBufferBuilder.class.getSimpleName() + " - main"));
/** The threads used to generate buffers. */
public static final ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.advancedModOptions.threading.numberOfBufferBuilderThreads.get(), new ThreadFactoryBuilder().setNameFormat("Buffer-Builder-%d").build());
/**
* When uploading to a buffer that is too small,
* recreate it this many times bigger than the upload payload
*/
public static final double BUFFER_EXPANSION_MULTIPLIER = 1.5;
/**
* When buffers are first created they are allocated to this size (in Bytes).
* This size will be too small, more than likely. The buffers will be expanded
* when need be to fit the larger sizes.
*/
public static final int DEFAULT_MEMORY_ALLOCATION = 1024;
public static int skyLightPlayer = 15;
/**
* How many buffers there are for the given region. <Br>
* This is done because some regions may require more memory than
* can be directly allocated, so we split the regions into smaller sections. <Br>
* This keeps track of those sections.
*/
public volatile int[][] numberOfBuffersPerRegion;
/** Stores the vertices when building the VBOs */
public volatile BufferBuilder[][][] buildableBuffers;
/** The OpenGL IDs of the storage buffers used by the buildableVbos */
public int[][][] buildableStorageBufferIds;
/** The OpenGL IDs of the storage buffers used by the drawableVbos */
public int[][][] drawableStorageBufferIds;
/** 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 final ReentrantLock bufferLock = new ReentrantLock();
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;
Thread thread = new Thread(() ->
generateLodBuffersThread(renderer, lodDim, playerBlockPos, fullRegen));
mainGenThread.execute(thread);
}
// this was pulled out as a separate method so that it could be
// more easily edited by hot swapping. Because, As far as James is aware
// you can't hot swap lambda expressions.
private void generateLodBuffersThread(LodRenderer renderer, LodDimension lodDim,
BlockPos playerBlockPos, boolean fullRegen)
{
bufferLock.lock();
try
{
// round the player's block position down to the nearest chunk BlockPos
ChunkPos playerChunkPos = new ChunkPos(playerBlockPos);
BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition();
long startTime = System.currentTimeMillis();
ArrayList<Callable<Boolean>> nodeToRenderThreads = new ArrayList<>(lodDim.getWidth() * lodDim.getWidth());
startBuffers(fullRegen, lodDim);
RegionPos playerRegionPos = new RegionPos(playerChunkPos);
if (center == null)
center = playerRegionPos;
if (setsToRender == null)
setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()];
if (setsToRender.length != lodDim.getWidth())
setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()];
if (boxCache == null)
boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()];
if (boxCache.length != lodDim.getWidth())
boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()];
// this will be the center of the VBOs once they have been built
buildableCenterChunkPos = playerChunkPos;
//================================//
// create the nodeToRenderThreads //
//================================//
ClientWorld world = mc.getClientWorld();
skyLightPlayer = world.getBrightness(LightType.SKY, playerBlockPos);
for (int xRegion = 0; xRegion < lodDim.getWidth(); xRegion++)
{
for (int zRegion = 0; zRegion < lodDim.getWidth(); zRegion++)
{
if (lodDim.doesRegionNeedBufferRegen(xRegion, zRegion) || fullRegen)
{
RegionPos regionPos = new RegionPos(
xRegion + lodDim.getCenterRegionPosX() - Math.floorDiv(lodDim.getWidth(), 2),
zRegion + lodDim.getCenterRegionPosZ() - Math.floorDiv(lodDim.getWidth(), 2));
// local position in the vbo and bufferBuilder arrays
BufferBuilder[] currentBuffers = buildableBuffers[xRegion][zRegion];
LodRegion region = lodDim.getRegion(regionPos.x, regionPos.z);
if (region == null)
continue;
// make sure the buffers weren't
// changed while we were running this method
if (currentBuffers == null || !currentBuffers[0].building())
return;
byte minDetail = region.getMinDetailLevel();
final int xR = xRegion;
final int zR = zRegion;
//we create the Callable to use for the buffer builder creation
Callable<Boolean> dataToRenderThread = () ->
{
//Variable initialization
byte detailLevel;
int posX;
int posZ;
int xAdj;
int zAdj;
int bufferIndex;
boolean posNotInPlayerChunk;
boolean adjPosInPlayerChunk;
Box box = ThreadMapUtil.getBox();
boolean[] adjShadeDisabled = ThreadMapUtil.getAdjShadeDisabledArray();
// determine how many LODs we can stack vertically
int maxVerticalData = DetailDistanceUtil.getMaxVerticalData((byte) 0);
//we get or create the map that will contain the adj data
Map<Direction, long[]> adjData = ThreadMapUtil.getAdjDataArray(maxVerticalData);
//previous setToRender cache
if (setsToRender[xR][zR] == null)
setsToRender[xR][zR] = new PosToRenderContainer(minDetail, regionPos.x, regionPos.z);
//We ask the lod dimension which block we have to render given the player position
PosToRenderContainer posToRender = setsToRender[xR][zR];
posToRender.clear(minDetail, regionPos.x, regionPos.z);
lodDim.getPosToRender(
posToRender,
regionPos,
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ());
// keep a local version, so we don't have to worry about indexOutOfBounds Exceptions
// if it changes in the LodRenderer while we are working here
boolean[][] vanillaRenderedChunks = renderer.vanillaRenderedChunks;
short gameChunkRenderDistance = (short) (vanillaRenderedChunks.length / 2 - 1);
for (int index = 0; index < posToRender.getNumberOfPos(); index++)
{
bufferIndex = Math.floorMod(index, currentBuffers.length);
detailLevel = posToRender.getNthDetailLevel(index);
posX = posToRender.getNthPosX(index);
posZ = posToRender.getNthPosZ(index);
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.x;
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.z;
//We don't want to render this fake block if
//The block is inside the render distance with, is not bigger than a chunk and is positioned in a chunk set as vanilla rendered
//
//The block is in the player chunk or in a chunk adjacent to the player
if(isThisPositionGoingToBeRendered(detailLevel, posX, posZ, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance))
{
continue;
}
//we check if the block to render is not in player chunk
posNotInPlayerChunk = !(chunkXdist == 0 && chunkZdist == 0);
// We extract the adj data in the four cardinal direction
// we first reset the adjShadeDisabled. This is used to disable the shade on the border when we have transparent block like water or glass
// to avoid having a "darker border" underground
Arrays.fill(adjShadeDisabled, false);
//We check every adj block in each direction
for (Direction direction : Box.ADJ_DIRECTIONS)
{
xAdj = posX + Box.DIRECTION_NORMAL_MAP.get(direction).getX();
zAdj = posZ + Box.DIRECTION_NORMAL_MAP.get(direction).getZ();
long data;
chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkPos.x;
chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkPos.z;
adjPosInPlayerChunk = (chunkXdist == 0 && chunkZdist == 0);
//If the adj block is rendered in the same region and with same detail
// and is positioned in a place that is not going to be rendered by vanilla game
// then we can set this position as adj
// We avoid cases where the adjPosition is in player chunk while the position is not
// to always have a wall underwater
if(posToRender.contains(detailLevel, xAdj, zAdj)
&& !isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance)
&& !(posNotInPlayerChunk && adjPosInPlayerChunk))
{
for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, xAdj, zAdj); verticalIndex++)
{
data = lodDim.getData(detailLevel, xAdj, zAdj, verticalIndex);
adjShadeDisabled[Box.DIRECTION_INDEX.get(direction)] = false;
adjData.get(direction)[verticalIndex] = data;
}
}
else
{
//Other wise we check if this position is
data = lodDim.getSingleData(detailLevel, xAdj, zAdj);
adjData.get(direction)[0] = DataPointUtil.EMPTY_DATA;
if ((isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance) || (posNotInPlayerChunk && adjPosInPlayerChunk))
&& !DataPointUtil.isVoid(data))
{
adjShadeDisabled[Box.DIRECTION_INDEX.get(direction)] = DataPointUtil.getAlpha(data) < 255;
}
}
}
// We render every vertical lod present in this position
// We only stop when we find a block that is void or non existing block
long data;
for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ); verticalIndex++)
{
//we get the above block as adj UP
if (verticalIndex > 0)
{
adjData.get(Direction.UP)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex - 1);
}
else
{
adjData.get(Direction.UP)[0] = DataPointUtil.EMPTY_DATA;
}
//we get the below block as adj DOWN
if (verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ) - 1)
{
adjData.get(Direction.DOWN)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex + 1);
}
else
{
adjData.get(Direction.DOWN)[0] = DataPointUtil.EMPTY_DATA;
}
//We extract the data to render
data = lodDim.getData(detailLevel, posX, posZ, verticalIndex);
//If the data is not renderable (Void or non existing) we stop since there is no data left in this position
if (DataPointUtil.isVoid(data) || !DataPointUtil.doesItExist(data))
break;
//We send the call to create the vertices
LodConfig.CLIENT.graphics.advancedGraphicsOption.lodTemplate.get().template.addLodToBuffer(currentBuffers[bufferIndex], playerBlockPosRounded, data, adjData,
detailLevel, posX, posZ, box, renderer.previousDebugMode, renderer.lightMap, adjShadeDisabled);
}
} // for pos to in list to render
// the thread executed successfully
return true;
};
nodeToRenderThreads.add(dataToRenderThread);
}
} // region z
} // region z
long executeStart = 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.");
break;
}
}
long executeEnd = System.currentTimeMillis();
long endTime = System.currentTimeMillis();
@SuppressWarnings("unused")
long buildTime = endTime - startTime;
@SuppressWarnings("unused")
long executeTime = executeEnd - executeStart;
// ClientProxy.LOGGER.info("Thread Build time: " + buildTime + " ms" + '\n' +
// "thread execute time: " + executeTime + " 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
{
// clean up any potentially open resources
if (buildableBuffers != null)
closeBuffers(fullRegen, lodDim);
try
{
// upload the new buffers
uploadBuffers(fullRegen, lodDim);
}
catch (Exception e)
{
ClientProxy.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" was unable to upload the buffers to the GPU: " + e.getMessage());
e.printStackTrace();
}
// regardless of whether we were able to successfully create
// the buffers, we are done generating.
generatingBuffers = false;
bufferLock.unlock();
}
}
private boolean isThisPositionGoingToBeRendered(byte detailLevel, int posX, int posZ, ChunkPos playerChunkPos, boolean[][] vanillaRenderedChunks, int gameChunkRenderDistance){
// skip any chunks that Minecraft is going to render
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.x;
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.z;
// check if the chunk is on the border
boolean isItBorderPos;
if (LodConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.get() == VanillaOverdraw.BORDER)
isItBorderPos = LodUtil.isBorderChunk(vanillaRenderedChunks, chunkXdist + gameChunkRenderDistance + 1, chunkZdist + gameChunkRenderDistance + 1);
else
isItBorderPos = false;
//boolean smallRenderDistance = gameChunkRenderDistance <= LodUtil.MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW;
// get the positions that will be rendered
boolean vanillaRenderedPosition = gameChunkRenderDistance >= Math.abs(chunkXdist)
&& gameChunkRenderDistance >= Math.abs(chunkZdist)
&& detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL
&& vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1];
return (vanillaRenderedPosition && (!(isItBorderPos)));
}
//===============================//
// 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(LodDimension lodDimension)
{
GlProxy glProxy = GlProxy.getInstance();
bufferLock.lock();
int numbRegionsWide = lodDimension.getWidth();
long regionMemoryRequired;
int numberOfBuffers;
previousRegionWidth = numbRegionsWide;
numberOfBuffersPerRegion = new int[numbRegionsWide][numbRegionsWide];
buildableBuffers = new BufferBuilder[numbRegionsWide][numbRegionsWide][];
buildableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide][];
drawableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide][];
if (glProxy.bufferStorageSupported)
{
buildableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][];
drawableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][];
}
for (int x = 0; x < numbRegionsWide; x++)
{
for (int z = 0; z < numbRegionsWide; z++)
{
regionMemoryRequired = DEFAULT_MEMORY_ALLOCATION;
// if the memory required is greater than the max buffer
// capacity, divide the memory across multiple buffers
if (regionMemoryRequired > LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY)
{
numberOfBuffers = (int) Math.ceil(regionMemoryRequired / LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY) + 1;
// TODO shouldn't this be determined with regionMemoryRequired?
// always allocating the max memory is a bit expensive isn't it?
regionMemoryRequired = LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY;
numberOfBuffersPerRegion[x][z] = numberOfBuffers;
buildableBuffers[x][z] = new BufferBuilder[numberOfBuffers];
buildableVbos[x][z] = new VertexBuffer[numberOfBuffers];
drawableVbos[x][z] = new VertexBuffer[numberOfBuffers];
if (glProxy.bufferStorageSupported)
{
buildableStorageBufferIds[x][z] = new int[numberOfBuffers];
drawableStorageBufferIds[x][z] = new int[numberOfBuffers];
}
}
else
{
// we only need one buffer for this region
numberOfBuffersPerRegion[x][z] = 1;
buildableBuffers[x][z] = new BufferBuilder[1];
buildableVbos[x][z] = new VertexBuffer[1];
drawableVbos[x][z] = new VertexBuffer[1];
if (glProxy.bufferStorageSupported)
{
buildableStorageBufferIds[x][z] = new int[1];
drawableStorageBufferIds[x][z] = new int[1];
}
}
for (int i = 0; i < numberOfBuffersPerRegion[x][z]; i++)
{
buildableBuffers[x][z][i] = new BufferBuilder((int) regionMemoryRequired);
buildableVbos[x][z][i] = new VertexBuffer(LodUtil.LOD_VERTEX_FORMAT);
drawableVbos[x][z][i] = new VertexBuffer(LodUtil.LOD_VERTEX_FORMAT);
// create the initial mapped buffers (system memory)
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableVbos[x][z][i].id);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.GL_DYNAMIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableVbos[x][z][i].id);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.GL_DYNAMIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
if (glProxy.bufferStorageSupported)
{
// create the buffer storage (GPU memory)
buildableStorageBufferIds[x][z][i] = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableStorageBufferIds[x][z][i]);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0); // the 0 flag means to create the storage in the GPUs memory
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
drawableStorageBufferIds[x][z][i] = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableStorageBufferIds[x][z][i]);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
}
}
bufferLock.unlock();
}
/**
* Sets the buffers and Vbos to null, forcing them to be recreated <br>
* and destroys any bound OpenGL objects. <br><br>
* <p>
* May have to wait for the bufferLock to open.
*/
public void destroyBuffers()
{
bufferLock.lock();
// destroy the buffer storages if they aren't already
if (buildableStorageBufferIds != null)
{
for (int x = 0; x < buildableStorageBufferIds.length; x++)
{
for (int z = 0; z < buildableStorageBufferIds.length; z++)
{
for (int i = 0; i < buildableStorageBufferIds[x][z].length; i++)
{
int buildableId = buildableStorageBufferIds[x][z][i];
int drawableId = drawableStorageBufferIds[x][z][i];
// Send this over to the render thread, if this is being
// called we aren't worried about stuttering anyway.
// This way we don't have to worry about what context this
// was called from (if any).
RenderSystem.recordRenderCall(() ->
{
GL15.glDeleteBuffers(buildableId);
GL15.glDeleteBuffers(drawableId);
});
}
}
}
}
buildableStorageBufferIds = null;
drawableStorageBufferIds = null;
// destroy the VBOs if they aren't already
if (buildableVbos != null)
{
for (int i = 0; i < buildableVbos.length; i++)
{
for (int j = 0; j < buildableVbos.length; j++)
{
for (int k = 0; k < buildableVbos[i][j].length; k++)
{
int buildableId;
int drawableId;
// variables passed into a lambda expression
// need to be effectively final, so we have
// to use an else statement here
if (buildableVbos[i][j][k] != null)
buildableId = buildableVbos[i][j][k].id;
else
buildableId = 0;
if (drawableVbos[i][j][k] != null)
drawableId = drawableVbos[i][j][k].id;
else
drawableId = 0;
RenderSystem.recordRenderCall(() ->
{
if (buildableId != 0)
GL15.glDeleteBuffers(buildableId);
if (drawableId != 0)
GL15.glDeleteBuffers(drawableId);
});
}
}
}
}
buildableVbos = null;
drawableVbos = null;
// these don't contain any OpenGL objects, so
// they don't require any special clean-up
buildableBuffers = 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.doesRegionNeedBufferRegen(x, z))
{
for (int i = 0; i < buildableBuffers[x][z].length; i++)
{
// for some reason BufferBuilder.vertexCounts
// isn't reset unless this is called, which can cause
// a false indexOutOfBoundsException
buildableBuffers[x][z][i].discard();
buildableBuffers[x][z][i].begin(GL11.GL_QUADS, LodUtil.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++)
for (int i = 0; i < buildableBuffers[x][z].length; i++)
if (buildableBuffers[x][z][i] != null && buildableBuffers[x][z][i].building() && (fullRegen || lodDim.doesRegionNeedBufferRegen(x, z)))
buildableBuffers[x][z][i].end();
}
/** Upload all buildableBuffers to the GPU. */
private void uploadBuffers(boolean fullRegen, LodDimension lodDim)
{
GlProxy glProxy = GlProxy.getInstance();
try
{
// make sure we are uploading to the builder context,
// this helps prevent interference (IE stuttering) with the Minecraft context.
glProxy.setGlContext(GlProxyContext.LOD_BUILDER);
// determine the upload method
GpuUploadMethod uploadMethod = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get();
if (!glProxy.bufferStorageSupported && uploadMethod == GpuUploadMethod.BUFFER_STORAGE)
{
// if buffer storage isn't supported
// default to SUB_DATA
LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(GpuUploadMethod.SUB_DATA);
uploadMethod = GpuUploadMethod.SUB_DATA;
}
// actually upload the buffers
for (int x = 0; x < buildableVbos.length; x++)
{
for (int z = 0; z < buildableVbos.length; z++)
{
if (fullRegen || lodDim.doesRegionNeedBufferRegen(x, z))
{
for (int i = 0; i < buildableBuffers[x][z].length; i++)
{
ByteBuffer uploadBuffer = buildableBuffers[x][z][i].popNextBuffer().getSecond();
vboUpload(buildableVbos[x][z][i], buildableStorageBufferIds[x][z][i], uploadBuffer, true, uploadMethod);
lodDim.setRegenRegionBufferByArrayIndex(x, z, false);
}
}
}
}
}
catch (Exception e)
{
// this doesn't appear to be necessary anymore, but just in case.
ClientProxy.LOGGER.error(LodBufferBuilder.class.getSimpleName() + " - UploadBuffers failed: " + e.getMessage());
e.printStackTrace();
}
finally
{
GL11.glFinish();
// close the context so it can be re-used later.
// I'm guessing we can't just leave it because the executor service
// does something that invalidates the OpenGL context.
glProxy.setGlContext(GlProxyContext.NONE);
}
}
/** Uploads the uploadBuffer so the GPU can use it.
* @param uploadMethod */
private void vboUpload(VertexBuffer vbo, int storageBufferId, ByteBuffer uploadBuffer,
boolean allowBufferExpansion, GpuUploadMethod uploadMethod)
{
// this shouldn't happen, but just to be safe
if (vbo.id != -1 && GlProxy.getInstance().getGlContext() == GlProxyContext.LOD_BUILDER)
{
// this is how many points will be rendered
vbo.vertexCount = (uploadBuffer.capacity() / vbo.format.getVertexSize());
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id);
try
{
// if possible use the faster buffer storage route
if (uploadMethod == GpuUploadMethod.BUFFER_STORAGE)
{
// get a pointer to the buffer in system memory
ByteBuffer vboBuffer = GL30.glMapBufferRange(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT);
if (vboBuffer == null)
{
int previousCapacity = uploadBuffer.capacity();
// only expand the buffers if the uploadBuffer actually
// has something in it and expansion is allowed
if (previousCapacity != 0 && allowBufferExpansion)
{
// the buffer(s) aren't big enough, expand them.
// This does cause lag/stuttering, so it should be avoided!
// expand the buffer in system memory
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW);
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer);
// un-bind the system memory buffer
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
// expand the buffer storage
GL15.glDeleteBuffers(storageBufferId);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, storageBufferId);
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
// recursively try to upload into the newly created buffer storage
// but don't recurse again if that fails
// (we don't want an infinitely expanding buffer!)
vboUpload(vbo, storageBufferId, uploadBuffer, false, uploadMethod);
}
}
else
{
// upload the buffer into system memory...
vboBuffer.put(uploadBuffer);
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
// ...then upload into GPU memory
// (uploading into GPU memory directly can only be done
// through the glCopyBufferSubData/glCopyNamed... methods)
GL45.glCopyNamedBufferSubData(vbo.id, storageBufferId, 0, 0, uploadBuffer.capacity());
}
}
else if (uploadMethod == GpuUploadMethod.BUFFER_MAPPING)
{
// no stuttering but high GPU usage
// stores everything in system memory instead of GPU memory
// making rendering much slower.
// Unless the user is running integrated graphics,
// in that case this will actually work better than SUB_DATA.
ByteBuffer vboBuffer = null;
// map buffer range is better since it can be explicitly unsynchronized
if (GlProxy.getInstance().mapBufferRangeSupported)
vboBuffer = GL30.glMapBufferRange(GL30.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT);
else
vboBuffer = GL15.glMapBuffer(GL30.GL_ARRAY_BUFFER, uploadBuffer.capacity());
if (vboBuffer == null)
{
GL15.glBufferData(GL45.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW);
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer);
}
else
{
vboBuffer.put(uploadBuffer);
}
}
else
{
// hybrid subData/bufferData //
// less stutter, low GPU usage
//long size = GL31.glGetBufferParameteri64(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE); // hopefully just a int should be long enough
long size = GL15.glGetBufferParameteri(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
if (size < uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER)
{
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW);
}
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer);
}
}
catch (Exception e)
{
ClientProxy.LOGGER.error("vboUpload failed: " + e.getClass().getSimpleName());
e.printStackTrace();
}
finally
{
if (uploadMethod == GpuUploadMethod.BUFFER_MAPPING)
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}//if vbo exists and in correct GL context
}//vboUpload
/** 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[][][] tmpVbo = drawableVbos;
drawableVbos = buildableVbos;
buildableVbos = tmpVbo;
int[][][] tmpStorage = drawableStorageBufferIds;
drawableStorageBufferIds = buildableStorageBufferIds;
buildableStorageBufferIds = tmpStorage;
drawableCenterChunkPos = buildableCenterChunkPos;
// the vbos have been swapped
switchVbos = false;
bufferLock.unlock();
}
return new VertexBuffersAndOffset(drawableVbos, drawableStorageBufferIds, drawableCenterChunkPos);
}
/** A simple container to pass multiple objects back in the getVertexBuffers method. */
public static class VertexBuffersAndOffset
{
public final VertexBuffer[][][] vbos;
public final int[][][] storageBufferIds;
public final ChunkPos drawableCenterChunkPos;
public VertexBuffersAndOffset(VertexBuffer[][][] newVbos, int[][][] newStorageBufferIds, ChunkPos newDrawableCenterChunkPos)
{
vbos = newVbos;
storageBufferIds = newStorageBufferIds;
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 newBuffersAvailable()
{
return switchVbos;
}
}
@@ -0,0 +1,54 @@
/*
* 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.builders.bufferBuilding.lodTemplates;
import java.util.Map;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.util.ColorUtil;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
/**
* This is the abstract class used to create different
* BufferBuilders.
* @author James Seibel
* @version 10-10-2021
*/
public abstract class AbstractLodTemplate
{
/** Uploads the given LOD to the buffer. */
public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap, boolean[] adjShadeDisabled);
/** add the given position and color to the buffer */
protected void addPosAndColor(BufferBuilder buffer,
double x, double y, double z,
int color)
{
// TODO re-add transparency by replacing the 255 with "ColorUtil.getAlpha(color)"
buffer.vertex(x, y, z).color(ColorUtil.getRed(color), ColorUtil.getGreen(color), ColorUtil.getBlue(color), 255).endVertex();
}
}
@@ -0,0 +1,593 @@
/*
* 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.builders.bufferBuilding.lodTemplates;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3i;
/**
* Similar to Minecraft's AxisAlignedBoundingBox.
* @author Leonardo Amato
* @version 10-2-2021
*/
public class Box
{
public static final int ADJACENT_HEIGHT_INDEX = 0;
public static final int ADJACENT_DEPTH_INDEX = 1;
public static final int X = 0;
public static final int Y = 1;
public static final int Z = 2;
public static final int MIN = 0;
public static final int MAX = 1;
public static final int VOID_FACE = 0;
/** The six cardinal directions */
public static final Direction[] DIRECTIONS = new Direction[] {
Direction.UP,
Direction.DOWN,
Direction.WEST,
Direction.EAST,
Direction.NORTH,
Direction.SOUTH };
/** North, South, East, West */
public static final Direction[] ADJ_DIRECTIONS = new Direction[] {
Direction.EAST,
Direction.WEST,
Direction.SOUTH,
Direction.NORTH };
/** All the faces and vertices of a cube. This is used to extract the vertex from the column */
public static final Map<Direction, int[][]> DIRECTION_VERTEX_MAP = new HashMap<Direction, int[][]>()
{{
put(Direction.UP, new int[][] {
{ 0, 1, 0 },
{ 0, 1, 1 },
{ 1, 1, 1 },
{ 1, 1, 0 } });
put(Direction.DOWN, new int[][] {
{ 1, 0, 0 },
{ 1, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 0 } });
put(Direction.EAST, new int[][] {
{ 1, 1, 0 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 0, 0 } });
put(Direction.WEST, new int[][] {
{ 0, 0, 0 },
{ 0, 0, 1 },
{ 0, 1, 1 },
{ 0, 1, 0 } });
put(Direction.SOUTH, new int[][] {
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 1, 1 },
{ 0, 0, 1 } });
put(Direction.NORTH, new int[][] {
{ 0, 0, 0 },
{ 0, 1, 0 },
{ 1, 1, 0 },
{ 1, 0, 0 } });
}};
/**
* This indicates which position is invariable in the DIRECTION_VERTEX_MAP.
* Is used to extract the vertex
*/
public static final Map<Direction, int[]> FACE_DIRECTION = new HashMap<Direction, int[]>()
{{
put(Direction.UP, new int[] { Y, MAX });
put(Direction.DOWN, new int[] { Y, MIN });
put(Direction.EAST, new int[] { X, MAX });
put(Direction.WEST, new int[] { X, MIN });
put(Direction.SOUTH, new int[] { Z, MAX });
put(Direction.NORTH, new int[] { Z, MIN });
}};
/**
* This is a map from Direction to the relative normal vector
* we are using this since I'm not sure if the getNormal create new object at every call
*/
public static final Map<Direction, Vector3i> DIRECTION_NORMAL_MAP = new HashMap<Direction, Vector3i>()
{{
put(Direction.UP, Direction.UP.getNormal());
put(Direction.DOWN, Direction.DOWN.getNormal());
put(Direction.EAST, Direction.EAST.getNormal());
put(Direction.WEST, Direction.WEST.getNormal());
put(Direction.SOUTH, Direction.SOUTH.getNormal());
put(Direction.NORTH, Direction.NORTH.getNormal());
}};
/** We use this index for all array that are going to */
public static final Map<Direction, Integer> DIRECTION_INDEX = new HashMap<Direction, Integer>()
{{
put(Direction.UP, 0);
put(Direction.DOWN, 1);
put(Direction.EAST, 2);
put(Direction.WEST, 3);
put(Direction.SOUTH, 4);
put(Direction.NORTH, 5);
}};
public static final Map<Direction, Integer> ADJ_DIRECTION_INDEX = new HashMap<Direction, Integer>()
{{
put(Direction.EAST, 0);
put(Direction.WEST, 1);
put(Direction.SOUTH, 2);
put(Direction.NORTH, 3);
}};
/** holds the box's x, y, z offset */
public final int[] boxOffset;
/** holds the box's x, y, z width */
public final int[] boxWidth;
/** Holds each direction's color */
public final int[] colorMap;
/** The original color (before shading) of this box */
public int color;
/**
*
*/
public final Map<Direction, int[]> adjHeight;
public final Map<Direction, int[]> adjDepth;
public final Map<Direction, byte[]> skyLights;
public byte blockLight;
/** Holds if the given direction should be culled or not */
public final boolean[] culling;
/** creates an empty box */
public Box()
{
boxOffset = new int[3];
boxWidth = new int[3];
colorMap = new int[6];
skyLights = new HashMap<Direction, byte[]>()
{{
put(Direction.UP, new byte[1]);
put(Direction.DOWN, new byte[1]);
put(Direction.EAST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(Direction.WEST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(Direction.SOUTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(Direction.NORTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
}};
adjHeight = new HashMap<Direction, int[]>()
{{
put(Direction.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(Direction.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(Direction.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(Direction.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
}};
adjDepth = new HashMap<Direction, int[]>()
{{
put(Direction.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(Direction.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(Direction.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(Direction.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
}};
culling = new boolean[6];
}
/** Set the light of the columns */
public void setLights(int skyLight, int blockLight)
{
this.blockLight = (byte) blockLight;
skyLights.get(Direction.UP)[0] = (byte) skyLight;
}
/**
* Set the color of the columns
* @param color color to add
* @param adjShadeDisabled this array indicates which face does not need shading
*/
public void setColor(int color, boolean[] adjShadeDisabled)
{
this.color = color;
for (Direction direction : DIRECTIONS)
{
if (!adjShadeDisabled[DIRECTION_INDEX.get(direction)])
colorMap[DIRECTION_INDEX.get(direction)] = ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientWorld().getShade(direction, true));
else
colorMap[DIRECTION_INDEX.get(direction)] = color;
}
}
/**
* @param direction of the face of which we want to get the color
* @return color of the face
*/
public int getColor(Direction direction)
{
if (LodConfig.CLIENT.advancedModOptions.debugging.debugMode.get() != DebugMode.SHOW_DETAIL)
return colorMap[DIRECTION_INDEX.get(direction)];
else
return ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientWorld().getShade(direction, true));
}
/**
*/
public byte getSkyLight(Direction direction, int verticalIndex)
{
if(direction == Direction.UP || direction == Direction.DOWN)
return skyLights.get(direction)[0];
else
return skyLights.get(direction)[verticalIndex];
}
/**
*/
public int getBlockLight()
{
return blockLight;
}
/** clears this box, resetting everything to default values */
public void reset()
{
Arrays.fill(boxWidth, 0);
Arrays.fill(boxOffset, 0);
Arrays.fill(colorMap, 0);
blockLight = 0;
for (Direction direction : ADJ_DIRECTIONS)
{
for (int i = 0; i < adjHeight.get(direction).length; i++)
{
adjHeight.get(direction)[i] = VOID_FACE;
adjDepth.get(direction)[i] = VOID_FACE;
skyLights.get(direction)[i] = 0;
}
}
}
/** determine which faces should be culled */
public void setUpCulling(int cullingDistance, BlockPos playerPos)
{
for (Direction direction : DIRECTIONS)
{
if (direction == Direction.DOWN || direction == Direction.WEST || direction == Direction.NORTH)
culling[DIRECTION_INDEX.get(direction)] = playerPos.get(direction.getAxis()) > getFacePos(direction) + cullingDistance;
else if (direction == Direction.UP || direction == Direction.EAST || direction == Direction.SOUTH)
culling[DIRECTION_INDEX.get(direction)] = playerPos.get(direction.getAxis()) < getFacePos(direction) - cullingDistance;
culling[DIRECTION_INDEX.get(direction)] = false;
}
}
/**
* @param direction direction that we want to check if it's culled
* @return true if and only if the face of the direction is culled
*/
public boolean isCulled(Direction direction)
{
return culling[DIRECTION_INDEX.get(direction)];
}
/**
* This method create all the shared face culling based on the adjacent data
* @param adjData data adjacent to the column we are going to render
*/
public void setAdjData(Map<Direction, long[]> adjData)
{
int height;
int depth;
int minY = getMinY();
int maxY = getMaxY();
long singleAdjDataPoint;
/* TODO implement attached vertical face culling
//Up direction case
if(DataPointUtil.doesItExist(adjData.get(Direction.UP)))
{
height = DataPointUtil.getHeight(singleAdjDataPoint);
depth = DataPointUtil.getDepth(singleAdjDataPoint);
}*/
//Down direction case
singleAdjDataPoint = adjData.get(Direction.DOWN)[0];
if(DataPointUtil.doesItExist(singleAdjDataPoint))
skyLights.get(Direction.DOWN)[0] = (byte) DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
else
skyLights.get(Direction.DOWN)[0] = skyLights.get(Direction.UP)[0];
//other sided
//TODO clean some similar cases
for (Direction direction : ADJ_DIRECTIONS)
{
if (isCulled(direction))
continue;
long[] dataPoint = adjData.get(direction);
if (dataPoint == null || DataPointUtil.isVoid(dataPoint[0]))
{
adjHeight.get(direction)[0] = maxY;
adjDepth.get(direction)[0] = minY;
adjHeight.get(direction)[1] = VOID_FACE;
adjDepth.get(direction)[1] = VOID_FACE;
skyLights.get(direction)[0] = 15; //in void set full sky light
continue;
}
int i;
int faceToDraw = 0;
boolean firstFace = true;
boolean toFinish = false;
int toFinishIndex = 0;
boolean allAbove = true;
for (i = 0; i < dataPoint.length; i++)
{
singleAdjDataPoint = dataPoint[i];
if (DataPointUtil.isVoid(singleAdjDataPoint) || !DataPointUtil.doesItExist(singleAdjDataPoint))
break;
height = DataPointUtil.getHeight(singleAdjDataPoint);
depth = DataPointUtil.getDepth(singleAdjDataPoint);
if (depth <= maxY)
{
allAbove = false;
if (height < minY)
{
// the adj data is lower than the current data
if (firstFace)
{
adjHeight.get(direction)[0] = getMaxY();
adjDepth.get(direction)[0] = getMinY();
skyLights.get(direction)[0] = skyLights.get(Direction.UP)[0];
}
else
{
adjDepth.get(direction)[faceToDraw] = getMinY();
skyLights.get(direction)[faceToDraw] = (byte) DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
}
faceToDraw++;
toFinish = false;
// break since all the other data will be lower
break;
}
else if (depth <= minY && height >= maxY)
{
// the adj data is inside the current data
// don't draw the face
adjHeight.get(direction)[0] = VOID_FACE;
adjDepth.get(direction)[0] = VOID_FACE;
break;
}
else if (depth <= minY)//&& height < maxY
{
// the adj data intersects the lower part of the current data
// if this is the only face, use the maxY and break,
// if there was another face we finish the last one and break
if (firstFace)
{
adjHeight.get(direction)[0] = getMaxY();
adjDepth.get(direction)[0] = height;
skyLights.get(direction)[0] = skyLights.get(Direction.UP)[0];
}
else
{
adjDepth.get(direction)[faceToDraw] = height;
skyLights.get(direction)[faceToDraw] = (byte) DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
}
toFinish = false;
faceToDraw++;
break;
}
else if (height >= maxY)//depth > minY &&
{
// the adj data intersects the higher part of the current data
// we start the creation of a new face
adjHeight.get(direction)[faceToDraw] = depth;
//skyLights.get(direction)[faceToDraw] = (byte) DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
firstFace = false;
toFinish = true;
toFinishIndex = i + 1;
}
else
{
// if (depth > minY && height < maxY)
// the adj data is contained in the current data
if (firstFace)
{
adjHeight.get(direction)[0] = getMaxY();
}
adjDepth.get(direction)[faceToDraw] = height;
skyLights.get(direction)[faceToDraw] = (byte) DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
faceToDraw++;
adjHeight.get(direction)[faceToDraw] = depth;
firstFace = false;
toFinish = true;
toFinishIndex = i + 1;
}
}
}
if (allAbove)
{
adjHeight.get(direction)[0] = getMaxY();
adjDepth.get(direction)[0] = getMinY();
skyLights.get(direction)[0] = skyLights.get(Direction.UP)[0];
faceToDraw++;
}
else if (toFinish)
{
adjDepth.get(direction)[faceToDraw] = minY;
if(toFinishIndex < dataPoint.length)
{
singleAdjDataPoint = dataPoint[toFinishIndex];
if (DataPointUtil.doesItExist(singleAdjDataPoint))
skyLights.get(direction)[faceToDraw] = (byte) DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
else
skyLights.get(direction)[faceToDraw] = skyLights.get(Direction.UP)[0];
}
faceToDraw++;
}
adjHeight.get(direction)[faceToDraw] = VOID_FACE;
adjDepth.get(direction)[faceToDraw] = VOID_FACE;
}
}
/** We use this method to set the various width of the column */
public void setWidth(int xWidth, int yWidth, int zWidth)
{
boxWidth[X] = xWidth;
boxWidth[Y] = yWidth;
boxWidth[Z] = zWidth;
}
/** We use this method to set the various offset of the column */
public void setOffset(int xOffset, int yOffset, int zOffset)
{
boxOffset[X] = xOffset;
boxOffset[Y] = yOffset;
boxOffset[Z] = zOffset;
}
/**
* This method return the position of a face in the axis of the face
* This is useful for the face culling
* @param direction that we want to check
* @return position in the axis of the face
*/
public int getFacePos(Direction direction)
{
return boxOffset[FACE_DIRECTION.get(direction)[0]] + boxWidth[FACE_DIRECTION.get(direction)[0]] * FACE_DIRECTION.get(direction)[1];
}
/**
* returns true if the given direction should be rendered.
*/
public boolean shouldRenderFace(Direction direction, int adjIndex)
{
if (direction == Direction.UP || direction == Direction.DOWN)
return adjIndex == 0;
return !(adjHeight.get(direction)[adjIndex] == VOID_FACE && adjDepth.get(direction)[adjIndex] == VOID_FACE);
}
/**
* @param direction direction of the face we want to render
* @param vertexIndex index of the vertex of the face (0-1-2-3)
* @return position x of the relative vertex
*/
public int getX(Direction direction, int vertexIndex)
{
return boxOffset[X] + boxWidth[X] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][X];
}
/**
* @param direction direction of the face we want to render
* @param vertexIndex index of the vertex of the face (0-1-2-3)
* @return position y of the relative vertex
*/
public int getY(Direction direction, int vertexIndex)
{
return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Y];
}
/**
* @param direction direction of the face we want to render
* @param vertexIndex index of the vertex of the face (0-1-2-3)
* @param adjIndex, index of the n-th culled face of this direction
* @return position x of the relative vertex
*/
public int getY(Direction direction, int vertexIndex, int adjIndex)
{
if (direction == Direction.DOWN || direction == Direction.UP)
return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Y];
else
{
// this could probably be cleaned up more,
// but it still works
if (1 - DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Y] == ADJACENT_HEIGHT_INDEX)
return adjHeight.get(direction)[adjIndex];
else
return adjDepth.get(direction)[adjIndex];
}
}
/**
* @param direction direction of the face we want to render
* @param vertexIndex index of the vertex of the face (0-1-2-3)
* @return position z of the relative vertex
*/
public int getZ(Direction direction, int vertexIndex)
{
return boxOffset[Z] + boxWidth[Z] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Z];
}
public int getMinX()
{
return boxOffset[X];
}
public int getMaxX()
{
return boxOffset[X] + boxWidth[X];
}
public int getMinY()
{
return boxOffset[Y];
}
public int getMaxY()
{
return boxOffset[Y] + boxWidth[Y];
}
public int getMinZ()
{
return boxOffset[Z];
}
public int getMaxZ()
{
return boxOffset[Z] + boxWidth[Z];
}
}
@@ -0,0 +1,143 @@
/*
* 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.builders.bufferBuilding.lodTemplates;
import java.util.Map;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
/**
* Builds LODs as rectangular prisms.
* @author James Seibel
* @version 10-10-2021
*/
public class CubicLodTemplate extends AbstractLodTemplate
{
public CubicLodTemplate()
{
}
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap, boolean[] adjShadeDisabled)
{
if (box == null)
return;
// equivalent to 2^detailLevel
int blockWidth = 1 << detailLevel;
int color;
if (debugging != DebugMode.OFF)
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB();
else
color = DataPointUtil.getColor(data);
generateBoundingBox(
box,
DataPointUtil.getHeight(data),
DataPointUtil.getDepth(data),
blockWidth,
posX * blockWidth, 0, posZ * blockWidth, // x, y, z offset
bufferCenterBlockPos,
adjData,
color,
DataPointUtil.getLightSkyAlt(data),
DataPointUtil.getLightBlock(data),
adjShadeDisabled);
addBoundingBoxToBuffer(buffer, box);
}
private void generateBoundingBox(Box box,
int height, int depth, int width,
double xOffset, double yOffset, double zOffset,
BlockPos bufferCenterBlockPos,
Map<Direction, long[]> adjData,
int color,
int skyLight,
int blockLight,
boolean[] adjShadeDisabled)
{
// 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 its 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.reset();
box.setColor(color, adjShadeDisabled);
box.setLights(skyLight, blockLight);
box.setWidth(width, height - depth, width);
box.setOffset((int) (xOffset + x), (int) (depth + yOffset), (int) (zOffset + z));
box.setUpCulling(32, bufferCenterBlockPos);
box.setAdjData(adjData);
}
private void addBoundingBoxToBuffer(BufferBuilder buffer, Box box)
{
int color;
int skyLight;
int blockLight;
for (Direction direction : Box.DIRECTIONS)
{
if(box.isCulled(direction))
continue;
int verticalFaceIndex = 0;
while (box.shouldRenderFace(direction, verticalFaceIndex))
{
for (int vertexIndex = 0; vertexIndex < 4; vertexIndex++)
{
color = box.getColor(direction);
skyLight = box.getSkyLight(direction, verticalFaceIndex);
blockLight = box.getBlockLight();
color = ColorUtil.applyLightValue(color, skyLight, blockLight, MinecraftWrapper.INSTANCE.getCurrentLightMap());
addPosAndColor(buffer,
box.getX(direction, vertexIndex),
box.getY(direction, vertexIndex, verticalFaceIndex),
box.getZ(direction, vertexIndex),
color);
}
verticalFaceIndex++;
}
}
}
}
@@ -0,0 +1,49 @@
/*
* 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.builders.bufferBuilding.lodTemplates;
import java.util.Map;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.proxy.ClientProxy;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.util.Direction;
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, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap, boolean[] adjShadeDisabled)
{
ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
}
}
@@ -0,0 +1,47 @@
/*
* 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.builders.bufferBuilding.lodTemplates;
import java.util.Map;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.proxy.ClientProxy;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.util.Direction;
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, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap, boolean[] adjShadeDisabled)
{
ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
}
}
@@ -0,0 +1,944 @@
/*
* 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.builders.lodBuilding;
import java.awt.Color;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
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.enums.HorizontalResolution;
import com.seibel.lod.enums.VerticalQuality;
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.DataPointUtil;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LevelPosUtil;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.util.ThreadMapUtil;
import com.seibel.lod.wrappers.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.ILiquidContainer;
import net.minecraft.block.IWaterLoggable;
import net.minecraft.block.LeavesBlock;
import net.minecraft.block.SixWayBlock;
import net.minecraft.block.TallGrassBlock;
import net.minecraft.block.material.MaterialColor;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.DimensionType;
import net.minecraft.world.IWorld;
import net.minecraft.world.LightType;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeColors;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.client.model.data.ModelDataMap;
/**
* This object is in charge of creating Lod related objects.
*
* @author Cola
* @author Leonardo Amato
* @author James Seibel
* @version 10-22-2021
*/
public class LodBuilder
{
private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
private final ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
public static final Direction[] directions = new Direction[] { Direction.UP, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.DOWN };
public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH;
public static final int CHUNK_SECTION_HEIGHT = CHUNK_DATA_WIDTH;
public static final ConcurrentMap<Block, Integer> colorMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, Integer> tintColor = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, Boolean> toTint = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, VoxelShape> shapeMap = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, Boolean> notFullBlock = new ConcurrentHashMap<>();
public static final ConcurrentMap<Block, Boolean> smallBlock = new ConcurrentHashMap<>();
public static final ModelDataMap dataMap = new ModelDataMap.Builder().build();
/** 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;
/** Minecraft's max light value */
public static final short DEFAULT_MAX_LIGHT = 15;
/**
* How wide LodDimensions should be in regions <br>
* Is automatically set before the first frame in ClientProxy.
*/
public int defaultDimensionWidthInRegions = 0;
//public static final boolean useExperimentalLighting = true;
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.getIsWorldNotLoaded())
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
{
// we need a loaded client world in order to
// get the textures for blocks
if (mc.getClientWorld() == null)
return;
// don't try to generate LODs if the user isn't in the world anymore
// (this happens a lot when the user leaves a world/server)
if (mc.getSinglePlayerServer() == null && mc.getCurrentServer() == null)
return;
DimensionType dim = world.dimensionType();
// make sure the dimension exists
LodDimension lodDim;
if (lodWorld.getLodDimension(dim) == null)
{
lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions);
lodWorld.addLodDimension(lodDim);
}
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);
}
/**
* Creates a LodNode 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 LodNode 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;
LodRegion region = lodDim.getRegion(chunk.getPos().getRegionX(), chunk.getPos().getRegionZ());
if (region == null)
return;
// determine how many LODs to generate horizontally
HorizontalResolution detail;
byte minDetailLevel = region.getMinDetailLevel();
detail = DetailDistanceUtil.getLodGenDetail(minDetailLevel);
// determine how many LODs to generate vertically
VerticalQuality verticalQuality = LodConfig.CLIENT.graphics.qualityOption.verticalQuality.get();
byte detailLevel = detail.detailLevel;
// generate the LODs
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];
posX = LevelPosUtil.convert((byte) 0, chunk.getPos().x * 16 + startX, detail.detailLevel);
posZ = LevelPosUtil.convert((byte) 0, chunk.getPos().z * 16 + startZ, detail.detailLevel);
long[] data;
long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ, endX, endZ);
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.worldHeight, DetailDistanceUtil.getMaxVerticalData(detailLevel));
//lodDim.clear(detailLevel, posX, posZ);
if (data != null && data.length != 0)
lodDim.addVerticalData(detailLevel, posX, posZ, data,false);
}
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, chunk.getPos().z);
}
/** creates a vertical DataPoint */
private long[] createVerticalDataToMerge(HorizontalResolution detail, IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX, int endZ)
{
// equivalent to 2^detailLevel
int size = 1 << detail.detailLevel;
long[] dataToMerge = ThreadMapUtil.getBuilderVerticalArray(detail.detailLevel);
int verticalData = DataPointUtil.worldHeight;
ChunkPos chunkPos = chunk.getPos();
int height;
int depth;
int color;
int light;
int lightSky;
int lightBlock;
int generation = config.distanceGenerationMode.complexity;
int xRel;
int zRel;
int xAbs;
int yAbs;
int zAbs;
boolean hasCeiling = mc.getClientWorld().dimensionType().hasCeiling();
boolean hasSkyLight = mc.getClientWorld().dimensionType().hasSkyLight();
boolean isDefault;
BlockPos.Mutable blockPos = new BlockPos.Mutable(0, 0, 0);
int index;
for (index = 0; index < size * size; index++)
{
xRel = Math.floorMod(index, size) + startX;
zRel = Math.floorDiv(index, size) + startZ;
xAbs = chunkPos.getMinBlockX() + xRel;
zAbs = chunkPos.getMinBlockZ() + zRel;
//Calculate the height of the lod
yAbs = DataPointUtil.worldHeight + 2;
int count = 0;
boolean topBlock = true;
while (yAbs > 0)
{
height = determineHeightPointFrom(chunk, config, xRel, zRel, yAbs, blockPos);
// If the lod is at the default height, it must be void data
if (height == DEFAULT_HEIGHT)
{
if (topBlock)
dataToMerge[index * verticalData] = DataPointUtil.createVoidDataPoint(generation);
break;
}
yAbs = height - 1;
// We search light on above air block
depth = determineBottomPointFrom(chunk, config, xRel, zRel, yAbs, blockPos);
if (hasCeiling && topBlock)
{
yAbs = depth;
blockPos.set(xAbs, yAbs, zAbs);
light = getLightValue(chunk, blockPos, hasCeiling, hasSkyLight, topBlock);
color = generateLodColor(chunk, config, xRel, yAbs, zRel, blockPos);
blockPos.set(xAbs, yAbs - 1, zAbs);
}
else
{
blockPos.set(xAbs, yAbs, zAbs);
light = getLightValue(chunk, blockPos, hasCeiling, hasSkyLight, topBlock);
color = generateLodColor(chunk, config, xRel, yAbs, zRel, blockPos);
blockPos.set(xAbs, yAbs + 1, zAbs);
}
lightBlock = light & 0b1111;
lightSky = (light >> 4) & 0b1111;
isDefault = ((light >> 8)) == 1;
dataToMerge[index * verticalData + count] = DataPointUtil.createDataPoint(height, depth, color, lightSky, lightBlock, generation, isDefault);
topBlock = false;
yAbs = depth - 1;
count++;
}
}
return dataToMerge;
}
/**
* Find the lowest valid point from the bottom.
* Used when creating a vertical LOD.
*/
private short determineBottomPointFrom(IChunk chunk, LodBuilderConfig config, int xRel, int zRel, int yAbs, BlockPos.Mutable blockPos)
{
short depth = DEFAULT_DEPTH;
/*if (config.useHeightmap)
{
// when using the generated heightmap there is no data about the lowest point
depth = 0; //DEFAULT_DEPTH == 0
}
else
{*/
boolean voidData = true;
ChunkSection[] chunkSections = chunk.getSections();
for (int sectionIndex = chunkSections.length - 1; sectionIndex >= 0; sectionIndex--)
{
for (int yRel = CHUNK_DATA_WIDTH - 1; yRel >= 0; yRel--)
{
if (sectionIndex * CHUNK_DATA_WIDTH + yRel > yAbs)
continue;
blockPos.set(chunk.getPos().getMinBlockX() + xRel, sectionIndex * CHUNK_DATA_WIDTH + yRel, chunk.getPos().getMinBlockZ() + zRel);
if (!isLayerValidLodPoint(chunk, blockPos))
{
depth = (short) (sectionIndex * CHUNK_DATA_WIDTH + yRel + 1);
voidData = false;
break;
}
}
if (!voidData)
break;
}
//}
return depth;
}
/** Find the highest valid point from the Top */
private short determineHeightPointFrom(IChunk chunk, LodBuilderConfig config, int xRel, int zRel, int yAbs, BlockPos.Mutable blockPos)
{
short height = DEFAULT_HEIGHT;
if (config.useHeightmap)
height = (short) chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(xRel, zRel);
else
{
boolean voidData = true;
ChunkSection[] chunkSections = chunk.getSections();
for (int sectionIndex = chunkSections.length - 1; sectionIndex >= 0; sectionIndex--)
{
for (int yRel = CHUNK_DATA_WIDTH - 1; yRel >= 0; yRel--)
{
if (sectionIndex * CHUNK_DATA_WIDTH + yRel > yAbs)
continue;
blockPos.set(chunk.getPos().getMinBlockX() + xRel, sectionIndex * CHUNK_DATA_WIDTH + yRel, chunk.getPos().getMinBlockZ() + zRel);
if (isLayerValidLodPoint(chunk, blockPos))
{
height = (short) (sectionIndex * CHUNK_DATA_WIDTH + yRel + 1);
voidData = false;
break;
}
}
if (!voidData)
break;
}
}
return height;
}
// =====================//
// constructor helpers //
// =====================//
/**
* Generate the color for the given chunk using biome water color, foliage
* color, and grass color.
*/
private int generateLodColor(IChunk chunk, LodBuilderConfig config, int xRel, int yAbs, int zRel, BlockPos.Mutable blockPos)
{
ChunkSection[] chunkSections = chunk.getSections();
int colorInt = 0;
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(xRel >> 2, yAbs >> 2, zRel >> 2);
colorInt = getColorForBiome(xRel, zRel, biome);
}
else
{
int sectionIndex = Math.floorDiv(yAbs, CHUNK_SECTION_HEIGHT);
int yRel = Math.floorMod(yAbs, CHUNK_SECTION_HEIGHT);
if (chunkSections[sectionIndex] != null)
{
blockPos.set(chunk.getPos().getMinBlockX() + xRel, sectionIndex * CHUNK_DATA_WIDTH + yRel, chunk.getPos().getMinBlockZ() + zRel);
colorInt = getColorForBlock(chunk, blockPos);
}
// if we are skipping non-full and non-solid blocks that means we ignore
// snow, flowers, etc. Get the above block so we can still get the color
// of the snow, flower, etc. that may be above this block
int aboveColorInt = 0;
if (LodConfig.CLIENT.worldGenerator.blockToAvoid.get().nonFull || LodConfig.CLIENT.worldGenerator.blockToAvoid.get().noCollision)
{
blockPos.set(chunk.getPos().getMinBlockX() + xRel, sectionIndex * CHUNK_DATA_WIDTH + yRel + 1, chunk.getPos().getMinBlockZ() + zRel);
aboveColorInt = getColorForBlock(chunk, blockPos);
}
if (colorInt == 0 && yAbs > 0)
// if this block is invisible, check the block below it
colorInt = generateLodColor(chunk, config, xRel, yAbs - 1, zRel, blockPos);
// override this block's color if there was a block above this
// and we were avoiding non-full/non-solid blocks
if (aboveColorInt != 0)
colorInt = aboveColorInt;
}
return colorInt;
}
/** Gets the light value for the given block position */
private int getLightValue(IChunk chunk, BlockPos.Mutable blockPos, boolean hasCeiling, boolean hasSkyLight, boolean topBlock)
{
int skyLight = 0;
int blockLight;
// 1 means the lighting is a guess
int isDefault = 0;
ClientWorld clientWorld = mc.getClientWorld();
if (clientWorld == null)
return 0;
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(clientWorld.dimensionType());
int blockBrightness = chunk.getLightEmission(blockPos);
// get the air block above or below this block
if (hasCeiling && topBlock)
blockPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ());
else
blockPos.set(blockPos.getX(), blockPos.getY() + 1, blockPos.getZ());
if (serverWorld != null)
{
// server world sky light (always accurate)
blockLight = serverWorld.getBrightness(LightType.BLOCK, blockPos);
if(topBlock && !hasCeiling && hasSkyLight)
skyLight = DEFAULT_MAX_LIGHT;
else
skyLight = serverWorld.getBrightness(LightType.SKY, blockPos);
if (!topBlock && skyLight == 15)
{
// we are on predicted terrain, and we don't know what the light here is,
// lets just take a guess
if (blockPos.getY() >= mc.getClientWorld().getSeaLevel() - 5)
{
skyLight = 12;
isDefault = 1;
}
else
skyLight = 0;
}
}
else
{
// client world sky light (almost never accurate)
blockLight = clientWorld.getBrightness(LightType.BLOCK, blockPos);
// estimate what the lighting should be
if (hasSkyLight || !hasCeiling)
{
if (topBlock)
skyLight = DEFAULT_MAX_LIGHT;
else
{
skyLight = clientWorld.getBrightness(LightType.SKY, blockPos);
if (!chunk.isLightCorrect() && (skyLight == 0 || skyLight == 15))
{
// we don't know what the light here is,
// lets just take a guess
if (blockPos.getY() >= mc.getClientWorld().getSeaLevel() - 5)
{
skyLight = 12;
isDefault = 1;
}
else
skyLight = 0;
}
}
}
}
blockLight = LodUtil.clamp(0, Math.max(blockLight, blockBrightness), DEFAULT_MAX_LIGHT);
return blockLight + (skyLight << 4) + (isDefault << 8);
}
/**
* Generate the color of the given block from its texture
* and store it for later use.
*/
private int getColorTextureForBlock(BlockState blockState, BlockPos blockPos, boolean useTopTexture)
{
// use the pre-generated color if we can
Block block = blockState.getBlock();
if (colorMap.containsKey(block) && toTint.containsKey(block))
return colorMap.get(block);
World world = mc.getClientWorld();
TextureAtlasSprite texture;
List<BakedQuad> quads = null;
int tintIndex = Integer.MIN_VALUE;
boolean isTinted = false;
int listSize = 0;
// get the first quad we can for this block
for (Direction direction : directions)
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(blockState).getQuads(blockState, direction, new Random(0), dataMap);
listSize = Math.max(listSize, quads.size());
for (BakedQuad bakedQuad : quads)
{
isTinted |= bakedQuad.isTinted();
tintIndex = Math.max(tintIndex, bakedQuad.getTintIndex());
}
}
toTint.put(block, isTinted);
tintColor.put(block, tintIndex);
for (Direction direction : directions)
{
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(blockState).getQuads(blockState, direction, new Random(0), dataMap);
if (!quads.isEmpty())
break;
}
if (useTopTexture && !quads.isEmpty())
texture = quads.get(0).getSprite();
else
texture = mc.getModelManager().getBlockModelShaper().getTexture(blockState, world, blockPos);
int count = 0;
int alpha = 0;
int red = 0;
int green = 0;
int blue = 0;
int numberOfGreyPixel = 0;
int color;
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.getHeight(); u++)
{
for (int v = 0; v < texture.getWidth(); v++)
{
if (texture.isTransparent(frameIndex, u, v))
continue;
color = texture.getPixelRGBA(frameIndex, u, v);
// determine if this pixel is gray
int colorMax = Math.max(Math.max(ColorUtil.getBlue(color), ColorUtil.getGreen(color)), ColorUtil.getRed(color));
int colorMin = 4 + Math.min(Math.min(ColorUtil.getBlue(color), ColorUtil.getGreen(color)), ColorUtil.getRed(color));
boolean isGray = colorMax < colorMin;
if (isGray)
numberOfGreyPixel++;
// for flowers, weight their non-green color higher
if (block instanceof FlowerBlock && (!(ColorUtil.getGreen(color) > (ColorUtil.getBlue(color) + 30)) || !(ColorUtil.getGreen(color) > (ColorUtil.getRed(color) + 30))))
colorMultiplier = 5;
else
colorMultiplier = 1;
// add to the running averages
count += colorMultiplier;
alpha += ColorUtil.getAlpha(color) * colorMultiplier;
red += ColorUtil.getBlue(color) * colorMultiplier;
green += ColorUtil.getGreen(color) * colorMultiplier;
blue += ColorUtil.getRed(color) * colorMultiplier;
}
}
}
if (count == 0)
// this block is entirely transparent
color = 0;
else
{
// determine the average color
alpha /= count;
red /= count;
green /= count;
blue /= count;
color = ColorUtil.rgbToInt(alpha, red, green, blue);
}
// determine if this block should use the biome color tint
if ((useGrassTint(block) || useLeafTint(block) || useWaterTint(block)) && (float) numberOfGreyPixel / count > 0.75f)
toTint.replace(block, true);
// add the newly generated block color to the map for later use
colorMap.put(block, color);
return color;
}
/** determine if the given block should use the biome's grass color */
private boolean useGrassTint(Block block)
{
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 useLeafTint(Block block)
{
return block instanceof LeavesBlock
|| block == Blocks.VINE
|| block == Blocks.SUGAR_CANE;
}
/** determine if the given block should use the biome's water color */
private boolean useWaterTint(Block block)
{
return block == Blocks.WATER;
}
/** Returns a color int for the given block. */
private int getColorForBlock(IChunk chunk, BlockPos blockPos)
{
int blockColor;
int colorInt;
int xRel = blockPos.getX() - chunk.getPos().getMinBlockX();
int zRel = blockPos.getZ() - chunk.getPos().getMinBlockZ();
int x = blockPos.getX();
int y = blockPos.getY();
int z = blockPos.getZ();
//Biome biome = chunk.getBiomes().getNoiseBiome(xRel >> 2, y >> 2, zRel >> 2);
BlockState blockState = chunk.getBlockState(blockPos);
if(isInWater(blockState))
blockState = Blocks.WATER.defaultBlockState();
// block special cases
// TODO: this needs to be replaced by a config file of some sort
if (blockState == Blocks.AIR.defaultBlockState()
|| blockState == Blocks.CAVE_AIR.defaultBlockState()
|| blockState == Blocks.BARRIER.defaultBlockState())
{
return 0;
}
blockColor = getColorTextureForBlock(blockState, blockPos, true);
//if the blockColor is 0 we reset it and don't use the faceColor
if (blockColor == 0)
{
tintColor.remove(blockState.getBlock());
toTint.remove(blockState.getBlock());
colorMap.remove(blockState.getBlock());
blockColor = getColorTextureForBlock(blockState, blockPos, false);
}
//if the blockColor is still 0 we use the default material color
if (blockColor == 0)
{
tintColor.replace(blockState.getBlock(), 0);
toTint.replace(blockState.getBlock(), false);
colorMap.replace(blockState.getBlock(), blockState.getBlock().defaultMaterialColor().col);
}
if (toTint.get(blockState.getBlock()))
{
Biome biome = chunk.getBiomes().getNoiseBiome(xRel >> 2, y >> 2, zRel >> 2);
ClientWorld clientWorld = mc.getClientWorld();
if (clientWorld == null)
return 0;
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(clientWorld.dimensionType());
int tintValue;
if (useGrassTint(blockState.getBlock()))
{
// grass and green plants
try
{
tintValue = BiomeColors.getAverageGrassColor(serverWorld, blockPos);
}
catch(NullPointerException e)
{
tintValue = biome.getGrassColor(x, z);
}
}
else if (useWaterTint(blockState.getBlock()))
{
// water
try
{
tintValue = BiomeColors.getAverageWaterColor(serverWorld, blockPos);
}
catch(NullPointerException e)
{
tintValue = biome.getWaterColor();
}
}
else
{
// leaves
try
{
tintValue = BiomeColors.getAverageFoliageColor(serverWorld, blockPos);
}
catch(NullPointerException e)
{
tintValue = biome.getFoliageColor();
}
}
colorInt = ColorUtil.multiplyRGBcolors(tintValue | 0xFF000000, blockColor);
}
else
colorInt = blockColor;
return colorInt;
}
/** Returns a color int for the given biome. */
private int getColorForBiome(int x, int z, Biome biome)
{
int colorInt;
switch (biome.getBiomeCategory())
{
case NETHER:
colorInt = Blocks.NETHERRACK.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;
}
private boolean isInWater(BlockState blockState)
{
//This type of block is always in water
if((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable))
return true;
//This type of block could be in water
if(blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).isPresent() && blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).get())
return true;
return false;
}
/** Is the block at the given blockPos a valid LOD point? */
private boolean isLayerValidLodPoint(IChunk chunk, BlockPos.Mutable blockPos)
{
BlockState blockState = chunk.getBlockState(blockPos);
if (isInWater(blockState))
return true;
boolean nonFullAvoidance = LodConfig.CLIENT.worldGenerator.blockToAvoid.get().nonFull;
boolean noCollisionAvoidance = LodConfig.CLIENT.worldGenerator.blockToAvoid.get().noCollision;
if (blockState != null)
{
if (nonFullAvoidance)
{
if(!blockState.getFluidState().isEmpty() || blockState.getBlock() instanceof SixWayBlock)
{
notFullBlock.put(blockState.getBlock(), false);
}
if (!notFullBlock.containsKey(blockState.getBlock()) || notFullBlock.get(blockState.getBlock()) == null)
{
VoxelShape voxelShape = blockState.getBlock().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);
if (xWidth < 1 && zWidth < 1 && yWidth < 1)
notFullBlock.put(blockState.getBlock(), true);
else
notFullBlock.put(blockState.getBlock(), false);
}
else
{
notFullBlock.put(blockState.getBlock(), false);
}
}
if (notFullBlock.get(blockState.getBlock()))
{
return false;
}
}
if (noCollisionAvoidance)
{
if(!blockState.getFluidState().isEmpty() || blockState.getBlock() instanceof SixWayBlock)
smallBlock.put(blockState.getBlock(), false);
if (!smallBlock.containsKey(blockState.getBlock()) || smallBlock.get(blockState.getBlock()) == null)
{
VoxelShape voxelShape = blockState.getCollisionShape(chunk, blockPos);
if (!blockState.getFluidState().isEmpty())
{
smallBlock.put(blockState.getBlock(), false);
}
else
{
if (voxelShape.isEmpty())
{
smallBlock.put(blockState.getBlock(), true);
}
else
{
smallBlock.put(blockState.getBlock(), false);
}
}
}
if (smallBlock.get(blockState.getBlock()))
{
return false;
}
}
return blockState.getBlock() != Blocks.AIR
&& blockState.getBlock() != Blocks.CAVE_AIR
&& blockState.getBlock() != Blocks.BARRIER;
}
return false;
}
}
@@ -0,0 +1,95 @@
/*
* 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.builders.lodBuilding;
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 an 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
*/
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, boolean newUseSolidBlocksInBiomeColor)
{
this();
useHeightmap = newUseHeightmap;
useBiomeColors = newUseBiomeColors;
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
distanceGenerationMode = newUseHeightmap ? DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT : DistanceGenerationMode.BIOME_ONLY;
}
/**
* @param newDistanceGenerationMode default = Server
*/
public LodBuilderConfig(DistanceGenerationMode newDistanceGenerationMode)
{
this();
distanceGenerationMode = newDistanceGenerationMode;
}
}
@@ -0,0 +1,713 @@
/*
* 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.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.google.common.util.concurrent.ThreadFactoryBuilder;
import com.seibel.lod.builders.lodBuilding.LodBuilder;
import com.seibel.lod.builders.lodBuilding.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.LodUtil;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
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.feature.template.TemplateManager;
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 10-22-2021
*/
public class LodGenWorker implements IWorker
{
public static ExecutorService genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.advancedModOptions.threading.numberOfWorldGenerationThreads.get(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
private boolean threadStarted = false;
private final 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 final ConcurrentHashMap<Integer, ConfiguredFeature<?, ?>> configuredFeaturesToAvoid = new ConcurrentHashMap<>();
public LodGenWorker(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(5);
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 static class LodChunkGenThread implements Runnable
{
public final ServerWorld serverWorld;
public final LodDimension lodDim;
public final DistanceGenerationMode generationMode;
public final LodBuilder lodBuilder;
private final 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', 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))
{
//
//{
// lodBuilder.generateLodNodeFromChunk(lodDim, loadedChunk, new LodBuilderConfig(DistanceGenerationMode.SERVER));
//}
//else
//{
/*
IChunk loadedChunk = null;
if (lodDim.isChunkPreGenerated(pos.x, pos.z) && LodConfig.CLIENT.worldGenerator.useExperimentalPreGenLoading.get())
{
// generate a Lod like normal
loadedChunk = ChunkLoader.getChunkFromFile(pos);
if(loadedChunk != null)
lodBuilder.generateLodNodeFromChunk(lodDim, loadedChunk, new LodBuilderConfig(DistanceGenerationMode.SERVER));
else
{
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
lodBuilder.generateLodNodeFromChunk(lodDim, serverWorld.getChunk(pos.x, pos.z, ChunkStatus.FEATURES), new LodBuilderConfig(DistanceGenerationMode.SERVER));
//generateWithServer();
break;
}
}
}
else
{*/
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
}
catch (Exception e)
{
ClientProxy.LOGGER.error(LodChunkGenThread.class.getSimpleName() + ": ran into an error: " + e.getMessage());
e.printStackTrace();
}
finally
{
// decrement how many threads are running
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.addAndGet(-1);
// this position is no longer being generated
LodWorldGenerator.INSTANCE.positionsWaitingToBeGenerated.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 awful.
//TODO it appears that 'if' can be collapsed, but comment says that it should not be a case
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();
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, 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
*/
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();
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 = 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 (List<Supplier<ConfiguredFeature<?, ?>>> suppliers : featuresForState)
{
for (Supplier<ConfiguredFeature<?, ?>> featureSupplier : suppliers)
{
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 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)
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
e.printStackTrace();
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
* <p>
* Note this should not be multithreaded and does cause server/simulation lag
* (Higher lag for generating than loading)
*/
private void generateWithServer()
{
lodBuilder.generateLodNodeFromChunk(lodDim, serverWorld.getChunk(pos.x, pos.z, ChunkStatus.FEATURES), new LodBuilderConfig(DistanceGenerationMode.SERVER));
}
//================//
// 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;
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("unknown 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();
provider.weightedList.entries.addAll(((WeightedBlockStateProvider) config.stateProvider).weightedList.entries);
HashSet<Block> whitelist = new HashSet<>(config.whitelist);
HashSet<BlockState> blacklist = new HashSet<>(config.blacklist);
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 Executor service. <br><br>
* <p>
* 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 restartExecutorService()
{
if (genThreads != null && !genThreads.isShutdown())
{
genThreads.shutdownNow();
}
genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.advancedModOptions.threading.numberOfWorldGenerationThreads.get(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
}
/*
* 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)
*/
}
@@ -0,0 +1,331 @@
/*
* 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.builders.worldGeneration;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.function.Predicate;
import java.util.stream.Stream;
import com.seibel.lod.util.LodUtil;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState;
import net.minecraft.particles.IParticleData;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.SectionPos;
import net.minecraft.util.registry.DynamicRegistries;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.DimensionType;
import net.minecraft.world.EmptyTickList;
import net.minecraft.world.ISeedReader;
import net.minecraft.world.ITickList;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeManager;
import net.minecraft.world.border.WorldBorder;
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;
import net.minecraft.world.server.ServerWorld;
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.
* @author James Seibel
* @version 7-26-2021
*/
public class LodServerWorld implements ISeedReader
{
public HashMap<Heightmap.Type, Heightmap> heightmaps = new HashMap<>();
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)
{
// 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);
}
@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)
{
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void addParticle(IParticleData particleData, double x, double y, double z, double xSpeed, double ySpeed,
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)
{
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,204 @@
/*
* 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.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.lodBuilding.LodBuilder;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
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.LevelPosUtil;
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 9-25-2021
*/
public class LodWorldGenerator
{
public final MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/** This holds the thread used to generate new LODs off the main thread. */
private final 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 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 final AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0);
public final Set<ChunkPos> positionsWaitingToBeGenerated = 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.advancedModOptions.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();
//=======================================//
// fill in positionsWaitingToBeGenerated //
//=======================================//
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate(
maxChunkGenRequests,
playerPosX,
playerPosZ);
byte detailLevel;
int posX;
int posZ;
int nearIndex = 0;
int farIndex = 0;
for (int i = 0; i < posToGenerate.getNumberOfPos(); i++)
{
// I wish there was a way to compress this code, but I'm not aware of
// an easy way to do so.
// add the near positions
if (posToGenerate.getNthDetail(nearIndex, true) != 0 && nearIndex < posToGenerate.getNumberOfNearPos())
{
detailLevel = (byte) (posToGenerate.getNthDetail(nearIndex, true) - 1);
posX = posToGenerate.getNthPosX(nearIndex, true);
posZ = posToGenerate.getNthPosZ(nearIndex, true);
nearIndex++;
ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
// prevent generating the same chunk multiple times
if (positionsWaitingToBeGenerated.contains(chunkPos))
continue;
// don't add more to the generation queue then allowed
if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
break;
positionsWaitingToBeGenerated.add(chunkPos);
numberOfChunksWaitingToGenerate.addAndGet(1);
LodGenWorker genWorker = new LodGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
WorldWorkerManager.addWorker(genWorker);
}
// add the far positions
if (posToGenerate.getNthDetail(farIndex, false) != 0 && farIndex < posToGenerate.getNumberOfFarPos())
{
detailLevel = (byte) (posToGenerate.getNthDetail(farIndex, false) - 1);
posX = posToGenerate.getNthPosX(farIndex, false);
posZ = posToGenerate.getNthPosZ(farIndex, false);
farIndex++;
ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
// don't add more to the generation queue then allowed
if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
continue;
//break;
// prevent generating the same chunk multiple times
if (positionsWaitingToBeGenerated.contains(chunkPos))
continue;
positionsWaitingToBeGenerated.add(chunkPos);
numberOfChunksWaitingToGenerate.addAndGet(1);
LodGenWorker genWorker = new LodGenWorker(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
} // queueGenerationRequests
}
@@ -0,0 +1,566 @@
/*
* 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.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.BlockToAvoid;
import com.seibel.lod.enums.BufferRebuildTimes;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.FogDistance;
import com.seibel.lod.enums.FogDrawOverride;
import com.seibel.lod.enums.GenerationPriority;
import com.seibel.lod.enums.GpuUploadMethod;
import com.seibel.lod.enums.HorizontalQuality;
import com.seibel.lod.enums.HorizontalResolution;
import com.seibel.lod.enums.HorizontalScale;
import com.seibel.lod.enums.LodTemplate;
import com.seibel.lod.enums.VanillaOverdraw;
import com.seibel.lod.enums.VerticalQuality;
import com.seibel.lod.util.LodUtil;
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 10-23-2021
*/
@Mod.EventBusSubscriber
public class LodConfig
{
// 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 fogQualityOption;
public final AdvancedGraphicsOption advancedGraphicsOption;
Graphics(ForgeConfigSpec.Builder builder)
{
builder.comment("These settings control how the mod will look in game").push("Graphics");
{
qualityOption = new QualityOption(builder);
advancedGraphicsOption = new AdvancedGraphicsOption(builder);
fogQualityOption = 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("These settings control how detailed the fake chunks will be.").push(this.getClass().getSimpleName());
verticalQuality = builder
.comment("\n\n"
+ " This indicates how detailed fake chunks will represent \n"
+ " overhangs, caves, floating islands, ect. \n"
+ " Higher options will use more memory and increase GPU usage. \n"
+ " " + VerticalQuality.LOW + ": uses at max 2 columns per position. \n"
+ " " + VerticalQuality.MEDIUM + ": uses at max 4 columns per position. \n"
+ " " + VerticalQuality.HIGH + ": uses at max 8 columns per position. \n")
.defineEnum("Vertical Quality", VerticalQuality.MEDIUM);
horizontalScale = builder
.comment("\n\n"
+ " This indicates how quickly fake chunks drop off in quality. \n"
+ " " + HorizontalScale.LOW + ": quality drops every " + HorizontalScale.LOW.distanceUnit / 16 + " chunks. \n"
+ " " + HorizontalScale.MEDIUM + ": quality drops every " + HorizontalScale.MEDIUM.distanceUnit / 16 + " chunks. \n"
+ " " + HorizontalScale.HIGH + ": quality drops every " + HorizontalScale.HIGH.distanceUnit / 16 + " chunks. \n")
.defineEnum("Horizontal Scale", HorizontalScale.MEDIUM);
horizontalQuality = builder
.comment("\n\n"
+ " This indicates the exponential base of the quadratic drop-off \n"
+ " " + HorizontalQuality.LOWEST + ": base " + HorizontalQuality.LOWEST.quadraticBase + ". \n"
+ " " + HorizontalQuality.LOW + ": base " + HorizontalQuality.LOW.quadraticBase + ". \n"
+ " " + HorizontalQuality.MEDIUM + ": base " + HorizontalQuality.MEDIUM.quadraticBase + ". \n"
+ " " + HorizontalQuality.HIGH + ": base " + HorizontalQuality.HIGH.quadraticBase + ". \n")
.defineEnum("Horizontal Quality", HorizontalQuality.MEDIUM);
drawResolution = builder
.comment("\n\n"
+ " What is the maximum detail fake chunks should be drawn at? \n"
+ " " + HorizontalResolution.CHUNK + ": render 1 LOD for each Chunk. \n"
+ " " + HorizontalResolution.HALF_CHUNK + ": render 4 LODs for each Chunk. \n"
+ " " + HorizontalResolution.FOUR_BLOCKS + ": render 16 LODs for each Chunk. \n"
+ " " + HorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n"
+ " " + HorizontalResolution.BLOCK + ": render 256 LODs for each Chunk. \n")
.defineEnum("Block size", HorizontalResolution.BLOCK);
lodChunkRenderDistance = builder
.comment("\n\n"
+ " The mod's render distance, measured in chunks. \n")
.defineInRange("Lod Render Distance", 64, 32, 1024);
builder.pop();
}
}
public static class FogQualityOption
{
public final ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
public final ForgeConfigSpec.EnumValue<FogDrawOverride> fogDrawOverride;
FogQualityOption(ForgeConfigSpec.Builder builder)
{
builder.comment("These settings control the fog quality.").push(this.getClass().getSimpleName());
fogDistance = builder
.comment("\n\n"
+ " At what distance should Fog be drawn on the fake chunks? \n"
+ " If the fog cuts off abruptly or you are using Optifine's \"fast\" fog option \n"
+ " set this to " + FogDistance.NEAR + " or " + FogDistance.FAR + ". \n")
.defineEnum("Fog Distance", FogDistance.NEAR_AND_FAR);
fogDrawOverride = builder
.comment("\n\n"
+ " When should fog be drawn? \n"
+ " " + FogDrawOverride.OPTIFINE_SETTING + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.FANCY + ". \n"
+ " " + FogDrawOverride.NO_FOG + ": Never draw fog on the LODs \n"
+ " " + FogDrawOverride.FAST + ": Always draw fast fog on the LODs \n"
+ " " + FogDrawOverride.FANCY + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
.defineEnum("Fog Draw Override", FogDrawOverride.FANCY);
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;
AdvancedGraphicsOption(ForgeConfigSpec.Builder builder)
{
builder.comment("Advanced graphics option for the mod").push(this.getClass().getSimpleName());
lodTemplate = builder
.comment("\n\n"
+ " How should the LODs be drawn? \n"
+ " NOTE: Currently only " + LodTemplate.CUBIC + " is implemented! \n"
+ " \n"
+ " " + LodTemplate.CUBIC + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
+ " " + LodTemplate.TRIANGULAR + ": LOD Chunks smoothly transition between other. \n"
+ " " + LodTemplate.DYNAMIC + ": LOD Chunks smoothly transition between each other, \n"
+ " " + " unless a neighboring chunk is at a significantly different height. \n")
.defineEnum("LOD Template", LodTemplate.CUBIC);
disableDirectionalCulling = builder
.comment("\n\n"
+ " If false fake chunks behind the player's camera \n"
+ " aren't drawn, increasing performance. \n\n"
+ ""
+ " If true all LODs are drawn, even those behind \n"
+ " the player's camera, decreasing performance. \n\n"
+ ""
+ " Disable this if you see LODs disappearing. \n"
+ " (Which may happen if you are using a camera mod) \n")
.define("Disable Directional Culling", false);
alwaysDrawAtMaxQuality = builder
.comment("\n\n"
+ " Disable quality falloff, \n"
+ " all fake chunks will be drawn at the highest \n"
+ " available detail level. \n\n"
+ " "
+ " WARNING: \n"
+ " This could cause a Out Of Memory crash on render \n"
+ " distances higher than 128 \n")
.define("Always Use Max Quality", false);
vanillaOverdraw = builder
.comment("\n\n"
+ " How often should LODs be drawn on top of regular chunks? \n"
+ " HALF and ALWAYS will prevent holes in the world, but may look odd for transparent blocks or in caves. \n\n"
+ " " + VanillaOverdraw.NEVER + ": LODs won't render on top of vanilla chunks. \n"
+ " " + VanillaOverdraw.DYNAMIC + ": LODs will render on top of distant vanilla chunks to hide delayed loading. \n"
+ " " + " More effective on higher render distances. \n"
+ " " + " For vanilla render distances less than or equal to " + LodUtil.MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW + " \n"
+ " " + " " + VanillaOverdraw.NEVER + " or " + VanillaOverdraw.ALWAYS + " may be used depending on the dimension. \n"
+ " " + VanillaOverdraw.ALWAYS + ": LODs will render on all vanilla chunks preventing holes in the world. \n"
+ " " + VanillaOverdraw.BORDER + ": LODs will render only on the border of vanilla chunks preventing only some holes in the world. \n")
.defineEnum("Vanilla Overdraw", VanillaOverdraw.DYNAMIC);
gpuUploadMethod = builder
.comment("\n\n"
+ " What method should be used to upload geometry to the GPU? \n\\n"
+ ""
+ " " + GpuUploadMethod.BUFFER_STORAGE + ": Default if OpenGL 4.5 is supported. Fast rendering, no stuttering. \n"
+ " " + GpuUploadMethod.SUB_DATA + ": Default if OpenGL 4.5 is NOT supported. Fast rendering but may stutter when uploading. \n"
+ " " + GpuUploadMethod.BUFFER_MAPPING + ": Slow rendering but won't stutter when uploading. Possibly better than " + GpuUploadMethod.SUB_DATA + " if using a integrated GPU. \n")
.defineEnum("GPU Upload Method", GpuUploadMethod.BUFFER_STORAGE);
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<BlockToAvoid> blockToAvoid;
//public final ForgeConfigSpec.BooleanValue useExperimentalPreGenLoading;
WorldGenerator(ForgeConfigSpec.Builder builder)
{
builder.comment("These settings control how fake chunks outside your normal view range are generated.").push("Generation");
generationPriority = builder
.comment("\n\n"
+ " " + GenerationPriority.FAR_FIRST + " \n"
+ " LODs are generated from low to high detail \n"
+ " with a small priority for far away regions. \n"
+ " This fills in the world fastest. \n\n"
+ ""
+ " " + GenerationPriority.NEAR_FIRST + " \n"
+ " LODs are generated around the player \n"
+ " in a spiral, similar to vanilla minecraft. \n")
.defineEnum("Generation Priority", GenerationPriority.FAR_FIRST);
distanceGenerationMode = builder
.comment("\n\n"
+ " Note: The times listed here are the amount of time it took \n"
+ " one of 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 + " \n"
+ " Don't run the distance generator. \n"
+ "\n"
+ " " + DistanceGenerationMode.BIOME_ONLY + " \n"
+ " Only generate the biomes and use the biome's \n"
+ " grass color, water color, or snow 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 + " \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 + " \n"
+ " Generate the world surface, \n"
+ " this does NOT include trees, \n"
+ " or structures. \n"
+ " Multithreaded - Faster (10-20 ms) \n"
+ "\n"
+ " " + DistanceGenerationMode.FEATURES + " \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 + " \n"
+ " Ask the server to generate/load each chunk. \n"
+ " This will show player made structures, which can \n"
+ " be useful if you are adding the mod to a pre-existing world. \n"
+ " This is the most compatible, but causes server/simulation lag. \n"
+ " SingleThreaded - Slow (15-50 ms, with spikes up to 200 ms) \n")
.defineEnum("Distance Generation Mode", DistanceGenerationMode.SURFACE);
allowUnstableFeatureGeneration = builder
.comment("\n\n"
+ " When using the " + DistanceGenerationMode.FEATURES + " 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("Allow Unstable Feature Generation", false);
blockToAvoid = builder
.comment("\n\n"
+ " " + BlockToAvoid.NONE + ": Use all blocks when generating fake chunks \n\n"
+ ""
+ " " + BlockToAvoid.NON_FULL + ": Only use full blocks when generating fake chunks (ignores slabs, lanterns, torches, grass, etc.) \n\n"
+ ""
+ " " + BlockToAvoid.NO_COLLISION + ": Only use solid blocks when generating fake chunks (ignores grass, torches, etc.) \n"
+ ""
+ " " + BlockToAvoid.BOTH + ": Only use full solid blocks when generating fake chunks \n"
+ "\n")
.defineEnum("Block to avoid", BlockToAvoid.BOTH);
/*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("Advanced mod settings").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("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 logical processors on your CPU. \n"
+ " Requires a restart to take effect. \n")
.defineInRange("numberOfWorldGenerationThreads", Math.max(1, 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 fake chunks). \n"
+ " If you experience high CPU usage when NOT generating distant \n"
+ " fake chunks, lower this number. \n"
+ " \n"
+ " The maximum value is the number of logical processors on your CPU. \n"
+ " Requires a restart to take effect. \n")
.defineInRange("numberOfBufferBuilderThreads", Math.max(1, Runtime.getRuntime().availableProcessors() / 2), 1, Runtime.getRuntime().availableProcessors());
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("These settings can be used to look for bugs, or see how certain aspects of the mod work.").push(this.getClass().getSimpleName());
drawLods = builder
.comment("\n\n"
+ " If true, the mod is enabled and fake chunks will be drawn. \n"
+ " If false, the mod will still generate fake chunks, \n"
+ " but they won't be rendered. \n")
.define("Disable Drawing", true);
debugMode = builder
.comment("\n\n"
+ " " + DebugMode.OFF + ": Fake chunks will be drawn with their normal colors. \n"
+ " " + DebugMode.SHOW_DETAIL + ": Fake chunks color will be based on their detail level. \n"
+ " " + DebugMode.SHOW_DETAIL_WIREFRAME + ": Fake chunks color will be based on their detail level, drawn as a wireframe. \n")
.defineEnum("Debug Mode", DebugMode.OFF);
enableDebugKeybindings = builder
.comment("\n\n"
+ " If true the F4 key can be used to cycle through the different debug modes. \n"
+ " and the F6 key can be used to enable and disable LOD rendering.")
.define("Enable Debug Keybinding", false);
builder.pop();
}
}
public static class Buffers
{
public final ForgeConfigSpec.EnumValue<BufferRebuildTimes> rebuildTimes;
Buffers(ForgeConfigSpec.Builder builder)
{
builder.comment("These settings affect how often geometry is are built.").push(this.getClass().getSimpleName());
rebuildTimes = builder
.comment("\n\n"
+ " How frequently should geometry be rebuilt and sent to the GPU? \n"
+ " Higher settings may cause stuttering, but will prevent holes in the world \n")
.defineEnum("rebuildFrequency", BufferRebuildTimes.NORMAL);
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!");
}
}
@@ -0,0 +1,47 @@
/*
* 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.enums;
/**
* heightmap <br>
* multi_lod <br>
*
* @author Leonardo Amato
* @version 19-10-2021
*/
public enum BlockToAvoid
{
NONE(false, false),
NON_FULL(true, false),
NO_COLLISION(false, true),
BOTH(true, true);
public boolean nonFull;
public boolean noCollision;
BlockToAvoid(boolean nonFull, boolean noCollision)
{
this.nonFull = nonFull;
this.noCollision = noCollision;
}
}
@@ -0,0 +1,49 @@
/*
* 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.enums;
/**
* Near_First <br>
* Far_First <br>
* <br>
* Determines how fast the buffers need to be regenerated
*
* @author Leonardo Amato
* @version 9-25-2021
*/
public enum BufferRebuildTimes
{
FREQUENT(1000, 500, 2500),
NORMAL(2000, 1000, 5000),
RARE(5000, 2000, 10000);
public final int playerMoveTimeout;
public final int renderedChunkTimeout;
public final int chunkChangeTimeout;
BufferRebuildTimes(int playerMoveTimeout, int renderedChunkTimeout, int chunkChangeTimeout)
{
this.playerMoveTimeout = playerMoveTimeout;
this.renderedChunkTimeout = renderedChunkTimeout;
this.chunkChangeTimeout = chunkChangeTimeout;
}
}
@@ -0,0 +1,54 @@
/*
* 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.enums;
/**
* off, detail, detail wireframe
*
* @author James Seibel
* @version 8-28-2021
*/
public enum DebugMode
{
/** LODs are rendered normally */
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;
}
}
@@ -0,0 +1,95 @@
/*
* 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.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;
}
}
@@ -0,0 +1,38 @@
/*
* 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.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
}
@@ -0,0 +1,47 @@
/*
* 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.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-3-2021
*/
public enum FogDrawOverride
{
/**
* Use whatever Fog setting optifine is using.
* If optifine isn't installed this defaults to ALWAYS_DRAW_FOG.
*/
OPTIFINE_SETTING,
/** Never draw fog on the LODs */
NO_FOG,
/** Always draw fast fog on the LODs */
FAST,
/** Always draw fancy fog on the LODs */
FANCY
}
@@ -0,0 +1,33 @@
/*
* 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.enums;
/**
* fast, fancy, or off
*
* @author James Seibel
* @version 02-14-2021
*/
public enum FogQuality
{
FAST,
FANCY,
OFF
}
@@ -0,0 +1,37 @@
/*
* 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.enums;
/**
* Near_First <br>
* Far_First <br>
* <br>
* Determines which LODs should have priority when generating
* outside the normal view distance.
*
* @author Leonardo Amato
* @version 9-25-2021
*/
public enum GenerationPriority
{
NEAR_FIRST,
FAR_FIRST
}
@@ -0,0 +1,38 @@
/*
* 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.enums;
/**
* Minecraft, Lod_Builder, None
*
* @author James Seibel
* @version 10-1-2021
*/
public enum GlProxyContext
{
/** Minecraft's render thread */
MINECRAFT,
/** The context we send buffers to the GPU on */
LOD_BUILDER,
/** used to un-bind threads */
NONE,
}
@@ -0,0 +1,38 @@
/*
* 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.enums;
/**
* Buffer_Storage, Sub_Data, Buffer_Mapping
*
* @author James Seibel
* @version 10-23-2021
*/
public enum GpuUploadMethod
{
/** Default if OpenGL 4.5 is supported. Fast rendering, no stuttering. */
BUFFER_STORAGE,
/** Default if OpenGL 4.5 is NOT supported. Fast rendering but may stutter when uploading. */
SUB_DATA,
/** May end up storing buffers in System memory. Slower rendering but won't stutter when uploading. */
BUFFER_MAPPING,
}
@@ -0,0 +1,53 @@
/*
* 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.enums;
/**
* Lowest <br>
* Low <br>
* Medium <br>
* High <br>
* <br>
* this indicates the base of the quadratic function we use for the quality drop off
*
* @author Leonardo Amato
* @version 9-29-2021
*/
public enum HorizontalQuality
{
/** 1.0 AKA Linear */
LOWEST(1.0f),
/** exponent 1.5 */
LOW(1.5f),
/** exponent 2.0 */
MEDIUM(2.0f),
/** exponent 2.2 */
HIGH(2.2f);
public final double quadraticBase;
HorizontalQuality(double distanceUnit)
{
this.quadraticBase = distanceUnit;
}
}
@@ -0,0 +1,175 @@
/*
* 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.enums;
import java.util.ArrayList;
import java.util.Collections;
import com.seibel.lod.util.LodUtil;
/**
* chunk <Br>
* half_chunk <Br>
* four_blocks <br>
* two_blocks <Br>
* block <br>
*
* @author James Seibel
* @author Leonardo Amato
* @version 9-25-2021
*/
public enum HorizontalResolution
{
/** render 256 LODs for each chunk */
BLOCK(16, 0),
/** render 64 LODs for each chunk */
TWO_BLOCKS(8, 1),
/** render 16 LODs for each chunk */
FOUR_BLOCKS(4, 2),
/** render 4 LODs for each chunk */
HALF_CHUNK(2, 3),
/** render 1 LOD for each chunk */
CHUNK(1, 4);
/**
* 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 HorizontalResolution[][] lowerDetailArrays;
HorizontalResolution(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 HorizontalResolution[] getSelfAndLowerDetails(HorizontalResolution detail)
{
if (lowerDetailArrays == null)
{
// run first time setup
lowerDetailArrays = new HorizontalResolution[HorizontalResolution.values().length][];
// go through each LodDetail
for (HorizontalResolution currentDetail : HorizontalResolution.values())
{
ArrayList<HorizontalResolution> lowerDetails = new ArrayList<>();
// find the details lower than currentDetail
for (HorizontalResolution compareDetail : HorizontalResolution.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 HorizontalResolution[lowerDetails.size()]);
}
}
return lowerDetailArrays[detail.detailLevel];
}
/** Returns what detail level should be used at a given distance and maxDistance. */
public static HorizontalResolution getDetailForDistance(HorizontalResolution maxDetailLevel, int distance, int maxDistance)
{
HorizontalResolution[] lowerDetails = getSelfAndLowerDetails(maxDetailLevel);
int distanceBetweenDetails = maxDistance / lowerDetails.length;
int index = LodUtil.clamp(0, distance / distanceBetweenDetails, lowerDetails.length - 1);
return lowerDetails[index];
}
}
@@ -0,0 +1,49 @@
/*
* 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.enums;
/**
* Low <br>
* Medium <br>
* High <br>
* <br>
* this is a quality scale for the detail drop-off
*
* @author Leonardo Amato
* @version 9-25-2021
*/
public enum HorizontalScale
{
/** Lods are 2D with heightMap */
LOW(64),
/** Lods expand in three dimension */
MEDIUM(128),
/** Lods expand in three dimension */
HIGH(256);
public final int distanceUnit;
HorizontalScale(int distanceUnit)
{
this.distanceUnit = distanceUnit;
}
}
@@ -0,0 +1,62 @@
/*
* 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.enums;
import com.seibel.lod.builders.bufferBuilding.lodTemplates.AbstractLodTemplate;
import com.seibel.lod.builders.bufferBuilding.lodTemplates.CubicLodTemplate;
import com.seibel.lod.builders.bufferBuilding.lodTemplates.DynamicLodTemplate;
import com.seibel.lod.builders.bufferBuilding.lodTemplates.TriangularLodTemplate;
/**
* Cubic, Triangular, Dynamic
*
* @author James Seibel
* @version 10-10-2021
*/
public enum LodTemplate
{
/**
* LODs are rendered as
* rectangular prisms.
*/
CUBIC(new CubicLodTemplate()),
/**
* LODs smoothly transition between
* each other.
*/
TRIANGULAR(new TriangularLodTemplate()),
/**
* LODs smoothly transition between
* each other, unless a neighboring LOD
* is at a significantly different height.
*/
DYNAMIC(new DynamicLodTemplate());
public final AbstractLodTemplate template;
LodTemplate(AbstractLodTemplate newTemplate)
{
template = newTemplate;
}
}
@@ -0,0 +1,22 @@
package com.seibel.lod.enums;
/**
* NONE, GAME_SHADING
*
* @author James Seibel
* @version 7-25-2020
*/
public enum ShadingMode
{
/**
* LODs will have darker sides and bottoms to simulate
* Minecraft's fast lighting.
*/
GAME_SHADING,
/**
* LODs will use ambient occlusion to mimic Minecarft's
* Fancy lighting.
*/
AMBIENT_OCCLUSION
}
@@ -0,0 +1,45 @@
/*
* 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.enums;
/**
* None, Dynamic, Always
*
* <p>
* This represents how far the LODs should overlap with
* the vanilla Minecraft terrain.
*
* @author James Seibel
* @version 10-11-2021
*/
public enum VanillaOverdraw
{
/** Never draw LODs where a minecraft chunk could be. */
NEVER,
/** Draw LODs over the farther minecraft chunks. */
DYNAMIC,
/** Draw LODs over all minecraft chunks. */
ALWAYS,
/** Draw LODs over border chunks. */
BORDER,
}
@@ -0,0 +1,80 @@
/*
* 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.enums;
/**
* heightmap <br>
* multi_lod <br>
*
* @author Leonardo Amato
* @version 10-07-2021
*/
public enum VerticalQuality
{
LOW(
new int[] { 2,
2,
2,
2,
1,
1,
1,
1,
1,
1,
1 }
),
MEDIUM(
new int[] { 4,
4,
2,
2,
2,
1,
1,
1,
1,
1,
1 }
),
HIGH(
new int[] {
8,
8,
4,
4,
2,
2,
2,
1,
1,
1,
1 }
);
public final int[] maxVerticalData;
VerticalQuality(int[] maxVerticalData)
{
this.maxVerticalData = maxVerticalData;
}
}
@@ -0,0 +1,74 @@
/*
* 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.handlers;
import java.io.File;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.chunk.storage.ChunkSerializer;
import net.minecraft.world.server.ServerWorld;
/**
*
* @author ??
* @version ??
*/
public class ChunkLoader
{
public static IChunk getChunkFromFile(ChunkPos pos){
ClientWorld clientWorld = MinecraftWrapper.INSTANCE.getClientWorld();
if (clientWorld == null)
return null;
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(clientWorld.dimensionType());
try
{
File file = new File(serverWorld.getChunkSource().getDataStorage().dataFolder.getParent() + File.separatorChar + "region", "r." + (pos.x >> 5) + "." + (pos.z >> 5) + ".mca");
if(!file.exists())
return null;
IChunk loadedChunk = ChunkSerializer.read(
serverWorld,
serverWorld.getStructureManager(),
serverWorld.getPoiManager(),
pos,
serverWorld.getChunkSource().chunkMap.read(pos)
);
boolean emptyChunk = true;
for(int i = 0; i < 16; i++){
for(int j = 0; j < 16; j++){
emptyChunk &= loadedChunk.isYSpaceEmpty(i,j);
}
}
if(emptyChunk)
return null;
else
return loadedChunk;
}
catch (Exception e)
{
return null;
}
}
}
@@ -0,0 +1,401 @@
/*
* 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.handlers;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
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 org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.VerticalQuality;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.LodRegion;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.objects.VerticalLevelContainer;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.util.ThreadMapUtil;
/**
* This object handles creating LodRegions
* from files and saving LodRegion objects
* to file.
*
* @author James Seibel
* @author Cola
* @version 9-25-2021
*/
public class LodDimensionFileHandler
{
/** This is the dimension that owns this file handler */
private LodDimension lodDimension;
private final File dimensionDataSaveFolder;
/** lod */
private static final String FILE_NAME_PREFIX = "lod";
/** .txt */
private static final String FILE_EXTENSION = ".xz";
/** 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 = 6;
/**
* Allow saving asynchronously, but never try to save multiple regions
* at a time
*/
private final ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLodDimension)
{
if (newSaveFolder == null)
throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to.");
dimensionDataSaveFolder = newSaveFolder;
lodDimension = newLodDimension;
}
//================//
// read from file //
//================//
/**
* Returns the LodRegion at the given coordinates.
* Returns an empty region if the file doesn't exist.
*/
public LodRegion loadRegionFromFile(byte detailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
{
int regionX = regionPos.x;
int regionZ = regionPos.z;
LodRegion region = new LodRegion(LodUtil.REGION_DETAIL_LEVEL, regionPos, generationMode, verticalQuality);
for (byte tempDetailLevel = LodUtil.REGION_DETAIL_LEVEL; tempDetailLevel >= detailLevel; tempDetailLevel--)
{
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, tempDetailLevel, verticalQuality);
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 file = new File(fileName);
if (!file.exists())
{
//there is no file for current gen mode
//search others above current from the most to the least detailed
DistanceGenerationMode tempGenMode = DistanceGenerationMode.SERVER;
while (tempGenMode != generationMode)
{
fileName = getFileNameAndPathForRegion(regionX, regionZ, tempGenMode, tempDetailLevel, verticalQuality);
if (fileName != null)
{
file = new File(fileName);
if (file.exists())
break;
}
//decrease gen mode
if (tempGenMode == DistanceGenerationMode.SERVER)
tempGenMode = DistanceGenerationMode.FEATURES;
else if (tempGenMode == DistanceGenerationMode.FEATURES)
tempGenMode = DistanceGenerationMode.SURFACE;
else if (tempGenMode == DistanceGenerationMode.SURFACE)
tempGenMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
else if (tempGenMode == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT)
tempGenMode = DistanceGenerationMode.BIOME_ONLY;
else if (tempGenMode == DistanceGenerationMode.BIOME_ONLY)
tempGenMode = DistanceGenerationMode.NONE;
}
if (!file.exists())
//there wasn't a file, don't return anything
continue;
}
// don't try parsing empty files
long dataSize = file.length();
dataSize -= 1;
if (dataSize > 0)
{
try (XZCompressorInputStream inputStream = new XZCompressorInputStream(new FileInputStream(file)))
{
int fileVersion;
fileVersion = inputStream.read();
// 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.
inputStream.close();
file.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 accidentally delete anything the user may want.
inputStream.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;
}
// this file is a readable version,
// read the file
byte[] data = ThreadMapUtil.getSaveContainer(tempDetailLevel);
inputStream.read(data);
inputStream.close();
// add the data to our region
region.addLevelContainer(new VerticalLevelContainer(data));
}
catch (IOException ioEx)
{
ClientProxy.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + ioEx.getMessage() + "]: ");
ioEx.printStackTrace();
}
}
}
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();
}
}// for each detail level
if (region.getMinDetailLevel() >= detailLevel)
region.growTree(detailLevel);
return region;
}
//==============//
// Save to File //
//==============//
/**
* Save all dirty regions in this LodDimension to file.
*/
public void saveDirtyRegionsToFileAsync()
{
fileWritingThreadPool.execute(saveDirtyRegionsThread);
}
private final Thread saveDirtyRegionsThread = new Thread(() ->
{
try
{
for (int i = 0; i < lodDimension.getWidth(); i++)
{
for (int j = 0; j < lodDimension.getWidth(); j++)
{
if (lodDimension.GetIsRegionDirty(i, j) && lodDimension.getRegionByArrayIndex(i, j) != null)
{
saveRegionToFile(lodDimension.getRegionByArrayIndex(i, j));
lodDimension.SetIsRegionDirty(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)
{
for (byte detailLevel = region.getMinDetailLevel(); detailLevel <= LodUtil.REGION_DETAIL_LEVEL; detailLevel++)
{
String fileName = getFileNameAndPathForRegion(region.regionPosX, region.regionPosZ, region.getGenerationMode(), detailLevel, region.getVerticalQuality());
// if the fileName was null that means the folder is inaccessible
// for some reason
if (fileName == null)
{
ClientProxy.LOGGER.warn("Unable to save region [" + region.regionPosX + ", " + region.regionPosZ + "] to file, file is inaccessible.");
return;
}
File oldFile = new File(fileName);
//ClientProxy.LOGGER.info("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] to file.");
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)
int fileVersion = LOD_SAVE_FILE_VERSION;
try (XZCompressorInputStream inputStream = new XZCompressorInputStream(new FileInputStream(oldFile)))
{
fileVersion = inputStream.read();
inputStream.close();
}
catch (IOException ex)
{
ex.printStackTrace();
}
// check if this file can be written to by the file handler
if (fileVersion > LOD_SAVE_FILE_VERSION)
{
// the file we are reading is a newer version,
// don't write anything, we don't want to accidentally
// delete anything the user may want.
return;
}
// if we got this far then we are good
// to overwrite the old file
}
// the old file is good, now create a new temporary save file
File newFile = new File(fileName + TMP_FILE_EXTENSION);
try (XZCompressorOutputStream outputStream = new XZCompressorOutputStream(new FileOutputStream(newFile), 3))
{
// add the version of this file
outputStream.write(LOD_SAVE_FILE_VERSION);
// add each LodChunk to the file
outputStream.write(region.getLevel(detailLevel).toDataString());
outputStream.close();
// overwrite the old file with the new one
Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
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 available to read and write. <br><br>
* <p>
* example: "lod.0.0.txt" <br>
* <p>
* Returns null if there is an IO or security Exception.
*/
private String getFileNameAndPathForRegion(int regionX, int regionZ, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality)
{
try
{
// saveFolder is something like
// ".\Super Flat\DIM-1\data\"
// or
// ".\Super Flat\data\"
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar +
verticalQuality + 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;
}
}
}
@@ -0,0 +1,120 @@
/*
* 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.handlers;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import com.seibel.lod.enums.FogQuality;
import com.seibel.lod.proxy.ClientProxy;
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-25-2021
*/
public class ReflectionHandler
{
public static final ReflectionHandler INSTANCE = new ReflectionHandler();
private final 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
ClientProxy.LOGGER.info(ReflectionHandler.class.getSimpleName() + ": unable to find the Optifine fog field. If Optifine isn't installed this can be ignored.");
}
/**
* 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)
{
default:
case 0:
// optifine's "default" option,
// it should never be called in this case
// normal options
case 1:
return FogQuality.FAST;
case 2:
return FogQuality.FANCY;
case 3:
return FogQuality.OFF;
}
}
}
@@ -0,0 +1,65 @@
/*
* 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.mixin;
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 net.minecraft.client.renderer.RenderType;
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, and we used Forge's
* render last event, the LODs would render on top
* of the normal terrain.
*
* @author James Seibel
* @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")
private void renderSky(MatrixStack matrixStackIn, float partialTicks, CallbackInfo callback)
{
// get the partial ticks since renderBlockLayer doesn't
// have access to them
previousPartialTicks = partialTicks;
}
@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.advancedModOptions.debugging.drawLods.get() && renderType.equals(RenderType.solid()))
LodMain.client_proxy.renderLods(matrixStackIn, previousPartialTicks);
}
}
@@ -0,0 +1,115 @@
/*
* 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.objects;
/**
* A level container is a quad tree level
*/
public interface LevelContainer
{
/**
* With this you can add data to the level container
* @param data actual data to add in an array of long format.
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @param index z position in the detail level
* @return true if correctly added, false otherwise
*/
boolean addData(long data, int posX, int posZ, int index);
/**
* With this you can add data to the level container
* @param data actual data to add in an array of long[] format.
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @return true if correctly added, false otherwise
*/
boolean addVerticalData(long[] data, int posX, int posZ);
/**
* With this you can add data to the level container
* @param data actual data to add in an array of long format.
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @return true if correctly added, false otherwise
*/
boolean addSingleData(long data, int posX, int posZ);
/**
* With this you can get data from the level container
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @return the data in long array format
*/
long getData(int posX, int posZ, int index);
/**
* With this you can get data from the level container
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @return the data in long array format
*/
long getSingleData(int posX, int posZ);
/**
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @return true only if the data exist
*/
boolean doesItExist(int posX, int posZ);
/**
* @return return the detailLevel of this level container
*/
byte getDetailLevel();
int getMaxVerticalData();
/** Clears the dataPoint at the given array index */
void clear(int posX, int posZ);
/**
* This return a level container with detail level lower than the current level.
* The new level container may use information of this level.
* @return the new level container
*/
LevelContainer expand();
/**
* @param lowerLevelContainer lower level where we extract the data
* @param posX x position in the detail level to update
* @param posZ z position in the detail level to update
*/
void updateData(LevelContainer lowerLevelContainer, int posX, int posZ);
/**
* This will give the data to save in the file
* @return data as a String
*/
byte[] toDataString();
/**
* This will give the data to save in the file
* @return data as a String
*/
int getMaxNumberOfLods();
}
@@ -0,0 +1,912 @@
/*
* 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.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.enums.GenerationPriority;
import com.seibel.lod.enums.VerticalQuality;
import com.seibel.lod.handlers.LodDimensionFileHandler;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LevelPosUtil;
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. <Br><Br>
*
* <strong>Coordinate Standard: </strong><br>
* Coordinate called posX or posZ are relative LevelPos coordinates <br>
* unless stated otherwise. <br>
*
* @author Leonardo Amato
* @author James Seibel
* @version 10-10-2021
*/
public class LodDimension
{
public final DimensionType dimension;
/** measured in regions */
private volatile int width;
/** measured in regions */
private volatile int halfWidth;
// these three variables are private to force use of the getWidth() method
// which is a safer way to get the width then directly asking the arrays
/** stores all the regions in this dimension */
public volatile LodRegion[][] regions;
/** stores if the region at the given x and z index needs to be saved to disk */
private volatile boolean[][] isRegionDirty;
/** stores if the region at the given x and z index needs to be regenerated */
private volatile boolean[][] regenRegionBuffer;
/** stores if the buffer size at the given x and z index needs to be changed */
private volatile boolean[][] recreateRegionBuffer;
/**
* if true that means there are regions in this dimension
* that need to have their buffers rebuilt.
*/
public volatile boolean regenDimensionBuffers = false;
private LodDimensionFileHandler fileHandler;
private final RegionPos center;
/** prevents the cutAndExpandThread from expanding at the same location multiple times */
private volatile ChunkPos lastExpandedChunk;
/** prevents the cutAndExpandThread from cutting at the same location multiple times */
private volatile ChunkPos lastCutChunk;
private final ExecutorService cutAndExpandThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - Cut and Expand"));
/**
* Creates the dimension centered at (0,0)
* @param newWidth in regions
*/
public LodDimension(DimensionType newDimension, LodWorld lodWorld, int newWidth)
{
lastCutChunk = null;
lastExpandedChunk = null;
dimension = newDimension;
width = newWidth;
halfWidth = 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];
regenRegionBuffer = new boolean[width][width];
recreateRegionBuffer = new boolean[width][width];
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 width, 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;
}
/**
* Gets the region at the given LevelPos
* <br>
* Returns null if the region doesn't exist
* or is outside the loaded area.
*/
public LodRegion getRegion(byte detailLevel, int levelPosX, int levelPosZ)
{
int xRegion = LevelPosUtil.getRegion(detailLevel, levelPosX);
int zRegion = LevelPosUtil.getRegion(detailLevel, levelPosZ);
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;
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");
return regions[xIndex][zIndex];
}
/** Useful when iterating over every region. */
public LodRegion getRegionByArrayIndex(int xIndex, int zIndex)
{
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;
}
/**
* Deletes nodes that are a higher detail then necessary, freeing
* up memory.
*/
public void cutRegionNodesAsync(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);
// don't run the tree cutter multiple times
// for the same location
if (newPlayerChunk.x != lastCutChunk.x || newPlayerChunk.z != lastCutChunk.z)
{
lastCutChunk = newPlayerChunk;
Thread thread = new Thread(() ->
{
int regionX;
int regionZ;
int minDistance;
byte detail;
byte minAllowedDetailLevel;
// go over every region in the dimension
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;
if (regions[x][z] != null)
{
// check what detail level this region should be
// and cut it if it is higher then that
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
detail = DetailDistanceUtil.getTreeCutDetailFromDistance(minDistance);
minAllowedDetailLevel = DetailDistanceUtil.getCutLodDetail(detail);
if (regions[x][z].getMinDetailLevel() > minAllowedDetailLevel)
{
regions[x][z].cutTree(minAllowedDetailLevel);
recreateRegionBuffer[x][z] = true;
}
}
}// region z
}// region z
});
cutAndExpandThread.execute(thread);
}
}
/** Either expands or loads all regions in the rendered LOD area */
public void expandOrLoadRegionsAsync(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));
VerticalQuality verticalQuality = LodConfig.CLIENT.graphics.qualityOption.verticalQuality.get();
if (lastExpandedChunk == null)
lastExpandedChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1);
// don't run the expander multiple times
// for the same location
if (newPlayerChunk.x != lastExpandedChunk.x || newPlayerChunk.z != lastExpandedChunk.z)
{
lastExpandedChunk = 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];
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
detail = DetailDistanceUtil.getTreeGenDetailFromDistance(minDistance);
levelToGen = DetailDistanceUtil.getLodGenDetail(detail).detailLevel;
// check that the region isn't null and at least this detail level
if (region == null || region.getGenerationMode() != generationMode)
{
// First case, region has to be created
// try to get the region from file
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality);
// if there is no region file create an empty region
if (regions[x][z] == null)
regions[x][z] = new LodRegion(levelToGen, regionPos, generationMode, verticalQuality);
regenRegionBuffer[x][z] = true;
regenDimensionBuffers = true;
recreateRegionBuffer[x][z] = true;
}
else if (region.getMinDetailLevel() > levelToGen)
{
// Second case, the region exists at a higher detail level.
// Expand the region by introducing the missing layer
region.growTree(levelToGen);
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality);
recreateRegionBuffer[x][z] = true;
}
}
}
});
cutAndExpandThread.execute(thread);
}
}
/**
* Add the given LOD to this dimension at the coordinate
* stored in the LOD. If an LOD already exists at the given
* coordinate it will be overwritten.
*/
public Boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data, boolean dontSave)
{
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
// don't continue if the region can't be saved
LodRegion region = getRegion(regionPosX, regionPosZ);
if (region == null)
return false;
boolean nodeAdded = region.addData(detailLevel, posX, posZ, verticalIndex, data);
// 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;
regenRegionBuffer[xIndex][zIndex] = true;
regenDimensionBuffers = true;
}
catch (ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
// If this happens, the method was probably
// called when the dimension was changing size.
// Hopefully this shouldn't be an issue.
}
}
return nodeAdded;
}
/**
* Add whole column of LODs to this dimension at the coordinate
* stored in the LOD. If an LOD already exists at the given
* coordinate it will be overwritten.
*/
public Boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data, boolean dontSave)
{
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
// don't continue if the region can't be saved
LodRegion region = getRegion(regionPosX, regionPosZ);
if (region == null)
return false;
boolean nodeAdded = region.addVerticalData(detailLevel, posX, posZ, data);
// 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;
regenRegionBuffer[xIndex][zIndex] = true;
regenDimensionBuffers = true;
}
catch (ArrayIndexOutOfBoundsException e)
{
e.printStackTrace();
// If this happens, the method was probably
// called when the dimension was changing size.
// Hopefully this shouldn't be an issue.
}
}
return nodeAdded;
}
/** marks the region at the given region position to have its buffer rebuilt */
public void markRegionBufferToRegen(int xRegion, int zRegion)
{
int xIndex = (xRegion - center.x) + halfWidth;
int zIndex = (zRegion - center.z) + halfWidth;
regenRegionBuffer[xIndex][zIndex] = true;
}
/**
* Returns every position that need to be generated based on the position of the player
*/
public PosToGenerateContainer getPosToGenerate(int maxDataToGenerate, int playerBlockPosX, int playerBlockPosZ)
{
PosToGenerateContainer posToGenerate;
LodRegion lodRegion;
// all the following values are used for the spiral matrix visit
// x and z are the matrix coord
// dx and dz is the next move on the coordinate in the range -1 0 +1
int x, z, dx, dz, t;
x = 0;
z = 0;
dx = 0;
dz = -1;
// We can use two type of generation scheduling
switch (LodConfig.CLIENT.worldGenerator.generationPriority.get())
{
default:
case NEAR_FIRST:
//in the NEAR_FIRST generation scheduling we prioritize the nearest un-generated position to the player
//the chunk position to generate will be stored in a posToGenerate object
posToGenerate = new PosToGenerateContainer((byte) 10, maxDataToGenerate, playerBlockPosX, playerBlockPosZ);
int playerChunkX = LevelPosUtil.getChunkPos(LodUtil.BLOCK_DETAIL_LEVEL, playerBlockPosX);
int playerChunkZ = LevelPosUtil.getChunkPos(LodUtil.BLOCK_DETAIL_LEVEL, playerBlockPosZ);
int complexity;
int xChunkToCheck;
int zChunkToCheck;
byte detailLevel;
int posX;
int posZ;
long data;
int numbChunksWide = (width) * 32;
int circleLimit = Integer.MAX_VALUE;
//posToGenerate is using an insertion sort algorithm which can become really fast if the
//original data order is almost ordered. For this reason we explore the matrix of the position to generate
//with a spiral matrix visit (a square spiral is almost ordered in the "nearest to farthest" order)
for (int i = 0; i < numbChunksWide * numbChunksWide; i++)
{
//Firstly we check if the posToGenerate has been filled
if (maxDataToGenerate == 0)
{
maxDataToGenerate--;
//if it has been filled then we set a stop distance
//the stop distance will be current distance (generically x) per square root of 2
//this would guarantee a circular generation since (Math.abs(x) * 1.41f) is the
//radius of a circle that inscribe a square
circleLimit = (int) (Math.abs(x) * 1.41f);
}
//This second if check if we reached the circleLimit decided in the previous if
//if so we stop
else if (maxDataToGenerate < 0)
{
if (circleLimit < Math.abs(x) && circleLimit < Math.abs(z))
break;
}
xChunkToCheck = x + playerChunkX;
zChunkToCheck = z + playerChunkZ;
//we get the lod region in which the chunk is present
lodRegion = getRegion(LodUtil.CHUNK_DETAIL_LEVEL, xChunkToCheck, zChunkToCheck);
if (lodRegion == null)
continue;
//Now we check if the current chunk has been generated with the correct complexity
//if(lodRegion.isChunkPreGenerated(xChunkToCheck,zChunkToCheck))
// complexity = DistanceGenerationMode.SERVER.complexity;
//else
complexity = LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get().complexity;
//we create the level position info of the chunk
detailLevel = lodRegion.getMinDetailLevel();
posX = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, xChunkToCheck, detailLevel);
posZ = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, zChunkToCheck, detailLevel);
data = getSingleData(detailLevel, posX, posZ);
//we will generate the position only if the current generation complexity is lower than the target one.
//an un-generated area will always have 0 generation
if (DataPointUtil.getGenerationMode(data) < complexity)
{
posToGenerate.addPosToGenerate(detailLevel, posX, posZ);
if (maxDataToGenerate >= 0)
maxDataToGenerate--;
}
//with this code section we find the next chunk to check
if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z)))
{
t = dx;
dx = -dz;
dz = t;
}
x += dx;
z += dz;
}
break;
case FAR_FIRST:
//in the FAR_FIRST generation we dedicate part of the generation process to the far region with really
//low detail quality.
posToGenerate = new PosToGenerateContainer((byte) 8, maxDataToGenerate, playerBlockPosX, playerBlockPosZ);
int xRegion;
int zRegion;
for (int i = 0; i < width * width; i++)
{
xRegion = x + center.x;
zRegion = z + center.z;
//All of this is handled directly by the region, which scan every pos from top to bottom of the quad tree
lodRegion = getRegion(xRegion, zRegion);
if (lodRegion != null)
lodRegion.getPosToGenerate(posToGenerate, playerBlockPosX, playerBlockPosZ);
//with this code section we find the next chunk to check
if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z)))
{
t = dx;
dx = -dz;
dz = t;
}
x += dx;
z += dz;
}
break;
}
return posToGenerate;
}
/**
* Fills the posToRender with the position to render for the regionPos given in input
*/
public void getPosToRender(PosToRenderContainer posToRender, RegionPos regionPos, int playerPosX,
int playerPosZ)
{
LodRegion region = getRegion(regionPos.x, regionPos.z);
if (region != null)
region.getPosToRender(posToRender, playerPosX, playerPosZ, LodConfig.CLIENT.worldGenerator.generationPriority.get() == GenerationPriority.NEAR_FIRST);
}
/**
* Determines how many vertical LODs could be used
* for the given region at the given detail level
*/
public int getMaxVerticalData(byte detailLevel, int posX, int posZ)
{
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getMaxVerticalData 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.getMaxVerticalData(detailLevel);
}
/**
* 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, int verticalIndex)
{
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 DataPointUtil.EMPTY_DATA;
return region.getData(detailLevel, posX, posZ, verticalIndex);
}
/**
* 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 getSingleData(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 DataPointUtil.EMPTY_DATA;
return region.getSingleData(detailLevel, posX, posZ);
}
/** Clears the given region */
public void clear(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.clear(detailLevel, posX, posZ);
}
/**
* Returns if the buffer at the given array index needs
* to have its buffer regenerated.
*/
public boolean doesRegionNeedBufferRegen(int xIndex, int zIndex)
{
return regenRegionBuffer[xIndex][zIndex];
}
/**
* Sets if the buffer at the given array index needs
* to have its buffer regenerated.
*/
public void setRegenRegionBufferByArrayIndex(int xIndex, int zIndex, boolean newRegen)
{
regenRegionBuffer[xIndex][zIndex] = newRegen;
}
/**
* Get the data point at the given LevelPos
* 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);
}
/**
* Returns true if a region exists at the given LevelPos
*/
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
{
LodRegion region = getRegion(detailLevel, posX, posZ);
return region != null && region.doesDataExist(detailLevel, posX, posZ);
}
/**
* Loads the region at the given RegionPos from file,
* if a file exists for that region.
*/
public LodRegion getRegionFromFile(RegionPos regionPos, byte detailLevel,
DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
{
return fileHandler != null ? fileHandler.loadRegionFromFile(detailLevel, regionPos, generationMode, verticalQuality) : null;
}
/**
* Save all dirty regions in this LodDimension to file.
*/
public void saveDirtyRegionsToFileAsync()
{
fileHandler.saveDirtyRegionsToFileAsync();
}
/**
* Return true if the chunk has been pregenerated in game
*/
//public boolean isChunkPreGenerated(int xChunkPos, int zChunkPos)
//{
//
// LodRegion region = getRegion(LodUtil.CHUNK_DETAIL_LEVEL, xChunkPos, zChunkPos);
// if (region == null)
// return false;
//
// return region.isChunkPreGenerated(xChunkPos, zChunkPos);
//}
/**
* Returns whether the region at the given RegionPos
* 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;
}
/** Returns the dimension's center region position X value */
public int getCenterRegionPosX()
{
return center.x;
}
/** Returns the dimension's center region position Z value */
public int getCenterRegionPosZ()
{
return center.z;
}
/**
* returns the width of the dimension in regions
*/
public int getWidth()
{
// we want to get the length directly from the
// source to make sure it is in sync with region
// and isRegionDirty
return regions != null ? regions.length : width;
}
/** Update the width of this dimension, in regions */
public void setRegionWidth(int newWidth)
{
width = newWidth;
halfWidth = Math.floorDiv(width, 2);
regions = new LodRegion[width][width];
isRegionDirty = new boolean[width][width];
regenRegionBuffer = new boolean[width][width];
recreateRegionBuffer = 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()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Dimension : \n");
for (LodRegion[] lodRegions : regions)
{
for (LodRegion region : lodRegions)
{
if (region == null)
stringBuilder.append("n");
else
stringBuilder.append(region.getMinDetailLevel());
stringBuilder.append("\t");
}
stringBuilder.append("\n");
}
return stringBuilder.toString();
}
public boolean GetIsRegionDirty(int i, int j)
{
return isRegionDirty[i][j];
}
public void SetIsRegionDirty(int i, int j, boolean val)
{
isRegionDirty[i][j] = val;
}
}
@@ -0,0 +1,625 @@
/*
* 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.objects;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.VerticalQuality;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LevelPosUtil;
import com.seibel.lod.util.LodUtil;
/**
* This object holds all loaded LevelContainers acting as a quad tree
* for a given region. <Br><Br>
*
* <strong>Coordinate Standard: </strong><br>
* Coordinate called posX or posZ are relative LevelPos coordinates <br>
* unless stated otherwise. <br>
*
* @author Leonardo Amato
* @version 10-10-2021
*/
public class LodRegion
{
/**
* Number of detail level supported by a region
*/
private static final byte POSSIBLE_LOD = 10;
/**
* Holds the lowest (least detailed) detail level in this region
*/
private byte minDetailLevel;
/**
* This holds all data for this region
*/
private final LevelContainer[] dataContainer;
/**
* This chunk Pos has been generated
*/
//private final boolean[] preGeneratedChunkPos;
/**
* the generation mode for this region
*/
private DistanceGenerationMode generationMode;
/**
* the vertical quality of this region
*/
private VerticalQuality verticalQuality;
/**
* this region's x RegionPos
*/
public final int regionPosX;
/**
* this region's z RegionPos
*/
public final int regionPosZ;
public LodRegion(byte minDetailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
{
this.minDetailLevel = minDetailLevel;
this.regionPosX = regionPos.x;
this.regionPosZ = regionPos.z;
this.verticalQuality = verticalQuality;
this.generationMode = generationMode;
dataContainer = new LevelContainer[POSSIBLE_LOD];
// Initialize all the different matrices
for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++)
{
dataContainer[lod] = new VerticalLevelContainer(lod);
}
boolean fileFound = false;
/*
preGeneratedChunkPos = new boolean[32 * 32];
if (MinecraftWrapper.INSTANCE.hasSinglePlayerServer() && LodConfig.CLIENT.worldGenerator.useExperimentalPreGenLoading.get())
{
File regionFileDirHead;
File regionFileDirParent;
// local world
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(MinecraftWrapper.INSTANCE.getCurrentDimension());
// provider needs a separate variable to prevent
// the compiler from complaining
StringBuilder string = new StringBuilder();
try
{
ServerChunkProvider provider = serverWorld.getChunkSource();
//System.out.println(provider.dataStorage.dataFolder);
regionFileDirHead = new File(provider.dataStorage.dataFolder.getCanonicalFile().getParentFile().toPath().toAbsolutePath().toString() + File.separatorChar + "region", "r." + regionPosZ + "." + regionPosX + ".mca");
if (regionFileDirHead.exists())
{
regionFileDirParent = regionFileDirHead.getParentFile();
//string.append(regionFileDirParent.toString());
string.append(regionFileDirHead);
RegionFile regionFile = new RegionFile(regionFileDirHead, regionFileDirParent, true);
for (int x = 0; x < 32; x++)
{
for (int z = 0; z < 32; z++)
{
preGeneratedChunkPos[x * 32 + z] = regionFile.doesChunkExist(new ChunkPos(regionPosX * 32 + x, regionPosZ * 32 + z));
}
}
string.append("region " + regionPosX + " " + regionPosZ + "\n");
for (int x = 0; x < 32; x++)
{
for (int z = 0; z < 32; z++)
{
//regionFile.doesChunkExist()
string.append(preGeneratedChunkPos[x * 32 + z] + "\t");
}
string.append("\n");
}
regionFile.close();
}
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println(string);
}*/
}
/**
* Return true if the chunk has been pregenerated in game
*/
//public boolean isChunkPreGenerated(int xChunkPos, int zChunkPos)
//{
// xChunkPos = LevelPosUtil.getRegionModule(LodUtil.CHUNK_DETAIL_LEVEL, xChunkPos);
// zChunkPos = LevelPosUtil.getRegionModule(LodUtil.CHUNK_DETAIL_LEVEL, zChunkPos);
// return preGeneratedChunkPos[xChunkPos * 32 + zChunkPos];
//}
/**
* Inserts the data point into the region.
* <p>
* TODO this will always return true unless it has
* @return true if the data was added successfully
*/
public boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
// The dataContainer could have null entries if the
// detailLevel changes.
if (this.dataContainer[detailLevel] == null)
{
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
}
this.dataContainer[detailLevel].addData(data, posX, posZ, verticalIndex);
return true;
}
/**
* Inserts the vertical data into the region.
* <p>
* TODO this will always return true unless it has
* @return true if the data was added successfully
*/
public boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data)
{
//position is already relative
//posX = LevelPosUtil.getRegionModule(detailLevel, posX);
//posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
// The dataContainer could have null entries if the
// detailLevel changes.
if (this.dataContainer[detailLevel] == null)
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
return this.dataContainer[detailLevel].addVerticalData(data, posX, posZ);
}
/**
* Get the dataPoint at the given relative position.
* @return the data at the relative pos and detail level,
* 0 if the data doesn't exist.
*/
public long getData(byte detailLevel, int posX, int posZ, int verticalIndex)
{
return dataContainer[detailLevel].getData(posX, posZ, verticalIndex);
}
/**
* Get the dataPoint at the given relative position.
* @return the data at the relative pos and detail level,
* 0 if the data doesn't exist.
*/
public long getSingleData(byte detailLevel, int posX, int posZ)
{
return dataContainer[detailLevel].getSingleData(posX, posZ);
}
/**
* Clears the datapoint at the given relative position
*/
public void clear(byte detailLevel, int posX, int posZ)
{
dataContainer[detailLevel].clear(posX, posZ);
}
/**
* This method will fill the posToGenerate array with all levelPos that
* are render-able.
* <p>
* TODO why don't we return the posToGenerate, it would make this easier to understand
*/
public void getPosToGenerate(PosToGenerateContainer posToGenerate,
int playerBlockPosX, int playerBlockPosZ)
{
getPosToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerBlockPosX, playerBlockPosZ);
}
/**
* A recursive method that fills the posToGenerate array with all levelPos that
* need to be generated.
* <p>
* TODO why don't we return the posToGenerate, it would make this easier to understand
*/
private void getPosToGenerate(PosToGenerateContainer posToGenerate, byte detailLevel,
int childOffsetPosX, int childOffsetPosZ, int playerPosX, int playerPosZ)
{
// equivalent to 2^(...)
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
// calculate what LevelPos are in range to generate
int maxDistance = LevelPosUtil.maxDistance(detailLevel, childOffsetPosX, childOffsetPosZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
// determine this child's levelPos
byte childDetailLevel = (byte) (detailLevel - 1);
int childPosX = childOffsetPosX * 2;
int childPosZ = childOffsetPosZ * 2;
int childSize = 1 << (LodUtil.REGION_DETAIL_LEVEL - childDetailLevel);
byte targetDetailLevel = DetailDistanceUtil.getLodGenDetail(DetailDistanceUtil.getGenerationDetailFromDistance(maxDistance)).detailLevel;
if (targetDetailLevel <= detailLevel)
{
if (targetDetailLevel == detailLevel)
{
if (!doesDataExist(detailLevel, childOffsetPosX, childOffsetPosZ))
posToGenerate.addPosToGenerate(detailLevel, childOffsetPosX + regionPosX * size, childOffsetPosZ + regionPosZ * size);
}
else
{
// we want at max one request per chunk (since the world generator creates chunks).
// So for lod smaller than a chunk, only recurse down
// the top right child
if (detailLevel > LodUtil.CHUNK_DETAIL_LEVEL)
{
int ungeneratedChildren = 0;
// make sure all children are generated to this detailLevel
for (int x = 0; x <= 1; x++)
{
for (int z = 0; z <= 1; z++)
{
if (!doesDataExist(childDetailLevel, childPosX + x, childPosZ + z))
{
ungeneratedChildren++;
posToGenerate.addPosToGenerate(childDetailLevel, childPosX + x + regionPosX * childSize, childPosZ + z + regionPosZ * childSize);
}
}
}
// only if all the children are correctly generated
// should we go deeper
if (ungeneratedChildren == 0)
for (int x = 0; x <= 1; x++)
for (int z = 0; z <= 1; z++)
getPosToGenerate(posToGenerate, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ);
}
else
{
// The detail Level is smaller than a chunk.
// Only recurse down 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
getPosToGenerate(posToGenerate, childDetailLevel, childPosX, childPosZ, playerPosX, playerPosZ);
}
}
}
}
// we have gone beyond the target Detail level
// we can stop generating
}
/**
* This method will fill the posToRender array with all levelPos that
* are render-able.
* <p>
* TODO why don't we return the posToRender, it would make this easier to understand
*/
public void getPosToRender(PosToRenderContainer posToRender,
int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel)
{
getPosToRender(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ, requireCorrectDetailLevel);
}
/**
* This method will fill the posToRender array with all levelPos that
* are render-able.
* <p>
* TODO why don't we return the posToRender, it would make this easier to understand
* TODO this needs some more comments, James was only able to figure out part of it
*/
private void getPosToRender(PosToRenderContainer posToRender,
byte detailLevel, int posX, int posZ,
int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel)
{
// equivalent to 2^(...)
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
byte desiredLevel;
int maxDistance;
int minDistance;
int childLevel;
// calculate the LevelPos that are in range
maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
desiredLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(maxDistance));
minDistance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
childLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(minDistance));
if (detailLevel == childLevel - 1)
{
posToRender.addPosToRender(detailLevel,
posX + regionPosX * size,
posZ + regionPosZ * size);
}
else
//if (desiredLevel > detailLevel)
//{
// we have gone beyond the target Detail level
// we can stop generating
//} else
if (desiredLevel == detailLevel)
{
posToRender.addPosToRender(detailLevel,
posX + regionPosX * size,
posZ + regionPosZ * size);
}
else //case where (detailLevel > desiredLevel)
{
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))
{
if (!requireCorrectDetailLevel)
childrenCount++;
else
getPosToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
}
}
}
if (!requireCorrectDetailLevel)
{
// If all the four children exist go deeper
if (childrenCount == 4)
{
for (int x = 0; x <= 1; x++)
for (int z = 0; z <= 1; z++)
getPosToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
}
else
{
posToRender.addPosToRender(detailLevel,
posX + regionPosX * size,
posZ + regionPosZ * size);
}
}
}
}
/**
* Updates all children.
* <p>
* TODO could this be renamed mergeArea?
*/
public void updateArea(byte detailLevel, int posX, int posZ)
{
int width;
int startX;
int startZ;
// TODO what are each of these loops updating?
for (byte down = (byte) (minDetailLevel + 1); down <= detailLevel; down++)
{
startX = LevelPosUtil.convert(detailLevel, posX, down);
startZ = LevelPosUtil.convert(detailLevel, posZ, down);
width = 1 << (detailLevel - down);
for (int x = 0; x < width; x++)
for (int z = 0; z < width; z++)
update(down, 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));
}
}
/**
* Update the child at the given relative Pos
* <p>
* TODO could this be renamed mergeChildData?
*/
private void update(byte detailLevel, int posX, int posZ)
{
dataContainer[detailLevel].updateData(dataContainer[detailLevel - 1], posX, posZ);
}
/**
* Returns if data exists at the given relative Pos.
*/
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
{
if (detailLevel < minDetailLevel)
return false;
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
if (dataContainer == null || dataContainer[detailLevel] == null)
return false;
return dataContainer[detailLevel].doesItExist(posX, posZ);
}
/**
* Gets the generation mode for the data point at the given relative pos.
*/
public byte getGenerationMode(byte detailLevel, int posX, int posZ)
{
if (dataContainer[detailLevel].doesItExist(posX, posZ))
// We take the bottom information always
// TODO what does that mean? bottom of what?
return DataPointUtil.getGenerationMode(dataContainer[detailLevel].getSingleData(posX, posZ));
else
return DistanceGenerationMode.NONE.complexity;
}
/**
* Returns the lowest (least detailed) detail level in this region
* TODO is that right?
*/
public byte getMinDetailLevel()
{
return minDetailLevel;
}
/**
* Returns the LevelContainer for the detailLevel
* @throws IllegalArgumentException if the detailLevel is less than minDetailLevel
*/
public LevelContainer getLevel(byte detailLevel)
{
if (detailLevel < minDetailLevel)
throw new IllegalArgumentException("getLevel asked for a detail level that does not exist: minimum: [" + minDetailLevel + "] level requested: [" + detailLevel + "]");
return dataContainer[detailLevel];
}
/**
* Add the levelContainer to this Region, updating the minDetailLevel
* if necessary.
* @throws IllegalArgumentException if the LevelContainer's detailLevel
* is 2 or more detail levels lower than the
* minDetailLevel of this region.
*/
public void addLevelContainer(LevelContainer levelContainer)
{
if (levelContainer.getDetailLevel() < minDetailLevel - 1)
{
throw new IllegalArgumentException(
"the LevelContainer's detailLevel was "
+ "[" + levelContainer.getDetailLevel() + "] but this region "
+ "only allows adding LevelContainers with a "
+ "detail level of [" + (minDetailLevel - 1) + "]");
}
if (levelContainer.getDetailLevel() == minDetailLevel - 1)
minDetailLevel = levelContainer.getDetailLevel();
dataContainer[levelContainer.getDetailLevel()] = levelContainer;
}
// TODO James thinks cutTree and growTree (which he renamed to match cutTree)
// should have more descriptive names, to make sure the "Tree" portion isn't
// confused with Minecraft trees (the plant).
/**
* Removes any dataContainers that are higher than
* the given detailLevel
*/
public void cutTree(byte detailLevel)
{
if (detailLevel > minDetailLevel)
{
for (byte detailLevelIndex = 0; detailLevelIndex < detailLevel; detailLevelIndex++)
dataContainer[detailLevelIndex] = null;
minDetailLevel = detailLevel;
}
}
/**
* Make this region more detailed to the detailLevel given.
* TODO is that correct?
*/
public void growTree(byte detailLevel)
{
if (detailLevel < minDetailLevel)
{
for (byte detailLevelIndex = (byte) (minDetailLevel - 1); detailLevelIndex >= detailLevel; detailLevelIndex--)
{
if (dataContainer[detailLevelIndex + 1] == null)
dataContainer[detailLevelIndex + 1] = new VerticalLevelContainer((byte) (detailLevelIndex + 1));
dataContainer[detailLevelIndex] = dataContainer[detailLevelIndex + 1].expand();
}
minDetailLevel = detailLevel;
}
}
/**
* return RegionPos of this lod region
*/
public RegionPos getRegionPos()
{
return new RegionPos(regionPosX, regionPosZ);
}
/**
* Returns how many LODs are in this region
*/
public int getNumberOfLods()
{
int count = 0;
for (LevelContainer container : dataContainer)
count += container.getMaxNumberOfLods();
return count;
}
public VerticalQuality getVerticalQuality()
{
return verticalQuality;
}
public DistanceGenerationMode getGenerationMode()
{
return generationMode;
}
public int getMaxVerticalData(byte detailLevel)
{
return dataContainer[detailLevel].getMaxVerticalData();
}
@Override
public String toString()
{
return getLevel(LodUtil.REGION_DETAIL_LEVEL).toString();
}
}
@@ -0,0 +1,172 @@
/*
* 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.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 9-27-2021
*/
public class LodWorld
{
/** name of this world */
private String worldName;
/** dimensions in this world */
private Map<DimensionType, LodDimension> lodDimensions;
/** If true then the LOD world is setup and ready to use */
private boolean isWorldLoaded = false;
/** the name given to the world if it isn't loaded */
public static final String NO_WORLD_LOADED = "No world loaded";
public LodWorld()
{
worldName = NO_WORLD_LOADED;
}
/**
* Set up the LodWorld with the given newWorldName. <br>
* This should be done whenever loading a new world. <br><br>
* <p>
* Note a System.gc() call may be in order after calling this <Br>
* since a lot of LOD data is now homeless. <br>
* @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<>();
isWorldLoaded = true;
}
/**
* Set the worldName to "No world loaded"
* and clear the lodDimensions Map. <br>
* This should be done whenever unloaded a world. <br><br>
* <p>
* Note a System.gc() call may be in order after calling this <Br>
* since a lot of LOD data is now homeless. <br>
*/
public void deselectWorld()
{
worldName = NO_WORLD_LOADED;
lodDimensions = null;
isWorldLoaded = false;
}
/**
* Adds newDimension to this world, if a LodDimension
* already exists for the given dimension it is replaced.
*/
public void addLodDimension(LodDimension newDimension)
{
if (lodDimensions == null)
return;
lodDimensions.put(newDimension.dimension, newDimension);
}
/**
* Returns null if no LodDimension 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 newRegionWidth)
{
if (lodDimensions == null)
return;
saveAllDimensions();
for (DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).setRegionWidth(newRegionWidth);
}
/**
* Requests all dimensions save any dirty regions they may have.
*/
public void saveAllDimensions()
{
if (lodDimensions == null)
return;
// TODO we should only print this if lods were actually saved to file
// but that requires a LodDimension.hasDirtyRegions() method or something similar
ClientProxy.LOGGER.info("Saving LODs");
for (DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).saveDirtyRegionsToFileAsync();
}
public boolean getIsWorldNotLoaded()
{
return !isWorldLoaded;
}
public String getWorldName()
{
return worldName;
}
@Override
public String toString()
{
return "World name: " + worldName;
}
}
@@ -0,0 +1,64 @@
/*
* 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.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 final NearOrFarSetting near = new NearOrFarSetting(FogDistance.NEAR);
public final NearOrFarSetting far = new NearOrFarSetting(FogDistance.FAR);
/**
* If true that means Minecraft is
* rendering fog alongside us
*/
public boolean vanillaIsRenderingFog = true;
public NearFarFogSettings()
{
}
/**
* This holds all relevant data to rendering fog at either
* near or far distances.
*/
public static class NearOrFarSetting
{
public FogQuality quality = FogQuality.FANCY;
public FogDistance distance;
public NearOrFarSetting(FogDistance newFogDistance)
{
distance = newFogDistance;
}
}
}
@@ -0,0 +1,210 @@
/*
* 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.objects;
import com.seibel.lod.util.LevelPosUtil;
/**
* Holds the levelPos that need to be generated.
* TODO is that correct?
*
* @author Leonardo Amato
* @version 9-27-2021
*/
public class PosToGenerateContainer
{
private final int playerPosX;
private final int playerPosZ;
private final byte farMinDetail;
private int nearSize;
private int farSize;
// TODO what is the format of these two arrays? [detailLevel][4-children]?
private final int[][] nearPosToGenerate;
private final int[][] farPosToGenerate;
public PosToGenerateContainer(byte farMinDetail, int maxDataToGenerate, int playerPosX, int playerPosZ)
{
this.playerPosX = playerPosX;
this.playerPosZ = playerPosZ;
this.farMinDetail = farMinDetail;
nearSize = 0;
farSize = 0;
nearPosToGenerate = new int[maxDataToGenerate][4];
farPosToGenerate = new int[maxDataToGenerate][4];
}
// TODO what is going on in this method?
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 < farPosToGenerate.length)
farSize++;
index = farSize - 1;
while (index > 0 && LevelPosUtil.compareDistance(distance, farPosToGenerate[index - 1][3]) <= 0)
{
farPosToGenerate[index][0] = farPosToGenerate[index - 1][0];
farPosToGenerate[index][1] = farPosToGenerate[index - 1][1];
farPosToGenerate[index][2] = farPosToGenerate[index - 1][2];
farPosToGenerate[index][3] = farPosToGenerate[index - 1][3];
index--;
}
if (index != farSize - 1 || farSize != farPosToGenerate.length)
{
farPosToGenerate[index][0] = detailLevel + 1;
farPosToGenerate[index][1] = posX;
farPosToGenerate[index][2] = posZ;
farPosToGenerate[index][3] = distance;
}
}
else
{
//We are introducing a position in the near array
if (nearSize < nearPosToGenerate.length)
nearSize++;
index = nearSize - 1;
while (index > 0 && LevelPosUtil.compareDistance(distance, nearPosToGenerate[index - 1][3]) <= 0)
{
nearPosToGenerate[index][0] = nearPosToGenerate[index - 1][0];
nearPosToGenerate[index][1] = nearPosToGenerate[index - 1][1];
nearPosToGenerate[index][2] = nearPosToGenerate[index - 1][2];
nearPosToGenerate[index][3] = nearPosToGenerate[index - 1][3];
index--;
}
if (index != nearSize - 1 || nearSize != nearPosToGenerate.length)
{
nearPosToGenerate[index][0] = detailLevel + 1;
nearPosToGenerate[index][1] = posX;
nearPosToGenerate[index][2] = posZ;
nearPosToGenerate[index][3] = distance;
}
}
}
public int getNumberOfPos()
{
return nearSize + farSize;
}
public int getNumberOfNearPos()
{
return nearSize;
}
public int getNumberOfFarPos()
{
return farSize;
}
// TODO what does getNth mean? could the name be more descriptive or is it just a index?
public int getNthDetail(int n, boolean near)
{
if (near)
return nearPosToGenerate[n][0];
else
return farPosToGenerate[n][0];
}
public int getNthPosX(int n, boolean near)
{
if (near)
return nearPosToGenerate[n][1];
else
return farPosToGenerate[n][1];
}
public int getNthPosZ(int n, boolean near)
{
if (near)
return nearPosToGenerate[n][2];
else
return farPosToGenerate[n][2];
}
public int getNthGeneration(int n, boolean near)
{
if (near)
return nearPosToGenerate[n][3];
else
return farPosToGenerate[n][3];
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append('\n');
builder.append('\n');
builder.append('\n');
builder.append("near pos to generate");
builder.append('\n');
for (int[] ints : nearPosToGenerate)
{
if (ints[0] == 0)
break;
builder.append(ints[0] - 1);
builder.append(" ");
builder.append(ints[1]);
builder.append(" ");
builder.append(ints[2]);
builder.append(" ");
builder.append(ints[3]);
builder.append('\n');
}
builder.append('\n');
builder.append("far pos to generate");
builder.append('\n');
for (int[] ints : farPosToGenerate)
{
if (ints[0] == 0)
break;
builder.append(ints[0] - 1);
builder.append(" ");
builder.append(ints[1]);
builder.append(" ");
builder.append(ints[2]);
builder.append(" ");
builder.append(ints[3]);
builder.append('\n');
}
return builder.toString();
}
}
@@ -0,0 +1,148 @@
/*
* 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.objects;
import java.util.Arrays;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.LevelPosUtil;
import com.seibel.lod.util.LodUtil;
/**
* @author Leonardo Amato
* @version 9-18-2021
*/
public class PosToRenderContainer
{
public byte minDetail;
private final int size;
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
* no since bools are stored as bytes anyway - cola*/
private byte[][] population;
public PosToRenderContainer(byte minDetail, int regionPosX, int regionPosZ)
{
this.minDetail = minDetail;
this.numberOfPosToRender = 0;
this.regionPosX = regionPosX;
this.regionPosZ = regionPosZ;
this.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)
{
// When rapidly changing dimensions the bufferBuilder can cause this,
// James isn't sure why, but this will prevent an exception at
// the very least (while stilling logging the problem).
if (numberOfPosToRender >= posToRender.length)
{
// This is might be due to dimensions having a different width
// when first loading in
ClientProxy.LOGGER.error("Unable to addPosToRender. numberOfPosToRender [" + numberOfPosToRender + "] detailLevel [" + detailLevel + "] Pos [" + posX + "," + posZ + "]");
numberOfPosToRender++; // incrementing so we can see how many pos over the limit we would go
return;
}
//if(numberOfPosToRender >= posToRender.length)
// posToRender = Arrays.copyOf(posToRender, posToRender.length*2);
posToRender[numberOfPosToRender * 3] = detailLevel;
posToRender[numberOfPosToRender * 3 + 1] = posX;
posToRender[numberOfPosToRender * 3 + 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)
{
Arrays.fill(posToRender, 0);
for (byte[] bytes : population)
Arrays.fill(bytes, (byte) 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 * 3];
}
public int getNthPosX(int n)
{
return posToRender[n * 3 + 1];
}
public int getNthPosZ(int n)
{
return posToRender[n * 3 + 2];
}
@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 * 3]);
builder.append(" ");
builder.append(posToRender[i * 3 + 1]);
builder.append(" ");
builder.append(posToRender[i * 3 + 2]);
builder.append('\n');
}
builder.append('\n');
return builder.toString();
}
}
@@ -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.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><br>
* <p>
* Sets x and z to 0
*/
public RegionPos()
{
x = 0;
z = 0;
}
/** simple constructor that sets x and z to new x and z. */
public RegionPos(int newX, int newZ)
{
x = newX;
z = newZ;
}
/** Converts from a BlockPos to a RegionPos */
public RegionPos(BlockPos pos)
{
this(new ChunkPos(pos));
}
/** Converts from a ChunkPos to a RegionPos */
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 + ")";
}
}
@@ -0,0 +1,242 @@
/*
* 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.objects;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LevelPosUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.util.ThreadMapUtil;
/**
*
* @author Leonardo Amato
* @version ??
*/
public class VerticalLevelContainer implements LevelContainer
{
public final byte detailLevel;
public final int size;
public final int maxVerticalData;
public final long[] dataContainer;
public VerticalLevelContainer(byte detailLevel)
{
this.detailLevel = detailLevel;
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
maxVerticalData = DetailDistanceUtil.getMaxVerticalData(detailLevel);
dataContainer = new long[size * size * DetailDistanceUtil.getMaxVerticalData(detailLevel)];
}
@Override
public byte getDetailLevel()
{
return detailLevel;
}
@Override
public void clear(int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
for (int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++)
{
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = DataPointUtil.EMPTY_DATA;
}
}
@Override
public boolean addData(long data, int posX, int posZ, int verticalIndex)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = data;
return true;
}
@Override
public boolean addVerticalData(long[] data, int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
for (int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++)
dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = data[verticalIndex];
return true;
}
@Override
public boolean addSingleData(long data, int posX, int posZ)
{
return addData(data, posX, posZ, 0);
}
@Override
public long getData(int posX, int posZ, int verticalIndex)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex];
}
@Override
public long getSingleData(int posX, int posZ)
{
return getData(posX, posZ, 0);
}
@Override
public int getMaxVerticalData()
{
return maxVerticalData;
}
public int getSize()
{
return size;
}
@Override
public boolean doesItExist(int posX, int posZ)
{
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return DataPointUtil.doesItExist(getSingleData(posX, posZ));
}
public VerticalLevelContainer(byte[] inputData)
{
int tempIndex;
int index = 0;
long newData;
detailLevel = inputData[index];
index++;
maxVerticalData = inputData[index];
index++;
size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
int x = size * size * maxVerticalData;
this.dataContainer = new long[x];
for (int i = 0; i < x; i++)
{
newData = 0;
for (tempIndex = 0; tempIndex < 8; tempIndex++)
newData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
index += 8;
dataContainer[i] = newData;
}
}
@Override
public LevelContainer expand()
{
return new VerticalLevelContainer((byte) (getDetailLevel() - 1));
}
@Override
public void updateData(LevelContainer lowerLevelContainer, int posX, int posZ)
{
//We reset the array
long[] dataToMerge = ThreadMapUtil.getVerticalUpdateArray(detailLevel);
int lowerMaxVertical = dataToMerge.length / 4;
int childPosX;
int childPosZ;
long[] data;
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;
for (int verticalIndex = 0; verticalIndex < lowerMaxVertical; verticalIndex++)
dataToMerge[(z * 2 + x) * lowerMaxVertical + verticalIndex] = lowerLevelContainer.getData(childPosX, childPosZ, verticalIndex);
}
}
data = DataPointUtil.mergeMultiData(dataToMerge, lowerMaxVertical, getMaxVerticalData());
for (int verticalIndex = 0; (verticalIndex < data.length) && (verticalIndex < maxVerticalData); verticalIndex++)
{
addData(data[verticalIndex],
posX,
posZ,
verticalIndex);
}
}
@Override
public byte[] toDataString()
{
int index = 0;
int x = size * size * maxVerticalData;
int tempIndex;
long current;
byte[] tempData = ThreadMapUtil.getSaveContainer(detailLevel);
tempData[index] = detailLevel;
index++;
tempData[index] = (byte) maxVerticalData;
index++;
for (int i = 0; i < x; i++)
{
current = dataContainer[i];
for (tempIndex = 0; tempIndex < 8; tempIndex++)
tempData[index + tempIndex] = (byte) (current >>> (8 * tempIndex));
index += 8;
}
return tempData;
}
@Override
@SuppressWarnings("unused")
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(dataContainer[x][z][0]));
stringBuilder.append(DATA_DELIMITER);
}
}
return stringBuilder.toString();
*/
return " ";
}
@Override
public int getMaxNumberOfLods()
{
return size * size * getMaxVerticalData();
}
}
@@ -0,0 +1,395 @@
/*
* 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.proxy;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.seibel.lod.builders.bufferBuilding.LodBufferBuilder;
import com.seibel.lod.builders.lodBuilding.LodBuilder;
import com.seibel.lod.builders.worldGeneration.LodGenWorker;
import com.seibel.lod.builders.worldGeneration.LodWorldGenerator;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
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.DataPointUtil;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.util.ThreadMapUtil;
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 the mod.
* @author James_Seibel
* @version 10-23-2021
*/
public class ClientProxy
{
public static final Logger LOGGER = LogManager.getLogger("LOD");
/**
* there is some setup that should only happen once,
* once this is true that setup has completed
*/
private boolean firstTimeSetupComplete = false;
private static final LodWorld lodWorld = new LodWorld();
private static final LodBuilder lodBuilder = new LodBuilder();
private static final LodBufferBuilder lodBufferBuilder = new LodBufferBuilder();
private static LodRenderer renderer = new LodRenderer(lodBufferBuilder);
private static final LodWorldGenerator lodWorldGenerator = LodWorldGenerator.INSTANCE;
private boolean configOverrideReminderPrinted = false;
private final 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(MatrixStack mcMatrixStack, float partialTicks)
{
// comment out when creating a release
// applyConfigOverrides();
// clear any out of date objects
mc.clearFrameObjectCache();
try
{
// only run the first time setup once
if (!firstTimeSetupComplete)
firstFrameSetup();
if (mc == null || mc.getPlayer() == null || lodWorld.getIsWorldNotLoaded())
return;
LodDimension lodDim = lodWorld.getLodDimension(mc.getCurrentDimension());
if (lodDim == null)
return;
DetailDistanceUtil.updateSettings();
viewDistanceChangedEvent();
playerMoveEvent(lodDim);
lodDim.cutRegionNodesAsync((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ());
lodDim.expandOrLoadRegionsAsync((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ());
// 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, mcMatrixStack, partialTicks, mc.getProfiler());
profiler.pop(); // end LOD
profiler.push("terrain"); // go back into "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.qualityOption.lodChunkRenderDistance.get();
}
catch (Exception e)
{
LOGGER.error("client proxy: " + e.getMessage());
e.printStackTrace();
}
}
/** used in a development environment to change settings on the fly */
private void applyConfigOverrides()
{
// remind the developer(s) that the config override is active
if (!configOverrideReminderPrinted)
{
// mc.getPlayer().sendMessage(new StringTextComponent("LOD experimental build 1.5.1"), mc.getPlayer().getUUID());
// mc.getPlayer().sendMessage(new StringTextComponent("Here be dragons!"), mc.getPlayer().getUUID());
mc.getPlayer().sendMessage(new StringTextComponent("Debug settings enabled!"), mc.getPlayer().getUUID());
configOverrideReminderPrinted = true;
}
// LodConfig.CLIENT.graphics.drawResolution.set(HorizontalResolution.BLOCK);
// LodConfig.CLIENT.worldGenerator.generationResolution.set(HorizontalResolution.BLOCK);
// requires a world restart?
// LodConfig.CLIENT.worldGenerator.lodQualityMode.set(VerticalQuality.VOXEL);
// LodConfig.CLIENT.graphics.fogQualityOption.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.vanillaOverdraw.set(VanillaOverdraw.HALF);
// LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(GpuUploadMethod.BUFFER_STORAGE);
// LodConfig.CLIENT.worldGenerator.distanceGenerationMode.set(DistanceGenerationMode.SURFACE);
// LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.set(128);
// LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR);
// LodConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.set(false);
// LodConfig.CLIENT.buffers.rebuildTimes.set(BufferRebuildTimes.FREQUENT);
LodConfig.CLIENT.advancedModOptions.debugging.enableDebugKeybindings.set(true);
// LodConfig.CLIENT.debugging.debugMode.set(DebugMode.SHOW_DETAIL);
}
//==============//
// forge events //
//==============//
@SubscribeEvent
public void serverTickEvent(TickEvent.ServerTickEvent event)
{
if (mc == null || mc.getPlayer() == null || lodWorld.getIsWorldNotLoaded())
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();
}
/** This is also called when a new dimension loads */
@SubscribeEvent
public void worldLoadEvent(WorldEvent.Load event)
{
DataPointUtil.worldHeight = event.getWorld().getHeight();
//LodNodeGenWorker.restartExecutorService();
//ThreadMapUtil.clearMaps();
// 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
ThreadMapUtil.clearMaps();
if (mc.getConnection().getLevel() == null)
{
// the player just left the server
// if this isn't done unfinished tasks may be left in the queue
// preventing new LodChunks form being generated
//LodNodeGenWorker.restartExecutorService();
//ThreadMapUtil.clearMaps();
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0);
lodWorld.deselectWorld();
// hopefully this should reduce issues related to the buffer builder
// breaking when changing worlds.
renderer.destroyBuffers();
recalculateWidths = true;
renderer = new LodRenderer(lodBufferBuilder);
// make sure the nilled objects are freed.
// (this prevents an out of memory error when
// changing worlds)
System.gc();
}
}
@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.advancedModOptions.debugging.enableDebugKeybindings.get()
&& event.getKey() == GLFW.GLFW_KEY_F4 && event.getAction() == GLFW.GLFW_PRESS)
{
LodConfig.CLIENT.advancedModOptions.debugging.debugMode.set(LodConfig.CLIENT.advancedModOptions.debugging.debugMode.get().getNext());
}
if (LodConfig.CLIENT.advancedModOptions.debugging.enableDebugKeybindings.get()
&& event.getKey() == GLFW.GLFW_KEY_F6 && event.getAction() == GLFW.GLFW_PRESS)
{
LodConfig.CLIENT.advancedModOptions.debugging.drawLods.set(!LodConfig.CLIENT.advancedModOptions.debugging.drawLods.get());
}
}
//============//
// 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.getCenterRegionPosX(), playerRegionPos.z - lodDim.getCenterRegionPosZ());
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 need to be. */
private void viewDistanceChangedEvent()
{
// calculate how wide the dimension(s) should be in regions
int chunksWide;
if (mc.getClientWorld().dimensionType().hasCeiling())
chunksWide = Math.min(LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * 2 + 1;
else
chunksWide = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * 2 + 1;
int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS);
// make sure we have an odd number of regions
newWidth += (newWidth & 1) == 0 ? 1 : 2;
// 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(lodWorld.getLodDimension(mc.getClientWorld().dimensionType()));
recalculateWidths = false;
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
}
DetailDistanceUtil.updateSettings();
}
/** This event is called once during the first frame Minecraft renders in the world. */
public void firstFrameSetup()
{
// make sure the GlProxy is created before the LodBufferBuilder needs it
GlProxy.getInstance();
firstTimeSetupComplete = true;
}
/** this method reset some static data every time we change world */
private void resetMod()
{
ThreadMapUtil.clearMaps();
LodGenWorker.restartExecutorService();
}
//================//
// public getters //
//================//
public static LodWorld getLodWorld()
{
return lodWorld;
}
public static LodBuilder getLodBuilder()
{
return lodBuilder;
}
public static LodRenderer getRenderer()
{
return renderer;
}
}
@@ -0,0 +1,244 @@
/*
* 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.proxy;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.lod.ModInfo;
import com.seibel.lod.enums.GlProxyContext;
import com.seibel.lod.wrappers.MinecraftWrapper;
/**
* A singleton that holds references to different openGL contexts
* and GPU capabilities.
*
* <p>
* Helpful OpenGL resources: <br><br>
* <p>
* https://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf <br>
* https://learnopengl.com/Advanced-OpenGL/Advanced-Data <br>
* https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one <br><br>
*
* @author James Seibel
* @version 10-23-2021
*/
public class GlProxy
{
private static GlProxy instance = null;
private static MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/** Minecraft's GLFW window */
public final long minecraftGlContext;
/** Minecraft's GL context */
public final GLCapabilities minecraftGlCapabilities;
/** the LodBuilder's GLFW window */
public final long lodBuilderGlContext;
/** the LodBuilder's GL context */
public final GLCapabilities lodBuilderGlCapabilities;
/**
* This is just used for debugging, hopefully it can be removed once
* the context switching is more stable.
*/
public Thread lodBuilderOwnerThread = null;
/** Does this computer's GPU support fancy fog? */
public final boolean fancyFogAvailable;
/** Requires OpenGL 4.5, and offers the best buffer uploading */
public final boolean bufferStorageSupported;
/** Requires OpenGL 3.0 */
public final boolean mapBufferRangeSupported;
private GlProxy()
{
ClientProxy.LOGGER.error("Creating " + GlProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error.");
// getting Minecraft's context has to be done on the render thread,
// where the GL context is
if (!RenderSystem.isOnRenderThread())
throw new IllegalStateException(GlProxy.class.getSimpleName() + " was created outside the render thread!");
//============================//
// create the builder context //
//============================//
// get Minecraft's context
minecraftGlContext = GLFW.glfwGetCurrentContext();
minecraftGlCapabilities = GL.getCapabilities();
// create the LodBuilder's context
// Hopefully this shouldn't cause any issues with other mods that need custom contexts
// (although the number that do should be relatively few)
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
// create an invisible window to hold the context
lodBuilderGlContext = GLFW.glfwCreateWindow(640, 480, "LOD window", 0L, minecraftGlContext);
GLFW.glfwMakeContextCurrent(lodBuilderGlContext);
lodBuilderGlCapabilities = GL.createCapabilities();
// Since this is called on the render thread, make sure the Minecraft context is used in the end
GLFW.glfwMakeContextCurrent(minecraftGlContext);
GL.setCapabilities(minecraftGlCapabilities);
//==============================//
// determine the OpenGL version //
//==============================//
bufferStorageSupported = minecraftGlCapabilities.OpenGL45;
mapBufferRangeSupported = minecraftGlCapabilities.OpenGL30;
if (!minecraftGlCapabilities.OpenGL15)
{
String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GlProxy.class.getSimpleName() + " and discoverd this GPU doesn't support OpenGL 1.5 or greater.";
mc.crashMinecraft(errorMessage + " Sorry I couldn't tell you sooner :(", new UnsupportedOperationException("This GPU doesn't support OpenGL 1.5 or greater."));
}
if (!bufferStorageSupported)
{
String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5";
ClientProxy.LOGGER.error("This GPU doesn't support OpenGL 4.5, falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance.");
}
//==================================//
// get any GPU related capabilities //
//==================================//
// 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 the fancy fog option will not be available.");
// GlProxy creation success
ClientProxy.LOGGER.error(GlProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day.");
}
/**
* A wrapper function to make switching contexts easier. <br>
* Does nothing if the calling thread is already using newContext.
*/
public void setGlContext(GlProxyContext newContext)
{
GlProxyContext currentContext = getGlContext();
// we don't have to change the context, we're already there.
if (currentContext == newContext)
return;
long contextPointer = 0L;
GLCapabilities newGlCapabilities = null;
// get the pointer(s) for this context
switch (newContext)
{
case LOD_BUILDER:
contextPointer = lodBuilderGlContext;
newGlCapabilities = lodBuilderGlCapabilities;
break;
case MINECRAFT:
contextPointer = minecraftGlContext;
newGlCapabilities = minecraftGlCapabilities;
break;
default: // default should never happen, it is just here to make the compiler happy
case NONE:
// 0L is equivalent to null
break;
}
GLFW.glfwMakeContextCurrent(contextPointer);
GL.setCapabilities(newGlCapabilities);
// used for debugging
if (newContext == GlProxyContext.LOD_BUILDER)
lodBuilderOwnerThread = Thread.currentThread();
else if (newContext == GlProxyContext.NONE && currentContext == GlProxyContext.LOD_BUILDER)
lodBuilderOwnerThread = null;
}
/** Returns this thread's OpenGL context. */
public GlProxyContext getGlContext()
{
long currentContext = GLFW.glfwGetCurrentContext();
if (currentContext == lodBuilderGlContext)
return GlProxyContext.LOD_BUILDER;
else if (currentContext == minecraftGlContext)
return GlProxyContext.MINECRAFT;
else if (currentContext == 0L)
return GlProxyContext.NONE;
else
// hopefully this shouldn't happen, but
// at least now we will be notified if it does happen
throw new IllegalStateException(Thread.currentThread().getName() + " has a unknown OpenGl context: [" + currentContext + "]. Minecraft context [" + minecraftGlContext + "], LodBuilder context [" + lodBuilderGlContext + "], no context [0].");
}
public static GlProxy getInstance()
{
if (instance == null)
instance = new GlProxy();
return instance;
}
}
@@ -0,0 +1,943 @@
/*
* 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.render;
import java.util.HashSet;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL15C;
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.bufferBuilding.LodBufferBuilder;
import com.seibel.lod.builders.bufferBuilding.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.enums.GpuUploadMethod;
import com.seibel.lod.handlers.ReflectionHandler;
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.proxy.GlProxy;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LevelPosUtil;
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.texture.NativeImage;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.profiler.IProfiler;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3d;
/**
* This is where all the magic happens. <br>
* This is where LODs are draw to the world.
*
* @author James Seibel
* @version 10-23-2021
*/
public class LodRenderer
{
/**
* this is the light used when rendering the LODs,
* it should be something different from what is used by Minecraft
*/
private static final int LOD_GL_LIGHT_NUMBER = GL11.GL_LIGHT2;
/**
* If true the LODs colors will be replaced with
* a checkerboard, this can be used for debugging.
*/
public DebugMode previousDebugMode = DebugMode.OFF;
private final MinecraftWrapper mc;
private final GameRenderer gameRender;
private IProfiler profiler;
private int farPlaneBlockDistance;
/** This is used to generate the buildable buffers */
private final LodBufferBuilder lodBufferBuilder;
/** Each VertexBuffer represents 1 region */
private VertexBuffer[][][] vbos;
/**
* the OpenGL IDs for the vbos of the same indices.
* These have to be separate because we can't override the
* buffers in the VBOs (and we don't want to)
*/
private int[][][] storageBufferIds;
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 };
public NativeImage lightMap = null;
// these variables are used to determine if the buffers should be rebuilt
private float prevSkyBrightness = 0;
private double prevBrightness = 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 int vanillaBlockRenderedDistance;
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 mcMatrixStack This matrix stack should come straight from MC's renderChunkLayer (or future equivalent) method
* @param partialTicks how far into the current tick this method was called.
*/
@SuppressWarnings("deprecation")
public void drawLODs(LodDimension lodDim, MatrixStack mcMatrixStack, 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");
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
// starting here...
determineIfLodsShouldRegenerate(lodDim, partialTicks);
//=================//
// 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.newBuffersAvailable())
{
// 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.newBuffersAvailable()
// 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
if (lodBufferBuilder.newBuffersAvailable())
{
swapBuffers();
}
//===========================//
// GL settings for rendering //
//===========================//
// set the required open GL settings
if (LodConfig.CLIENT.advancedModOptions.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);
// enable transparent rendering
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glEnable(GL11.GL_BLEND);
// 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[] mcProjMatrixRaw = new float[16];
GL11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
Matrix4f mcProjectionMatrix = new Matrix4f(mcProjMatrixRaw);
// OpenGl outputs their matrices in col,row form instead of row,col
// (or maybe vice versa I have no idea :P)
mcProjectionMatrix.transpose();
Matrix4f modelViewMatrix = offsetTheModelViewMatrix(mcMatrixStack, partialTicks);
vanillaBlockRenderedDistance = mc.getRenderDistance() * LodUtil.CHUNK_WIDTH;
// required for setupFog and setupProjectionMatrix
if (mc.getClientWorld().dimensionType().hasCeiling())
farPlaneBlockDistance = Math.min(LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * LodUtil.CHUNK_WIDTH;
else
farPlaneBlockDistance = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH;
setupProjectionMatrix(mcProjectionMatrix, partialTicks);
// commented out until we can add shaders to handle lighting
//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)
{
ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera();
Vector3d cameraDir = new Vector3d(renderInfo.getLookVector());
boolean cullingDisabled = LodConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get();
boolean renderBufferStorage = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get() == GpuUploadMethod.BUFFER_STORAGE && GlProxy.getInstance().bufferStorageSupported;
// used to determine what type of fog to render
int halfWidth = vbos.length / 2;
int quarterWidth = vbos.length / 4;
// where the center of the built buffers is (needed when culling regions)
RegionPos vboCenterRegionPos = new RegionPos(vbosCenter);
for (int x = 0; x < vbos.length; x++)
{
for (int z = 0; z < vbos.length; z++)
{
RegionPos vboPos = new RegionPos(
x + vboCenterRegionPos.x - (lodDim.getWidth() / 2),
z + vboCenterRegionPos.z - (lodDim.getWidth() / 2));
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(renderInfo.getBlockPosition(), cameraDir, vboPos.blockPos()))
{
if ((x > halfWidth - quarterWidth && x < halfWidth + quarterWidth)
&& (z > halfWidth - quarterWidth && z < halfWidth + quarterWidth))
setupFog(fogSettings.near.distance, fogSettings.near.quality);
else
setupFog(fogSettings.far.distance, fogSettings.far.quality);
if (storageBufferIds != null && renderBufferStorage)
for (int i = 0; i < storageBufferIds[x][z].length; i++)
drawStorageBuffer(vbos[x][z][i], storageBufferIds[x][z][i], modelViewMatrix);
else
for (int i = 0; i < vbos[x][z].length; i++)
drawVertexBuffer(vbos[x][z][i], 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);
GL11.glDisable(GL11.GL_BLEND);
// 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
gameRender.resetProjectionMatrix(mcProjectionMatrix);
// clear the depth buffer so anything drawn is drawn
// over the LODs
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
// end of internal LOD profiling
profiler.pop();
}
/** This is where the actual drawing happens. */
private void drawStorageBuffer(VertexBuffer vbo, int bufferStorageId, Matrix4f modelViewMatrix)
{
if (vbo == null)
return;
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferStorageId);
// 0L is the starting pointer
LodUtil.LOD_VERTEX_FORMAT.setupBufferState(0L);
vbo.draw(modelViewMatrix, GL11.GL_QUADS);
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
LodUtil.LOD_VERTEX_FORMAT.clearBufferState();
}
/** This is where the actual drawing happens. */
private void drawVertexBuffer(VertexBuffer vbo, Matrix4f modelViewMatrix)
{
if (vbo == null)
return;
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id);
// 0L is the starting pointer
LodUtil.LOD_VERTEX_FORMAT.setupBufferState(0L);
vbo.draw(modelViewMatrix, GL11.GL_QUADS);
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
LodUtil.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.fogQualityOption.fogDistance.get() == FogDistance.NEAR_AND_FAR)
RenderSystem.fogStart(farPlaneBlockDistance * 1.6f * 0.9f);
else
RenderSystem.fogStart(Math.min(vanillaBlockRenderedDistance * 1.5f, farPlaneBlockDistance * 0.9f * 1.6f));
RenderSystem.fogEnd(farPlaneBlockDistance * 1.6f);
}
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 * 0.75f);
RenderSystem.fogEnd(farPlaneBlockDistance * 1.0f);
}
}
else if (fogDistance == FogDistance.NEAR)
{
if (fogQuality == FogQuality.FANCY)
{
RenderSystem.fogEnd(vanillaBlockRenderedDistance * 1.41f);
RenderSystem.fogStart(vanillaBlockRenderedDistance * 1.6f);
}
else if (fogQuality == FogQuality.FAST)
{
RenderSystem.fogEnd(vanillaBlockRenderedDistance * 1.0f);
RenderSystem.fogStart(vanillaBlockRenderedDistance * 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.
*/
@SuppressWarnings("deprecation")
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);
}
}
/**
* Translate the camera relative to the LodDimension's center,
* this is done since all LOD buffers are created in world space
* instead of object space.
* (since AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
* accuracy vs the model view matrix, which only uses floats)
*/
private Matrix4f offsetTheModelViewMatrix(MatrixStack mcMatrixStack, float partialTicks)
{
// duplicate the last matrix
mcMatrixStack.pushPose();
// get all relevant camera info
ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera();
Vector3d projectedView = renderInfo.getPosition();
// 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();
mcMatrixStack.translate(-xDiff, -projectedView.y, -zDiff);
// get the modified model view matrix
Matrix4f lodModelViewMatrix = mcMatrixStack.last().pose();
// remove the lod ModelViewMatrix
mcMatrixStack.popPose();
return lodModelViewMatrix;
}
/**
* James added this to test if Vivecraft is not using
* the MC FOV setting or if the problem is deeper
*/
public enum FovTest
{
LOD_USE_FOV(true, false),
MC_USE_FOV(false, true),
NEITHER(false, false),
BOTH(true, true);
final boolean lodProjUseFov;
final boolean defaultMcProjUseFov;
FovTest(boolean newLodProjUseFov, boolean newDefaultMcProjUseFov)
{
lodProjUseFov = newLodProjUseFov;
defaultMcProjUseFov = newDefaultMcProjUseFov;
}
}
/**
* create a new projection matrix and send it over to the GPU
* @param currentProjectionMatrix this is Minecraft's current projection matrix
* @param partialTicks how many ticks into the frame we are
*/
private void setupProjectionMatrix(Matrix4f currentProjectionMatrix, float partialTicks)
{
//Minimum radious of view in 2 render distance
int minDistance = 1;
// create the new projection matrix
Matrix4f lodPoj =
Matrix4f.perspective(
getFov(partialTicks, true),
(float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(),
minDistance,
farPlaneBlockDistance * LodUtil.CHUNK_WIDTH / 2);
// get Minecraft's un-edited projection matrix
// (this is before it is zoomed, distorted, etc.)
Matrix4f defaultMcProj = mc.getGameRenderer().getProjectionMatrix(mc.getGameRenderer().getMainCamera(), partialTicks, true);
// true here means use "use fov setting" (probably)
// this logic strips away the defaultMcProj matrix, so we
// can get the distortionMatrix, which represents all
// transformations, zooming, distortions, etc. done
// to Minecraft's Projection matrix
Matrix4f defaultMcProjInv = defaultMcProj.copy();
defaultMcProjInv.invert();
Matrix4f distortionMatrix = defaultMcProjInv.copy();
distortionMatrix.multiply(currentProjectionMatrix);
// edit the lod projection to match Minecraft's
// (so the LODs line up with the real world)
lodPoj.multiply(distortionMatrix);
// send the projection over to the GPU
gameRender.resetProjectionMatrix(lodPoj);
}
///** 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 gamma = (float) mc.getOptions().gamma - 0.0f;
float dayEffect = (sunBrightness - 0.2f) * 1.25f;
float lightStrength = (gamma * 0.34f - 0.01f) * (1.0f - dayEffect) + dayEffect - 0.20f; //gamma * 0.2980392157f + 0.1647058824f
float blueLightStrength = (gamma * 0.44f + 0.12f) * (1.0f - dayEffect) + dayEffect - 0.20f; //gamma * 0.4235294118f + 0.2784313725f
float[] lightAmbient = {lightStrength, lightStrength, blueLightStrength, 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(LodDimension lodDim)
{
lodBufferBuilder.setupBuffers(lodDim);
}
//======================//
// 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>
* <p>
* 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;
storageBufferIds = result.storageBufferIds;
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.fogQualityOption.fogDrawOverride.get();
fogSettings.vanillaIsRenderingFog = quality != FogQuality.OFF;
// use any fog overrides the user may have set
switch (override)
{
case FANCY:
quality = FogQuality.FANCY;
break;
case NO_FOG:
quality = FogQuality.OFF;
break;
case FAST:
quality = FogQuality.FAST;
break;
case OPTIFINE_SETTING:
// don't override anything
break;
}
// only use fancy fog if the user's GPU can deliver
if (!GlProxy.getInstance().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.fogQualityOption.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.fogQualityOption.fogDistance.get())
{
case NEAR_AND_FAR:
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, float partialTicks)
{
short chunkRenderDistance = (short) mc.getRenderDistance();
int vanillaRenderedChunksWidth = chunkRenderDistance * 2 + 2;
//=============//
// full regens //
//=============//
// check if the view distance changed
if (ClientProxy.previousLodRenderDistance != LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get()
|| chunkRenderDistance != prevRenderDistance
|| prevFogDistance != LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get())
{
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
DetailDistanceUtil.updateSettings();
fullRegen = true;
previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk);
prevFogDistance = LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get();
prevRenderDistance = chunkRenderDistance;
}
// did the user change the debug setting?
if (LodConfig.CLIENT.advancedModOptions.debugging.debugMode.get() != previousDebugMode)
{
previousDebugMode = LodConfig.CLIENT.advancedModOptions.debugging.debugMode.get();
fullRegen = true;
}
long newTime = System.currentTimeMillis();
// check if the player has moved
if (newTime - prevPlayerPosTime > LodConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.get().playerMoveTimeout)
{
if (LevelPosUtil.getDetailLevel(previousPos) == 0
|| mc.getPlayer().xChunk != LevelPosUtil.getPosX(previousPos)
|| mc.getPlayer().zChunk != LevelPosUtil.getPosZ(previousPos))
{
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
fullRegen = true;
previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk);
}
prevPlayerPosTime = newTime;
}
// determine how far the lighting has to
// change in order to rebuild the buffers
// the max brightness is 1 and the minimum is 0.2
float skyBrightness = lodDim.dimension.hasSkyLight() ? mc.getSkyDarken(partialTicks) : 0.2f;
float minLightingDifference;
switch (LodConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.get())
{
case FREQUENT:
minLightingDifference = 0.025f;
break;
case NORMAL:
minLightingDifference = 0.05f;
break;
default:
case RARE:
minLightingDifference = 0.1f;
break;
}
// check if the lighting changed
if (Math.abs(skyBrightness - prevSkyBrightness) > minLightingDifference
// make sure the lighting gets to the max/minimum value
// (just in case the minLightingDifference is too large to notice the change)
|| (skyBrightness == 1.0f && prevSkyBrightness != 1.0f) // noon
|| (skyBrightness == 0.2f && prevSkyBrightness != 0.2f) // midnight
|| mc.getOptions().gamma != prevBrightness || lightMap == null)
{
fullRegen = true;
lightMap = mc.getCurrentLightMap();
prevBrightness = mc.getOptions().gamma;
prevSkyBrightness = skyBrightness;
}
//================//
// partial regens //
//================//
// check if the vanilla rendered chunks changed
if (newTime - prevVanillaChunkTime > LodConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.get().renderedChunkTimeout)
{
if (vanillaRenderedChunksChanged)
{
partialRegen = true;
vanillaRenderedChunksChanged = false;
}
prevVanillaChunkTime = newTime;
}
// check if there is any newly generated terrain to show
if (newTime - prevChunkTime > LodConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.get().chunkChangeTimeout)
{
if (lodDim.regenDimensionBuffers)
{
partialRegen = true;
lodDim.regenDimensionBuffers = 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 xIndex;
int zIndex;
for (ChunkPos pos : chunkPosToSkip)
{
xIndex = (pos.x - mc.getPlayer().xChunk) + (chunkRenderDistance + 1);
zIndex = (pos.z - mc.getPlayer().zChunk) + (chunkRenderDistance + 1);
// sometimes we are given chunks that are outside the render distance,
// This prevents index out of bounds exceptions
if (xIndex >= 0 && zIndex >= 0
&& xIndex < vanillaRenderedChunks.length
&& zIndex < vanillaRenderedChunks.length)
{
if (!vanillaRenderedChunks[xIndex][zIndex])
{
vanillaRenderedChunks[xIndex][zIndex] = true;
vanillaRenderedChunksChanged = true;
lodDim.markRegionBufferToRegen(pos.getRegionX(), pos.getRegionZ());
}
}
}
// if the player is high enough, draw all LODs
if (chunkPosToSkip.isEmpty() && mc.getPlayer().position().y > 256)
{
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
vanillaRenderedChunksChanged = true;
}
}
}
@@ -0,0 +1,123 @@
/*
* 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.render;
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 10-19-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 center 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);
}
/**
* 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);
Vector3d vboSwVec = new Vector3d(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z + halfRegionWidth);
Vector3d vboNwVec = new Vector3d(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth);
Vector3d vboNeVec = new Vector3d(vboCenterVec.x + halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth);
// 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 disappearances
return objectVector.dot(cameraDir) > -0.1;
}
}
@@ -0,0 +1,115 @@
/*
* 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.util;
import java.awt.Color;
import net.minecraft.client.renderer.texture.NativeImage;
/**
*
* @author Cola
* @author Leonardo Amato
* @version ??
*/
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);
}
/** This method apply the lightmap to the color to use */
public static int applyLightValue(int color, int skyLight, int blockLight, NativeImage lightMap)
{
int lightColor = lightMap.getPixelRGBA(blockLight, skyLight);
int red = ColorUtil.getBlue(lightColor);
int green = ColorUtil.getGreen(lightColor);
int blue = ColorUtil.getRed(lightColor);
return ColorUtil.multiplyRGBcolors(color, ColorUtil.rgbToInt(red, green, blue));
}
/** Edit the given color as an 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();
}
/** Multiply 2 RGB colors */
public static int multiplyRGBcolors(int color1, int color2)
{
return ((getAlpha(color1) * getAlpha(color2) / 255) << 24) | ((getRed(color1) * getRed(color2) / 255) << 16) | ((getGreen(color1) * getGreen(color2) / 255) << 8) | (getBlue(color1) * getBlue(color2) / 255);
}
@SuppressWarnings("unused")
public static String toString(int color)
{
return Integer.toHexString(getAlpha(color)) + " " +
Integer.toHexString(getRed(color)) + " " +
Integer.toHexString(getGreen(color)) + " " +
Integer.toHexString(getBlue(color));
}
}
@@ -0,0 +1,554 @@
/*
* 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.util;
import static com.seibel.lod.builders.bufferBuilding.LodBufferBuilder.skyLightPlayer;
import com.seibel.lod.enums.DistanceGenerationMode;
import net.minecraft.client.renderer.texture.NativeImage;
/**
*
* @author Leonardo Amato
* @version ??
*/
public class DataPointUtil
{
/*
|a |a |a |a |r |r |r |r |
|r |r |r |r |g |g |g |g |
|g |g |g |g |b |b |b |b |
|b |b |b |b |h |h |h |h |
|h |h |h |h |h |h |d |d |
|d |d |d |d |d |d |d |d |
|bl |bl |bl |bl |sl |sl |sl |sl |
|l |l |f |g |g |g |v |e |
*/
// Reminder: bytes have range of [-128, 127].
// When converting to or from an int a 128 should be added or removed.
// If there is a bug with color then it's probably caused by this.
//To be used in the future for negative value
//public final static int MIN_DEPTH = -64;
//public final static int MIN_HEIGHT = -64;
public final static int EMPTY_DATA = 0;
public static int worldHeight = 256;
public final static int ALPHA_DOWNSIZE_SHIFT = 4;
//public final static int BLUE_COLOR_SHIFT = 0;
//public final static int GREEN_COLOR_SHIFT = 8;
//public final static int RED_COLOR_SHIFT = 16;
//public final static int ALPHA_COLOR_SHIFT = 24;
public final static int BLUE_SHIFT = 36;
public final static int GREEN_SHIFT = BLUE_SHIFT + 8;
public final static int RED_SHIFT = BLUE_SHIFT + 16;
public final static int ALPHA_SHIFT = BLUE_SHIFT + 24;
public final static int COLOR_SHIFT = 36;
public final static int HEIGHT_SHIFT = 26;
public final static int DEPTH_SHIFT = 16;
public final static int BLOCK_LIGHT_SHIFT = 12;
public final static int SKY_LIGHT_SHIFT = 8;
//public final static int LIGHTS_SHIFT = SKY_LIGHT_SHIFT;
//public final static int VERTICAL_INDEX_SHIFT = 6;
public final static int FLAG_SHIFT = 5;
public final static int GEN_TYPE_SHIFT = 2;
public final static int VOID_SHIFT = 1;
public final static int EXISTENCE_SHIFT = 0;
public final static long ALPHA_MASK = 0b1111;
public final static long RED_MASK = 0b1111_1111;
public final static long GREEN_MASK = 0b1111_1111;
public final static long BLUE_MASK = 0b1111_1111;
public final static long COLOR_MASK = 0b11111111_11111111_11111111;
public final static long HEIGHT_MASK = 0b11_1111_1111;
public final static long DEPTH_MASK = 0b11_1111_1111;
//public final static long LIGHTS_MASK = 0b1111_1111;
public final static long BLOCK_LIGHT_MASK = 0b1111;
public final static long SKY_LIGHT_MASK = 0b1111;
//public final static long VERTICAL_INDEX_MASK = 0b11;
public final static long FLAG_MASK = 0b1;
public final static long GEN_TYPE_MASK = 0b111;
public final static long VOID_MASK = 1;
public final static long EXISTENCE_MASK = 1;
public static long createVoidDataPoint(int generationMode)
{
long dataPoint = 0;
dataPoint += (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
dataPoint += VOID_MASK << VOID_SHIFT;
dataPoint += EXISTENCE_MASK << EXISTENCE_SHIFT;
return dataPoint;
}
public static long createDataPoint(int height, int depth, int color, int lightSky, int lightBlock, int generationMode, boolean flag)
{
return createDataPoint(
ColorUtil.getAlpha(color),
ColorUtil.getRed(color),
ColorUtil.getGreen(color),
ColorUtil.getBlue(color),
height, depth, lightSky, lightBlock, generationMode, flag);
}
public static long createDataPoint(int alpha, int red, int green, int blue, int height, int depth, int lightSky, int lightBlock, int generationMode, boolean flag)
{
long dataPoint = 0;
dataPoint += (long) (alpha >>> ALPHA_DOWNSIZE_SHIFT) << ALPHA_SHIFT;
dataPoint += (red & RED_MASK) << RED_SHIFT;
dataPoint += (green & GREEN_MASK) << GREEN_SHIFT;
dataPoint += (blue & BLUE_MASK) << BLUE_SHIFT;
dataPoint += (height & HEIGHT_MASK) << HEIGHT_SHIFT;
dataPoint += (depth & DEPTH_MASK) << DEPTH_SHIFT;
dataPoint += (lightBlock & BLOCK_LIGHT_MASK) << BLOCK_LIGHT_SHIFT;
dataPoint += (lightSky & SKY_LIGHT_MASK) << SKY_LIGHT_SHIFT;
dataPoint += (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
if (flag) dataPoint += FLAG_MASK << FLAG_SHIFT;
dataPoint += EXISTENCE_MASK << EXISTENCE_SHIFT;
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 getAlpha(long dataPoint)
{
return (short) ((((dataPoint >>> ALPHA_SHIFT) & ALPHA_MASK) << ALPHA_DOWNSIZE_SHIFT) | 0b1111);
}
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 int getLightSky(long dataPoint)
{
return (int) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
}
public static int getLightSkyAlt(long dataPoint)
{
if (skyLightPlayer == 0 && ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1)
return 0;
else
return (int) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
}
public static int getLightBlock(long dataPoint)
{
return (int) ((dataPoint >>> BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
}
public static boolean getFlag(long dataPoint)
{
return ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1;
}
public static byte getGenerationMode(long dataPoint)
{
return (byte) ((dataPoint >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK);
}
public static boolean isVoid(long dataPoint)
{
return (((dataPoint >>> VOID_SHIFT) & VOID_MASK) == 1);
}
public static boolean doesItExist(long dataPoint)
{
return (((dataPoint >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1);
}
public static int getColor(long dataPoint)
{
return (int) (((dataPoint >>> COLOR_SHIFT) & COLOR_MASK) | (/*((dataPoint >>> (ALPHA_SHIFT - ALPHA_DOWNSIZE_SHIFT)) | 0b1111)*/255 << 24));
}
/** This method apply the lightmap to the color to use */
public static int getLightColor(long dataPoint, NativeImage lightMap)
{
int lightBlock = getLightBlock(dataPoint);
int lightSky = getLightSky(dataPoint);
int color = lightMap.getPixelRGBA(lightBlock, lightSky);
int red = ColorUtil.getBlue(color);
int green = ColorUtil.getGreen(color);
int blue = ColorUtil.getRed(color);
return ColorUtil.multiplyRGBcolors(getColor(dataPoint), ColorUtil.rgbToInt(red, green, blue));
}
/** This is used to convert a dataPoint to string (useful for the print function) */
@SuppressWarnings("unused")
public static String toString(long dataPoint)
{
return getHeight(dataPoint) + " " +
getDepth(dataPoint) + " " +
getAlpha(dataPoint) + " " +
getRed(dataPoint) + " " +
getBlue(dataPoint) + " " +
getGreen(dataPoint) + " " +
getLightBlock(dataPoint) + " " +
getLightSky(dataPoint) + " " +
getGenerationMode(dataPoint) + " " +
isVoid(dataPoint) + " " +
doesItExist(dataPoint) + '\n';
}
public static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize)
{
start *= packetSize;
length *= packetSize;
arraySize *= packetSize;
for (int i = 0; i < arraySize - start; i++)
{
array[start + i] = array[start + length + i];
//remove comment to not leave garbage at the end
//array[start + packetSize + i] = 0;
}
}
public static void extendArray(short[] array, int packetSize, int start, int length, int arraySize)
{
start *= packetSize;
length *= packetSize;
arraySize *= packetSize;
for (int i = arraySize - start - 1; i >= 0; i--)
{
array[start + length + i] = array[start + i];
array[start + i] = 0;
}
}
/**
* This method merge column of multiple data together
* @param dataToMerge one or more columns of data
* @param inputVerticalData vertical size of an input data
* @param maxVerticalData max vertical size of the merged data
* @return one column of correctly parsed data
*/
public static long[] mergeMultiData(long[] dataToMerge, int inputVerticalData, int maxVerticalData)
{
int size = dataToMerge.length / inputVerticalData;
// We initialize the arrays that are going to be used
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((worldHeight + 1) * 2);
long[] dataPoint = ThreadMapUtil.getVerticalDataArray(DetailDistanceUtil.getMaxVerticalData(0));
int genMode = DistanceGenerationMode.SERVER.complexity;
boolean allEmpty = true;
boolean allVoid = true;
boolean allDefault;
long singleData;
short depth;
short height;
int count = 0;
int i;
int ii;
int dataIndex;
//We collect the indexes of the data, ordered by the depth
for (int index = 0; index < size; index++)
{
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
{
singleData = dataToMerge[index * inputVerticalData + dataIndex];
if (doesItExist(singleData))
{
genMode = Math.min(genMode, getGenerationMode(singleData));
allEmpty = false;
if (!isVoid(singleData))
{
allVoid = false;
depth = getDepth(singleData);
height = getHeight(singleData);
int botPos = -1;
int topPos = -1;
//values fall in between and possibly require extension of array
boolean botExtend = false;
boolean topExtend = false;
for (i = 0; i < count; i++)
{
if (depth <= heightAndDepth[i * 2] && depth >= heightAndDepth[i * 2 + 1])
{
botPos = i;
break;
}
else if (depth < heightAndDepth[i * 2 + 1] && ((i + 1 < count && depth > heightAndDepth[(i + 1) * 2]) || i + 1 == count))
{
botPos = i;
botExtend = true;
break;
}
}
for (i = 0; i < count; i++)
{
if (height <= heightAndDepth[i * 2] && height >= heightAndDepth[i * 2 + 1])
{
topPos = i;
break;
}
else if (height < heightAndDepth[i * 2 + 1] && ((i + 1 < count && height > heightAndDepth[(i + 1) * 2]) || i + 1 == count))
{
topPos = i;
topExtend = true;
break;
}
}
if (topPos == -1)
{
if (botPos == -1)
{
//whole block falls above
extendArray(heightAndDepth, 2, 0, 1, count);
heightAndDepth[0] = height;
heightAndDepth[1] = depth;
count++;
}
else if (!botExtend)
{
//only top falls above extending it there, while bottom is inside existing
shrinkArray(heightAndDepth, 2, 0, botPos, count);
heightAndDepth[0] = height;
count -= botPos;
}
else
{
//top falls between some blocks, extending those as well
shrinkArray(heightAndDepth, 2, 0, botPos, count);
heightAndDepth[0] = height;
heightAndDepth[1] = depth;
count -= botPos;
}
}
else if (!topExtend)
{
if (!botExtend)
//both top and bottom are within some exiting blocks, possibly merging them
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
else
//top falls between some blocks, extending it there
heightAndDepth[topPos * 2 + 1] = depth;
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
count -= botPos - topPos;
}
else
{
if (!botExtend)
{
//only top is within some exiting block, extending it
topPos++; //to make it easier
heightAndDepth[topPos * 2] = height;
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
count -= botPos - topPos;
}
else
{
//both top and bottom are outside existing blocks
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
count -= botPos - topPos;
extendArray(heightAndDepth, 2, topPos + 1, 1, count);
count++;
heightAndDepth[topPos * 2 + 2] = height;
heightAndDepth[topPos * 2 + 3] = depth;
}
}
}
}
else
break;
}
}
//We check if there is any data that's not empty or void
if (allEmpty)
return dataPoint;
if (allVoid)
{
dataPoint[0] = createVoidDataPoint(genMode);
return dataPoint;
}
//we cut the 1 block gaps
/*int j;
for (i = 0; i < count - 1; i++)
{
if (heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2] == 1)
{
heightAndDepth[i * 2 + 1] = heightAndDepth[(i + 1) * 2 + 1];
for (j = i + 1; j < count - 1; j++)
{
heightAndDepth[j * 2] = heightAndDepth[(j + 1) * 2];
heightAndDepth[j * 2 + 1] = heightAndDepth[(j + 1) * 2 + 1];
}
count--;
i--;
}
}*/
//we limit the vertical portion to maxVerticalData
int j = 0;
while (count > maxVerticalData)
{
ii = worldHeight;
for (i = 0; i < count - 1; i++)
{
if (heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2] < ii)
{
ii = heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2];
j = i;
}
}
heightAndDepth[j * 2 + 1] = heightAndDepth[(j + 1) * 2 + 1];
for (i = j + 1; i < count - 1; i++)
{
heightAndDepth[i * 2] = heightAndDepth[(i + 1) * 2];
heightAndDepth[i * 2 + 1] = heightAndDepth[(i + 1) * 2 + 1];
}
//System.arraycopy(heightAndDepth, j + 1, heightAndDepth, j, count - j - 1);
count--;
}
//As standard the vertical lods are ordered from top to bottom
for (j = count - 1; j >= 0; j--)
{
height = heightAndDepth[j * 2];
depth = heightAndDepth[j * 2 + 1];
if ((depth == 0 && height == 0) || j >= heightAndDepth.length / 2)
break;
int numberOfChildren = 0;
int tempAlpha = 0;
int tempRed = 0;
int tempGreen = 0;
int tempBlue = 0;
int tempLightBlock = 0;
int tempLightSky = 0;
byte tempGenMode = DistanceGenerationMode.SERVER.complexity;
allEmpty = true;
allVoid = true;
allDefault = true;
long data = 0;
for (int index = 0; index < size; index++)
{
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
{
singleData = dataToMerge[index * inputVerticalData + dataIndex];
if (doesItExist(singleData) && !isVoid(singleData))
{
if ((depth <= getDepth(singleData) && getDepth(singleData) <= height)
|| (depth <= getHeight(singleData) && getHeight(singleData) <= height))
{
if (getHeight(singleData) > getHeight(data))
data = singleData;
}
}
else
break;
}
if (!doesItExist(data))
{
singleData = dataToMerge[index * inputVerticalData];
data = createVoidDataPoint(getGenerationMode(singleData));
}
if (doesItExist(data))
{
allEmpty = false;
if (!isVoid(data))
{
numberOfChildren++;
allVoid = false;
tempAlpha += getAlpha(data);
tempRed += getRed(data);
tempGreen += getGreen(data);
tempBlue += getBlue(data);
tempLightBlock += getLightBlock(data);
tempLightSky += getLightSky(data);
if (!getFlag(data)) allDefault = false;
}
tempGenMode = (byte) Math.min(tempGenMode, getGenerationMode(data));
}
else
tempGenMode = (byte) Math.min(tempGenMode, DistanceGenerationMode.NONE.complexity);
}
if (allEmpty)
//no child has been initialized
dataPoint[j] = EMPTY_DATA;
else if (allVoid)
//all the children are void
dataPoint[j] = createVoidDataPoint(tempGenMode);
else
{
//we have at least 1 child
tempAlpha = tempAlpha / numberOfChildren;
tempRed = tempRed / numberOfChildren;
tempGreen = tempGreen / numberOfChildren;
tempBlue = tempBlue / numberOfChildren;
tempLightBlock = tempLightBlock / numberOfChildren;
tempLightSky = tempLightSky / numberOfChildren;
dataPoint[j] = createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
}
}
return dataPoint;
}
}
@@ -0,0 +1,190 @@
/*
* 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.util;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.enums.HorizontalQuality;
import com.seibel.lod.enums.HorizontalResolution;
import com.seibel.lod.wrappers.MinecraftWrapper;
/**
*
* @author Leonardo Amato
* @version ??
*/
public class DetailDistanceUtil
{
private static final double genMultiplier = 1.0;
private static final double treeGenMultiplier = 1.0;
private static final double treeCutMultiplier = 1.0;
private static int minGenDetail = LodConfig.CLIENT.graphics.qualityOption.drawResolution.get().detailLevel;
private static int minDrawDetail = Math.max(LodConfig.CLIENT.graphics.qualityOption.drawResolution.get().detailLevel, LodConfig.CLIENT.graphics.qualityOption.drawResolution.get().detailLevel);
private static final int maxDetail = LodUtil.REGION_DETAIL_LEVEL + 1;
private static final int minDistance = 0;
private static int minDetailDistance = (int) (MinecraftWrapper.INSTANCE.getRenderDistance()*16 * 1.42f);
private static int maxDistance = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * 16 * 2;
private static final HorizontalResolution[] lodGenDetails = {
HorizontalResolution.BLOCK,
HorizontalResolution.TWO_BLOCKS,
HorizontalResolution.FOUR_BLOCKS,
HorizontalResolution.HALF_CHUNK,
HorizontalResolution.CHUNK,
HorizontalResolution.CHUNK,
HorizontalResolution.CHUNK,
HorizontalResolution.CHUNK,
HorizontalResolution.CHUNK,
HorizontalResolution.CHUNK,
HorizontalResolution.CHUNK };
public static void updateSettings()
{
minDetailDistance = (int) (MinecraftWrapper.INSTANCE.getRenderDistance()*16 * 1.42f);
minGenDetail = LodConfig.CLIENT.graphics.qualityOption.drawResolution.get().detailLevel;
minDrawDetail = Math.max(LodConfig.CLIENT.graphics.qualityOption.drawResolution.get().detailLevel, LodConfig.CLIENT.graphics.qualityOption.drawResolution.get().detailLevel);
maxDistance = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * 16 * 8;
}
public static int baseDistanceFunction(int detail)
{
if (detail <= minGenDetail)
return minDistance;
if (detail >= maxDetail)
return maxDistance;
int distanceUnit = LodConfig.CLIENT.graphics.qualityOption.horizontalScale.get().distanceUnit;
if (LodConfig.CLIENT.graphics.qualityOption.horizontalQuality.get() == HorizontalQuality.LOWEST)
return (detail * distanceUnit);
else
{
double base = LodConfig.CLIENT.graphics.qualityOption.horizontalQuality.get().quadraticBase;
return (int) (Math.pow(base, detail) * distanceUnit);
}
}
public static int getDrawDistanceFromDetail(int detail)
{
return baseDistanceFunction(detail);
}
public static byte baseInverseFunction(int distance, int minDetail, boolean useRenderMinDistance)
{
int detail;
if (distance == 0)
return (byte) minDetail;
if (distance < minDetailDistance && useRenderMinDistance)
return (byte) minDetail;
int distanceUnit = LodConfig.CLIENT.graphics.qualityOption.horizontalScale.get().distanceUnit;
if (LodConfig.CLIENT.graphics.qualityOption.horizontalQuality.get() == HorizontalQuality.LOWEST)
detail = (byte) Math.floorDiv(distance, distanceUnit);
else
{
double base = LodConfig.CLIENT.graphics.qualityOption.horizontalQuality.get().quadraticBase;
double logBase = Math.log(base);
detail = (byte) (Math.log(Math.floorDiv(distance, distanceUnit)) / logBase);
}
return (byte) LodUtil.clamp(minDetail, detail, maxDetail - 1);
}
public static byte getDrawDetailFromDistance(int distance)
{
return baseInverseFunction(distance, minDrawDetail, false);
}
public static byte getGenerationDetailFromDistance(int distance)
{
return baseInverseFunction((int) (distance * genMultiplier), minGenDetail, true);
}
public static byte getTreeCutDetailFromDistance(int distance)
{
return baseInverseFunction((int) (distance * treeCutMultiplier), minGenDetail, true);
}
public static byte getTreeGenDetailFromDistance(int distance)
{
return baseInverseFunction((int) (distance * treeGenMultiplier), minGenDetail, true);
}
public static DistanceGenerationMode getDistanceGenerationMode(int detail)
{
return LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get();
}
public static byte getLodDrawDetail(int detail)
{
if (detail < minDrawDetail)
{
if (LodConfig.CLIENT.graphics.advancedGraphicsOption.alwaysDrawAtMaxQuality.get())
return getLodGenDetail(minDrawDetail).detailLevel;
else
return (byte) minDrawDetail;
}
else
{
if (LodConfig.CLIENT.graphics.advancedGraphicsOption.alwaysDrawAtMaxQuality.get())
return getLodGenDetail(detail).detailLevel;
else
return (byte) detail;
}
}
public static HorizontalResolution 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 int getMaxVerticalData(int detail)
{
return LodConfig.CLIENT.graphics.qualityOption.verticalQuality.get().maxVerticalData[LodUtil.clamp(minGenDetail, detail, LodUtil.REGION_DETAIL_LEVEL)];
}
}
@@ -0,0 +1,258 @@
/*
* 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.util;
/**
*
* @author Leonardo Amato
* @version ??
*/
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 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 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
int newPosX = xRegion * width + posX;
int newPosZ = zRegion * width + posZ;
return maxDistance(detailLevel, newPosX, newPosZ, 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 minDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ, int xRegion, int zRegion)
{
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
int newPosX = xRegion * width + posX;
int newPosZ = zRegion * width + posZ;
return minDistance(detailLevel, newPosX, newPosZ, playerPosX, playerPosZ);
}
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);
if (compareResult == 0)
{
compareResult = Integer.compare(
firstDistance,
secondDistance);
}
return compareResult;
}
@SuppressWarnings("unused")
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);
}
}
@@ -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.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 final String threadName;
public LodThreadFactory(String newThreadName)
{
threadName = newThreadName + " Thread";
}
@Override
public Thread newThread(Runnable r)
{
return new Thread(r, threadName);
}
}

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