Compare commits
535 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c8b8817909 | |||
| 8a70545439 | |||
| d8e5cbda0a | |||
| 6f4b0a54e9 | |||
| 61ea29a6fd | |||
| 7d6c4fa1f9 | |||
| 108b4c7470 | |||
| 63fff244eb | |||
| 5e86aa1275 | |||
| db72d1cdc2 | |||
| 5e99efe093 | |||
| 9557912101 | |||
| ca2b09c2c8 | |||
| e1b2c62854 | |||
| c3bb079b42 | |||
| 9670cbbb74 | |||
| 3ca0757358 | |||
| 4f2076b48e | |||
| e91bcb7964 | |||
| f0f18993d1 | |||
| 026816053c | |||
| 0d3c005e0f | |||
| a93c04a654 | |||
| 1601f0d19f | |||
| 6ad6ecc731 | |||
| 29fab65ee9 | |||
| a3022d2c64 | |||
| d2fef22719 | |||
| 1cb27f2f78 | |||
| 892570a442 | |||
| 705060fa97 | |||
| b98082980b | |||
| d3b1635538 | |||
| 73f9edc091 | |||
| 7f6d123292 | |||
| ca64027b0c | |||
| 175c48a49e | |||
| 6f75197085 | |||
| c1d6541729 | |||
| 0905f60034 | |||
| f012ac44a7 | |||
| c19e2613b4 | |||
| 351decad34 | |||
| d22057e1b1 | |||
| a7e174cf74 | |||
| d88e0f5e9c | |||
| 743e504ccc | |||
| 633b229d2e | |||
| ee803e10b5 | |||
| 3df603a0e2 | |||
| ce7217bad5 | |||
| 3e42541fe9 | |||
| ff1c6cad2a | |||
| 2bba421a5b | |||
| 1fd1082a80 | |||
| 25d2b9df7b | |||
| 7eb0d62f75 | |||
| 4fc816838d | |||
| 22bc6b5c9a | |||
| 2e282433cc | |||
| e76735a509 | |||
| 29aa95437e | |||
| 8296b006b4 | |||
| 02b0637adc | |||
| 9f12cc9528 | |||
| 0981870ea3 | |||
| 347383f440 | |||
| 4209937c35 | |||
| f877030eca | |||
| a12f2e9e01 | |||
| 6d12acc30a | |||
| 4bac38c99f | |||
| 3259f502e8 | |||
| aa3cde0537 | |||
| e3018485ed | |||
| 5a58fa92b3 | |||
| 8a76c8cadc | |||
| 7f9b035665 | |||
| 368238821b | |||
| 4c984c5c10 | |||
| 7c935bb581 | |||
| 3e825c11c4 | |||
| 11982d701c | |||
| 21d8f1124c | |||
| b4009336a3 | |||
| a2d2e5d87b | |||
| f97815cac7 | |||
| 53c518b307 | |||
| 03f5a086f0 | |||
| bc72142659 | |||
| de51efc866 | |||
| 0658106822 | |||
| 5178aa7def | |||
| a80f69f522 | |||
| 665a5a8bee | |||
| 8a851a70c7 | |||
| 621bf7341d | |||
| 4820c11f8e | |||
| ebc4ee2ab7 | |||
| 95c5854d8f | |||
| fe798bf90c | |||
| 4a2a6fb4bd | |||
| ef80271f09 | |||
| 6a5263a8f4 | |||
| 260ba1a70e | |||
| ca625f9f8b | |||
| 4f2bf9b834 | |||
| 467f4a260f | |||
| 21253d1308 | |||
| 8f534fb51c | |||
| 6cd0281d0e | |||
| df73d1d275 | |||
| 804738cfe5 | |||
| 254d908807 | |||
| 9fa7fa843d | |||
| 82065cd1ce | |||
| d0da822813 | |||
| 595aa79eb6 | |||
| 8cdc0fc830 | |||
| c869047b30 | |||
| 2907c8e3e7 | |||
| 4097cf7619 | |||
| cf519c02be | |||
| 4f563c2be5 | |||
| e8de59a226 | |||
| 323da0b12c | |||
| 1c0e7839c0 | |||
| d2056d824f | |||
| 7da2b90611 | |||
| f1eb06bbb1 | |||
| 78a1cc3330 | |||
| fbd8f48433 | |||
| 49cc46dc25 | |||
| 1c7d87b9d3 | |||
| bca2b61800 | |||
| c9cabd8a32 | |||
| d731424e93 | |||
| 510058b7df | |||
| 05e70416b7 | |||
| d5eb094256 | |||
| b7aa341961 | |||
| 3f091af3f8 | |||
| 566eb3651f | |||
| 58392a8ac1 | |||
| 2149da59df | |||
| e41b19ba2e | |||
| d84d535896 | |||
| e6f8c0d65f | |||
| 7db3789bc2 | |||
| 8d18ba861e | |||
| 6e63e39cd8 | |||
| 1dcc973a24 | |||
| 7ad2e82646 | |||
| f32dfbcbe7 | |||
| 18b7835c4c | |||
| c5f23dabb2 | |||
| d6ba5205ad | |||
| 6948e4e437 | |||
| d39912e235 | |||
| fa3d07edb5 | |||
| bf4eb954d3 | |||
| 1ddf2091b1 | |||
| 4708da8e8f | |||
| 59fa6eed73 | |||
| 0edca2402d | |||
| 59bf66ec58 | |||
| a14046ef88 | |||
| cff317937f | |||
| cead0346ad | |||
| f3f3c3d54d | |||
| 9765da97a6 | |||
| 57f5ad5ea0 | |||
| 3cd71c1c69 | |||
| d79c873145 | |||
| 401cbbd8eb | |||
| a1652fe68a | |||
| c450f5b960 | |||
| bcf8237131 | |||
| 3e7fed7ad4 | |||
| fd81a8e067 | |||
| 891cfdf4a9 | |||
| b1795079b8 | |||
| 8e347959e1 | |||
| acd5116223 | |||
| 2ba7c6be6e | |||
| 77492a8abf | |||
| a0c7ebedcf | |||
| ae752c6f63 | |||
| fbbccf4739 | |||
| cf1c371311 | |||
| 87ff857b59 | |||
| 50819c30da | |||
| 67f12c136c | |||
| b35db97abc | |||
| 241447d55a | |||
| 5d72d321db | |||
| 595b8ecbb5 | |||
| f422df6280 | |||
| 14d3418651 | |||
| f7da53622c | |||
| e5a636fccb | |||
| 080d33baf1 | |||
| d8c082ba42 | |||
| b169e246b4 | |||
| 07d4050502 | |||
| c70d2a27f2 | |||
| 4919c0cabb | |||
| 40a8f228ac | |||
| 1b711e2b66 | |||
| 104d4a7400 | |||
| 01870e4875 | |||
| 7203acf628 | |||
| cbcb7ca6ac | |||
| 1b27161518 | |||
| b51cb7ca51 | |||
| 1e08fc51ec | |||
| c2d4475d44 | |||
| ddefb5f67a | |||
| 8bdb3146ed | |||
| 8860e46d9b | |||
| a73da4102a | |||
| bb8df761bb | |||
| e459994d6d | |||
| 65f16463ec | |||
| 31717ad202 | |||
| de4dd44209 | |||
| 6c73f8b723 | |||
| 67a171da56 | |||
| 09b2d952b6 | |||
| 255d2adbb0 | |||
| 069978ee1d | |||
| aff7e90e37 | |||
| 1032f550ed | |||
| ac32697204 | |||
| 95220d4fd7 | |||
| 068622895f | |||
| a0dd0d5aca | |||
| ca81ed1efe | |||
| 35ab1ce47f | |||
| c5aeac5091 | |||
| 834cfe2e10 | |||
| 99ca5f6bc6 | |||
| 4bcb6c0acd | |||
| 4f63fbb14d | |||
| 522a74e6e3 | |||
| 6ef789c087 | |||
| 410fe684ec | |||
| 43feb2d7fe | |||
| 7fa99ea070 | |||
| bdd877abe8 | |||
| b2d5e18fe4 | |||
| 880be5ed60 | |||
| 90440f3e06 | |||
| c3abb9c46b | |||
| ab3880a5e5 | |||
| f939839941 | |||
| e9e2af2807 | |||
| a5a4a3e6e2 | |||
| 9e7703ac53 | |||
| 9580335692 | |||
| 608dc443dc | |||
| 14e72c68cb | |||
| dc0e48ae2a | |||
| 0bfb47cc68 | |||
| c69569c95f | |||
| 914a76297d | |||
| 22e47b9734 | |||
| 930113a6f9 | |||
| 6c77164a65 | |||
| 546d60f1fc | |||
| 6b6d011cd5 | |||
| 7bf54d20c0 | |||
| bba7f34d46 | |||
| ade5500c6d | |||
| a3bd2ed70b | |||
| 43304da80b | |||
| 7d53da40ec | |||
| bd489a66c7 | |||
| a50ef74a5e | |||
| 4702aa7888 | |||
| 57a6483aec | |||
| 31cb1ef401 | |||
| be5b5de170 | |||
| f665a8b8f6 | |||
| 0c4de076ca | |||
| 4c122512ac | |||
| 976874e7a3 | |||
| bb22ad58bc | |||
| 0b2be5580f | |||
| 02143d0951 | |||
| 3ebdfd6e87 | |||
| 3caf8facf9 | |||
| efd1d67f3f | |||
| ee2c6e2a06 | |||
| d1e1970c18 | |||
| 943a2d5cad | |||
| e5f5d33db9 | |||
| 8b761ca31a | |||
| a402fa5f0b | |||
| d4123a44ed | |||
| cba75123c7 | |||
| 5ac51dd2a5 | |||
| a8d52c1059 | |||
| c664564fb0 | |||
| 1a5fd87346 | |||
| e71660eb41 | |||
| d9dc33a105 | |||
| 4bf004ae6b | |||
| f9871ef16d | |||
| 054988851d | |||
| 44bcc5ae01 | |||
| 3b475886ef | |||
| ff6edec461 | |||
| ae7e416810 | |||
| d72805d1fe | |||
| f80c43385a | |||
| 219ad9c45a | |||
| 662a1ec8bc | |||
| e60a0526ca | |||
| 1266ed6dc1 | |||
| 816c4fa76a | |||
| 975c24c8a9 | |||
| 4db3b7b729 | |||
| 32e341fe4f | |||
| 0ce249ab17 | |||
| f9372c6a07 | |||
| 6ea7ecd215 | |||
| 6a828ee931 | |||
| e8d25daabf | |||
| 7c8b073b02 | |||
| b623fc530f | |||
| 5fdf09209c | |||
| 515afe4d45 | |||
| 7f5eea865e | |||
| 13d6232790 | |||
| 607f3e8afe | |||
| 8b3404e5f8 | |||
| df6253af39 | |||
| b1f3b23ba1 | |||
| c4708ed173 | |||
| fcab0d3b20 | |||
| 1034360b88 | |||
| f92f656876 | |||
| e052a0c96f | |||
| 8bb8217c7b | |||
| 0563cde3c2 | |||
| 5fe192f4c5 | |||
| 428e12081c | |||
| ac102402cc | |||
| 966677b89e | |||
| 64e73b7d83 | |||
| 52ea2e96b7 | |||
| c658697ecd | |||
| a5b259f098 | |||
| e9da9c26f4 | |||
| 2f6ff1a3ea | |||
| 766e5a358d | |||
| a19189c2a8 | |||
| 0627f779d7 | |||
| 01bfb65d9e | |||
| cccad08a61 | |||
| 96be86cacf | |||
| b75791006c | |||
| 9047ebb970 | |||
| 2d1c2d6efb | |||
| 8577363438 | |||
| 4229ed75ae | |||
| b8408bc6fa | |||
| 56c4911316 | |||
| 244ead9451 | |||
| 5709a4c660 | |||
| 3913b955be | |||
| a295dcafd4 | |||
| f9914f9336 | |||
| 13e9df5b48 | |||
| 98c394bad1 | |||
| 444bf3b8bc | |||
| 455281a32d | |||
| 7b613ae8e3 | |||
| 443d6165fa | |||
| a4ebe3e3c1 | |||
| 6ac3edf280 | |||
| a1163dc340 | |||
| 38b7e66ef8 | |||
| a843a0ed65 | |||
| e0d2d2530f | |||
| 44e2936b68 | |||
| dfa717e0e3 | |||
| 69844417ce | |||
| 8370402dc1 | |||
| cbb32bc996 | |||
| ac876c0030 | |||
| 2176807a0a | |||
| 47732b7f57 | |||
| ff9afee5b4 | |||
| b5665a59c0 | |||
| 483ecf2e4d | |||
| 30eab27a49 | |||
| d1b5200fed | |||
| 4a4728d41e | |||
| c9204a2094 | |||
| c45f0b2414 | |||
| 97b2db84cd | |||
| c389f3b391 | |||
| 6049840aa6 | |||
| 2dee6b0326 | |||
| 27d9e6aeb7 | |||
| c751b6fcc6 | |||
| 31f173c8e8 | |||
| 90ca3bd394 | |||
| 30dd5526cd | |||
| a9e31b8133 | |||
| e71cd864b4 | |||
| 8efc7d54e7 | |||
| e323e8d62e | |||
| b970ad0ab8 | |||
| c811e6bad6 | |||
| 8ef0d40f0c | |||
| 227f7d0a23 | |||
| b385b018f1 | |||
| 4ece2de991 | |||
| a172961112 | |||
| 9e882951ef | |||
| 1c9fe23633 | |||
| 92b6a9695d | |||
| acc5e7af98 | |||
| aa9e49b3e7 | |||
| 7d86e24db5 | |||
| 71986d8818 | |||
| e948b4dac1 | |||
| 0a5f7d0c11 | |||
| 1c65ef8323 | |||
| 2c744dd22b | |||
| 009d7ede1b | |||
| 4199954843 | |||
| c7ab36c6b7 | |||
| 1da6544550 | |||
| c2896d1f73 | |||
| 302abb1e57 | |||
| 34c6f28173 | |||
| 75676df736 | |||
| 56b80f00e8 | |||
| c1651edb6a | |||
| c1375f7a10 | |||
| a82ef3dcde | |||
| 74a97dab0d | |||
| 16621773af | |||
| c8988ad1b7 | |||
| 1f3f432fef | |||
| 56032b6d6b | |||
| 6157c659d2 | |||
| d2cae841e7 | |||
| d1711be390 | |||
| 93332d6256 | |||
| 8292dc67c0 | |||
| 1b82acec9f | |||
| da4f423d10 | |||
| 5a4c04b5a3 | |||
| cd50be6531 | |||
| 5d4698621c | |||
| aba392ace4 | |||
| d1c0ea123a | |||
| 816efb2837 | |||
| 0221a39819 | |||
| cc08707f32 | |||
| 8db253f886 | |||
| f59bdf15d7 | |||
| ac0d439b3f | |||
| 4e652c7573 | |||
| 3686bfb4e0 | |||
| fe1b2c2683 | |||
| d5ed9a22fa | |||
| e3f9c974f8 | |||
| daead98102 | |||
| 5c31927d54 | |||
| f9fa1a5260 | |||
| d0472ee56d | |||
| e4e21d2dc8 | |||
| 79e4dce569 | |||
| 408f09c0f4 | |||
| 65bfedc942 | |||
| fab99cd4ec | |||
| 019f4b7c1e | |||
| f3b6b15bcb | |||
| 7c086cdc40 | |||
| 5617e1312c | |||
| 9dd51bfdde | |||
| c9dc998eef | |||
| 068df9d5e0 | |||
| 44dc7c96af | |||
| 89cc3513f3 | |||
| 7a94db77ef | |||
| f2fc669b37 | |||
| a3d4163b67 | |||
| aea4542616 | |||
| a489810d68 | |||
| 806a1e99db | |||
| 4843027e43 | |||
| af4ba453ca | |||
| adc5853f0b | |||
| c42ddd29a9 | |||
| 9b7d3c083f | |||
| 87bb4ae840 | |||
| b34f5e7f5f | |||
| a053a79d99 | |||
| 73c041e02f | |||
| f96a5fc794 | |||
| d591458cd6 | |||
| 88305d8db3 | |||
| 40b0517656 | |||
| 2289826363 | |||
| e1c2b2a0a9 | |||
| 70f7a2422b | |||
| 8ad6f184dd | |||
| 4a93bde7be | |||
| b9dfd93b56 | |||
| 5e78c98f17 | |||
| 31035ccb1e | |||
| e840a23d01 | |||
| 08b8f8778a | |||
| 044f87eef2 | |||
| 30cd7fd4e0 | |||
| fbf5dfaa9d | |||
| 58d4bc7f0f | |||
| 511a771351 | |||
| e806098544 | |||
| 4316cfbfd7 | |||
| 6be4c0303f | |||
| 7633c7bc70 | |||
| 0bc96a98cf | |||
| a0529a310b | |||
| ace3c03019 | |||
| 16b44695ec | |||
| 151ca3902f | |||
| 1ba24659bc |
@@ -1,68 +1,21 @@
|
||||
# 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"></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)
|
||||
This repo is for the Distant Horizons mod.
|
||||
The purpose of this submodule is to isolate code that isn't tied to a specific version of minecraft. This prevents us from having duplicate code; reducing errors and potentially helping us port to different versions faster and easier.
|
||||
|
||||
Check out the mod's main GitLab page here:
|
||||
https://gitlab.com/jeseibel/minecraft-lod-mod
|
||||
|
||||
## 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`
|
||||
You shouldn't download this repo directly.
|
||||
It should be automatically included when pulling the full mod.
|
||||
|
||||
|
||||
## Open Source Acknowledgements
|
||||
|
||||
XZ for Java (data compression)\
|
||||
https://tukaani.org/xz/java.html
|
||||
|
||||
Toml for Java (config handeling)\
|
||||
https://github.com/TheElectronWill/night-config
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
@@ -0,0 +1,221 @@
|
||||
<mxfile host="app.diagrams.net" modified="2022-02-06T23:07:56.707Z" agent="5.0 (Windows)" etag="McdhDnDn_b3JSW_6sAfD" version="16.5.3" type="gitlab">
|
||||
<diagram id="xLs7mM1S-vncSruOQYJG" name="Page-1">
|
||||
<mxGraphModel dx="2854" dy="971" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" background="none" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<object label="Background" id="1">
|
||||
<mxCell parent="0" />
|
||||
</object>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-46" value="" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFE9CC;strokeColor=#d79b00;shadow=0;" vertex="1" parent="1">
|
||||
<mxGeometry x="400" y="300" width="720" height="320" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ZgqzLkNpqH_WLmXpxmUH-3" value="" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFE9CC;strokeColor=#d79b00;" parent="1" vertex="1">
|
||||
<mxGeometry x="30" y="10" width="1090" height="290" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="lUieYn43trCVNQSoQYE8-16" value="" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#CBF2CB;strokeColor=#06962D;gradientColor=none;" parent="1" vertex="1">
|
||||
<mxGeometry x="-680" y="70" width="200" height="220" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="lUieYn43trCVNQSoQYE8-18" value="Minecraft <br>Version Specific" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#CBF2CB;strokeColor=#06962D;gradientColor=none;" parent="1" vertex="1">
|
||||
<mxGeometry x="-680" y="10" width="200" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="gNZkowd1tYjNP-PxMyY5-1" value="Minecraft" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#A5E8A5;strokeColor=#82b366;" parent="1" vertex="1">
|
||||
<mxGeometry x="-650" y="205" width="140" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-27" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=3;" parent="1" source="lUieYn43trCVNQSoQYE8-14" target="XNAtI1EKQKx7pIlif8ke-22" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="lUieYn43trCVNQSoQYE8-14" value="Mod API<br>(Forge or Fabric)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#A5E8A5;strokeColor=#82b366;" parent="1" vertex="1">
|
||||
<mxGeometry x="-650" y="90" width="140" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ZgqzLkNpqH_WLmXpxmUH-2" value="Distant Horizons Core<br>(Version Independent)" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFE9CC;strokeColor=#d79b00;" parent="1" vertex="1">
|
||||
<mxGeometry x="450" y="10" width="220" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;strokeWidth=3;" parent="1" source="XNAtI1EKQKx7pIlif8ke-1" target="XNAtI1EKQKx7pIlif8ke-11" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="10" y="120" />
|
||||
<mxPoint x="10" y="176" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-72" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontColor=#FFFFFF;strokeWidth=3;" parent="1" source="XNAtI1EKQKx7pIlif8ke-11" target="bW8zysbPbXfA33rAzGtO-39" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="134" y="90" />
|
||||
<mxPoint x="1000" y="90" />
|
||||
</Array>
|
||||
<mxPoint x="997.5" y="165.0000000000001" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-1" value="Wrappers<br>Or<br><div>DH-Object</div>" style="shape=step;perimeter=stepPerimeter;whiteSpace=wrap;html=1;fixedSize=1;fillColor=#FFD7B0;strokeColor=#d79b00;rounded=0;labelBackgroundColor=none;" parent="1" vertex="1">
|
||||
<mxGeometry x="-170" y="80" width="120" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;" parent="1" source="XNAtI1EKQKx7pIlif8ke-3" target="XNAtI1EKQKx7pIlif8ke-21" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-3" value="Mixins" style="shape=step;perimeter=stepPerimeter;whiteSpace=wrap;html=1;fixedSize=1;fillColor=#A5E8A5;strokeColor=#82b366;rounded=0;labelBackgroundColor=none;" parent="1" vertex="1">
|
||||
<mxGeometry x="-470" y="190" width="120" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-4" value="" style="endArrow=classic;html=1;rounded=0;strokeWidth=3;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="gNZkowd1tYjNP-PxMyY5-1" target="XNAtI1EKQKx7pIlif8ke-3" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="-420" y="130" as="sourcePoint" />
|
||||
<mxPoint x="-220" y="-25" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;strokeWidth=3;" parent="1" source="XNAtI1EKQKx7pIlif8ke-5" target="XNAtI1EKQKx7pIlif8ke-11" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-5" value="Wrappers<br>Or<br><div>DH-Object</div>" style="shape=step;perimeter=stepPerimeter;whiteSpace=wrap;html=1;fixedSize=1;fillColor=#FFD7B0;strokeColor=#d79b00;rounded=0;labelBackgroundColor=none;" parent="1" vertex="1">
|
||||
<mxGeometry x="-170" y="190" width="120" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-60" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;fontColor=#FFFFFF;strokeWidth=3;" parent="1" source="XNAtI1EKQKx7pIlif8ke-11" target="XNAtI1EKQKx7pIlif8ke-33" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-63" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontColor=#FFFFFF;strokeWidth=3;" parent="1" source="XNAtI1EKQKx7pIlif8ke-11" target="lUieYn43trCVNQSoQYE8-19" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-11" value="core.api" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" parent="1" vertex="1">
|
||||
<mxGeometry x="60" y="165" width="147.5" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;" parent="1" source="XNAtI1EKQKx7pIlif8ke-21" target="XNAtI1EKQKx7pIlif8ke-5" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-21" value="" style="shape=step;perimeter=stepPerimeter;whiteSpace=wrap;html=1;fixedSize=1;fillColor=#C7C7C7;strokeColor=#666666;fontColor=#333333;rounded=0;labelBackgroundColor=none;" parent="1" vertex="1">
|
||||
<mxGeometry x="-320" y="175" width="120" height="85" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-26" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;" parent="1" source="XNAtI1EKQKx7pIlif8ke-22" target="XNAtI1EKQKx7pIlif8ke-1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-22" value="" style="shape=step;perimeter=stepPerimeter;whiteSpace=wrap;html=1;fixedSize=1;fillColor=#C7C7C7;strokeColor=#666666;fontColor=#333333;rounded=0;labelBackgroundColor=none;" parent="1" vertex="1">
|
||||
<mxGeometry x="-320" y="90" width="120" height="85" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-28" value="<div>Wrapper</div><div>Factory<br></div>" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;fontColor=default;" parent="1" vertex="1">
|
||||
<mxGeometry x="-290" y="110" width="60" height="130" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-44" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;" edge="1" parent="1" source="XNAtI1EKQKx7pIlif8ke-31" target="bW8zysbPbXfA33rAzGtO-39">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-31" value="GPU Buffer building" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" parent="1" vertex="1">
|
||||
<mxGeometry x="710" y="165" width="130" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-67" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;fontColor=#FFFFFF;strokeWidth=3;" parent="1" source="XNAtI1EKQKx7pIlif8ke-33" target="XNAtI1EKQKx7pIlif8ke-54" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-33" value="Lod Building" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" parent="1" vertex="1">
|
||||
<mxGeometry x="290" y="165" width="120" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-52" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="460" y="110" width="200" height="175" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-25" value="" style="group" vertex="1" connectable="0" parent="XNAtI1EKQKx7pIlif8ke-52">
|
||||
<mxGeometry width="200" height="175" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-53" value="LOD Storage" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="bW8zysbPbXfA33rAzGtO-25" vertex="1">
|
||||
<mxGeometry width="200" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-54" value="" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFF4E8;strokeColor=#d79b00;" parent="bW8zysbPbXfA33rAzGtO-25" vertex="1">
|
||||
<mxGeometry y="45" width="200" height="130" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-55" value="Memory Storage" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" parent="bW8zysbPbXfA33rAzGtO-25" vertex="1">
|
||||
<mxGeometry x="10" y="115" width="180" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-56" value="<div>File Storage</div>" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" parent="bW8zysbPbXfA33rAzGtO-25" vertex="1">
|
||||
<mxGeometry x="10" y="60" width="180" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-61" value="" style="group" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="130" y="310" width="220" height="200" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="lUieYn43trCVNQSoQYE8-13" value="" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#C7C7C7;strokeColor=#666666;fontColor=#333333;" parent="XNAtI1EKQKx7pIlif8ke-61" vertex="1">
|
||||
<mxGeometry y="70" width="220" height="130" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="lUieYn43trCVNQSoQYE8-19" value="Distant Horizons<br>(Version dependent)" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#C7C7C7;strokeColor=#666666;fontColor=#333333;" parent="XNAtI1EKQKx7pIlif8ke-61" vertex="1">
|
||||
<mxGeometry width="220" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-19" value="Chunk Generation" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#EBEBEB;strokeColor=#666666;fontColor=#333333;" parent="XNAtI1EKQKx7pIlif8ke-61" vertex="1">
|
||||
<mxGeometry x="12.5" y="80" width="195" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-29" value="<div>Chunk loading from file</div>" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#EBEBEB;strokeColor=#666666;fontColor=#333333;" parent="XNAtI1EKQKx7pIlif8ke-61" vertex="1">
|
||||
<mxGeometry x="12.5" y="140" width="195" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-64" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;fontColor=#FFFFFF;strokeWidth=3;" parent="1" source="lUieYn43trCVNQSoQYE8-19" target="XNAtI1EKQKx7pIlif8ke-33" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="XNAtI1EKQKx7pIlif8ke-66" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontColor=#FFFFFF;strokeWidth=3;" parent="1" source="XNAtI1EKQKx7pIlif8ke-54" target="XNAtI1EKQKx7pIlif8ke-31" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-23" value="" style="group" vertex="1" connectable="0" parent="1">
|
||||
<mxGeometry x="455" y="324" width="210" height="270" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-16" value="Memory Storage" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#ffe6cc;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-23">
|
||||
<mxGeometry width="210" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-17" value="" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFF4E8;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-23">
|
||||
<mxGeometry y="45" width="210" height="225" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-18" value="dimension" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-23">
|
||||
<mxGeometry x="10.5" y="110" width="189" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-19" value="World" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-23">
|
||||
<mxGeometry x="10.5" y="60" width="189" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-21" value="Region" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-23">
|
||||
<mxGeometry x="10.5" y="160" width="189" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-22" value="Level Container<br>(Long primitive)" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-23">
|
||||
<mxGeometry x="10.5" y="210" width="189" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-26" value="" style="group" vertex="1" connectable="0" parent="1">
|
||||
<mxGeometry x="680" y="419" width="200" height="175" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-27" value="Config System" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#ffe6cc;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-26">
|
||||
<mxGeometry width="200" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-28" value="" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFF4E8;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-26">
|
||||
<mxGeometry y="45" width="200" height="130" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-29" value="Config GUI" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-26">
|
||||
<mxGeometry x="10" y="115" width="180" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-30" value="Config File" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-26">
|
||||
<mxGeometry x="10" y="60" width="180" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-38" value="" style="group" vertex="1" connectable="0" parent="1">
|
||||
<mxGeometry x="900" y="165" width="200" height="175" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-39" value="Rendering" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#ffe6cc;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-38">
|
||||
<mxGeometry width="200" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-40" value="" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFF4E8;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-38">
|
||||
<mxGeometry y="45" width="200" height="130" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-41" value="Cloud Rendering" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-38">
|
||||
<mxGeometry x="10" y="115" width="180" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-42" value="LOD Rendering" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-38">
|
||||
<mxGeometry x="10" y="60" width="180" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-47" value="" style="group" vertex="1" connectable="0" parent="1">
|
||||
<mxGeometry x="900" y="419" width="200" height="175" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-32" value="Mod Support" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#ffe6cc;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-47">
|
||||
<mxGeometry width="200" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-33" value="" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFF4E8;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-47">
|
||||
<mxGeometry y="45" width="200" height="130" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-34" value="Mod API" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-47">
|
||||
<mxGeometry x="10" y="115" width="180" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-35" value="<div>Mod Accessors</div>" style="rounded=0;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#FFD7B0;strokeColor=#d79b00;" vertex="1" parent="bW8zysbPbXfA33rAzGtO-47">
|
||||
<mxGeometry x="10" y="60" width="180" height="45" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bW8zysbPbXfA33rAzGtO-20" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="XNAtI1EKQKx7pIlif8ke-55" target="bW8zysbPbXfA33rAzGtO-16">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="410" y="-36" as="sourcePoint" />
|
||||
<mxPoint x="360" y="-86" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1 @@
|
||||
<mxfile host="app.diagrams.net" modified="2022-01-04T16:15:44.618Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.62" etag="-Vdo5wDmcYQvA_9MRiBE" version="16.0.0" type="google"><diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">7V1db5s6GP41kbaLTHxDLpt063aWHfWs2zrtzgtOwhnBOcZpm/36Y2M7CRg60mGoNEuVGt44Bvw876eNGbmzzcMVBtv1BxTDdORY8cPIvRw5jmPZDv3HJHsuCQIhWOEk5iL7KLhJfkIhtIR0l8QwLzUkCKUk2ZaFC5RlcEFKMoAxui83W6K0fNYtWEFFcLMAqSq9TWKy5tLICY/ytzBZreWZ7WDCv9kA2VjcSb4GMbo/EbmvR+4MI0T4p83DDKZs8OS43L7b36bzH8HVX//k/4HP0/ef/v4y5p29Oecnh1vAMCNP7prcfA4vxsv3wY+3YTb+mb27Dq/GchhyspcDBmM6fuIQYbJGK5SB9PVROsVol8WQdWvRo2ObOUJbKrSp8F9IyF6QAewIoqI12aTi25xg9APOUIowlcRwCXYpvbEpzOILBjkVZiiDXPImSVNxKn6t7AIr2P5iYES7HO3wAj4yGq4vCArwCpLHGk4O+FPFgWgDCd7TH2KYApLclS8PCAavDu0OP71GCb1wxxLa5kqqCV1z/KjcBb8D8atTrNWOXlmu51Ga+1HgWGGp28NpZLf8fpVu6YeTuzuKCmKdQTJpCvol2eBkmrQkk+f2QiYvsp5MplJHftAzffw/kT6cFS3oE9i90Cd0PB22KLLakYliAfYnzbasQa6DbtIJ3IF0JwZvjuJbhGngUeVhfp9sUlCwY4kyIinJ+APSZJXRzwtKB0jd3PQOYpLQuORCfEEYEaeLdZLGc7BHO4ZxTsDihzyarhFOftJuwdFvAkwEY52g1OKG/VJQE8OctrmWTLQrog/godRwDnIiBAuUpmCbJ98Pt7GhcCTZFBGCNiXms9uBD0/gvspUGWt6Fe8nqXF/jNtsaXvWJzFbFDSTu8SOs6kQKFQYOVMWhSaQhaEXTD/3W6jwgo4MUeIcYUaW1IZURJIrKVySRqbkW7BIstW8aHPpHSUfxUgwEaK/XaaF1VoncQyzwhQSQACHlOEnVIdeqD+lf3RAZ9Yrf+TTC5/RY/t4TP9Yc0xmKKP3ApICXUj5cg8ZZ9pR4REtU7kgsQ/aQe9ogz6sgb6CcpoU6HGUZXJhPwniDQWr8FsC00+FlxrbCu6uirtbg3EKvsP0GuUJSRDrH/O2FewHg9d3Wmq2LnSjBsWm3a9R/IIp9Uuj3/oIELQ07fr0e1Ln5S+TDcxypjLG0/fo6d22dLBlNapzPniqRRjzqtQyWeXcFMzhHUzzWSEyJuF8k9BQNHkuLt92nRoOzJzRhZUy5GdM/ajLxydsOMoMHzrmQ9sYQRsfPNuEgPrgDaNhQ0BPVXYTAvZJgGhoex+5CqjPoK7YNDcyei71Rr+TcnVzPa++iuhblXKkVZnt+kX7wPcqhOFX0GnpUI7haVLxeJRg0ooO04qD+ThEEGpacShrl0yMdAbdexmvNq0o4skbuGCuWSQXH9AdM+BXOInnCR1PJyjUfn7SkspWhSngjtt4pHM9UtNMW3MGUk8XbRGnOvVgIs7O4K1JKGrh1RZxNs0m7LYxIHBOg84YUj+RFjpPFWKL8q/83zcRid4hegaj9x0To6b61K/eR6qX4MygkZjhxCCcqMlO+uWEbzVyIvuEoUlIO4f8UFweDvOmmsRiRwzmWjB31TJUv5jbVlNYkEPyEWYxxHTs36Rglb8wFl8nFQJvaCrYdYsOBBWuYAYxYJG14YJ+LkyGDgn9usmHggrgDl4mmOwPRQEDfsdlAHvw2K+mjohYkRi+BRlN2fGIndq1j8U0U1LUU1L0K6tgA9tuxw1taxL9unDB1Ija6n7T7MWZJUBdNSJfXZbEzX6KQGwsvibU3aEjv6Ap2Wfe3sCuCfbB636BGuSV53uMa+9vtjDyWtLBtxxdfFDrP2y28GTYCzBY3icfjzEG4RyD0PSs1HOZAAxVgzAWt8qgMfBrhb/tBKE+f6DmfeOi+lO4g2s29WPQ14V+zYLEntFXY/8y+t8M+trQt62hc4Cw3vkL+C9PZoELFnzfEzMf1D0N3KFzglBNBRkNtqJkckmHlBMgp2EhMUvBNPHAHzwUVNeCnYaCRx6wQTEs0MOCaOiIMKxfP7pgyBkK9JESWkOHhWF9UpCyUxsK9EGB4cvDdesDzZxPZ1WfljGfrkmfqGmuny8MrqwA5cHg18Onb/wTVTSIT6vHL40l6J4pQzuDoO4JAbFS+PokRXhh0O8e/cngfqBpqSBF/8tJYmDQ7x59t/XDZNrQb1odSNGfyYTAQK8Bem/wYkDTfkUU+rlMBAz0GqCXVdnhng1qXBpiPL529CdDx3th03qw3Hh83eh7ztDxXvjI8wDG4+uE3h882Gvy+Lnx+HqhD4cO9iaBguofvC+NXEjf05785+5L41U3LRY5etO+NNX2bmhVCKNhX5pIrRs8upehWWd6PpVbP0JyeDPKrwyMvs0uIzWuYFNKG/AgY8rySkMxsdT8zJHxOr/yOlwFn+/K00n9cgO+8WWCFyn8COIE7XLDiv5YMfiC1Kgu/TQTjx3B23bFqa55x0lTesnnHc1Gx3pgH3xrGds2GcapGkQtMwxNL2ryJuUA0ZfHT34lTv15qlti+uIdF2230JTvfdKaqkzUusccxdMdTSfM9pn9Jiqtt890K2+G6nBvHLMF9+84Iq5Mv5106Io/bEsNQKiy802PjLbr1faxUzHvrRc46FN3WRkx6v4kdRf69Hz1XTLHxJ18OKy+As/fhE1Vy+luuYTYhGVDGOogGtxQuyYu+y1DzTVqAENND48vLOc52/G17+7r/wE=</diagram></mxfile>
|
||||
-234
@@ -1,234 +0,0 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url = 'https://files.minecraftforge.net' }
|
||||
mavenCentral()
|
||||
// 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'
|
||||
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.3'
|
||||
group = 'com.seibel.lod'
|
||||
archivesBaseName = 'Distant-Horizons_1.16.5'
|
||||
|
||||
|
||||
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
|
||||
|
||||
println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch'))
|
||||
minecraft {
|
||||
// The mappings can be changed at any time, and must be in the following format.
|
||||
// snapshot_YYYYMMDD Snapshot are built nightly.
|
||||
// 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: 'official', version: '1.16.5'
|
||||
|
||||
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
|
||||
|
||||
accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||
|
||||
// Default run configurations.
|
||||
// These can be tweaked, removed, or duplicated as needed.
|
||||
runs {
|
||||
client {
|
||||
workingDirectory project.file('run')
|
||||
arg "-mixin.config=lod.mixins.json"
|
||||
|
||||
// Recommended logging data for a userdev environment
|
||||
// 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 {
|
||||
examplemod {
|
||||
source sourceSets.main
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
workingDirectory project.file('run')
|
||||
arg "-mixin.config=lod.mixins.json"
|
||||
|
||||
// Recommended logging data for a userdev environment
|
||||
// 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 {
|
||||
examplemod {
|
||||
source sourceSets.main
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data {
|
||||
workingDirectory project.file('run')
|
||||
|
||||
// Recommended logging data for a userdev environment
|
||||
// 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.
|
||||
args '--mod', 'lod', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
|
||||
|
||||
mods {
|
||||
examplemod {
|
||||
source sourceSets.main
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.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"
|
||||
// compile "some.group:artifact:version"
|
||||
|
||||
// Real examples
|
||||
// compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env
|
||||
// compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env
|
||||
|
||||
// The 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime.
|
||||
// provided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
|
||||
|
||||
// These dependencies get remapped to your current MCP mappings
|
||||
// deobf 'com.mod-buildcraft:buildcraft:6.0.8:dev'
|
||||
|
||||
// For more info...
|
||||
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
||||
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
||||
|
||||
}
|
||||
|
||||
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": "LOD",
|
||||
"Specification-Version": "1", // We are version 1 of ourselves
|
||||
"Implementation-Title": project.name,
|
||||
"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')
|
||||
// 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')
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifact jar
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url "file:///${project.projectDir}/mcmodsrepo"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mixin {
|
||||
add sourceSets.main, "lod.refmap.json"
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
|
||||
# This is required to provide enough memory for the Minecraft decompilation process.
|
||||
org.gradle.jvmargs=-Xmx3G
|
||||
org.gradle.daemon=false
|
||||
Vendored
BIN
Binary file not shown.
-5
@@ -1,5 +0,0 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip
|
||||
@@ -1,172 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
Vendored
-84
@@ -1,84 +0,0 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_MC_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_MC_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_MC_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_MC_HOME=%JAVA_MC_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_MC_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_MC_HOME is set to an invalid directory: %JAVA_MC_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_MC_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core;
|
||||
|
||||
public class JarMain {
|
||||
public static void main(String[] args){
|
||||
System.out.println("Why are you running the jar, this isn't done yet >:(");
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -18,7 +18,6 @@
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core;
|
||||
|
||||
/**
|
||||
* This file is similar to mcmod.info
|
||||
* <br>
|
||||
@@ -28,15 +27,18 @@ package com.seibel.lod.core;
|
||||
* Pretty much all of the mod stems from there.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
* @author Ran
|
||||
* @version 11-29-2021
|
||||
*/
|
||||
public final class ModInfo
|
||||
{
|
||||
public static final String ID = "lod";
|
||||
/** The internal protocol version used for networking */
|
||||
public static final int PROTOCOL_VERSION = 1;
|
||||
/** The internal mod name */
|
||||
public static final String NAME = "DistantHorizons";
|
||||
/** Human readable version of NAME */
|
||||
public static final String READABLE_NAME = "Distant Horizons";
|
||||
public static final String API = "LodAPI";
|
||||
public static final String VERSION = "a1.5.3";
|
||||
}
|
||||
public static final String VERSION = "1.6.7a-dev";
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -19,9 +19,12 @@
|
||||
|
||||
package com.seibel.lod.core.api;
|
||||
|
||||
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory;
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||
import com.seibel.lod.core.objects.lod.LodWorld;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* This stores objects and variables that
|
||||
@@ -32,9 +35,9 @@ import com.seibel.lod.core.objects.lod.LodWorld;
|
||||
*/
|
||||
public class ApiShared
|
||||
{
|
||||
public static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME);
|
||||
public ApiShared INSTANCE = new ApiShared();
|
||||
|
||||
public static final LodBufferBuilderFactory lodBufferBuilderFactory = new LodBufferBuilderFactory();
|
||||
public static final LodWorld lodWorld = new LodWorld();
|
||||
public static final LodBuilder lodBuilder = new LodBuilder();
|
||||
|
||||
@@ -42,12 +45,14 @@ public class ApiShared
|
||||
public static int previousChunkRenderDistance = 0;
|
||||
/** Used to determine if the LODs should be regenerated */
|
||||
public static int previousLodRenderDistance = 0;
|
||||
public static VerticalQuality previousVertQual = null;
|
||||
|
||||
|
||||
|
||||
/** Signal whether a world is shutting down */
|
||||
public static volatile boolean isShuttingDown = false;
|
||||
|
||||
private ApiShared()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -19,22 +19,37 @@
|
||||
|
||||
package com.seibel.lod.core.api;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.enums.rendering.RendererType;
|
||||
import com.seibel.lod.core.logging.ConfigBasedLogger;
|
||||
import com.seibel.lod.core.logging.ConfigBasedSpamLogger;
|
||||
import com.seibel.lod.core.render.RenderSystemTest;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import com.seibel.lod.core.handlers.LodDimensionFinder;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.builders.worldGeneration.LodGenWorker;
|
||||
import com.seibel.lod.core.builders.lodBuilding.bufferBuilding.LodBufferBuilderFactory;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.render.GLProxy;
|
||||
import com.seibel.lod.core.render.LodRenderer;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
import com.seibel.lod.core.logging.SpamReducedLogger;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
/**
|
||||
* This holds the methods that should be called
|
||||
@@ -42,19 +57,43 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
* Specifically for the client.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-12-2021
|
||||
* @version 2022-3-26
|
||||
*/
|
||||
public class ClientApi
|
||||
{
|
||||
{
|
||||
public static boolean prefLoggerEnabled = false;
|
||||
|
||||
public static final ClientApi INSTANCE = new ClientApi();
|
||||
public static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME);
|
||||
|
||||
public static final LodBufferBuilderFactory lodBufferBuilderFactory = new LodBufferBuilderFactory();
|
||||
public static LodRenderer renderer = new LodRenderer(lodBufferBuilderFactory);
|
||||
public static RenderSystemTest testRenderer = new RenderSystemTest();
|
||||
|
||||
public static LodRenderer renderer = new LodRenderer(ApiShared.lodBufferBuilderFactory);
|
||||
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class);
|
||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
private static final EventApi EVENT_API = EventApi.INSTANCE;
|
||||
|
||||
public static final boolean ENABLE_LAG_SPIKE_LOGGING = false;
|
||||
public static final long LAG_SPIKE_THRESOLD_NS = TimeUnit.NANOSECONDS.convert(16, TimeUnit.MILLISECONDS);
|
||||
|
||||
public static final long SPAM_LOGGER_FLUSH_NS = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
|
||||
|
||||
public static LodDimensionFinder DIMENSION_FINDER = new LodDimensionFinder();;
|
||||
|
||||
public static class LagSpikeCatcher {
|
||||
|
||||
long timer = System.nanoTime();
|
||||
public LagSpikeCatcher() {}
|
||||
public void end(String source) {
|
||||
if (!ENABLE_LAG_SPIKE_LOGGING) return;
|
||||
timer = System.nanoTime() - timer;
|
||||
if (timer > LAG_SPIKE_THRESOLD_NS) {
|
||||
ApiShared.LOGGER.info("LagSpikeCatcher: "+source+" took "+Duration.ofNanos(timer)+"!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* there is some setup that should only happen once,
|
||||
@@ -63,61 +102,196 @@ public class ClientApi
|
||||
private boolean firstTimeSetupComplete = false;
|
||||
private boolean configOverrideReminderPrinted = false;
|
||||
|
||||
public boolean rendererDisabledBecauseOfExceptions = false;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private ClientApi()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static void logToChat(Level logLevel, String str) {
|
||||
String prefix = "["+ModInfo.READABLE_NAME+"] ";
|
||||
if (logLevel == Level.ERROR) {
|
||||
prefix += "\u00A74";
|
||||
} else if (logLevel == Level.WARN) {
|
||||
prefix += "\u00A76";
|
||||
} else if (logLevel == Level.INFO) {
|
||||
prefix += "\u00A7f";
|
||||
} else if (logLevel == Level.DEBUG) {
|
||||
prefix += "\u00A77";
|
||||
} else if (logLevel == Level.TRACE) {
|
||||
prefix += "\u00A78";
|
||||
} else {
|
||||
prefix += "\u00A7f";
|
||||
}
|
||||
prefix += "\u00A7l\u00A7u";
|
||||
prefix += logLevel.name();
|
||||
prefix += ":\u00A7r ";
|
||||
if (MC != null) MC.sendChatMessage(prefix + str);
|
||||
}
|
||||
|
||||
private final ConcurrentHashMap.KeySetView<Long,Boolean> generating = ConcurrentHashMap.newKeySet();
|
||||
public final ConcurrentHashMap.KeySetView<Long,Boolean> toBeLoaded = ConcurrentHashMap.newKeySet();
|
||||
|
||||
|
||||
|
||||
public void clientChunkLoadEvent(IChunkWrapper chunk, IWorldWrapper world)
|
||||
{
|
||||
LagSpikeCatcher clientChunkLoad = new LagSpikeCatcher();
|
||||
//ApiShared.LOGGER.info("Lod Generating add: "+chunk.getLongChunkPos());
|
||||
toBeLoaded.add(chunk.getLongChunkPos());
|
||||
clientChunkLoad.end("clientChunkLoad");
|
||||
}
|
||||
|
||||
private long lastFlush = 0;
|
||||
|
||||
public void renderLods(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks)
|
||||
{
|
||||
// comment out when creating a release
|
||||
applyConfigOverrides();
|
||||
|
||||
//applyConfigOverrides();
|
||||
|
||||
// clear any out of date objects
|
||||
MC.clearFrameObjectCache();
|
||||
|
||||
try
|
||||
{
|
||||
boolean doFlush = System.nanoTime() - lastFlush >= SPAM_LOGGER_FLUSH_NS;
|
||||
if (doFlush) {
|
||||
lastFlush = System.nanoTime();
|
||||
SpamReducedLogger.flushAll();
|
||||
}
|
||||
ConfigBasedLogger.updateAll();
|
||||
ConfigBasedSpamLogger.updateAll(doFlush);
|
||||
|
||||
if (ApiShared.previousVertQual != CONFIG.client().graphics().quality().getVerticalQuality()) {
|
||||
ApiShared.previousVertQual = CONFIG.client().graphics().quality().getVerticalQuality();
|
||||
EventApi.INSTANCE.worldUnloadEvent(MC.getWrappedServerWorld());
|
||||
EventApi.INSTANCE.worldLoadEvent(MC.getWrappedClientWorld());
|
||||
return;
|
||||
}
|
||||
|
||||
// only run the first time setup once
|
||||
if (!firstTimeSetupComplete)
|
||||
firstFrameSetup();
|
||||
|
||||
|
||||
|
||||
if (!MC.playerExists() || ApiShared.lodWorld.getIsWorldNotLoaded())
|
||||
return;
|
||||
|
||||
LodDimension lodDim = ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension());
|
||||
if (lodDim == null)
|
||||
IWorldWrapper world = MC.getWrappedClientWorld();
|
||||
if (world == null)
|
||||
return;
|
||||
LodDimension lodDim = ApiShared.lodWorld.getLodDimension(world.getDimensionType());
|
||||
|
||||
// Make sure the player's data is up-to-date
|
||||
DIMENSION_FINDER.updatePlayerData();
|
||||
|
||||
// Make the LodDim if it does not exist
|
||||
if (lodDim == null)
|
||||
{
|
||||
if (DIMENSION_FINDER.isDone())
|
||||
{
|
||||
lodDim = DIMENSION_FINDER.getAndClearFoundLodDimension();
|
||||
ApiShared.lodWorld.addLodDimension(lodDim);
|
||||
}
|
||||
else
|
||||
{
|
||||
DIMENSION_FINDER.AttemptToDetermineSubDimensionAsync(MC.getCurrentDimension());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (prefLoggerEnabled) {
|
||||
lodDim.dumpRamUsage();
|
||||
lodBufferBuilderFactory.dumpBufferMemoryUsage();
|
||||
}
|
||||
|
||||
LagSpikeCatcher updateToBeLoadedChunk = new LagSpikeCatcher();
|
||||
for (long pos : toBeLoaded) {
|
||||
if (generating.size() >= 1) {
|
||||
//ApiShared.LOGGER.info("Lod Generating Full! Remining: "+toBeLoaded.size());
|
||||
break;
|
||||
}
|
||||
IChunkWrapper chunk = world.tryGetChunk(FACTORY.createChunkPos(pos));
|
||||
if (chunk == null) {
|
||||
toBeLoaded.remove(pos);
|
||||
LodBuilder.EVENT_LOGGER.debug("Manual Chunk: {} not ready. Remaining queue: {}", FACTORY.createChunkPos(pos), toBeLoaded.size());
|
||||
continue;
|
||||
}
|
||||
if (!chunk.isLightCorrect()) continue;
|
||||
if (!chunk.doesNearbyChunksExist()) continue;
|
||||
toBeLoaded.remove(pos);
|
||||
generating.add(pos);
|
||||
//ApiShared.LOGGER.info("Lod Generation trying "+pos+". Remining: " +toBeLoaded.size());
|
||||
ApiShared.lodBuilder.generateLodNodeAsync(chunk, ApiShared.lodWorld,
|
||||
world.getDimensionType(), DistanceGenerationMode.FULL, true, true, () -> {
|
||||
generating.remove(pos);
|
||||
LodBuilder.EVENT_LOGGER.debug("Manual Chunk: {} done. Remaining queue: {}", FACTORY.createChunkPos(pos), toBeLoaded.size());
|
||||
}, () -> {
|
||||
generating.remove(pos);
|
||||
toBeLoaded.add(pos);
|
||||
LodBuilder.EVENT_LOGGER.debug("Manual Chunk: {} not ready. Remaining queue: {}", FACTORY.createChunkPos(pos), toBeLoaded.size());
|
||||
});
|
||||
}
|
||||
updateToBeLoadedChunk.end("updateToBeLoadedChunk");
|
||||
|
||||
|
||||
|
||||
LagSpikeCatcher updateSettings = new LagSpikeCatcher();
|
||||
DetailDistanceUtil.updateSettings();
|
||||
EVENT_API.viewDistanceChangedEvent();
|
||||
updateSettings.end("updateSettings");
|
||||
LagSpikeCatcher updatePlayerMove = new LagSpikeCatcher();
|
||||
EVENT_API.playerMoveEvent(lodDim);
|
||||
updatePlayerMove.end("updatePlayerMove");
|
||||
|
||||
|
||||
|
||||
LagSpikeCatcher cutAndExpendAsync = new LagSpikeCatcher();
|
||||
lodDim.cutRegionNodesAsync(MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getZ());
|
||||
lodDim.expandOrLoadRegionsAsync(MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getZ());
|
||||
cutAndExpendAsync.end("cutAndExpendAsync");
|
||||
|
||||
|
||||
// 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
|
||||
IProfilerWrapper profiler = MC.getProfiler();
|
||||
profiler.pop(); // get out of "terrain"
|
||||
profiler.push("LOD");
|
||||
|
||||
|
||||
ClientApi.renderer.drawLODs(lodDim, mcModelViewMatrix, mcProjectionMatrix, partialTicks, MC.getProfiler());
|
||||
|
||||
profiler.pop(); // end LOD
|
||||
profiler.push("terrain"); // go back into "terrain"
|
||||
|
||||
|
||||
if (CONFIG.client().advanced().debugging().getRendererType() == RendererType.DEFAULT)
|
||||
{
|
||||
// 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
|
||||
IProfilerWrapper profiler = MC.getProfiler();
|
||||
profiler.pop(); // get out of "terrain"
|
||||
profiler.push("LOD");
|
||||
|
||||
if (!rendererDisabledBecauseOfExceptions) {
|
||||
try {
|
||||
ClientApi.renderer.drawLODs(lodDim, mcModelViewMatrix, mcProjectionMatrix, partialTicks, MC.getProfiler());
|
||||
} catch (RuntimeException e) {
|
||||
rendererDisabledBecauseOfExceptions = true;
|
||||
ApiShared.LOGGER.error("Renderer thrown an uncaught exception: ",e);
|
||||
try {
|
||||
MC.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons"
|
||||
+ " renderer has encountered an exception!");
|
||||
MC.sendChatMessage("\u00A74Renderer is now disabled to prevent futher issues.");
|
||||
MC.sendChatMessage("\u00A74Exception detail: "+e.toString());
|
||||
} catch (RuntimeException ignored) {}
|
||||
}
|
||||
}
|
||||
profiler.pop(); // end LOD
|
||||
profiler.push("terrain"); // go back into "terrain"
|
||||
} else if (CONFIG.client().advanced().debugging().getRendererType() == RendererType.DEBUG) {
|
||||
IProfilerWrapper profiler = MC.getProfiler();
|
||||
profiler.pop(); // get out of "terrain"
|
||||
profiler.push("LODTestRendering");
|
||||
ClientApi.testRenderer.render();
|
||||
profiler.pop(); // end LODTestRendering
|
||||
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
|
||||
ApiShared.previousChunkRenderDistance = MC_RENDER.getRenderDistance();
|
||||
@@ -125,8 +299,7 @@ public class ClientApi
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ClientApi.LOGGER.error("client proxy: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
ApiShared.LOGGER.error("client proxy uncaught exception: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,21 +310,54 @@ public class ClientApi
|
||||
if (!configOverrideReminderPrinted)
|
||||
{
|
||||
MC.sendChatMessage(ModInfo.READABLE_NAME + " experimental build " + ModInfo.VERSION);
|
||||
MC.sendChatMessage("You are running a unsupported version of the mod!");
|
||||
MC.sendChatMessage("You are running an unsupported version of the mod!");
|
||||
MC.sendChatMessage("Here be dragons!");
|
||||
|
||||
configOverrideReminderPrinted = true;
|
||||
}
|
||||
|
||||
// CONFIG.client().worldGenerator().setDistanceGenerationMode(DistanceGenerationMode.SURFACE);
|
||||
// CONFIG.client().worldGenerator().setDistanceGenerationMode(DistanceGenerationMode.FULL);
|
||||
|
||||
// CONFIG.client().worldGenerator().setGenerationPriority(GenerationPriority.AUTO);
|
||||
|
||||
// CONFIG.client().graphics().advancedGraphics().setGpuUploadMethod(GpuUploadMethod.BUFFER_STORAGE);
|
||||
// CONFIG.client().graphics().quality().setLodChunkRenderDistance(128);
|
||||
|
||||
// CONFIG.client().graphics().fogQuality().setFogDrawMode(FogDrawMode.FOG_ENABLED);
|
||||
// CONFIG.client().graphics().fogQuality().setFogDistance(FogDistance.FAR);
|
||||
// CONFIG.client().graphics().fogQuality().setDisableVanillaFog(true);
|
||||
|
||||
// CONFIG.client().advanced().buffers().setRebuildTimes(BufferRebuildTimes.FREQUENT);
|
||||
|
||||
// CONFIG.client().advanced().debugging().setDebugKeybindingsEnabled(true);
|
||||
}
|
||||
|
||||
//=================//
|
||||
// DUBUG USE //
|
||||
//=================//
|
||||
// Trigger once on key press, with CLIENT PLAYER.
|
||||
public void keyPressedEvent(int glfwKey) {
|
||||
|
||||
if (!CONFIG.client().advanced().debugging().getDebugKeybindingsEnabled()) return;
|
||||
|
||||
if (glfwKey == GLFW.GLFW_KEY_F8) {
|
||||
CONFIG.client().advanced().debugging()
|
||||
.setDebugMode(CONFIG.client().advanced().debugging().getDebugMode().getNext());
|
||||
MC.sendChatMessage("F8: Set debug mode to " + CONFIG.client().advanced().debugging().getDebugMode());
|
||||
}
|
||||
|
||||
if (glfwKey == GLFW.GLFW_KEY_F6) {
|
||||
CONFIG.client().advanced().debugging()
|
||||
.setRendererType(RendererType.next(CONFIG.client().advanced().debugging().getRendererType()));
|
||||
MC.sendChatMessage("F6: Set rendering to " + CONFIG.client().advanced().debugging().getRendererType());
|
||||
}
|
||||
|
||||
if (glfwKey == GLFW.GLFW_KEY_P) {
|
||||
prefLoggerEnabled = !prefLoggerEnabled;
|
||||
MC.sendChatMessage("P: Debug Pref Logger is " + (prefLoggerEnabled ? "enabled" : "disabled"));
|
||||
}
|
||||
|
||||
CONFIG.client().advanced().debugging().setDebugKeybindingsEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -159,6 +365,8 @@ public class ClientApi
|
||||
// Lod maintenance //
|
||||
//=================//
|
||||
|
||||
// FIXME: I need a onLastFrameCleanup() callback in Render Thread... Which calls renderer.cleanup()
|
||||
|
||||
/** This event is called once during the first frame Minecraft renders in the world. */
|
||||
public void firstFrameSetup()
|
||||
{
|
||||
@@ -168,14 +376,6 @@ public class ClientApi
|
||||
firstTimeSetupComplete = true;
|
||||
}
|
||||
|
||||
/** this method reset some static data every time we change world */
|
||||
private void resetMod()
|
||||
{
|
||||
// TODO when should this be used?
|
||||
ThreadMapUtil.clearMaps();
|
||||
LodGenWorker.restartExecutorService();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -19,177 +19,206 @@
|
||||
|
||||
package com.seibel.lod.core.api;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import com.seibel.lod.core.builders.worldGeneration.LodWorldGenerator;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.api.ClientApi.LagSpikeCatcher;
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.builders.worldGeneration.BatchGenerator;
|
||||
import com.seibel.lod.core.enums.WorldType;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.render.GLProxy;
|
||||
import com.seibel.lod.core.render.LodRenderer;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IVersionConstants;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
/**
|
||||
* This holds the methods that should be called
|
||||
* by the host mod loader (Fabric, Forge, etc.).
|
||||
* Specifically server and client events.
|
||||
*
|
||||
* This holds the methods that should be called by the host mod loader (Fabric,
|
||||
* Forge, etc.). Specifically server and client events.
|
||||
* @author James Seibel
|
||||
* @version 11-12-2021
|
||||
*/
|
||||
public class EventApi
|
||||
{
|
||||
public static final boolean ENABLE_STACK_DUMP_LOGGING = false;
|
||||
public static final EventApi INSTANCE = new EventApi();
|
||||
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IVersionConstants VERSION_CONSTANTS = SingletonHandler.get(IVersionConstants.class);
|
||||
|
||||
/**
|
||||
* can be set if we want to recalculate variables related
|
||||
* to the LOD view distance
|
||||
* can be set if we want to recalculate variables related to the LOD view
|
||||
* distance
|
||||
*/
|
||||
private boolean recalculateWidths = false;
|
||||
|
||||
private boolean isCurrentlyOnSinglePlayerServer = false;
|
||||
|
||||
|
||||
private EventApi()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// =============//
|
||||
// tick events //
|
||||
//=============//
|
||||
|
||||
// =============//
|
||||
public BatchGenerator batchGenerator = null;
|
||||
|
||||
private int lastWorldGenTickDelta = 0;
|
||||
public void serverTickEvent()
|
||||
{
|
||||
lastWorldGenTickDelta--;
|
||||
if (!MC.playerExists() || ApiShared.lodWorld.getIsWorldNotLoaded())
|
||||
return;
|
||||
|
||||
LodDimension lodDim = ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension());
|
||||
if (lodDim == null)
|
||||
return;
|
||||
|
||||
LodWorldGenerator.INSTANCE.queueGenerationRequests(lodDim, ClientApi.renderer, ApiShared.lodBuilder);
|
||||
if (ApiShared.isShuttingDown)
|
||||
return;
|
||||
|
||||
if (CONFIG.client().worldGenerator().getEnableDistantGeneration())
|
||||
{
|
||||
if (lastWorldGenTickDelta <= 0) {
|
||||
lastWorldGenTickDelta = 20; // 20 ticks is 1 second. We don't need to refresh world gen status every tick.
|
||||
try {
|
||||
if (batchGenerator == null)
|
||||
batchGenerator = new BatchGenerator(ApiShared.lodBuilder, lodDim);
|
||||
batchGenerator.queueGenerationRequests(lodDim, ApiShared.lodBuilder);
|
||||
} catch (Exception e) {
|
||||
// Exception may happen if world got unloaded unorderly
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (batchGenerator != null)
|
||||
{
|
||||
batchGenerator.stop(false);
|
||||
batchGenerator = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// ==============//
|
||||
// world events //
|
||||
//==============//
|
||||
|
||||
public void chunkLoadEvent(IChunkWrapper chunk, IDimensionTypeWrapper dimType)
|
||||
{
|
||||
ApiShared.lodBuilder.generateLodNodeAsync(chunk, ApiShared.lodWorld, dimType, DistanceGenerationMode.FULL);
|
||||
}
|
||||
// ==============//
|
||||
|
||||
public void worldSaveEvent()
|
||||
{
|
||||
ApiShared.lodWorld.saveAllDimensions();
|
||||
ApiShared.lodWorld.saveAllDimensions(false); // Do an async save.
|
||||
}
|
||||
|
||||
/** This is also called when a new dimension loads */
|
||||
public void worldLoadEvent(IWorldWrapper world)
|
||||
{
|
||||
DataPointUtil.worldHeight = world.getHeight();
|
||||
//LodNodeGenWorker.restartExecutorService();
|
||||
//ThreadMapUtil.clearMaps();
|
||||
if (ENABLE_STACK_DUMP_LOGGING)
|
||||
ApiShared.LOGGER.info(
|
||||
"WorldLoadEvent called here for "
|
||||
+ (world.getWorldType() == WorldType.ClientWorld ? "clientLevel" : "serverLevel"),
|
||||
new RuntimeException());
|
||||
// Always ignore ServerWorld event
|
||||
if (world.getWorldType() == WorldType.ServerWorld)
|
||||
return;
|
||||
isCurrentlyOnSinglePlayerServer = MC.hasSinglePlayerServer();
|
||||
if (!ApiShared.isShuttingDown) ApiShared.LOGGER.warn("WorldLoadEvent called on {} while another world is loaded!",
|
||||
(world.getWorldType() == WorldType.ClientWorld ? "clientLevel" : "serverLevel"));
|
||||
ApiShared.isShuttingDown = false;
|
||||
//DataPointUtil.WORLD_HEIGHT = world.getHeight();
|
||||
LodBuilder.MIN_WORLD_HEIGHT = world.getMinHeight(); // This updates the World height
|
||||
|
||||
// LodNodeGenWorker.restartExecutorService();
|
||||
// ThreadMapUtil.clearMaps();
|
||||
|
||||
// the player just loaded a new world/dimension
|
||||
ApiShared.lodWorld.selectWorld(LodUtil.getWorldID(world));
|
||||
String worldID = LodUtil.getWorldID(world);
|
||||
ApiShared.LOGGER.info("Loading new world/dimension: {}",worldID);
|
||||
ApiShared.lodWorld.selectWorld(worldID);
|
||||
ApiShared.LOGGER.info("World/dimension loaded: {}",worldID);
|
||||
|
||||
// make sure the correct LODs are being rendered
|
||||
// (if this isn't done the previous world's LODs may be drawn)
|
||||
ClientApi.renderer.regenerateLODsNextFrame();
|
||||
ApiShared.previousVertQual = CONFIG.client().graphics().quality().getVerticalQuality();
|
||||
}
|
||||
|
||||
public void worldUnloadEvent()
|
||||
/** This is also called when the user disconnects from a server+ */
|
||||
public void worldUnloadEvent(IWorldWrapper world)
|
||||
{
|
||||
// the player just unloaded a world/dimension
|
||||
ThreadMapUtil.clearMaps();
|
||||
if (ENABLE_STACK_DUMP_LOGGING)
|
||||
ApiShared.LOGGER.info(
|
||||
"WorldUnloadEvent called here for "
|
||||
+ (world.getWorldType() == WorldType.ClientWorld ? "clientLevel" : "serverLevel"),
|
||||
new RuntimeException());
|
||||
|
||||
// If it's single player, ignore the client side world unload event
|
||||
// Note: using isCurrentlyOnSinglePlayerServer as often API call unload event
|
||||
// AFTER setting MC to not be in a singlePlayerServer
|
||||
if (isCurrentlyOnSinglePlayerServer && world.getWorldType() == WorldType.ClientWorld)
|
||||
return;
|
||||
|
||||
// if this isn't done unfinished tasks may be left in the queue
|
||||
// preventing new LodChunks form being generated
|
||||
if (ApiShared.isShuttingDown) return; // Don't do this if we're already shutting down
|
||||
ApiShared.isShuttingDown = true;
|
||||
|
||||
if (MC.getWrappedClientWorld() == null)
|
||||
{
|
||||
// the player just left the server
|
||||
|
||||
// TODO should "resetMod()" be called here? -James
|
||||
|
||||
// if this isn't done unfinished tasks may be left in the queue
|
||||
// preventing new LodChunks form being generated
|
||||
//LodNodeGenWorker.restartExecutorService(); // TODO why was this commented out? -James
|
||||
//ThreadMapUtil.clearMaps();
|
||||
|
||||
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0);
|
||||
ApiShared.lodWorld.deselectWorld();
|
||||
|
||||
|
||||
// prevent issues related to the buffer builder
|
||||
// breaking when changing worlds.
|
||||
ClientApi.renderer.destroyBuffers();
|
||||
recalculateWidths = true;
|
||||
ClientApi.renderer = new LodRenderer(ApiShared.lodBufferBuilderFactory);
|
||||
|
||||
|
||||
// make sure the nulled objects are freed.
|
||||
// (this prevents an out of memory error when
|
||||
// changing worlds)
|
||||
System.gc();
|
||||
}
|
||||
// TODO Better report on when world gen is stuck and timeout
|
||||
if (batchGenerator != null)
|
||||
batchGenerator.stop(true);
|
||||
batchGenerator = null;
|
||||
|
||||
ApiShared.lodWorld.deselectWorld(); // This force a save and shutdown lodDim properly
|
||||
|
||||
// prevent issues related to the buffer builder
|
||||
// breaking or retaining previous data when changing worlds.
|
||||
ClientApi.renderer.destroyBuffers();
|
||||
ClientApi.renderer.requestCleanup();
|
||||
GLProxy.ensureAllGLJobCompleted();
|
||||
recalculateWidths = true;
|
||||
ApiShared.previousVertQual = null;
|
||||
|
||||
// TODO: Check if after the refactoring, is this still needed
|
||||
ClientApi.renderer = new LodRenderer(ClientApi.lodBufferBuilderFactory);
|
||||
ClientApi.INSTANCE.rendererDisabledBecauseOfExceptions = false;
|
||||
ApiShared.LOGGER.info("Distant Horizon unloaded");
|
||||
}
|
||||
|
||||
public void blockChangeEvent(IChunkWrapper chunk, IDimensionTypeWrapper dimType)
|
||||
{
|
||||
if (dimType != MC.getCurrentDimension())
|
||||
return;
|
||||
// recreate the LOD where the blocks were changed
|
||||
ApiShared.lodBuilder.generateLodNodeAsync(chunk, ApiShared.lodWorld, dimType);
|
||||
LagSpikeCatcher blockChangeUpdate = new LagSpikeCatcher();
|
||||
ClientApi.INSTANCE.toBeLoaded.add(chunk.getLongChunkPos());
|
||||
blockChangeUpdate.end("clientChunkLoad");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// =============//
|
||||
// Misc Events //
|
||||
//=============//
|
||||
// =============//
|
||||
|
||||
public void onKeyInput(int key, int keyAction)
|
||||
{
|
||||
if (CONFIG.client().advanced().debugging().getDebugKeybindingsEnabled())
|
||||
{
|
||||
if (key == GLFW.GLFW_KEY_F4 && keyAction == GLFW.GLFW_PRESS)
|
||||
{
|
||||
CONFIG.client().advanced().debugging().setDebugMode(CONFIG.client().advanced().debugging().getDebugMode().getNext());
|
||||
}
|
||||
|
||||
if (key == GLFW.GLFW_KEY_F6 && keyAction == GLFW.GLFW_PRESS)
|
||||
{
|
||||
CONFIG.client().advanced().debugging().setDrawLods(!CONFIG.client().advanced().debugging().getDrawLods());
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOTE: This is being called from Render Thread.
|
||||
|
||||
/** Re-centers the given LodDimension if it needs to be. */
|
||||
public void playerMoveEvent(LodDimension lodDim)
|
||||
{
|
||||
// make sure the dimension is centered
|
||||
RegionPos playerRegionPos = new RegionPos(MC.getPlayerBlockPos());
|
||||
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterRegionPosX(), playerRegionPos.z - lodDim.getCenterRegionPosZ());
|
||||
RegionPos center = lodDim.getCenterRegionPos();
|
||||
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - center.x, playerRegionPos.z - center.z);
|
||||
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
|
||||
{
|
||||
ApiShared.lodWorld.saveAllDimensions();
|
||||
lodDim.move(worldRegionOffset);
|
||||
//LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ());
|
||||
// LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z +
|
||||
// "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,29 +228,28 @@ public class EventApi
|
||||
// calculate how wide the dimension(s) should be in regions
|
||||
int chunksWide;
|
||||
if (MC.getWrappedClientWorld().getDimensionType().hasCeiling())
|
||||
chunksWide = Math.min(CONFIG.client().graphics().quality().getLodChunkRenderDistance(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * 2 + 1;
|
||||
chunksWide = Math.min(CONFIG.client().graphics().quality().getLodChunkRenderDistance(),
|
||||
LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * 2 + 1;
|
||||
else
|
||||
chunksWide = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * 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;
|
||||
newWidth += (newWidth & 1) == 0 ? 1 : 0;
|
||||
|
||||
// do the dimensions need to change in size?
|
||||
if (ApiShared.lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths)
|
||||
{
|
||||
ApiShared.lodWorld.saveAllDimensions();
|
||||
|
||||
// update the dimensions to fit the new width
|
||||
ApiShared.lodWorld.resizeDimensionRegionWidth(newWidth);
|
||||
ApiShared.lodBuilder.defaultDimensionWidthInRegions = newWidth;
|
||||
ClientApi.renderer.setupBuffers(ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension()));
|
||||
ClientApi.renderer.setupBuffers();
|
||||
|
||||
recalculateWidths = false;
|
||||
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
|
||||
// LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: "
|
||||
// + newWidth );
|
||||
}
|
||||
DetailDistanceUtil.updateSettings();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
-992
@@ -1,992 +0,0 @@
|
||||
/*
|
||||
* 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.core.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.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.config.GpuUploadMethod;
|
||||
import com.seibel.lod.core.enums.config.VanillaOverdraw;
|
||||
import com.seibel.lod.core.enums.rendering.GLProxyContext;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.PosToRenderContainer;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.lod.LodRegion;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.objects.opengl.LodVertexBuffer;
|
||||
import com.seibel.lod.core.render.GLProxy;
|
||||
import com.seibel.lod.core.render.LodRenderer;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodThreadFactory;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
|
||||
/**
|
||||
* This object creates the buffers that are
|
||||
* rendered by the LodRenderer.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-21-2021
|
||||
*/
|
||||
public class LodBufferBuilderFactory
|
||||
{
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
/** The thread used to generate new LODs off the main thread. */
|
||||
public static final ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(LodBufferBuilderFactory.class.getSimpleName() + " - main"));
|
||||
/** The threads used to generate buffers. */
|
||||
public static final ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads(), 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 LodBufferBuilder[][][] 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 LodVertexBuffer[][][] buildableVbos;
|
||||
/** VBOs that are sent over to the LodNodeRenderer */
|
||||
public volatile LodVertexBuffer[][][] 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 ChunkPosWrapper the player was at the last time the buffers were built.
|
||||
* IE the center of the buffers last time they were built
|
||||
*/
|
||||
private volatile AbstractChunkPosWrapper drawableCenterChunkPos = WRAPPER_FACTORY.createChunkPos();
|
||||
private volatile AbstractChunkPosWrapper buildableCenterChunkPos = WRAPPER_FACTORY.createChunkPos();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public LodBufferBuilderFactory()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
AbstractBlockPosWrapper 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,
|
||||
AbstractBlockPosWrapper playerBlockPos, boolean fullRegen)
|
||||
{
|
||||
bufferLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
// round the player's block position down to the nearest chunk BlockPos
|
||||
AbstractChunkPosWrapper playerChunkPos = WRAPPER_FACTORY.createChunkPos(playerBlockPos);
|
||||
AbstractBlockPosWrapper 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 //
|
||||
//================================//
|
||||
|
||||
skyLightPlayer = MC.getWrappedClientWorld().getSkyLight(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() - lodDim.getWidth() / 2,
|
||||
zRegion + lodDim.getCenterRegionPosZ() - lodDim.getWidth() / 2);
|
||||
|
||||
// local position in the vbo and bufferBuilder arrays
|
||||
LodBufferBuilder[] 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<LodDirection, 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 = index % currentBuffers.length;
|
||||
detailLevel = posToRender.getNthDetailLevel(index);
|
||||
posX = posToRender.getNthPosX(index);
|
||||
posZ = posToRender.getNthPosZ(index);
|
||||
|
||||
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.getX();
|
||||
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.getZ();
|
||||
|
||||
//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 (LodDirection lodDirection : Box.ADJ_DIRECTIONS)
|
||||
{
|
||||
|
||||
xAdj = posX + Box.DIRECTION_NORMAL_MAP.get(lodDirection).x;
|
||||
zAdj = posZ + Box.DIRECTION_NORMAL_MAP.get(lodDirection).z;
|
||||
long data;
|
||||
chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkPos.getX();
|
||||
chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkPos.getZ();
|
||||
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(lodDirection)] = false;
|
||||
adjData.get(lodDirection)[verticalIndex] = data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Otherwise, we check if this position is
|
||||
data = lodDim.getSingleData(detailLevel, xAdj, zAdj);
|
||||
|
||||
adjData.get(lodDirection)[0] = DataPointUtil.EMPTY_DATA;
|
||||
|
||||
if ((isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance) || (posNotInPlayerChunk && adjPosInPlayerChunk))
|
||||
&& !DataPointUtil.isVoid(data))
|
||||
{
|
||||
adjShadeDisabled[Box.DIRECTION_INDEX.get(lodDirection)] = 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(LodDirection.UP)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex - 1);
|
||||
else
|
||||
adjData.get(LodDirection.UP)[0] = DataPointUtil.EMPTY_DATA;
|
||||
|
||||
|
||||
//we get the below block as adj DOWN
|
||||
if (verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ) - 1)
|
||||
adjData.get(LodDirection.DOWN)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex + 1);
|
||||
else
|
||||
adjData.get(LodDirection.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
|
||||
CONFIG.client().graphics().advancedGraphics().getLodTemplate().template.addLodToBuffer(currentBuffers[bufferIndex], playerBlockPosRounded, data, adjData,
|
||||
detailLevel, posX, posZ, box, renderer.previousDebugMode, 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())
|
||||
{
|
||||
ClientApi.LOGGER.warn("LodBufferBuilder ran into trouble and had to start over.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
//long executeEnd = System.currentTimeMillis();
|
||||
|
||||
|
||||
//long endTime = System.currentTimeMillis();
|
||||
//long buildTime = endTime - startTime;
|
||||
//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)
|
||||
{
|
||||
ClientApi.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)
|
||||
{
|
||||
ClientApi.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, AbstractChunkPosWrapper playerChunkPos, boolean[][] vanillaRenderedChunks, int gameChunkRenderDistance){
|
||||
|
||||
|
||||
// skip any chunks that Minecraft is going to render
|
||||
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.getX();
|
||||
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.getZ();
|
||||
|
||||
// check if the chunk is on the border
|
||||
boolean isItBorderPos;
|
||||
if (CONFIG.client().graphics().advancedGraphics().getVanillaOverdraw() == 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
|
||||
|
||||
return (gameChunkRenderDistance >= Math.abs(chunkXdist)
|
||||
&& gameChunkRenderDistance >= Math.abs(chunkZdist)
|
||||
&& detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL
|
||||
&& vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1])
|
||||
&& (!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)
|
||||
{
|
||||
bufferLock.lock();
|
||||
int numbRegionsWide = lodDimension.getWidth();
|
||||
long regionMemoryRequired;
|
||||
int numberOfBuffers;
|
||||
|
||||
GLProxy glProxy = GLProxy.getInstance();
|
||||
GLProxyContext oldContext = glProxy.getGlContext();
|
||||
glProxy.setGlContext(GLProxyContext.LOD_BUILDER);
|
||||
|
||||
|
||||
previousRegionWidth = numbRegionsWide;
|
||||
numberOfBuffersPerRegion = new int[numbRegionsWide][numbRegionsWide];
|
||||
buildableBuffers = new LodBufferBuilder[numbRegionsWide][numbRegionsWide][];
|
||||
|
||||
buildableVbos = new LodVertexBuffer[numbRegionsWide][numbRegionsWide][];
|
||||
drawableVbos = new LodVertexBuffer[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) 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 LodBufferBuilder[numberOfBuffers];
|
||||
buildableVbos[x][z] = new LodVertexBuffer[numberOfBuffers];
|
||||
drawableVbos[x][z] = new LodVertexBuffer[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 LodBufferBuilder[1];
|
||||
buildableVbos[x][z] = new LodVertexBuffer[1];
|
||||
drawableVbos[x][z] = new LodVertexBuffer[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 LodBufferBuilder((int) regionMemoryRequired);
|
||||
|
||||
buildableVbos[x][z][i] = new LodVertexBuffer();
|
||||
drawableVbos[x][z][i] = new LodVertexBuffer();
|
||||
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glProxy.setGlContext(oldContext);
|
||||
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];
|
||||
|
||||
// make sure the buffers are deleted in a openGL context
|
||||
GLProxy.getInstance().recordOpenGlCall(() ->
|
||||
{
|
||||
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;
|
||||
|
||||
|
||||
GLProxy.getInstance().recordOpenGlCall(() ->
|
||||
{
|
||||
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 = CONFIG.client().graphics().advancedGraphics().getGpuUploadMethod();
|
||||
if (!glProxy.bufferStorageSupported && uploadMethod == GpuUploadMethod.BUFFER_STORAGE)
|
||||
{
|
||||
// if buffer storage isn't supported
|
||||
// default to SUB_DATA
|
||||
CONFIG.client().graphics().advancedGraphics().setGpuUploadMethod(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].getCleanedByteBuffer();
|
||||
int storageBufferId = 0;
|
||||
if (buildableStorageBufferIds != null)
|
||||
storageBufferId = buildableStorageBufferIds[x][z][i];
|
||||
|
||||
vboUpload(buildableVbos[x][z][i], storageBufferId, uploadBuffer, true, uploadMethod);
|
||||
lodDim.setRegenRegionBufferByArrayIndex(x, z, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// this doesn't appear to be necessary anymore, but just in case.
|
||||
ClientApi.LOGGER.error(LodBufferBuilderFactory.class.getSimpleName() + " - UploadBuffers failed: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
GL15.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. */
|
||||
private void vboUpload(LodVertexBuffer 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() / ((Float.BYTES * 3) + (Byte.BYTES * 4))); // TODO make this change with the LodTemplate
|
||||
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id);
|
||||
try
|
||||
{
|
||||
// if possible use the faster buffer storage route
|
||||
if (uploadMethod == GpuUploadMethod.BUFFER_STORAGE && storageBufferId != 0)
|
||||
{
|
||||
// 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());
|
||||
|
||||
// alternative way that doesn't require GL45
|
||||
// GL15.glBindBuffer(GL45.GL_COPY_WRITE_BUFFER, storageBufferId);
|
||||
// GL45.glCopyBufferSubData(GL15.GL_ARRAY_BUFFER, GL45.GL_COPY_WRITE_BUFFER, 0, 0, uploadBuffer.capacity());
|
||||
// GL15.glBindBuffer(GL45.GL_COPY_WRITE_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
// 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 if (uploadMethod == GpuUploadMethod.DATA)
|
||||
{
|
||||
// hybrid bufferData //
|
||||
// high stutter, low GPU usage
|
||||
// But simplest/most compatible
|
||||
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, uploadBuffer, GL15.GL_DYNAMIC_DRAW);
|
||||
}
|
||||
else
|
||||
{
|
||||
// hybrid subData/bufferData //
|
||||
// less stutter, low GPU usage
|
||||
|
||||
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)
|
||||
{
|
||||
ClientApi.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())
|
||||
{
|
||||
LodVertexBuffer[][][] 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 LodVertexBuffer[][][] vbos;
|
||||
public final int[][][] storageBufferIds;
|
||||
public final AbstractChunkPosWrapper drawableCenterChunkPos;
|
||||
|
||||
public VertexBuffersAndOffset(LodVertexBuffer[][][] newVbos, int[][][] newStorageBufferIds, AbstractChunkPosWrapper 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;
|
||||
}
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* 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.core.builders.bufferBuilding.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
/**
|
||||
* This is the abstract class used to create different
|
||||
* BufferBuilders.
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
*/
|
||||
public abstract class AbstractLodTemplate
|
||||
{
|
||||
/** Uploads the given LOD to the buffer. */
|
||||
public abstract void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled);
|
||||
|
||||
/** add the given position and color to the buffer */
|
||||
protected void addPosAndColor(LodBufferBuilder buffer,
|
||||
float x, float y, float 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();
|
||||
}
|
||||
|
||||
}
|
||||
-142
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* 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.core.builders.bufferBuilding.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
/**
|
||||
* Builds LODs as rectangular prisms.
|
||||
* @author James Seibel
|
||||
* @version 11-8-2021
|
||||
*/
|
||||
public class CubicLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
|
||||
public CubicLodTemplate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, 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,
|
||||
AbstractBlockPosWrapper bufferCenterBlockPos,
|
||||
Map<LodDirection, 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(LodBufferBuilder buffer, Box box)
|
||||
{
|
||||
int color;
|
||||
int skyLight;
|
||||
int blockLight;
|
||||
for (LodDirection lodDirection : Box.DIRECTIONS)
|
||||
{
|
||||
if(box.isCulled(lodDirection))
|
||||
continue;
|
||||
|
||||
int verticalFaceIndex = 0;
|
||||
while (box.shouldRenderFace(lodDirection, verticalFaceIndex))
|
||||
{
|
||||
for (int vertexIndex = 0; vertexIndex < 6; vertexIndex++)
|
||||
{
|
||||
color = box.getColor(lodDirection);
|
||||
skyLight = box.getSkyLight(lodDirection, verticalFaceIndex);
|
||||
blockLight = box.getBlockLight();
|
||||
color = ColorUtil.applyLightValue(color, skyLight, blockLight);
|
||||
addPosAndColor(buffer,
|
||||
box.getX(lodDirection, vertexIndex),
|
||||
box.getY(lodDirection, vertexIndex, verticalFaceIndex),
|
||||
box.getZ(lodDirection, vertexIndex),
|
||||
color);
|
||||
}
|
||||
verticalFaceIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.core.builders.bufferBuilding.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
/**
|
||||
* 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(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
|
||||
{
|
||||
ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
|
||||
}
|
||||
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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.core.builders.bufferBuilding.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
/**
|
||||
* 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(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
|
||||
{
|
||||
ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -19,34 +19,31 @@
|
||||
|
||||
package com.seibel.lod.core.builders.lodBuilding;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.config.BlocksToAvoid;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.HorizontalResolution;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.logging.ConfigBasedLogger;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.lod.LodRegion;
|
||||
import com.seibel.lod.core.objects.lod.LodWorld;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodThreadFactory;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockDetailWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
/**
|
||||
* This object is in charge of creating Lod related objects.
|
||||
@@ -54,24 +51,28 @@ import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
* @author Cola
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 10-22-2021
|
||||
* @version 12-11-2021
|
||||
*/
|
||||
@SuppressWarnings("GrazieInspection")
|
||||
public class LodBuilder
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final IBlockColorSingletonWrapper BLOCK_COLOR = SingletonHandler.get(IBlockColorSingletonWrapper.class);
|
||||
private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
private static final ILodConfigWrapperSingleton config = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
|
||||
public static final ConfigBasedLogger EVENT_LOGGER = new ConfigBasedLogger(LogManager.getLogger(LodBuilder.class),
|
||||
() -> config.client().advanced().debugging().debugSwitch().getLogLodBuilderEvent());
|
||||
|
||||
/** 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;
|
||||
/** This cannot be final! Different world have different height, and in menu, this causes Null Exceptions*/
|
||||
//public static final short MIN_WORLD_HEIGHT = MC.getWrappedClientWorld().getMinHeight();
|
||||
public static short MIN_WORLD_HEIGHT = 0; // Currently modified in EventApi.onWorldLoaded(...)
|
||||
/** Minecraft's max light value */
|
||||
public static final short DEFAULT_MAX_LIGHT = 15;
|
||||
|
||||
|
||||
private final ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
|
||||
private final ILodConfigWrapperSingleton config = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
//public static final ExecutorService lodGenThreadPool = Executors.newFixedThreadPool(8, new ThreadFactoryBuilder().setNameFormat("Lod-Builder-%d").build());
|
||||
|
||||
private final ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(
|
||||
new LodThreadFactory(this.getClass().getSimpleName(), Thread.NORM_PRIORITY-1));
|
||||
|
||||
|
||||
|
||||
@@ -79,7 +80,7 @@ public class LodBuilder
|
||||
* How wide LodDimensions should be in regions <br>
|
||||
* Is automatically set before the first frame in ClientProxy.
|
||||
*/
|
||||
public int defaultDimensionWidthInRegions = 0;
|
||||
public int defaultDimensionWidthInRegions = 1;
|
||||
|
||||
//public static final boolean useExperimentalLighting = true;
|
||||
|
||||
@@ -91,24 +92,31 @@ public class LodBuilder
|
||||
|
||||
}
|
||||
|
||||
public void generateLodNodeAsync(IChunkWrapper chunk, LodWorld lodWorld, IDimensionTypeWrapper dim)
|
||||
public void generateLodNodeAsync(IChunkWrapper chunk, LodWorld lodWorld, IDimensionTypeWrapper dim, boolean genAll)
|
||||
{
|
||||
generateLodNodeAsync(chunk, lodWorld, dim, DistanceGenerationMode.FULL);
|
||||
// Block change event
|
||||
generateLodNodeAsync(chunk, lodWorld, dim, DistanceGenerationMode.FULL, true, genAll, ()->{},
|
||||
()->{generateLodNodeAsync(chunk,lodWorld,dim, genAll);});
|
||||
}
|
||||
|
||||
public void generateLodNodeAsync(IChunkWrapper chunk, LodWorld lodWorld, IDimensionTypeWrapper dim, DistanceGenerationMode generationMode)
|
||||
public void generateLodNodeAsync(IChunkWrapper chunk, LodWorld lodWorld, IDimensionTypeWrapper dim,
|
||||
DistanceGenerationMode generationMode, boolean override, boolean genAll, Runnable endCallback, Runnable retryCallback)
|
||||
{
|
||||
if (lodWorld == null || lodWorld.getIsWorldNotLoaded())
|
||||
if (lodWorld == null || lodWorld.getIsWorldNotLoaded()) {
|
||||
endCallback.run();
|
||||
return;
|
||||
|
||||
}
|
||||
// don't try to create an LOD object
|
||||
// if for some reason we aren't
|
||||
// given a valid chunk object
|
||||
if (chunk == null)
|
||||
if (chunk == null) {
|
||||
endCallback.run();
|
||||
return;
|
||||
}
|
||||
|
||||
Thread thread = new Thread(() ->
|
||||
Runnable thread = () ->
|
||||
{
|
||||
boolean retryNeeded = false;
|
||||
try
|
||||
{
|
||||
// we need a loaded client world in order to
|
||||
@@ -122,26 +130,25 @@ public class LodBuilder
|
||||
return;
|
||||
|
||||
// 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));
|
||||
// if not, it prob means that player left
|
||||
LodDimension lodDim = lodWorld.getLodDimension(dim);
|
||||
if (lodDim == null) return;
|
||||
|
||||
retryNeeded = !generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode), override, genAll);
|
||||
}
|
||||
catch (IllegalArgumentException | NullPointerException e)
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
EVENT_LOGGER.error("LodBuilder Thread Uncaught Exception: ", e);
|
||||
// if the world changes while LODs are being generated
|
||||
// they will throw errors as they try to access things that no longer
|
||||
// exist.
|
||||
} finally {
|
||||
if (!retryNeeded)
|
||||
endCallback.run();
|
||||
else
|
||||
retryCallback.run();
|
||||
}
|
||||
});
|
||||
};
|
||||
lodGenThreadPool.execute(thread);
|
||||
}
|
||||
|
||||
@@ -149,186 +156,275 @@ public class LodBuilder
|
||||
* 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, IChunkWrapper chunk) throws IllegalArgumentException
|
||||
public boolean generateLodNodeFromChunk(LodDimension lodDim, IChunkWrapper chunk, LodBuilderConfig config, boolean override, boolean genAll)
|
||||
{
|
||||
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig());
|
||||
try {
|
||||
if (chunk == null)
|
||||
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
|
||||
LodRegion region = lodDim.getRegion(chunk.getRegionPosX(), chunk.getRegionPosZ());
|
||||
if (region == null)
|
||||
return false;
|
||||
// this happens if a LOD is generated after the user leaves the world.
|
||||
if (MC.getWrappedClientWorld() == null)
|
||||
return false;
|
||||
if (!canGenerateLodFromChunk(chunk))
|
||||
return false;
|
||||
|
||||
|
||||
// generate the LODs
|
||||
int maxVerticalData = DetailDistanceUtil.getMaxVerticalData((byte)0);
|
||||
long[] data = new long[maxVerticalData*16*16];
|
||||
boolean isAllVoid = true;
|
||||
|
||||
if (!config.quickFillWithVoid) {
|
||||
for (int i = 0; i < 16*16; i++)
|
||||
{
|
||||
int subX = i/16;
|
||||
int subZ = i%16;
|
||||
writeVerticalData(data, i*maxVerticalData, maxVerticalData, chunk, config, subX, subZ);
|
||||
isAllVoid &= DataPointUtil.isVoid(data[i*maxVerticalData]);
|
||||
if (!DataPointUtil.doesItExist(data[i*maxVerticalData]))
|
||||
throw new RuntimeException("writeVerticalData result: Datapoint does not exist at "+ chunk.getMinX()+subX +", "+ chunk.getMinZ()+subZ);
|
||||
if (DataPointUtil.getGenerationMode(data[i*maxVerticalData]) != config.distanceGenerationMode.complexity)
|
||||
throw new RuntimeException("writeVerticalData result: Datapoint invalid at "+ chunk.getMinX()+subX +", "+ chunk.getMinZ()+subZ);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 16*16; i++)
|
||||
{
|
||||
data[i*maxVerticalData] = DataPointUtil.createVoidDataPoint(config.distanceGenerationMode.complexity);
|
||||
}
|
||||
}
|
||||
if (isAllVoid) EVENT_LOGGER.debug("The chunk {} is completely void.", chunk);
|
||||
|
||||
// This MUST be done after the data is generated, to ensure that during the generation, the data is valid.
|
||||
if (!canGenerateLodFromChunk(chunk)) // TODO Why are we calling this again? - James
|
||||
return false; // Answer: Because concurrency change may cause the chunk to have invalid data, like light.
|
||||
|
||||
if (genAll) {
|
||||
return writeAllLodNodeData(lodDim, region, chunk.getChunkPosX(), chunk.getChunkPosZ(), data, config, override);
|
||||
} else {
|
||||
return writePartialLodNodeData(lodDim, region, chunk.getChunkPosX(), chunk.getChunkPosZ(), data, config, override);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
EVENT_LOGGER.error("LodBuilder encountered an error on building lod: ", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static boolean canGenerateLodFromChunk(IChunkWrapper chunk)
|
||||
{
|
||||
return chunk != null && chunk.isLightCorrect() && chunk.doesNearbyChunksExist();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, IChunkWrapper chunk, LodBuilderConfig config)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
if (chunk == null)
|
||||
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
|
||||
|
||||
int startX;
|
||||
int startZ;
|
||||
|
||||
|
||||
LodRegion region = lodDim.getRegion(chunk.getPos().getRegionX(), chunk.getPos().getRegionZ());
|
||||
if (region == null)
|
||||
return;
|
||||
|
||||
// this happens if a LOD is generated after the user leaves the world.
|
||||
if (MC.getWrappedClientWorld() == null)
|
||||
return;
|
||||
|
||||
// determine how many LODs to generate horizontally
|
||||
byte minDetailLevel = region.getMinDetailLevel();
|
||||
HorizontalResolution 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];
|
||||
|
||||
long[] data;
|
||||
long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ);
|
||||
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.worldHeight / 2 + 1, DetailDistanceUtil.getMaxVerticalData(detailLevel));
|
||||
|
||||
|
||||
//lodDim.clear(detailLevel, posX, posZ);
|
||||
if (data != null && data.length != 0)
|
||||
{
|
||||
posX = LevelPosUtil.convert((byte) 0, chunk.getPos().getX() * 16 + startX, detail.detailLevel);
|
||||
posZ = LevelPosUtil.convert((byte) 0, chunk.getPos().getZ() * 16 + startZ, detail.detailLevel);
|
||||
lodDim.addVerticalData(detailLevel, posX, posZ, data, false);
|
||||
|
||||
private boolean writeAllLodNodeData(LodDimension lodDim, LodRegion region, int chunkX, int chunkZ,
|
||||
long[] data, LodBuilderConfig config, boolean override)
|
||||
{
|
||||
region.isWriting.incrementAndGet();
|
||||
try {
|
||||
if (region.getMinDetailLevel()!= 0) {
|
||||
if (!LodUtil.checkRamUsage(0.05, 16)) {
|
||||
EVENT_LOGGER.debug("LodBuilder: Not enough RAM avalible for loading files to build lods! Returning...");
|
||||
return false;
|
||||
}
|
||||
|
||||
LodRegion newRegion = lodDim.getRegionFromFile(region, (byte)0, region.getVerticalQuality());
|
||||
if (region!=newRegion)
|
||||
throw new RuntimeException();
|
||||
}
|
||||
//ApiShared.LOGGER.info("Generate chunk: {}, {} ({}, {}) at genMode {}",
|
||||
// chunk.getChunkPosX(), chunk.getChunkPosZ(), chunk.getMinX(), chunk.getMinZ(), config.distanceGenerationMode);
|
||||
region.addChunkOfData((byte)0, chunkX*16, chunkZ*16, 16, 16, data, data.length/16/16, override);
|
||||
region.regenerateLodFromArea((byte)0, chunkX*16, chunkZ*16, 16, 16);
|
||||
|
||||
if (!region.doesDataExist((byte)0, chunkX*16, chunkZ*16, config.distanceGenerationMode))
|
||||
throw new RuntimeException("data at detail 0 is still null after writes to it!");
|
||||
if (!region.doesDataExist(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, chunkZ, config.distanceGenerationMode))
|
||||
throw new RuntimeException("data at chunk detail level is still null after writes to it!");
|
||||
} finally {
|
||||
region.isWriting.decrementAndGet();
|
||||
}
|
||||
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().getX(), chunk.getPos().getZ());
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean writePartialLodNodeData(LodDimension lodDim, LodRegion region, int chunkX, int chunkZ,
|
||||
long[] data, LodBuilderConfig config, boolean override)
|
||||
{
|
||||
region.isWriting.incrementAndGet();
|
||||
try {
|
||||
byte targetLevel = region.getMinDetailLevel();
|
||||
int vertQual = DetailDistanceUtil.getMaxVerticalData(targetLevel);
|
||||
int lodCount = (targetLevel >= LodUtil.CHUNK_DETAIL_LEVEL) ?
|
||||
1 : 1 << (LodUtil.CHUNK_DETAIL_LEVEL - targetLevel);
|
||||
if (targetLevel != 0) {
|
||||
int lodWidth = 16/lodCount;
|
||||
int inputVertQual = data.length/16/16;
|
||||
long[] mergedData = new long[vertQual*lodCount*lodCount];
|
||||
for (int subX=0; subX<lodCount; subX++) {
|
||||
for (int subZ=0; subZ<lodCount; subZ++) {
|
||||
long[] toBeMerged = DataPointUtil.extractDataArray(
|
||||
data, 16, 16, subX*lodWidth, subZ*lodWidth, lodWidth, lodWidth);
|
||||
if(toBeMerged.length != lodWidth*lodWidth*inputVertQual) throw new RuntimeException();
|
||||
long[] merged = DataPointUtil.mergeMultiData(toBeMerged, inputVertQual, vertQual);
|
||||
if (merged.length != vertQual) throw new RuntimeException();
|
||||
if (!DataPointUtil.doesItExist(merged[0]) ||
|
||||
DataPointUtil.getGenerationMode(merged[0]) != config.distanceGenerationMode.complexity)
|
||||
throw new RuntimeException();
|
||||
System.arraycopy(merged, 0, mergedData, (subZ+subX*lodCount)*vertQual, vertQual);
|
||||
}
|
||||
}
|
||||
data = mergedData;
|
||||
}
|
||||
if (lodCount*lodCount*vertQual != data.length) throw new RuntimeException();
|
||||
for (int i=0; i<data.length; i+=vertQual) {
|
||||
if (!DataPointUtil.doesItExist(data[i]) ||
|
||||
DataPointUtil.getGenerationMode(data[i]) != config.distanceGenerationMode.complexity) {
|
||||
EVENT_LOGGER.error("NULL data at {}, detail {}, vertQual {}, lodCount {}, chunkPos [{},{}]\n"
|
||||
+ "Data: {}",
|
||||
i, targetLevel, vertQual, lodCount, chunkX, chunkZ, DataPointUtil.toString(data[i]));
|
||||
throw new RuntimeException("Null data!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//ApiShared.LOGGER.info("Generate chunk: {}, {} ({}, {}) at genMode {}",
|
||||
// chunk.getChunkPosX(), chunk.getChunkPosZ(), chunk.getMinX(), chunk.getMinZ(), config.distanceGenerationMode);
|
||||
if (targetLevel != region.getMinDetailLevel()) {
|
||||
//Concurrency issues happened.
|
||||
throw new ConcurrentModificationException("Min detail level changed while writing data");
|
||||
}
|
||||
region.addChunkOfData(targetLevel,
|
||||
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, targetLevel),
|
||||
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkZ, targetLevel),
|
||||
lodCount, lodCount, data, vertQual, override);
|
||||
region.regenerateLodFromArea(targetLevel,
|
||||
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, targetLevel),
|
||||
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkZ, targetLevel),
|
||||
lodCount, lodCount);
|
||||
|
||||
if (!region.doesDataExist(targetLevel,
|
||||
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, targetLevel),
|
||||
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkZ, targetLevel),
|
||||
config.distanceGenerationMode))
|
||||
throw new RuntimeException("data at detail "+ targetLevel+" is still null after writes to it!");
|
||||
} catch (Exception e) {
|
||||
EVENT_LOGGER.error("LodBuilder encountered an error on writePartialLodNodeData: ", e);
|
||||
} finally {
|
||||
region.isWriting.decrementAndGet();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** creates a vertical DataPoint */
|
||||
private long[] createVerticalDataToMerge(HorizontalResolution detail, IChunkWrapper chunk, LodBuilderConfig config, int startX, int startZ)
|
||||
private void writeVerticalData(long[] data, int dataOffset, int maxVerticalData,
|
||||
IChunkWrapper chunk, LodBuilderConfig config, int chunkSubPosX, int chunkSubPosZ)
|
||||
{
|
||||
// equivalent to 2^detailLevel
|
||||
int size = 1 << detail.detailLevel;
|
||||
|
||||
int totalVerticalData = (chunk.getHeight());
|
||||
long[] dataToMerge = new long[totalVerticalData];
|
||||
|
||||
long[] dataToMerge = ThreadMapUtil.getBuilderVerticalArray(detail.detailLevel);
|
||||
int verticalData = DataPointUtil.worldHeight / 2 + 1;
|
||||
|
||||
AbstractChunkPosWrapper 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.getWrappedClientWorld().getDimensionType().hasCeiling();
|
||||
boolean hasSkyLight = MC.getWrappedClientWorld().getDimensionType().hasSkyLight();
|
||||
boolean isDefault;
|
||||
AbstractBlockPosWrapper blockPos = FACTORY.createBlockPos();
|
||||
int index;
|
||||
byte generation = config.distanceGenerationMode.complexity;
|
||||
int count = 0;
|
||||
// FIXME: This yAbs is just messy!
|
||||
int x = chunk.getMinX() + chunkSubPosX;
|
||||
int z = chunk.getMinZ() + chunkSubPosZ;
|
||||
int y = chunk.getMaxY(x, z);
|
||||
|
||||
for (index = 0; index < size * size; index++)
|
||||
{
|
||||
xRel = startX + index % size;
|
||||
zRel = startZ + index / size;
|
||||
xAbs = chunkPos.getMinBlockX() + xRel;
|
||||
zAbs = chunkPos.getMinBlockZ() + zRel;
|
||||
|
||||
//Calculate the height of the lod
|
||||
yAbs = DataPointUtil.worldHeight + 1;
|
||||
int count = 0;
|
||||
boolean topBlock = true;
|
||||
while (yAbs > 0)
|
||||
{
|
||||
height = determineHeightPointFrom(chunk, config, xRel, yAbs, zRel, 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, yAbs, zRel, blockPos);
|
||||
if (hasCeiling && topBlock)
|
||||
{
|
||||
yAbs = depth;
|
||||
blockPos.set(xAbs, yAbs, zAbs);
|
||||
light = getLightValue(chunk, blockPos, true, hasSkyLight, true);
|
||||
color = generateLodColor(chunk, config, xAbs, yAbs, zAbs, 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++;
|
||||
boolean topBlock = true;
|
||||
if (y < chunk.getMinBuildHeight())
|
||||
dataToMerge[0] = DataPointUtil.createVoidDataPoint(generation);
|
||||
int maxConnectedLods = LodBuilder.config.client().graphics().quality().getVerticalQuality().maxVerticalData[0];
|
||||
while (y >= chunk.getMinBuildHeight()) {
|
||||
int height = determineHeightPointFrom(chunk, config, x, y, z);
|
||||
// If the lod is at the default height, it must be void data
|
||||
if (height < chunk.getMinBuildHeight()) {
|
||||
if (topBlock) dataToMerge[0] = DataPointUtil.createVoidDataPoint(generation);
|
||||
break;
|
||||
}
|
||||
y = height - 1;
|
||||
// We search light on above air block
|
||||
int depth = determineBottomPointFrom(chunk, config, x, y, z,
|
||||
count < maxConnectedLods && (!hasCeiling || !topBlock));
|
||||
if (hasCeiling && topBlock)
|
||||
y = depth;
|
||||
int light = getLightValue(chunk, x, y, z, hasCeiling, hasSkyLight, topBlock);
|
||||
int color = generateLodColor(chunk, config, x, y, z);
|
||||
int lightBlock = light & 0b1111;
|
||||
int lightSky = (light >> 4) & 0b1111;
|
||||
dataToMerge[count] = DataPointUtil.createDataPoint(height-chunk.getMinBuildHeight(), depth-chunk.getMinBuildHeight(),
|
||||
color, lightSky, lightBlock, generation);
|
||||
topBlock = false;
|
||||
y = depth - 1;
|
||||
count++;
|
||||
}
|
||||
return dataToMerge;
|
||||
long[] result = DataPointUtil.mergeMultiData(dataToMerge, totalVerticalData, maxVerticalData);
|
||||
if (result.length != maxVerticalData) throw new ArrayIndexOutOfBoundsException();
|
||||
System.arraycopy(result, 0, data, dataOffset, maxVerticalData);
|
||||
}
|
||||
|
||||
public static final LodDirection[] DIRECTIONS = new LodDirection[] {
|
||||
LodDirection.UP,
|
||||
LodDirection.DOWN,
|
||||
LodDirection.WEST,
|
||||
LodDirection.EAST,
|
||||
LodDirection.NORTH,
|
||||
LodDirection.SOUTH };
|
||||
|
||||
private boolean hasCliffFace(IChunkWrapper chunk, int x, int y, int z) {
|
||||
for (LodDirection dir : DIRECTIONS) {
|
||||
IBlockDetailWrapper block = chunk.getBlockDetailAtFace(x, y, z, dir);
|
||||
if (block == null || !block.hasFaceCullingFor(LodDirection.OPPOSITE_DIRECTIONS[dir.ordinal()]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the lowest valid point from the bottom.
|
||||
* Used when creating a vertical LOD.
|
||||
*/
|
||||
private short determineBottomPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, AbstractBlockPosWrapper blockPos)
|
||||
private int determineBottomPointFrom(IChunkWrapper chunk, LodBuilderConfig builderConfig, int xAbs, int yAbs, int zAbs, boolean strictEdge)
|
||||
{
|
||||
short depth = DEFAULT_DEPTH;
|
||||
|
||||
for (int y = yAbs; y >= 0; y--)
|
||||
int depth = chunk.getMinBuildHeight();
|
||||
IBlockDetailWrapper currentBlockDetail = null;
|
||||
if (strictEdge)
|
||||
{
|
||||
blockPos.set(xAbs, y, zAbs);
|
||||
if (!isLayerValidLodPoint(chunk, blockPos))
|
||||
{
|
||||
depth = (short) (y + 1);
|
||||
break;
|
||||
IBlockDetailWrapper blockAbove = chunk.getBlockDetail(xAbs, yAbs + 1, zAbs);
|
||||
if (blockAbove != null && !blockAbove.shouldRender(config.client().worldGenerator().getBlocksToAvoid()))
|
||||
{ // The above block is skipped. Lets use its skipped color for currrent block
|
||||
currentBlockDetail = blockAbove;
|
||||
}
|
||||
if (currentBlockDetail == null) currentBlockDetail = chunk.getBlockDetail(xAbs, yAbs, zAbs);
|
||||
}
|
||||
|
||||
for (int y = yAbs - 1; y >= chunk.getMinBuildHeight(); y--)
|
||||
{
|
||||
IBlockDetailWrapper nextBlock = chunk.getBlockDetail(xAbs, y, zAbs);
|
||||
if (isLayerValidLodPoint(nextBlock)) {
|
||||
if (!strictEdge) continue;
|
||||
if (currentBlockDetail.equals(nextBlock)) continue;
|
||||
if (!hasCliffFace(chunk, xAbs, y, zAbs)) continue;
|
||||
}
|
||||
depth = (y + 1);
|
||||
break;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
/** Find the highest valid point from the Top */
|
||||
private short determineHeightPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, AbstractBlockPosWrapper blockPos)
|
||||
private int determineHeightPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs)
|
||||
{
|
||||
short height = DEFAULT_HEIGHT;
|
||||
if (config.useHeightmap)
|
||||
height = (short) chunk.getHeightMapValue(xAbs, zAbs);
|
||||
else
|
||||
//TODO find a way to skip bottom of the world
|
||||
int height = chunk.getMinBuildHeight()-1;
|
||||
for (int y = yAbs; y >= chunk.getMinBuildHeight(); y--)
|
||||
{
|
||||
for (int y = yAbs; y >= 0; y--)
|
||||
if (isLayerValidLodPoint(chunk, xAbs, y, zAbs))
|
||||
{
|
||||
blockPos.set(xAbs, y, zAbs);
|
||||
if (isLayerValidLodPoint(chunk, blockPos))
|
||||
{
|
||||
height = (short) (y + 1);
|
||||
break;
|
||||
}
|
||||
height = (y + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return height;
|
||||
@@ -344,191 +440,129 @@ public class LodBuilder
|
||||
* Generate the color for the given chunk using biome water color, foliage
|
||||
* color, and grass color.
|
||||
*/
|
||||
private int generateLodColor(IChunkWrapper chunk, LodBuilderConfig builderConfig, int xRel, int yAbs, int zRel, AbstractBlockPosWrapper blockPos)
|
||||
private int generateLodColor(IChunkWrapper chunk, LodBuilderConfig builderConfig, int x, int y, int z)
|
||||
{
|
||||
int colorInt;
|
||||
if (builderConfig.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.
|
||||
colorInt = chunk.getBiome(xRel, yAbs, zRel).getColorForBiome(xRel, zRel);
|
||||
colorInt = chunk.getBiome(x, y, z).getColorForBiome(x, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs, 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 (config.client().worldGenerator().getBlocksToAvoid().nonFull || config.client().worldGenerator().getBlocksToAvoid().noCollision)
|
||||
{
|
||||
blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs + 1, chunk.getPos().getMinBlockZ() + zRel);
|
||||
aboveColorInt = getColorForBlock(chunk, blockPos);
|
||||
colorInt = 0;
|
||||
if (chunk.blockPosInsideChunk(x, y+1, z)) {
|
||||
IBlockDetailWrapper blockAbove = chunk.getBlockDetail(x, y+1, z);
|
||||
if (blockAbove != null && !blockAbove.shouldRender(config.client().worldGenerator().getBlocksToAvoid()))
|
||||
{ // The above block is skipped. Lets use its skipped color for currrent block
|
||||
colorInt = blockAbove.getAndResolveFaceColor(null, chunk, FACTORY.createBlockPos(x, y+1, z));
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
if (colorInt == 0) {
|
||||
IBlockDetailWrapper detail = chunk.getBlockDetail(x, y, z);
|
||||
colorInt = detail.getAndResolveFaceColor(null, chunk, FACTORY.createBlockPos(x, y, z));
|
||||
}
|
||||
}
|
||||
|
||||
return colorInt;
|
||||
}
|
||||
|
||||
/** Gets the light value for the given block position */
|
||||
private int getLightValue(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos, boolean hasCeiling, boolean hasSkyLight, boolean topBlock)
|
||||
private int getLightValue(IChunkWrapper chunk, int x, int y, int z, boolean hasCeiling, boolean hasSkyLight, boolean topBlock)
|
||||
{
|
||||
int skyLight = 0;
|
||||
int skyLight;
|
||||
int blockLight;
|
||||
// 1 means the lighting is a guess
|
||||
int isDefault = 0;
|
||||
|
||||
IWorldWrapper world = MC.getWrappedServerWorld();
|
||||
|
||||
int blockBrightness = chunk.getEmittedBrightness(blockPos);
|
||||
int blockBrightness = chunk.getEmittedBrightness(x, y, z);
|
||||
// get the air block above or below this block
|
||||
if (hasCeiling && topBlock)
|
||||
blockPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ());
|
||||
y--;
|
||||
else
|
||||
blockPos.set(blockPos.getX(), blockPos.getY() + 1, blockPos.getZ());
|
||||
y++;
|
||||
|
||||
blockLight = chunk.getBlockLight(x, y, z);
|
||||
skyLight = hasSkyLight ? chunk.getSkyLight(x, y, z) : 0;
|
||||
|
||||
|
||||
if (world != null && !world.isEmpty())
|
||||
if (blockLight == -1 || skyLight == -1)
|
||||
{
|
||||
// server world sky light (always accurate)
|
||||
blockLight = world.getBlockLight(blockPos);
|
||||
if (topBlock && !hasCeiling && hasSkyLight)
|
||||
skyLight = DEFAULT_MAX_LIGHT;
|
||||
else
|
||||
|
||||
IWorldWrapper world = MC.getWrappedServerWorld();
|
||||
|
||||
if (world != null)
|
||||
{
|
||||
if (hasSkyLight)
|
||||
skyLight = world.getSkyLight(blockPos);
|
||||
//else
|
||||
// skyLight = 0;
|
||||
}
|
||||
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.getWrappedClientWorld().getSeaLevel() - 5)
|
||||
{
|
||||
skyLight = 12;
|
||||
isDefault = 1;
|
||||
}
|
||||
else
|
||||
skyLight = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
world = MC.getWrappedServerWorld();
|
||||
if (world.isEmpty())
|
||||
return 0;
|
||||
// client world sky light (almost never accurate)
|
||||
blockLight = world.getBlockLight(blockPos);
|
||||
// estimate what the lighting should be
|
||||
if (hasSkyLight || !hasCeiling)
|
||||
{
|
||||
if (topBlock)
|
||||
// server world sky light (always accurate)
|
||||
blockLight = world.getBlockLight(x, y, z);
|
||||
|
||||
if (topBlock && !hasCeiling && hasSkyLight)
|
||||
skyLight = DEFAULT_MAX_LIGHT;
|
||||
else
|
||||
skyLight = hasSkyLight ? world.getSkyLight(x, y, z) : 0;
|
||||
|
||||
if (!topBlock && skyLight == 15)
|
||||
{
|
||||
|
||||
if (hasSkyLight)
|
||||
skyLight = world.getSkyLight(blockPos);
|
||||
//else
|
||||
// skyLight = 0;
|
||||
|
||||
if (!chunk.isLightCorrect() && (skyLight == 0 || skyLight == 15))
|
||||
// we are on predicted terrain, and we don't know what the light here is,
|
||||
// lets just take a guess
|
||||
skyLight = 12;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
world = MC.getWrappedClientWorld();
|
||||
if (world == null)
|
||||
{
|
||||
blockLight = 0;
|
||||
skyLight = 12;
|
||||
}
|
||||
else
|
||||
{
|
||||
// client world sky light (almost never accurate)
|
||||
blockLight = world.getBlockLight(x, y, z);
|
||||
// estimate what the lighting should be
|
||||
if (hasSkyLight || !hasCeiling)
|
||||
{
|
||||
// we don't know what the light here is,
|
||||
// lets just take a guess
|
||||
if (blockPos.getY() >= MC.getWrappedClientWorld().getSeaLevel() - 5)
|
||||
{
|
||||
skyLight = 12;
|
||||
isDefault = 1;
|
||||
}
|
||||
if (topBlock)
|
||||
skyLight = DEFAULT_MAX_LIGHT;
|
||||
else
|
||||
skyLight = 0;
|
||||
{
|
||||
if (hasSkyLight)
|
||||
skyLight = world.getSkyLight(x, y, z);
|
||||
//else
|
||||
// skyLight = 0;
|
||||
if (!chunk.isLightCorrect() && (skyLight == 0 || skyLight == 15))
|
||||
{
|
||||
// we don't know what the light here is,
|
||||
// lets just take a guess
|
||||
skyLight = 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasSkyLight)
|
||||
skyLight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
blockLight = LodUtil.clamp(0, Math.max(blockLight, blockBrightness), DEFAULT_MAX_LIGHT);
|
||||
|
||||
return blockLight + (skyLight << 4) + (isDefault << 8);
|
||||
return blockLight + (skyLight << 4);
|
||||
}
|
||||
|
||||
/** Returns a color int for the given block. */
|
||||
private int getColorForBlock(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos)
|
||||
|
||||
/** Is the block at the given blockPos a valid LOD point? */
|
||||
private boolean isLayerValidLodPoint(IBlockDetailWrapper blockDetail)
|
||||
{
|
||||
int colorOfBlock;
|
||||
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();
|
||||
|
||||
IBlockColorWrapper blockColorWrapper;
|
||||
IBlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(blockPos);
|
||||
|
||||
if (chunk.isWaterLogged(blockPos))
|
||||
blockColorWrapper = BLOCK_COLOR.getWaterColor();
|
||||
else
|
||||
blockColorWrapper = chunk.getBlockColorWrapper(blockPos);
|
||||
|
||||
if (blockShapeWrapper.isToAvoid())
|
||||
return 0;
|
||||
|
||||
colorOfBlock = blockColorWrapper.getColor();
|
||||
|
||||
|
||||
if (blockColorWrapper.hasTint())
|
||||
{
|
||||
IBiomeWrapper biome = chunk.getBiome(xRel, y, zRel);
|
||||
int tintValue;
|
||||
if (blockColorWrapper.hasGrassTint())
|
||||
// grass and green plants
|
||||
tintValue = biome.getGrassTint(0,0);
|
||||
else if (blockColorWrapper.hasFolliageTint())
|
||||
tintValue = biome.getFolliageTint();
|
||||
else
|
||||
//we can reintroduce this with the wrappers
|
||||
tintValue = biome.getWaterTint();
|
||||
|
||||
colorInt = ColorUtil.multiplyRGBcolors(tintValue | 0xFF000000, colorOfBlock);
|
||||
}
|
||||
else
|
||||
colorInt = colorOfBlock;
|
||||
return colorInt;
|
||||
BlocksToAvoid avoid = config.client().worldGenerator().getBlocksToAvoid();
|
||||
return blockDetail != null && blockDetail.shouldRender(avoid);
|
||||
}
|
||||
|
||||
|
||||
/** Is the block at the given blockPos a valid LOD point? */
|
||||
private boolean isLayerValidLodPoint(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos)
|
||||
private boolean isLayerValidLodPoint(IChunkWrapper chunk, int x, int y, int z)
|
||||
{
|
||||
if (chunk.isWaterLogged(blockPos))
|
||||
return true;
|
||||
|
||||
boolean nonFullAvoidance = config.client().worldGenerator().getBlocksToAvoid().nonFull;
|
||||
boolean noCollisionAvoidance = config.client().worldGenerator().getBlocksToAvoid().noCollision;
|
||||
|
||||
IBlockShapeWrapper block = chunk.getBlockShapeWrapper(blockPos);
|
||||
return !block.isToAvoid()
|
||||
&& !(nonFullAvoidance && block.isNonFull())
|
||||
&& !(noCollisionAvoidance && block.hasNoCollision());
|
||||
|
||||
BlocksToAvoid avoid = config.client().worldGenerator().getBlocksToAvoid();
|
||||
IBlockDetailWrapper block = chunk.getBlockDetail(x, y, z);
|
||||
return block != null && block.shouldRender(avoid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -39,6 +39,7 @@ public class LodBuilderConfig
|
||||
public boolean useSolidBlocksInColorGen;
|
||||
/** default: server */
|
||||
public DistanceGenerationMode distanceGenerationMode;
|
||||
public boolean quickFillWithVoid;
|
||||
|
||||
/**
|
||||
* default settings for a normal chunk <br>
|
||||
@@ -47,49 +48,18 @@ public class LodBuilderConfig
|
||||
* useSolidBlocksInColorGen = true <br>
|
||||
* generationMode = Server <br>
|
||||
*/
|
||||
public LodBuilderConfig()
|
||||
public LodBuilderConfig(DistanceGenerationMode newDistanceGenerationMode)
|
||||
{
|
||||
useHeightmap = false;
|
||||
useBiomeColors = false;
|
||||
useSolidBlocksInColorGen = true;
|
||||
distanceGenerationMode = DistanceGenerationMode.FULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
quickFillWithVoid = false;
|
||||
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;
|
||||
public static LodBuilderConfig getFillVoidConfig() {
|
||||
LodBuilderConfig config = new LodBuilderConfig(DistanceGenerationMode.NONE);
|
||||
config.quickFillWithVoid = true;
|
||||
return config;
|
||||
}
|
||||
}
|
||||
+12
-16
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -17,23 +17,19 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.wrapperInterfaces.block;
|
||||
package com.seibel.lod.core.builders.lodBuilding.bufferBuilding;
|
||||
|
||||
/**
|
||||
* EastWest <Br>
|
||||
* NorthSouthOrUpDown
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
* @version 2022-4-9
|
||||
*/
|
||||
public interface IBlockShapeWrapper
|
||||
public enum BufferMergeDirectionEnum
|
||||
{
|
||||
public boolean ofBlockToAvoid();
|
||||
EastWest,
|
||||
|
||||
//-----------------//
|
||||
//Avoidance getters//
|
||||
//-----------------//
|
||||
|
||||
public boolean isNonFull();
|
||||
|
||||
public boolean hasNoCollision();
|
||||
|
||||
public boolean isToAvoid();
|
||||
}
|
||||
/** NorthSouth and UpDown are merged since */
|
||||
NorthSouthOrUpDown
|
||||
}
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.builders.lodBuilding.bufferBuilding;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
|
||||
import static com.seibel.lod.core.render.LodRenderer.EVENT_LOGGER;
|
||||
|
||||
/**
|
||||
* Represents a renderable quad.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @author ?
|
||||
* @version 4-9-2022
|
||||
*/
|
||||
public final class BufferQuad
|
||||
{
|
||||
final short x;
|
||||
final short y;
|
||||
final short z;
|
||||
short widthEastWest;
|
||||
/** This is both North/South and Up/Down since the merging logic is the same either way */
|
||||
short widthNorthSouthOrUpDown;
|
||||
final int color;
|
||||
final byte skyLight;
|
||||
final byte blockLight;
|
||||
final LodDirection direction;
|
||||
|
||||
boolean hasError = false;
|
||||
|
||||
BufferQuad(short x, short y, short z, short widthEastWest, short widthNorthSouthOrUpDown,
|
||||
int color, byte skylight, byte blocklight,
|
||||
LodDirection direction)
|
||||
{
|
||||
if (widthEastWest == 0 || widthNorthSouthOrUpDown == 0)
|
||||
throw new IllegalArgumentException("Size 0 quad!");
|
||||
if (widthEastWest < 0 || widthNorthSouthOrUpDown < 0)
|
||||
throw new IllegalArgumentException("Negative sized quad!");
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.widthEastWest = widthEastWest;
|
||||
this.widthNorthSouthOrUpDown = widthNorthSouthOrUpDown;
|
||||
this.color = color;
|
||||
this.skyLight = skylight;
|
||||
this.blockLight = blocklight;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
/** a rough but fast calculation */
|
||||
double calculateDistance(double relativeX, double relativeY, double relativeZ)
|
||||
{
|
||||
return Math.pow(relativeX - x, 2) + Math.pow(relativeY - y, 2) + Math.pow(relativeZ - z, 2);
|
||||
}
|
||||
|
||||
/** compares this quad's position to the given quad */
|
||||
public int compare(BufferQuad quad, BufferMergeDirectionEnum compareDirection)
|
||||
{
|
||||
if (direction != quad.direction)
|
||||
throw new IllegalArgumentException("The other quad is not in the same direction: " + quad.direction + " vs " + direction);
|
||||
|
||||
if (compareDirection == BufferMergeDirectionEnum.EastWest)
|
||||
{
|
||||
switch (direction.getAxis())
|
||||
{
|
||||
case X:
|
||||
return threeDimensionalCompare(x, y, z, quad.x, quad.y, quad.z);
|
||||
case Y:
|
||||
return threeDimensionalCompare(y, z, x, quad.y, quad.z, quad.x);
|
||||
case Z:
|
||||
return threeDimensionalCompare(z, y, x, quad.z, quad.y, quad.x);
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid Axis enum: " + direction.getAxis());
|
||||
}
|
||||
}
|
||||
else // if ()
|
||||
{
|
||||
switch (direction.getAxis())
|
||||
{
|
||||
case X:
|
||||
return threeDimensionalCompare(x, z, y, quad.x, quad.z, quad.y);
|
||||
case Y:
|
||||
return threeDimensionalCompare(y, x, z, quad.y, quad.x, quad.z);
|
||||
case Z:
|
||||
return threeDimensionalCompare(z, x, y, quad.z, quad.x, quad.y);
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid Axis enum: " + direction.getAxis());
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Compares two 3D points A and B. <br>
|
||||
* The X, Y, and Z coordinates can be passed into parameters 0, 1, and 2 in any order
|
||||
* provided they are in the same order for both A and B. <br>
|
||||
* With the 0th parameter being the most significant when comparing.
|
||||
*/
|
||||
private static int threeDimensionalCompare(short a0, short a1, short a2, short b0, short b1, short b2)
|
||||
{
|
||||
long a = (long) a0 << 48 | (long) a1 << 32 | (long) a2 << 16;
|
||||
long b = (long) b0 << 48 | (long) b1 << 32 | (long) b2 << 16;
|
||||
return Long.compare(a, b);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to merge the given quad into this one.
|
||||
* @returns true if the quads were merged, false otherwise.
|
||||
*/
|
||||
public boolean tryMerge(BufferQuad quad, BufferMergeDirectionEnum mergeDirection)
|
||||
{
|
||||
if (quad.hasError || this.hasError) return false;
|
||||
// only merge quads that are in the same direction
|
||||
if (direction != quad.direction)
|
||||
return false;
|
||||
|
||||
// make sure these quads share the same perpendicular axis
|
||||
if ((mergeDirection == BufferMergeDirectionEnum.EastWest && this.y != quad.y) ||
|
||||
(mergeDirection == BufferMergeDirectionEnum.NorthSouthOrUpDown && this.x != quad.x))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// get the position of each quad to compare against
|
||||
short thisPerpendicularCompareStartPos; // edge perpendicular to the merge direction
|
||||
short thisParallelCompareStartPos; // edge parallel to the merge direction
|
||||
short otherPerpendicularCompareStartPos;
|
||||
short otherParallelCompareStartPos;
|
||||
switch (this.direction.getAxis())
|
||||
{
|
||||
default: // shouldn't normally happen, just here to make the compiler happy
|
||||
case X:
|
||||
if (mergeDirection == BufferMergeDirectionEnum.EastWest)
|
||||
{
|
||||
thisPerpendicularCompareStartPos = this.z;
|
||||
thisParallelCompareStartPos = this.x;
|
||||
|
||||
otherPerpendicularCompareStartPos = quad.z;
|
||||
otherParallelCompareStartPos = quad.x;
|
||||
}
|
||||
else //if (mergeDirection == MergeDirection.NorthSouthOrUpDown)
|
||||
{
|
||||
thisPerpendicularCompareStartPos = this.y;
|
||||
thisParallelCompareStartPos = this.z;
|
||||
|
||||
otherPerpendicularCompareStartPos = quad.y;
|
||||
otherParallelCompareStartPos = quad.z;
|
||||
}
|
||||
break;
|
||||
|
||||
case Y:
|
||||
if (mergeDirection == BufferMergeDirectionEnum.EastWest)
|
||||
{
|
||||
thisPerpendicularCompareStartPos = this.x;
|
||||
thisParallelCompareStartPos = this.z;
|
||||
|
||||
otherPerpendicularCompareStartPos = quad.x;
|
||||
otherParallelCompareStartPos = quad.z;
|
||||
}
|
||||
else //if (mergeDirection == MergeDirection.NorthSouthOrUpDown)
|
||||
{
|
||||
thisPerpendicularCompareStartPos = this.z;
|
||||
thisParallelCompareStartPos = this.y;
|
||||
|
||||
otherPerpendicularCompareStartPos = quad.z;
|
||||
otherParallelCompareStartPos = quad.y;
|
||||
}
|
||||
break;
|
||||
|
||||
case Z:
|
||||
if (mergeDirection == BufferMergeDirectionEnum.EastWest)
|
||||
{
|
||||
thisPerpendicularCompareStartPos = this.x;
|
||||
thisParallelCompareStartPos = this.z;
|
||||
|
||||
otherPerpendicularCompareStartPos = quad.x;
|
||||
otherParallelCompareStartPos = quad.z;
|
||||
}
|
||||
else //if (mergeDirection == MergeDirection.NorthSouthOrUpDown)
|
||||
{
|
||||
thisPerpendicularCompareStartPos = this.y;
|
||||
thisParallelCompareStartPos = this.z;
|
||||
|
||||
otherPerpendicularCompareStartPos = quad.y;
|
||||
otherParallelCompareStartPos = quad.z;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// get the width of this quad in the relevant axis
|
||||
short thisPerpendicularCompareWidth;
|
||||
short thisParallelCompareWidth;
|
||||
short otherParallelCompareWidth;
|
||||
if (mergeDirection == BufferMergeDirectionEnum.EastWest)
|
||||
{
|
||||
thisPerpendicularCompareWidth = this.widthEastWest;
|
||||
|
||||
thisParallelCompareWidth = this.widthNorthSouthOrUpDown;
|
||||
otherParallelCompareWidth = quad.widthNorthSouthOrUpDown;
|
||||
}
|
||||
else
|
||||
{
|
||||
thisPerpendicularCompareWidth = this.widthNorthSouthOrUpDown;
|
||||
|
||||
thisParallelCompareWidth = this.widthEastWest;
|
||||
otherParallelCompareWidth = quad.widthEastWest;
|
||||
}
|
||||
|
||||
// FIXME: TEMP: Hard limit for width
|
||||
if (thisPerpendicularCompareWidth >= 16) return false;
|
||||
if (Math.floorDiv(otherPerpendicularCompareStartPos, 16)
|
||||
!= Math.floorDiv(thisPerpendicularCompareStartPos, 16)) return false;
|
||||
|
||||
// check if these quads are adjacent
|
||||
if (thisPerpendicularCompareStartPos + thisPerpendicularCompareWidth < otherPerpendicularCompareStartPos ||
|
||||
thisParallelCompareStartPos != otherParallelCompareStartPos)
|
||||
{
|
||||
// these quads aren't adjacent, they can't be merged
|
||||
return false;
|
||||
}
|
||||
else if (thisPerpendicularCompareStartPos + thisPerpendicularCompareWidth > otherPerpendicularCompareStartPos)
|
||||
{
|
||||
// these quads are overlapping, they can't be merged
|
||||
EVENT_LOGGER.warn("Overlapping quads detected!");
|
||||
quad.hasError = true;
|
||||
this.hasError = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// only merge quads that have the same width edges
|
||||
if (thisParallelCompareWidth != otherParallelCompareWidth)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// do the quads' color, light, etc. match?
|
||||
if (color != quad.color ||
|
||||
skyLight != quad.skyLight ||
|
||||
blockLight != quad.blockLight)
|
||||
{
|
||||
// we can only merge identically colored/lit quads
|
||||
return false;
|
||||
}
|
||||
|
||||
// merge the two quads
|
||||
if (mergeDirection == BufferMergeDirectionEnum.NorthSouthOrUpDown)
|
||||
{
|
||||
widthNorthSouthOrUpDown += quad.widthNorthSouthOrUpDown;
|
||||
}
|
||||
else // if (mergeDirection == MergeDirection.EastWest)
|
||||
{
|
||||
widthEastWest += quad.widthEastWest;
|
||||
}
|
||||
|
||||
// merge successful
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.builders.lodBuilding.bufferBuilding;
|
||||
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.objects.opengl.LodBox;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
|
||||
/**
|
||||
* Builds LODs as rectangular prisms.
|
||||
* @author James Seibel
|
||||
* @version 3-19-2022
|
||||
*/
|
||||
public class CubicLodTemplate
|
||||
{
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
|
||||
public static void addLodToBuffer(long data, long topData, long botData, long[][][] adjData,
|
||||
boolean[] adjFillBlack, byte detailLevel,int offsetPosX, int offsetOosZ, LodQuadBuilder quadBuilder, DebugMode debugging)
|
||||
{
|
||||
short width = (short) (1 << detailLevel);
|
||||
short x = (short) LevelPosUtil.convert(detailLevel, offsetPosX, LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
short y = DataPointUtil.getDepth(data);
|
||||
short z = (short) LevelPosUtil.convert(detailLevel, offsetOosZ, LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
short dy = (short) (DataPointUtil.getHeight(data) - y);
|
||||
if (dy == 0)
|
||||
return;
|
||||
if (dy < 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Negative y size for the data! Data: " + DataPointUtil.toString(data));
|
||||
}
|
||||
|
||||
int color;
|
||||
boolean fullBright = false;
|
||||
switch (debugging) {
|
||||
case OFF:
|
||||
case SHOW_WIREFRAME:
|
||||
{
|
||||
float saturationMultiplier = (float)CONFIG.client().graphics().advancedGraphics().getSaturationMultiplier();
|
||||
float brightnessMultiplier = (float)CONFIG.client().graphics().advancedGraphics().getBrightnessMultiplier();
|
||||
if (saturationMultiplier == 1.0 && brightnessMultiplier == 1.0) {
|
||||
color = DataPointUtil.getColor(data);
|
||||
} else {
|
||||
float[] ahsv = ColorUtil.argbToAhsv(DataPointUtil.getColor(data));
|
||||
color = ColorUtil.ahsvToArgb(ahsv[0], ahsv[1], ahsv[2] * saturationMultiplier, ahsv[3] * brightnessMultiplier);
|
||||
//ApiShared.LOGGER.info("Raw color:[{}], AHSV:{}, Out color:[{}]",
|
||||
// ColorUtil.toString(DataPointUtil.getColor(data)),
|
||||
// ahsv, ColorUtil.toString(color));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SHOW_DETAIL:
|
||||
case SHOW_DETAIL_WIREFRAME:
|
||||
{
|
||||
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel];
|
||||
fullBright = true;
|
||||
break;
|
||||
}
|
||||
case SHOW_GENMODE:
|
||||
case SHOW_GENMODE_WIREFRAME:
|
||||
{
|
||||
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[DataPointUtil.getGenerationMode(data)];
|
||||
fullBright = true;
|
||||
break;
|
||||
}
|
||||
case SHOW_OVERLAPPING_QUADS:
|
||||
case SHOW_OVERLAPPING_QUADS_WIREFRAME:
|
||||
{
|
||||
color = ColorUtil.WHITE;
|
||||
fullBright = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown debug mode: " + debugging);
|
||||
}
|
||||
LodBox.addBoxQuadsToBuilder(quadBuilder, // buffer
|
||||
width, dy, width, // setWidth
|
||||
x, y, z, // setOffset
|
||||
color, // setColor
|
||||
DataPointUtil.getLightSky(data), // setSkyLights
|
||||
fullBright ? 15 : DataPointUtil.getLightBlock(data), // setBlockLights
|
||||
topData, botData, adjData, adjFillBlack); // setAdjData
|
||||
}
|
||||
}
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.builders.lodBuilding.bufferBuilding;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import com.seibel.lod.core.api.ApiShared;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.logging.SpamReducedLogger;
|
||||
import com.seibel.lod.core.objects.Pos2D;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.objects.opengl.RenderRegion;
|
||||
import com.seibel.lod.core.render.LodRenderer;
|
||||
import com.seibel.lod.core.render.objects.GLBuffer;
|
||||
import com.seibel.lod.core.util.*;
|
||||
import com.seibel.lod.core.util.gridList.MovableGridRingList;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
|
||||
/**
|
||||
* This object creates the buffers that are rendered by the LodRenderer.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 12-9-2021
|
||||
*/
|
||||
public class LodBufferBuilderFactory {
|
||||
|
||||
// TODO: Do some Perf logging of Buffer Building
|
||||
public static final boolean ENABLE_BUFFER_PERF_LOGGING = false;
|
||||
public static final boolean ENABLE_EVENT_LOGGING = false;
|
||||
public static final boolean ENABLE_LAG_SPIKE_LOGGING = false;
|
||||
public static final long LAG_SPIKE_THRESOLD_NS = TimeUnit.NANOSECONDS.convert(16, TimeUnit.MILLISECONDS);
|
||||
|
||||
public static class LagSpikeCatcher {
|
||||
|
||||
long timer = System.nanoTime();
|
||||
|
||||
public LagSpikeCatcher() {
|
||||
}
|
||||
|
||||
public void end(String source) {
|
||||
if (!ENABLE_LAG_SPIKE_LOGGING)
|
||||
return;
|
||||
timer = System.nanoTime() - timer;
|
||||
if (timer > LAG_SPIKE_THRESOLD_NS) {
|
||||
ApiShared.LOGGER.info("LagSpikeCatcher: " + source + " took " + Duration.ofNanos(timer) + "!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class);
|
||||
|
||||
/** The thread used to generate new LODs off the main thread. */
|
||||
private static LodThreadFactory mainGenThreadFactory = new LodThreadFactory(
|
||||
LodBufferBuilderFactory.class.getSimpleName() + " - main", Thread.NORM_PRIORITY - 2);
|
||||
public static ExecutorService mainGenThread = Executors.newSingleThreadExecutor(mainGenThreadFactory);
|
||||
|
||||
/** The threads used to generate buffers. */
|
||||
private static LodThreadFactory bufferBuilderThreadFactory = new LodThreadFactory("BufferBuilder",
|
||||
Thread.NORM_PRIORITY - 2);
|
||||
private static int previousBufferBuilderThreads = CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads();
|
||||
public static ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(previousBufferBuilderThreads, bufferBuilderThreadFactory);
|
||||
|
||||
/** The thread used to upload buffers. */
|
||||
private static LodThreadFactory bufferUploadThreadFactory = new LodThreadFactory(
|
||||
LodBufferBuilderFactory.class.getSimpleName() + " - upload", Thread.NORM_PRIORITY - 1);
|
||||
public static ExecutorService bufferUploadThread = Executors.newSingleThreadExecutor(bufferUploadThreadFactory);
|
||||
|
||||
/**
|
||||
* 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 = (LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 3) * 8;
|
||||
public static final int QUADS_BYTE_SIZE = LodUtil.LOD_VERTEX_FORMAT.getByteSize() * (LodRenderer.ENABLE_IBO ? 4 : 6);
|
||||
public static final int MAX_TRIANGLES_PER_BUFFER = (1024 * 1024 * 1)
|
||||
/ (LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 3);
|
||||
public static final int MAX_QUADS_PER_BUFFER = (1024 * 1024 * 1) / QUADS_BYTE_SIZE;
|
||||
public static final int FULL_SIZED_BUFFER = MAX_QUADS_PER_BUFFER * QUADS_BYTE_SIZE;
|
||||
|
||||
public static int skyLightPlayer = 15;
|
||||
|
||||
public MovableGridRingList<RenderRegion> renderRegions = null;
|
||||
|
||||
/** 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;
|
||||
|
||||
private boolean builderThreadRunning = false;
|
||||
|
||||
public ReentrantLock regionsListLock = new ReentrantLock();
|
||||
|
||||
public LodBufferBuilderFactory() {
|
||||
|
||||
}
|
||||
|
||||
public void setRegionNeedRegen(int regionX, int regionZ) {
|
||||
MovableGridRingList<RenderRegion> r = renderRegions;
|
||||
if (r==null) return;
|
||||
RenderRegion rr = r.get(regionX, regionZ);
|
||||
if (rr==null) return;
|
||||
rr.setNeedRegen();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return whether it has started a generation task or is blocked
|
||||
*/
|
||||
public boolean updateAndSwapLodBuffersAsync(LodRenderer renderer, LodDimension lodDim, int playerX, int playerY,
|
||||
int playerZ, boolean fullRegen) {
|
||||
|
||||
// only allow one generation process to happen at a time
|
||||
if (builderThreadRunning) return false;
|
||||
|
||||
builderThreadRunning = true;
|
||||
|
||||
Runnable thread = () -> generateLodBuffersThread(renderer, lodDim, playerX, playerY, playerZ, fullRegen);
|
||||
mainGenThread.execute(thread);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateRingList(int playerX, int playerZ, int regionWidth) throws InterruptedException {
|
||||
if (renderRegions != null && regionWidth != renderRegions.getSize()) {
|
||||
renderRegions.clear(RenderRegion::close);
|
||||
renderRegions = null;
|
||||
}
|
||||
LodUtil.checkInterrupts();
|
||||
if (renderRegions == null) {
|
||||
renderRegions = new MovableGridRingList<RenderRegion>(regionWidth/2,
|
||||
LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerX),
|
||||
LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerZ));
|
||||
ApiShared.LOGGER.info("============Render Regions rebuilt============");
|
||||
} else {
|
||||
renderRegions.move(LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerX),
|
||||
LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerZ), RenderRegion::close);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetThreadPools(boolean dumpThread) {
|
||||
if (dumpThread) {
|
||||
bufferBuilderThreadFactory.dumpAllThreadStacks();
|
||||
bufferUploadThreadFactory.dumpAllThreadStacks();
|
||||
}
|
||||
bufferBuilderThreads.shutdownNow();
|
||||
bufferUploadThread.shutdownNow();
|
||||
previousBufferBuilderThreads = CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads();
|
||||
bufferBuilderThreadFactory = new LodThreadFactory("BufferBuilder", Thread.NORM_PRIORITY - 2);
|
||||
bufferBuilderThreads = Executors.newFixedThreadPool(previousBufferBuilderThreads, bufferBuilderThreadFactory);
|
||||
|
||||
bufferUploadThreadFactory = new LodThreadFactory(
|
||||
LodBufferBuilderFactory.class.getSimpleName() + " - upload", Thread.NORM_PRIORITY - 1);
|
||||
bufferUploadThread = Executors.newSingleThreadExecutor(bufferUploadThreadFactory);
|
||||
|
||||
}
|
||||
|
||||
private void generateLodBuffersThread(LodRenderer renderer, LodDimension lodDim, int playerX, int playerY,
|
||||
int playerZ, boolean fullRegen) {
|
||||
//ArrayList<RenderRegion> regionsToCleanup = new ArrayList<RenderRegion>();
|
||||
try {
|
||||
if (previousBufferBuilderThreads != CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads())
|
||||
resetThreadPools(false);
|
||||
regionsListLock.lockInterruptibly();
|
||||
if (ENABLE_EVENT_LOGGING)
|
||||
ApiShared.LOGGER.info("BufferBuilderStarter locked the region lock! LodDim: [{}], RenderRegion: [{}]",
|
||||
lodDim, renderRegions==null ? "NULL" : renderRegions.toString());
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
boolean doCaveCulling = CONFIG.client().graphics().advancedGraphics().getEnableCaveCulling();
|
||||
doCaveCulling &= !lodDim.dimension.hasCeiling();
|
||||
doCaveCulling &= lodDim.dimension.hasSkyLight();
|
||||
doCaveCulling &= playerY > CONFIG.client().graphics().advancedGraphics().getCaveCullingHeight() + 5;
|
||||
int playerSkylight = MC.getPlayerSkylight(); // if fail returns -1.
|
||||
doCaveCulling &= playerSkylight > 7;
|
||||
|
||||
try {
|
||||
updateRingList(playerX, playerZ, lodDim.getWidth());
|
||||
|
||||
// ================================//
|
||||
// create the nodeToRenderThreads //
|
||||
// ================================//
|
||||
|
||||
skyLightPlayer = MC.getWrappedClientWorld().getSkyLight(playerX, playerY, playerZ);
|
||||
// int minCullingRange =
|
||||
// SingletonHandler.get(ILodConfigWrapperSingleton.class).client().graphics().advancedGraphics().getBacksideCullingRange();
|
||||
// int cullingRangeX = Math.max((int)(1.5 * Math.abs(lastX - playerX)),
|
||||
// minCullingRange);
|
||||
// int cullingRangeZ = Math.max((int)(1.5 * Math.abs(lastZ - playerZ)),
|
||||
// minCullingRange);
|
||||
|
||||
Pos2D minPos = renderRegions.getMinInRange();
|
||||
Pos2D maxPos = renderRegions.getMaxInRange();
|
||||
CompletableFuture<Void> future = CompletableFuture.completedFuture(null);
|
||||
|
||||
try {
|
||||
int numOfJobs = 0;
|
||||
for (int regX = minPos.x; regX < maxPos.x; regX++) {
|
||||
for (int regZ = minPos.y; regZ < maxPos.y; regZ++) {
|
||||
RenderRegion r = renderRegions.get(regX, regZ);
|
||||
RegionPos regPos = new RegionPos(regX, regZ);
|
||||
if (r != null && !r.canRender(lodDim, regPos)) {
|
||||
renderRegions.set(regX, regZ, null);
|
||||
r.close();
|
||||
r = null;
|
||||
}
|
||||
|
||||
if (r == null) {
|
||||
r = new RenderRegion(regPos, lodDim);
|
||||
renderRegions.set(regX, regZ, r);
|
||||
}
|
||||
|
||||
CompletableFuture<Void> newFuture =
|
||||
r.updateStatus(bufferUploadThread, bufferBuilderThreads, fullRegen,
|
||||
playerX, playerZ, doCaveCulling).orElse(null);
|
||||
if (newFuture != null) {
|
||||
future = CompletableFuture.allOf(future, newFuture);
|
||||
numOfJobs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ================================//
|
||||
// wait on completion //
|
||||
// ================================//
|
||||
|
||||
long executeStart = System.currentTimeMillis();
|
||||
try {
|
||||
future.get(1, TimeUnit.MINUTES);
|
||||
} catch (CancellationException ce) {
|
||||
throw new InterruptedException("Future interrupted");
|
||||
} catch (ExecutionException ee) {
|
||||
ApiShared.LOGGER.error("LodBufferBuilder ran into trouble: ", ee.getCause());
|
||||
}
|
||||
long executeEnd = System.currentTimeMillis();
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long buildTime = endTime - startTime;
|
||||
long executeTime = executeEnd - executeStart;
|
||||
if (ENABLE_BUFFER_PERF_LOGGING)
|
||||
ApiShared.LOGGER.info("Thread Build&Upload(" + numOfJobs + "/"
|
||||
+ (lodDim.getWidth() * lodDim.getWidth()) + (fullRegen ? "FULL" : "") + ") time: " + buildTime
|
||||
+ " ms" + '\n' + "thread execute time: " + executeTime + " ms");
|
||||
|
||||
} catch (InterruptedException ie) {
|
||||
resetThreadPools(false);
|
||||
try {
|
||||
future.get();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
} catch (TimeoutException te) {
|
||||
ApiShared.LOGGER.error("LodBufferBuilder timed out: ", te);
|
||||
resetThreadPools(true);
|
||||
}
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
ApiShared.LOGGER.error("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: ", e);
|
||||
}
|
||||
}
|
||||
catch (InterruptedException ignored) { }
|
||||
finally {
|
||||
regionsListLock.unlock();
|
||||
if (ENABLE_EVENT_LOGGING) ApiShared.LOGGER.info("BufferBuilderStarter unlocked the region lock!");
|
||||
builderThreadRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
private final SpamReducedLogger ramLogger = new SpamReducedLogger(1);
|
||||
|
||||
public void dumpBufferMemoryUsage() {
|
||||
if (!ramLogger.canMaybeLog())
|
||||
return;
|
||||
ramLogger.info("Dumping Ram Usage for buffer usage...");
|
||||
StatsMap statsMap = new StatsMap();
|
||||
|
||||
if (renderRegions == null) {
|
||||
ramLogger.info("Buildable VBOs are null!");
|
||||
} else {
|
||||
for (RenderRegion buffers : renderRegions) {
|
||||
if (buffers == null)
|
||||
continue;
|
||||
buffers.debugDumpStats(statsMap);
|
||||
}
|
||||
}
|
||||
statsMap.incStat("Total Buffers", GLBuffer.count.get());
|
||||
ramLogger.info("================================================");
|
||||
ramLogger.info("Stats: {}", statsMap);
|
||||
ramLogger.info("================================================");
|
||||
ramLogger.incLogTries();
|
||||
}
|
||||
|
||||
// ===============================//
|
||||
// BufferBuilder related methods //
|
||||
// ===============================//
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
ApiShared.LOGGER.info("Destroying LodBufferBuilder...");
|
||||
mainGenThread.shutdownNow();
|
||||
mainGenThread = Executors.newSingleThreadExecutor(mainGenThreadFactory);
|
||||
boolean locked = false;
|
||||
try {
|
||||
locked = regionsListLock.tryLock(5, TimeUnit.SECONDS); // FIXME: For some reason we get a deadlock here sometimes
|
||||
} catch (InterruptedException ignored) {}
|
||||
try {
|
||||
if (renderRegions != null) renderRegions.clear(RenderRegion::close);
|
||||
renderRegions = null;
|
||||
} finally {
|
||||
if (locked) regionsListLock.unlock();
|
||||
}
|
||||
ApiShared.LOGGER.info("LodBufferBuilder destroyed.");
|
||||
}
|
||||
|
||||
/** Get the newly created VBOs
|
||||
* Note: SHOULD NEVER MODIFY THE LIST */
|
||||
public MovableGridRingList<RenderRegion> getRenderRegions() {
|
||||
return renderRegions;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void triggerReset() {
|
||||
|
||||
}
|
||||
}
|
||||
+539
@@ -0,0 +1,539 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.builders.lodBuilding.bufferBuilding;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.LodDirection.Axis;
|
||||
import com.seibel.lod.core.enums.config.GpuUploadMethod;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.render.LodRenderer;
|
||||
import com.seibel.lod.core.render.objects.GLVertexBuffer;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
|
||||
import static com.seibel.lod.core.render.LodRenderer.EVENT_LOGGER;
|
||||
|
||||
/**
|
||||
* Used to create the quads before they are converted to renderable buffers.
|
||||
*
|
||||
* @version 2022-4-9
|
||||
*/
|
||||
public class LodQuadBuilder
|
||||
{
|
||||
static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
|
||||
public final boolean skipQuadsWithZeroSkylight;
|
||||
public final short skyLightCullingBelow;
|
||||
|
||||
final ArrayList<BufferQuad>[] quads = (ArrayList<BufferQuad>[]) new ArrayList[6];
|
||||
|
||||
public static final int[][][] DIRECTION_VERTEX_IBO_QUAD = new int[][][]
|
||||
{
|
||||
// X,Z //
|
||||
{ // UP
|
||||
{ 1, 0 }, // 0
|
||||
{ 1, 1 }, // 1
|
||||
{ 0, 1 }, // 2
|
||||
{ 0, 0 }, // 3
|
||||
},
|
||||
{ // DOWN
|
||||
{ 0, 0 }, // 0
|
||||
{ 0, 1 }, // 1
|
||||
{ 1, 1 }, // 2
|
||||
{ 1, 0 }, // 3
|
||||
},
|
||||
|
||||
// X,Y //
|
||||
{ // NORTH
|
||||
{ 0, 0 }, // 0
|
||||
{ 0, 1 }, // 1
|
||||
{ 1, 1 }, // 2
|
||||
|
||||
{ 1, 0 }, // 3
|
||||
},
|
||||
{ // SOUTH
|
||||
{ 1, 0 }, // 0
|
||||
{ 1, 1 }, // 1
|
||||
{ 0, 1 }, // 2
|
||||
|
||||
{ 0, 0 }, // 3
|
||||
},
|
||||
|
||||
// Z,Y //
|
||||
{ // WEST
|
||||
{ 0, 0 }, // 0
|
||||
{ 1, 0 }, // 1
|
||||
{ 1, 1 }, // 2
|
||||
|
||||
{ 0, 1 }, // 3
|
||||
},
|
||||
{ // EAST
|
||||
{ 0, 1 }, // 0
|
||||
{ 1, 1 }, // 1
|
||||
{ 1, 0 }, // 2
|
||||
|
||||
{ 0, 0 }, // 3
|
||||
},
|
||||
};
|
||||
public static final int[][][] DIRECTION_VERTEX_QUAD = new int[][][]
|
||||
{
|
||||
// X,Z //
|
||||
{ // UP
|
||||
{ 1, 0 }, // 0
|
||||
{ 1, 1 }, // 1
|
||||
{ 0, 1 }, // 2
|
||||
|
||||
{ 1, 0 }, // 0
|
||||
{ 0, 1 }, // 2
|
||||
{ 0, 0 }, // 3
|
||||
},
|
||||
{ // DOWN
|
||||
{ 0, 0 }, // 0
|
||||
{ 0, 1 }, // 1
|
||||
{ 1, 1 }, // 2
|
||||
|
||||
{ 0, 0 }, // 0
|
||||
{ 1, 1 }, // 2
|
||||
{ 1, 0 }, // 3
|
||||
},
|
||||
|
||||
// X,Y //
|
||||
{ // NORTH
|
||||
{ 0, 0 }, // 0
|
||||
{ 0, 1 }, // 1
|
||||
{ 1, 1 }, // 2
|
||||
|
||||
{ 0, 0 }, // 0
|
||||
{ 1, 1 }, // 2
|
||||
{ 1, 0 }, // 3
|
||||
},
|
||||
{ // SOUTH
|
||||
{ 1, 0 }, // 0
|
||||
{ 1, 1 }, // 1
|
||||
{ 0, 1 }, // 2
|
||||
|
||||
{ 1, 0 }, // 0
|
||||
{ 0, 1 }, // 2
|
||||
{ 0, 0 }, // 3
|
||||
},
|
||||
|
||||
// Z,Y //
|
||||
{ // WEST
|
||||
{ 0, 0 }, // 0
|
||||
{ 1, 0 }, // 1
|
||||
{ 1, 1 }, // 2
|
||||
|
||||
{ 0, 0 }, // 0
|
||||
{ 1, 1 }, // 2
|
||||
{ 0, 1 }, // 3
|
||||
},
|
||||
{ // EAST
|
||||
{ 0, 1 }, // 0
|
||||
{ 1, 1 }, // 1
|
||||
{ 1, 0 }, // 2
|
||||
|
||||
{ 0, 1 }, // 0
|
||||
{ 1, 0 }, // 2
|
||||
{ 0, 0 }, // 3
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
public LodQuadBuilder(boolean enableSkylightCulling, int skyLightCullingBelow)
|
||||
{
|
||||
for (int i = 0; i < 6; i++)
|
||||
quads[i] = new ArrayList<>();
|
||||
|
||||
this.skipQuadsWithZeroSkylight = enableSkylightCulling;
|
||||
this.skyLightCullingBelow = (short) (skyLightCullingBelow - LodBuilder.MIN_WORLD_HEIGHT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void addQuadAdj(LodDirection dir, short x, short y, short z,
|
||||
short widthEastWest, short widthNorthSouthOrUpDown,
|
||||
int color, byte skylight, byte blocklight)
|
||||
{
|
||||
if (dir.ordinal() <= LodDirection.DOWN.ordinal())
|
||||
throw new IllegalArgumentException("addQuadAdj() is only for adj direction! Not UP or Down!");
|
||||
if (skipQuadsWithZeroSkylight && skylight == 0 && y < skyLightCullingBelow)
|
||||
return;
|
||||
quads[dir.ordinal()].add(new BufferQuad(x, y, z, widthEastWest, widthNorthSouthOrUpDown, color, skylight, blocklight, dir));
|
||||
}
|
||||
|
||||
// XZ
|
||||
public void addQuadUp(short x, short y, short z, short width, short wz, int color, byte skylight, byte blocklight)
|
||||
{
|
||||
if (skipQuadsWithZeroSkylight && skylight == 0 && y < skyLightCullingBelow)
|
||||
return;
|
||||
quads[LodDirection.UP.ordinal()].add(new BufferQuad(x, y, z, width, wz, color, skylight, blocklight, LodDirection.UP));
|
||||
}
|
||||
|
||||
public void addQuadDown(short x, short y, short z, short width, short wz, int color, byte skylight, byte blocklight)
|
||||
{
|
||||
if (skipQuadsWithZeroSkylight && skylight == 0 && y < skyLightCullingBelow)
|
||||
return;
|
||||
quads[LodDirection.DOWN.ordinal()].add(new BufferQuad(x, y, z, width, wz, color, skylight, blocklight, LodDirection.DOWN));
|
||||
}
|
||||
|
||||
// XY
|
||||
public void addQuadN(short x, short y, short z, short width, short wy, int color, byte skylight, byte blocklight)
|
||||
{
|
||||
if (skipQuadsWithZeroSkylight && skylight == 0 && y < skyLightCullingBelow)
|
||||
return;
|
||||
quads[LodDirection.NORTH.ordinal()].add(new BufferQuad(x, y, z, width, wy, color, skylight, blocklight, LodDirection.NORTH));
|
||||
}
|
||||
|
||||
public void addQuadS(short x, short y, short z, short width, short wy, int color, byte skylight, byte blocklight)
|
||||
{
|
||||
if (skipQuadsWithZeroSkylight && skylight == 0 && y < skyLightCullingBelow)
|
||||
return;
|
||||
quads[LodDirection.SOUTH.ordinal()].add(new BufferQuad(x, y, z, width, wy, color, skylight, blocklight, LodDirection.SOUTH));
|
||||
}
|
||||
|
||||
// ZY
|
||||
public void addQuadW(short x, short y, short z, short width, short wy, int color, byte skylight, byte blocklight)
|
||||
{
|
||||
if (skipQuadsWithZeroSkylight && skylight == 0 && y < skyLightCullingBelow)
|
||||
return;
|
||||
quads[LodDirection.WEST.ordinal()].add(new BufferQuad(x, y, z, width, wy, color, skylight, blocklight, LodDirection.WEST));
|
||||
}
|
||||
|
||||
public void addQuadE(short x, short y, short z, short width, short wy, int color, byte skylight, byte blocklight)
|
||||
{
|
||||
if (skipQuadsWithZeroSkylight && skylight == 0 && y < skyLightCullingBelow)
|
||||
return;
|
||||
quads[LodDirection.EAST.ordinal()].add(new BufferQuad(x, y, z, width, wy, color, skylight, blocklight, LodDirection.EAST));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void putVertex(ByteBuffer bb, short x, short y, short z, int color, byte skylight, byte blocklight, int mx, int my, int mz)
|
||||
{
|
||||
skylight %= 16;
|
||||
blocklight %= 16;
|
||||
|
||||
bb.putShort(x);
|
||||
bb.putShort(y);
|
||||
bb.putShort(z);
|
||||
|
||||
short meta = 0;
|
||||
meta |= (skylight | (blocklight << 4));
|
||||
byte mirco = 0;
|
||||
// mirco offset which is a xyz 2bit value
|
||||
// 0b00 = no offset
|
||||
// 0b01 = positive offset
|
||||
// 0b11 = negative offset
|
||||
// format is: 0b00zzyyxx
|
||||
if (mx != 0) mirco |= mx > 0 ? 0b01 : 0b11;
|
||||
if (my != 0) mirco |= my > 0 ? 0b0100 : 0b1100;
|
||||
if (mz != 0) mirco |= mz > 0 ? 0b010000 : 0b110000;
|
||||
meta |= mirco << 8;
|
||||
|
||||
bb.putShort(meta);
|
||||
byte r = (byte) ColorUtil.getRed(color);
|
||||
byte g = (byte) ColorUtil.getGreen(color);
|
||||
byte b = (byte) ColorUtil.getBlue(color);
|
||||
byte a = (byte) ColorUtil.getAlpha(color);
|
||||
bb.put(r);
|
||||
bb.put(g);
|
||||
bb.put(b);
|
||||
bb.put(a);
|
||||
}
|
||||
|
||||
private static void putQuad(ByteBuffer bb, BufferQuad quad)
|
||||
{
|
||||
int[][] quadBase = LodRenderer.ENABLE_IBO ? DIRECTION_VERTEX_IBO_QUAD[quad.direction.ordinal()] : DIRECTION_VERTEX_QUAD[quad.direction.ordinal()];
|
||||
short widthEastWest = quad.widthEastWest;
|
||||
short widthNorthSouth = quad.widthNorthSouthOrUpDown;
|
||||
Axis axis = quad.direction.getAxis();
|
||||
for (int i = 0; i < quadBase.length; i++)
|
||||
{
|
||||
short dx, dy, dz;
|
||||
int mx, my, mz;
|
||||
switch (axis)
|
||||
{
|
||||
case X: // ZY
|
||||
dx = 0;
|
||||
dy = quadBase[i][1] == 1 ? widthNorthSouth : 0;
|
||||
dz = quadBase[i][0] == 1 ? widthEastWest : 0;
|
||||
mx = 0;
|
||||
my = quadBase[i][1] == 1 ? 1 : -1;
|
||||
mz = quadBase[i][0] == 1 ? 1 : -1;
|
||||
break;
|
||||
case Y: // XZ
|
||||
dx = quadBase[i][0] == 1 ? widthEastWest : 0;
|
||||
dy = 0;
|
||||
dz = quadBase[i][1] == 1 ? widthNorthSouth : 0;
|
||||
mx = quadBase[i][0] == 1 ? 1 : -1;
|
||||
my = 0;
|
||||
mz = quadBase[i][1] == 1 ? 1 : -1;
|
||||
break;
|
||||
case Z: // XY
|
||||
dx = quadBase[i][0] == 1 ? widthEastWest : 0;
|
||||
dy = quadBase[i][1] == 1 ? widthNorthSouth : 0;
|
||||
dz = 0;
|
||||
mx = quadBase[i][0] == 1 ? 1 : -1;
|
||||
my = quadBase[i][1] == 1 ? 1 : -1;
|
||||
mz = 0;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid Axis enum: " + axis);
|
||||
}
|
||||
putVertex(bb, (short) (quad.x + dx), (short) (quad.y + dy), (short) (quad.z + dz),
|
||||
quad.hasError ? ColorUtil.RED : quad.color,
|
||||
quad.hasError ? 15 : quad.skyLight,
|
||||
quad.hasError ? 15 : quad.blockLight,
|
||||
mx, my, mz);
|
||||
}
|
||||
}
|
||||
|
||||
/** Uses Greedy meshing to merge this builder's Quads. */
|
||||
public void mergeQuads()
|
||||
{
|
||||
long mergeCount = 0;
|
||||
long preQuadsCount = getCurrentQuadsCount();
|
||||
if (preQuadsCount <= 1)
|
||||
return;
|
||||
|
||||
for (int directionIndex = 0; directionIndex < 6; directionIndex++)
|
||||
{
|
||||
mergeCount += mergeQuadsInternal(directionIndex, BufferMergeDirectionEnum.EastWest);
|
||||
|
||||
// only merge after the top has been merged
|
||||
if (directionIndex == 1)
|
||||
{
|
||||
long pass2 = mergeQuadsInternal(directionIndex, BufferMergeDirectionEnum.NorthSouthOrUpDown);
|
||||
mergeCount += pass2;
|
||||
}
|
||||
}
|
||||
long postQuadsCount = getCurrentQuadsCount();
|
||||
//if (mergeCount != 0)
|
||||
EVENT_LOGGER.debug("Merged {}/{}({}) quads", mergeCount, preQuadsCount, mergeCount / (double) preQuadsCount);
|
||||
}
|
||||
/** Merges all of this builder's quads for the given directionIndex (up, down, left, etc.) in the given direction */
|
||||
private long mergeQuadsInternal(int directionIndex, BufferMergeDirectionEnum mergeDirection)
|
||||
{
|
||||
if (quads[directionIndex].size() <= 1)
|
||||
return 0;
|
||||
|
||||
quads[directionIndex].sort( (objOne, objTwo) -> objOne.compare(objTwo, mergeDirection) );
|
||||
|
||||
long mergeCount = 0;
|
||||
ListIterator<BufferQuad> iter = quads[directionIndex].listIterator();
|
||||
BufferQuad currentQuad = iter.next();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
BufferQuad nextQuad = iter.next();
|
||||
|
||||
if (currentQuad.tryMerge(nextQuad, mergeDirection))
|
||||
{
|
||||
// merge successful, attempt to merge the next quad
|
||||
mergeCount++;
|
||||
iter.set(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// merge fail, move on to the next quad
|
||||
currentQuad = nextQuad;
|
||||
}
|
||||
}
|
||||
quads[directionIndex].removeIf(o -> o == null);
|
||||
return mergeCount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Iterator<ByteBuffer> makeVertexBuffers()
|
||||
{
|
||||
return new Iterator<ByteBuffer>()
|
||||
{
|
||||
final ByteBuffer bb = ByteBuffer.allocateDirect(LodBufferBuilderFactory.FULL_SIZED_BUFFER)
|
||||
.order(ByteOrder.nativeOrder());
|
||||
int dir = skipEmpty(0);
|
||||
int quad = 0;
|
||||
|
||||
private int skipEmpty(int d)
|
||||
{
|
||||
while (d < 6 && quads[d].isEmpty())
|
||||
d++;
|
||||
return d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return dir < 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer next()
|
||||
{
|
||||
if (dir >= 6)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
bb.clear();
|
||||
bb.limit(LodBufferBuilderFactory.FULL_SIZED_BUFFER);
|
||||
while (bb.hasRemaining() && dir < 6)
|
||||
{
|
||||
writeData();
|
||||
}
|
||||
bb.limit(bb.position());
|
||||
bb.rewind();
|
||||
return bb;
|
||||
}
|
||||
|
||||
private void writeData()
|
||||
{
|
||||
int i = quad;
|
||||
for (; i < quads[dir].size(); i++)
|
||||
{
|
||||
if (!bb.hasRemaining())
|
||||
{
|
||||
break;
|
||||
}
|
||||
putQuad(bb, quads[dir].get(i));
|
||||
}
|
||||
|
||||
if (i >= quads[dir].size())
|
||||
{
|
||||
quad = 0;
|
||||
dir++;
|
||||
dir = skipEmpty(dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
quad = i;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public interface BufferFiller
|
||||
{
|
||||
/** If true: more data needs to be filled */
|
||||
boolean fill(GLVertexBuffer vbo);
|
||||
}
|
||||
|
||||
public BufferFiller makeBufferFiller(GpuUploadMethod method)
|
||||
{
|
||||
return new BufferFiller()
|
||||
{
|
||||
int dir = 0;
|
||||
int quad = 0;
|
||||
|
||||
public boolean fill(GLVertexBuffer vbo)
|
||||
{
|
||||
if (dir >= 6)
|
||||
{
|
||||
vbo.setVertexCount(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
int numOfQuads = _countRemainingQuads();
|
||||
if (numOfQuads > LodBufferBuilderFactory.MAX_QUADS_PER_BUFFER)
|
||||
numOfQuads = LodBufferBuilderFactory.MAX_QUADS_PER_BUFFER;
|
||||
if (numOfQuads == 0)
|
||||
{
|
||||
vbo.setVertexCount(0);
|
||||
return false;
|
||||
}
|
||||
ByteBuffer bb = vbo.mapBuffer(numOfQuads * LodBufferBuilderFactory.QUADS_BYTE_SIZE, method,
|
||||
LodBufferBuilderFactory.FULL_SIZED_BUFFER);
|
||||
if (bb == null)
|
||||
throw new NullPointerException("mapBuffer returned null");
|
||||
bb.clear();
|
||||
bb.limit(numOfQuads * LodBufferBuilderFactory.QUADS_BYTE_SIZE);
|
||||
while (bb.hasRemaining() && dir < 6)
|
||||
{
|
||||
writeData(bb);
|
||||
}
|
||||
bb.rewind();
|
||||
vbo.unmapBuffer();
|
||||
vbo.setVertexCount(LodRenderer.ENABLE_IBO ? numOfQuads*4 : numOfQuads*6);
|
||||
return dir < 6;
|
||||
}
|
||||
|
||||
private int _countRemainingQuads()
|
||||
{
|
||||
int a = quads[dir].size() - quad;
|
||||
for (int i = dir + 1; i < quads.length; i++)
|
||||
{
|
||||
a += quads[i].size();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
private void writeData(ByteBuffer bb)
|
||||
{
|
||||
int startQ = quad;
|
||||
|
||||
int i = startQ;
|
||||
for (i = startQ; i < quads[dir].size(); i++)
|
||||
{
|
||||
if (!bb.hasRemaining())
|
||||
{
|
||||
break;
|
||||
}
|
||||
putQuad(bb, quads[dir].get(i));
|
||||
}
|
||||
|
||||
if (i >= quads[dir].size())
|
||||
{
|
||||
quad = 0;
|
||||
dir++;
|
||||
while (dir < 6 && quads[dir].isEmpty())
|
||||
dir++;
|
||||
}
|
||||
else
|
||||
{
|
||||
quad = i;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getCurrentQuadsCount()
|
||||
{
|
||||
int i = 0;
|
||||
for (ArrayList<BufferQuad> qs : quads)
|
||||
i += qs.size();
|
||||
return i;
|
||||
}
|
||||
|
||||
/** Returns how many Buffers will be needed to render everything in this builder. */
|
||||
public int getCurrentNeededVertexBufferCount()
|
||||
{
|
||||
return LodUtil.ceilDiv(getCurrentQuadsCount(), LodBufferBuilderFactory.MAX_QUADS_PER_BUFFER);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2021 Tom Lee (TomTheFurry) & James Seibel (Original code)
|
||||
*
|
||||
* 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.core.builders.worldGeneration;
|
||||
|
||||
import com.seibel.lod.core.api.ApiShared;
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.GenerationPriority;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.objects.PosToGenerateContainer;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper.Steps;
|
||||
|
||||
public class BatchGenerator {
|
||||
|
||||
public static final boolean ENABLE_GENERATOR_STATS_LOGGING = false;
|
||||
|
||||
private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
public AbstractBatchGenerationEnvionmentWrapper generationGroup;
|
||||
public LodDimension targetLodDim;
|
||||
public static final int generationGroupSize = 4;
|
||||
public static int previousThreadCount = CONFIG.client().advanced().threading()._getWorldGenerationThreadPoolSize();
|
||||
|
||||
private int estimatedSampleNeeded = 128;
|
||||
private int estimatedPointsToQueue = 1;
|
||||
|
||||
public BatchGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension) {
|
||||
IWorldWrapper world = LodUtil.getServerWorldFromDimension(newLodDimension.dimension);
|
||||
targetLodDim = newLodDimension;
|
||||
generationGroup = FACTORY.createBatchGenerator(newLodBuilder, newLodDimension, world);
|
||||
MC.sendChatMessage("NOTE: You are currently using Distant Horizon's Batch Chunk Pre-Generator.");
|
||||
ApiShared.LOGGER.info("Batch Chunk Generator initialized");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void queueGenerationRequests(LodDimension lodDim, LodBuilder lodBuilder) {
|
||||
if (lodDim != targetLodDim) {
|
||||
stop(false);
|
||||
IWorldWrapper dim = LodUtil.getServerWorldFromDimension(lodDim.dimension);
|
||||
generationGroup = FACTORY.createBatchGenerator(lodBuilder, lodDim, dim);
|
||||
targetLodDim = lodDim;
|
||||
ApiShared.LOGGER.info("1.18 Experimental Chunk Generator reinitialized");
|
||||
}
|
||||
|
||||
DistanceGenerationMode mode = CONFIG.client().worldGenerator().getDistanceGenerationMode();
|
||||
int newThreadCount = CONFIG.client().advanced().threading()._getWorldGenerationThreadPoolSize();
|
||||
if (newThreadCount != previousThreadCount) {
|
||||
generationGroup.resizeThreadPool(newThreadCount);
|
||||
previousThreadCount = newThreadCount;
|
||||
}
|
||||
if (estimatedPointsToQueue < newThreadCount)
|
||||
estimatedPointsToQueue = newThreadCount;
|
||||
|
||||
GenerationPriority priority = CONFIG.client().worldGenerator().getGenerationPriority();
|
||||
if (priority == GenerationPriority.AUTO)
|
||||
priority = MC.hasSinglePlayerServer() ? GenerationPriority.FAR_FIRST : GenerationPriority.NEAR_FIRST;
|
||||
|
||||
generationGroup.updateAllFutures();
|
||||
if (!MC.hasSinglePlayerServer())
|
||||
return;
|
||||
if (!LodUtil.checkRamUsage(0.1, 64)) return;
|
||||
|
||||
int eventsCount = generationGroup.getEventCount();
|
||||
// If we still all jobs running, return.
|
||||
if (eventsCount >= estimatedPointsToQueue) {
|
||||
estimatedPointsToQueue--;
|
||||
if (estimatedPointsToQueue < newThreadCount)
|
||||
estimatedPointsToQueue = newThreadCount;
|
||||
return;
|
||||
}
|
||||
|
||||
final int targetToGenerate = estimatedPointsToQueue - eventsCount;
|
||||
int toGenerate = targetToGenerate;
|
||||
int positionGoneThough = 0;
|
||||
|
||||
// round the player's block position down to the nearest chunk BlockPos
|
||||
int playerPosX = MC.getPlayerBlockPos().getX();
|
||||
int playerPosZ = MC.getPlayerBlockPos().getZ();
|
||||
double runTimeRatio = CONFIG.client().advanced().threading()._getWorldGenerationPartialRunTime();
|
||||
|
||||
PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate(estimatedSampleNeeded, playerPosX, playerPosZ,
|
||||
priority, mode);
|
||||
|
||||
if (eventsCount == 0 && posToGenerate.getNumberOfPos() >= estimatedSampleNeeded) {
|
||||
estimatedPointsToQueue++;
|
||||
if (estimatedPointsToQueue > newThreadCount * 10)
|
||||
estimatedPointsToQueue = newThreadCount * 10;
|
||||
}
|
||||
|
||||
// ApiShared.LOGGER.info("PosToGenerate: {}", posToGenerate);
|
||||
|
||||
// Find the max number of iterations we need to go though.
|
||||
// We are checking one FarPos, and one NearPos per iterations. This ensure we
|
||||
// aren't just
|
||||
// always picking one or the other.
|
||||
Steps targetStep;
|
||||
switch (mode) {
|
||||
case NONE:
|
||||
targetStep = Steps.Empty; // NOTE: Only load in existing chunks. No new chunk generation
|
||||
break;
|
||||
case BIOME_ONLY:
|
||||
targetStep = Steps.Biomes; // NOTE: No block. Require fake height in LodBuilder
|
||||
break;
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
targetStep = Steps.Noise; // NOTE: Stone only. Require fake surface
|
||||
break;
|
||||
case SURFACE:
|
||||
targetStep = Steps.Surface; // Carvers or Surface???
|
||||
break;
|
||||
case FEATURES:
|
||||
case FULL:
|
||||
targetStep = Steps.Features;
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ENABLE_GENERATOR_STATS_LOGGING)
|
||||
ApiShared.LOGGER.info("WorldGen. Near:" + posToGenerate.getNumberOfNearPos() + " Far:"
|
||||
+ posToGenerate.getNumberOfFarPos());
|
||||
if (priority == GenerationPriority.FAR_FIRST || priority == GenerationPriority.BALANCED) {
|
||||
|
||||
int nearCount = posToGenerate.getNumberOfNearPos();
|
||||
int farCount = posToGenerate.getNumberOfFarPos();
|
||||
if (ENABLE_GENERATOR_STATS_LOGGING)
|
||||
ApiShared.LOGGER.info("WorldGen. Near:" + nearCount + " Far:" + farCount);
|
||||
int maxIteration = Math.max(nearCount, farCount);
|
||||
for (int i = 0; i < maxIteration; i++) {
|
||||
|
||||
// We have farPos to go though
|
||||
if (i < farCount && posToGenerate.getNthDetail(i, false) != 0) {
|
||||
positionGoneThough++;
|
||||
byte detailLevel = (byte) (posToGenerate.getNthDetail(i, false) - 1);
|
||||
int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, false));
|
||||
int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, false));
|
||||
int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize;
|
||||
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep, false, runTimeRatio)) {
|
||||
toGenerate--;
|
||||
}
|
||||
}
|
||||
if (toGenerate <= 0)
|
||||
break;
|
||||
|
||||
// We have nearPos to go though
|
||||
if (i < nearCount && posToGenerate.getNthDetail(i, true) != 0) {
|
||||
positionGoneThough++;
|
||||
byte detailLevel = (byte) (posToGenerate.getNthDetail(i, true) - 1);
|
||||
int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, true));
|
||||
int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, true));
|
||||
int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize;
|
||||
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep, true, runTimeRatio)) {
|
||||
toGenerate--;
|
||||
}
|
||||
}
|
||||
|
||||
if (toGenerate <= 0)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
int nearCount = posToGenerate.getNumberOfNearPos();
|
||||
for (int i = 0; i < nearCount; i++) {
|
||||
|
||||
// We have nearPos to go though
|
||||
if (posToGenerate.getNthDetail(i, true) != 0) {
|
||||
positionGoneThough++;
|
||||
byte detailLevel = (byte) (posToGenerate.getNthDetail(i, true) - 1);
|
||||
int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, true));
|
||||
int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, true));
|
||||
int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize;
|
||||
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep, true, runTimeRatio)) {
|
||||
toGenerate--;
|
||||
}
|
||||
if (toGenerate <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Only do far gen if toGenerate is non 0 and that we have requested all samples
|
||||
// we can get.
|
||||
if (toGenerate > 0 && estimatedSampleNeeded > posToGenerate.getNumberOfPos()) {
|
||||
int farCount = posToGenerate.getNumberOfFarPos();
|
||||
for (int i = 0; i < farCount; i++) {
|
||||
// We have farPos to go though
|
||||
if (posToGenerate.getNthDetail(i, false) != 0) {
|
||||
positionGoneThough++;
|
||||
byte detailLevel = (byte) (posToGenerate.getNthDetail(i, false) - 1);
|
||||
int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, false));
|
||||
int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, false));
|
||||
int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize;
|
||||
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep, false, runTimeRatio)) {
|
||||
toGenerate--;
|
||||
}
|
||||
}
|
||||
if (toGenerate <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetToGenerate != toGenerate && ENABLE_GENERATOR_STATS_LOGGING) {
|
||||
if (toGenerate <= 0) {
|
||||
ApiShared.LOGGER.info(
|
||||
"WorldGenerator: Sampled " + posToGenerate.getNumberOfPos() + " out of " + estimatedSampleNeeded
|
||||
+ " points, started all targeted " + targetToGenerate + " generations.");
|
||||
} else {
|
||||
ApiShared.LOGGER.info("WorldGenerator: Sampled " + posToGenerate.getNumberOfPos() + " out of "
|
||||
+ estimatedSampleNeeded + " points, started " + (targetToGenerate - toGenerate)
|
||||
+ " out of targeted " + targetToGenerate + " generations.");
|
||||
}
|
||||
}
|
||||
|
||||
if (toGenerate > 0 && estimatedSampleNeeded <= posToGenerate.getNumberOfPos()) {
|
||||
// We failed to generate enough points from the samples.
|
||||
// Let's increase the estimatedSampleNeeded.
|
||||
estimatedSampleNeeded *= 1.3;
|
||||
// Ensure wee don't go to basically infinity
|
||||
if (estimatedSampleNeeded > 32768)
|
||||
estimatedSampleNeeded = 32768;
|
||||
if (ENABLE_GENERATOR_STATS_LOGGING)
|
||||
ApiShared.LOGGER.info("WorldGenerator: Increasing estimatedSampleNeeeded to " + estimatedSampleNeeded);
|
||||
|
||||
} else if (toGenerate <= 0 && positionGoneThough * 1.5 < posToGenerate.getNumberOfPos()) {
|
||||
// We haven't gone though half of them and it's already enough.
|
||||
// Let's shink the estimatedSampleNeeded.
|
||||
estimatedSampleNeeded /= 1.2;
|
||||
// Ensure we don't go to near zero.
|
||||
if (estimatedSampleNeeded < 4)
|
||||
estimatedSampleNeeded = 4;
|
||||
if (ENABLE_GENERATOR_STATS_LOGGING)
|
||||
ApiShared.LOGGER.info("WorldGenerator: Decreasing estimatedSampleNeeeded to " + estimatedSampleNeeded);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void stop(boolean blocking) {
|
||||
ApiShared.LOGGER.info("1.18 Experimental Chunk Generator shutting down...");
|
||||
generationGroup.stop(blocking);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* 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.core.builders.worldGeneration;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
|
||||
|
||||
/**
|
||||
* This is used to generate a LodChunk at a given ChunkPos.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public class LodGenWorker
|
||||
{
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
public static ExecutorService genThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
|
||||
|
||||
private final LodChunkGenThread thread;
|
||||
|
||||
|
||||
|
||||
public LodGenWorker(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode,
|
||||
LodBuilder newLodBuilder,
|
||||
LodDimension newLodDimension, IWorldWrapper serverWorld)
|
||||
{
|
||||
// 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 (serverWorld == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null ServerWorld");
|
||||
|
||||
|
||||
|
||||
thread = new LodChunkGenThread(newPos, newGenerationMode,
|
||||
newLodBuilder,
|
||||
newLodDimension, serverWorld);
|
||||
}
|
||||
|
||||
public void queueWork()
|
||||
{
|
||||
if (CONFIG.client().worldGenerator().getDistanceGenerationMode() == DistanceGenerationMode.FULL)
|
||||
{
|
||||
// if we are using FULL generation there is no reason
|
||||
// to queue up a bunch of generation requests,
|
||||
// because MC's internal server (as of 1.16.5) only
|
||||
// responds with a single thread. And we don't
|
||||
// want to cause more lag then necessary or queue up
|
||||
// requests that may end up being unneeded.
|
||||
thread.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Every other method can
|
||||
// be done asynchronously
|
||||
genThreads.execute(thread);
|
||||
}
|
||||
|
||||
// useful for debugging
|
||||
// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods());
|
||||
// ClientProxy.LOGGER.info(genThreads.toString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static class LodChunkGenThread implements Runnable
|
||||
{
|
||||
private AbstractWorldGeneratorWrapper worldGenWrapper;
|
||||
|
||||
public final LodDimension lodDim;
|
||||
public final DistanceGenerationMode generationMode;
|
||||
|
||||
private final AbstractChunkPosWrapper pos;
|
||||
|
||||
public LodChunkGenThread(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode,
|
||||
LodBuilder newLodBuilder,
|
||||
LodDimension newLodDimension, IWorldWrapper worldWrapper)
|
||||
{
|
||||
worldGenWrapper = FACTORY.createWorldGenerator(newLodBuilder, newLodDimension, worldWrapper);
|
||||
|
||||
pos = newPos;
|
||||
generationMode = newGenerationMode;
|
||||
lodDim = newLodDimension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// only generate LodChunks if they can
|
||||
// be added to the current LodDimension
|
||||
|
||||
if (lodDim.regionIsInRange(pos.getX() / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.getZ() / LodUtil.REGION_WIDTH_IN_CHUNKS))
|
||||
{
|
||||
switch (generationMode)
|
||||
{
|
||||
case NONE:
|
||||
// don't generate
|
||||
break;
|
||||
case BIOME_ONLY:
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
// fastest
|
||||
worldGenWrapper.generateBiomesOnly(pos, generationMode);
|
||||
break;
|
||||
case SURFACE:
|
||||
// faster
|
||||
worldGenWrapper.generateSurface(pos);
|
||||
break;
|
||||
case FEATURES:
|
||||
// fast
|
||||
worldGenWrapper.generateFeatures(pos);
|
||||
break;
|
||||
case FULL:
|
||||
// very slow
|
||||
worldGenWrapper.generateFull(pos);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// 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)
|
||||
{
|
||||
ClientApi.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
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
* 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.core.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.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.objects.PosToGenerateContainer;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.render.LodRenderer;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodThreadFactory;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
/**
|
||||
* A singleton that handles all long distance LOD world generation.
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 9-25-2021
|
||||
*/
|
||||
public class LodWorldGenerator
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
|
||||
/** This holds the thread used to create LOD generation requests 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<AbstractChunkPosWrapper> 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 (CONFIG.client().worldGenerator().getDistanceGenerationMode() != 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 = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads() * 8;
|
||||
|
||||
Thread generatorThread = new Thread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
// round the player's block position down to the nearest chunk BlockPos
|
||||
int playerPosX = MC.getPlayerBlockPos().getX();
|
||||
int playerPosZ = MC.getPlayerBlockPos().getZ();
|
||||
|
||||
|
||||
//=======================================//
|
||||
// fill in positionsWaitingToBeGenerated //
|
||||
//=======================================//
|
||||
|
||||
IWorldWrapper 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++;
|
||||
|
||||
AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos(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);
|
||||
genWorker.queueWork();
|
||||
}
|
||||
|
||||
|
||||
// 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++;
|
||||
|
||||
AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos(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);
|
||||
genWorker.queueWork();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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,93 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.config;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Where the annotations for the config are defined
|
||||
* If there is no annotation then the config will not touch it
|
||||
*
|
||||
* REMOVED IN a1.7
|
||||
*
|
||||
* @author coolGi
|
||||
* @version 02-07-2022
|
||||
*/
|
||||
@Deprecated
|
||||
public class ConfigAnnotations {
|
||||
/** A textField, button, etc. that can be interacted with */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Entry
|
||||
{
|
||||
String name() default "";
|
||||
|
||||
@Deprecated
|
||||
int width() default 150;
|
||||
|
||||
@Deprecated
|
||||
double minValue() default Double.MIN_NORMAL;
|
||||
|
||||
@Deprecated
|
||||
double maxValue() default Double.MAX_VALUE;
|
||||
|
||||
}
|
||||
|
||||
/** For making categories */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Category
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/** Makes text (looks like @Entry but dosnt save and has no button */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Comment
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a comment to the file,
|
||||
* This should only be used in special cases where comments from an entry cant reach
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface FileComment
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/** DONT USE AS IT WILL BE REMOVED IN THE REWORK OF THE CONFIG */
|
||||
@Deprecated
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ScreenEntry
|
||||
{
|
||||
String name() default "";
|
||||
|
||||
int width() default 100;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.dataFormat;
|
||||
|
||||
public class ColorFormat
|
||||
{
|
||||
public final static int BLUE_SHIFT = 0;
|
||||
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 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 static int createColorData(int alpha, int red, int green, int blue)
|
||||
{
|
||||
int colorData = 0;
|
||||
colorData += (alpha & ALPHA_MASK) << ALPHA_SHIFT;
|
||||
colorData += (red & RED_MASK) << RED_SHIFT;
|
||||
colorData += (green & GREEN_MASK) << GREEN_SHIFT;
|
||||
colorData += (blue & BLUE_MASK) << BLUE_SHIFT;
|
||||
|
||||
return colorData;
|
||||
}
|
||||
|
||||
public static short getAlpha(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> ALPHA_SHIFT) & ALPHA_MASK);
|
||||
}
|
||||
|
||||
public static short getRed(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> RED_SHIFT) & RED_MASK);
|
||||
}
|
||||
|
||||
public static short getGreen(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> GREEN_SHIFT) & GREEN_MASK);
|
||||
}
|
||||
|
||||
public static short getBlue(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> BLUE_SHIFT) & BLUE_MASK);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.dataFormat;
|
||||
|
||||
@Deprecated //Unused
|
||||
public class DataMergeUtil
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.dataFormat;
|
||||
|
||||
public class LightFormat
|
||||
{
|
||||
public final static byte INT_BLOCK_LIGHT_SHIFT = 16;
|
||||
public final static byte INT_SKY_LIGHT_SHIFT = 0;
|
||||
|
||||
public final static byte BYTE_BLOCK_LIGHT_SHIFT = 4;
|
||||
public final static byte BYTE_SKY_LIGHT_SHIFT = 0;
|
||||
|
||||
public final static byte BLOCK_LIGHT_MASK = 0b1111;
|
||||
public final static byte SKY_LIGHT_MASK = 0b1111;
|
||||
|
||||
|
||||
public static byte formatLightAsByte(byte skyLight, byte blockLight)
|
||||
{
|
||||
return (byte) (((skyLight & SKY_LIGHT_MASK) << (BYTE_SKY_LIGHT_SHIFT + 4)) | ((blockLight & BLOCK_LIGHT_MASK) << (BYTE_BLOCK_LIGHT_SHIFT + 4)));
|
||||
}
|
||||
|
||||
public static int formatLightAsInt(byte skyLight, byte blockLight)
|
||||
{
|
||||
return ((skyLight & SKY_LIGHT_MASK) << INT_SKY_LIGHT_SHIFT) | ((blockLight & BLOCK_LIGHT_MASK) << INT_BLOCK_LIGHT_SHIFT);
|
||||
}
|
||||
|
||||
public static int convertByteToIntFormat(byte lights)
|
||||
{
|
||||
return formatLightAsInt((byte) ((lights >>> BYTE_SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK), (byte) ((lights >>> BYTE_BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK));
|
||||
}
|
||||
|
||||
public static byte getSkyLight(byte lights)
|
||||
{
|
||||
return (byte) ((lights >>> BYTE_SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
|
||||
}
|
||||
|
||||
public static byte getBlockLight(byte lights)
|
||||
{
|
||||
return (byte) ((lights >>> BYTE_BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.dataFormat;
|
||||
|
||||
public class PositionDataFormat
|
||||
{
|
||||
public final static byte LOD_COUNT_SHIFT = 6;
|
||||
public final static byte CORRECT_LIGHT_SHIFT = 5;
|
||||
public final static byte GEN_TYPE_SHIFT = 2;
|
||||
public final static byte VOID_SHIFT = 1;
|
||||
public final static byte EXISTENCE_SHIFT = 0;
|
||||
|
||||
//We are able to count up to 64 different lods in a column
|
||||
public final static short LOD_COUNT_MASK = 0b11_1111;
|
||||
public final static short CORRECT_LIGHT_MASK = 0b1;
|
||||
public final static short GEN_TYPE_MASK = 0b111;
|
||||
public final static short VOID_MASK = 0b1;
|
||||
public final static short EXISTENCE_MASK = 0b1;
|
||||
|
||||
public final static int EMPTY_DATA = 0;
|
||||
public final static int VOID_DATA = VOID_MASK<<VOID_SHIFT + EXISTENCE_MASK<<EXISTENCE_SHIFT;
|
||||
|
||||
public static short createVoidPositionData(byte generationMode)
|
||||
{
|
||||
short positionData = 0;
|
||||
positionData |= (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
|
||||
positionData |= VOID_MASK << VOID_SHIFT;
|
||||
positionData |= EXISTENCE_MASK << EXISTENCE_SHIFT;
|
||||
|
||||
return positionData;
|
||||
}
|
||||
|
||||
public static short createPositionData(int lodCount, boolean correctLight, byte generationMode)
|
||||
{
|
||||
short positionData = 0;
|
||||
positionData |= (lodCount & LOD_COUNT_MASK) << LOD_COUNT_SHIFT;
|
||||
positionData |= (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
|
||||
if (correctLight)
|
||||
positionData |= CORRECT_LIGHT_MASK << CORRECT_LIGHT_SHIFT;
|
||||
positionData |= EXISTENCE_MASK << EXISTENCE_SHIFT;
|
||||
|
||||
return positionData;
|
||||
}
|
||||
|
||||
public static byte getLodCount(short dataPoint)
|
||||
{
|
||||
return (byte) ((dataPoint >>> LOD_COUNT_SHIFT) & LOD_COUNT_MASK);
|
||||
}
|
||||
public static boolean getFlag(short dataPoint)
|
||||
{
|
||||
return ((dataPoint >>> CORRECT_LIGHT_SHIFT) & CORRECT_LIGHT_MASK) == 1;
|
||||
}
|
||||
public static byte getGenerationMode(short dataPoint)
|
||||
{
|
||||
return (byte) ((dataPoint >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK);
|
||||
}
|
||||
public static boolean isVoid(short dataPoint)
|
||||
{
|
||||
return (((dataPoint >>> VOID_SHIFT) & VOID_MASK) == 1);
|
||||
}
|
||||
public static boolean doesItExist(short dataPoint)
|
||||
{
|
||||
return (((dataPoint >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1);
|
||||
}
|
||||
|
||||
public static short setLodCount(short dataPoint, short lodCount)
|
||||
{
|
||||
return (short) (dataPoint | ((lodCount & LOD_COUNT_MASK) << LOD_COUNT_SHIFT));
|
||||
}
|
||||
public static short setFlag(short dataPoint)
|
||||
{
|
||||
return (short) (dataPoint | ((CORRECT_LIGHT_MASK) << CORRECT_LIGHT_SHIFT));
|
||||
}
|
||||
public static short setGenerationMode(short dataPoint, byte generationMode)
|
||||
{
|
||||
return (short) (dataPoint | ((generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT));
|
||||
}
|
||||
public static short setVoid(short dataPoint)
|
||||
{
|
||||
return (short) (dataPoint | (VOID_MASK << VOID_SHIFT));
|
||||
}
|
||||
public static short setExistence(short dataPoint)
|
||||
{
|
||||
return (short) (dataPoint | (EXISTENCE_MASK << EXISTENCE_SHIFT));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.dataFormat;
|
||||
|
||||
public class VerticalDataFormat
|
||||
{
|
||||
public final static short MIN_WORLD_HEIGHT = -2048;
|
||||
public final static short MAX_WORLD_HEIGHT = 2047;
|
||||
|
||||
public final static byte HEIGHT_SHIFT = 20;
|
||||
public final static byte DEPTH_SHIFT = 8;
|
||||
public final static byte LEVEL_SHIFT = 3;
|
||||
public final static byte BOTTOM_TYPE_SHIFT = 2;
|
||||
public final static byte TRANSPARENCY_SHIFT = 1;
|
||||
public final static byte EXISTENCE_SHIFT = 0;
|
||||
|
||||
|
||||
public final static int FULL_MASK = ~0;
|
||||
|
||||
public final static int HEIGHT_MASK = 0b1111_1111_1111;
|
||||
public final static int DEPTH_MASK = 0b1111_1111_1111;
|
||||
public final static int LEVEL_MASK = 0b111;
|
||||
public final static int TRANSPARENCY_MASK = 0b1;
|
||||
public final static int BOTTOM_TYPE_MASK = 0b1;
|
||||
public final static int EXISTENCE_MASK = 0b1;
|
||||
|
||||
|
||||
public final static int HEIGHT_RESET = ~(HEIGHT_MASK << HEIGHT_SHIFT);
|
||||
public final static int DEPTH_RESET = ~(DEPTH_MASK << DEPTH_SHIFT);
|
||||
public final static int LEVEL_RESET = ~(LEVEL_MASK << LEVEL_SHIFT);
|
||||
public final static int TRANSPARENCY_RESET = ~(TRANSPARENCY_MASK << BOTTOM_TYPE_SHIFT);
|
||||
public final static int BOTTOM_TYPE_RESET = ~(BOTTOM_TYPE_MASK << TRANSPARENCY_SHIFT);
|
||||
public final static int EXISTENCE_RESET = ~(EXISTENCE_MASK << EXISTENCE_SHIFT);
|
||||
|
||||
public final static int EMPTY_LOD = 0;
|
||||
|
||||
|
||||
public static int createVerticalData(int height, int depth, int level, boolean transparent, boolean bottom)
|
||||
{
|
||||
int verticalData = 0;
|
||||
verticalData |= (height & HEIGHT_MASK) << HEIGHT_SHIFT;
|
||||
verticalData |= (depth & DEPTH_MASK) << DEPTH_SHIFT;
|
||||
verticalData |= (level & LEVEL_MASK) << LEVEL_SHIFT;
|
||||
if (bottom)
|
||||
verticalData |= BOTTOM_TYPE_MASK << BOTTOM_TYPE_SHIFT;
|
||||
if (transparent)
|
||||
verticalData |= TRANSPARENCY_MASK << TRANSPARENCY_SHIFT;
|
||||
verticalData |= EXISTENCE_MASK << EXISTENCE_SHIFT;
|
||||
|
||||
return verticalData;
|
||||
}
|
||||
|
||||
public static short getHeight(int verticalData)
|
||||
{
|
||||
return (short) ((verticalData >>> HEIGHT_SHIFT) & HEIGHT_MASK);
|
||||
}
|
||||
|
||||
public static short getDepth(int verticalData)
|
||||
{
|
||||
return (short) ((verticalData >>> DEPTH_SHIFT) & DEPTH_MASK);
|
||||
}
|
||||
|
||||
public static byte getLevel(int verticalData)
|
||||
{
|
||||
return (byte) ((verticalData >>> LEVEL_SHIFT) & LEVEL_MASK);
|
||||
}
|
||||
|
||||
public static boolean isTransparent(int verticalData)
|
||||
{
|
||||
return ((verticalData >>> TRANSPARENCY_SHIFT) & TRANSPARENCY_MASK) == 1;
|
||||
}
|
||||
|
||||
public static boolean isBottom(int verticalData)
|
||||
{
|
||||
return ((verticalData >>> BOTTOM_TYPE_SHIFT) & BOTTOM_TYPE_MASK) == 1;
|
||||
}
|
||||
|
||||
public static boolean doesItExist(int verticalData)
|
||||
{
|
||||
return (((verticalData >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1);
|
||||
}
|
||||
|
||||
|
||||
public static int setHeight(int verticalData, int height)
|
||||
{
|
||||
return verticalData | ((height & HEIGHT_MASK) << HEIGHT_SHIFT);
|
||||
}
|
||||
|
||||
public static int setDepth(int verticalData, int depth)
|
||||
{
|
||||
return verticalData | ((depth & DEPTH_MASK) << DEPTH_SHIFT);
|
||||
}
|
||||
|
||||
public static int setLevel(int verticalData, int level)
|
||||
{
|
||||
return verticalData | ((level & LEVEL_MASK) << LEVEL_SHIFT);
|
||||
}
|
||||
|
||||
public static int setTransparency(int verticalData)
|
||||
{
|
||||
return verticalData | ((TRANSPARENCY_MASK) << TRANSPARENCY_SHIFT);
|
||||
}
|
||||
|
||||
public static int setBottom(int verticalData)
|
||||
{
|
||||
return verticalData | ((BOTTOM_TYPE_MASK) << BOTTOM_TYPE_SHIFT);
|
||||
}
|
||||
|
||||
public static int setExistence(int verticalData)
|
||||
{
|
||||
return verticalData | ((EXISTENCE_MASK) << EXISTENCE_SHIFT);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -6,16 +25,21 @@ import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.seibel.lod.core.objects.math.Vec3i;
|
||||
|
||||
/**
|
||||
* A (almost) exact copy of Minecraft's
|
||||
* An (almost) exact copy of Minecraft's
|
||||
* Direction enum.
|
||||
*
|
||||
*
|
||||
* Up <Br>
|
||||
* Down <Br>
|
||||
* North <Br>
|
||||
* South <Br>
|
||||
* East <Br>
|
||||
* West <Br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
* @version 2021-11-13
|
||||
*/
|
||||
public enum LodDirection
|
||||
{
|
||||
@@ -25,7 +49,27 @@ public enum LodDirection
|
||||
SOUTH(3, 2, 0, "south", LodDirection.AxisDirection.POSITIVE, LodDirection.Axis.Z, new Vec3i(0, 0, 1)),
|
||||
WEST(4, 5, 1, "west", LodDirection.AxisDirection.NEGATIVE, LodDirection.Axis.X, new Vec3i(-1, 0, 0)),
|
||||
EAST(5, 4, 3, "east", LodDirection.AxisDirection.POSITIVE, LodDirection.Axis.X, new Vec3i(1, 0, 0));
|
||||
|
||||
public static final LodDirection[] DIRECTIONS = new LodDirection[] {
|
||||
LodDirection.UP,
|
||||
LodDirection.DOWN,
|
||||
LodDirection.WEST,
|
||||
LodDirection.EAST,
|
||||
LodDirection.NORTH,
|
||||
LodDirection.SOUTH };
|
||||
|
||||
public static final LodDirection[] OPPOSITE_DIRECTIONS = new LodDirection[] {
|
||||
LodDirection.UP,
|
||||
LodDirection.DOWN,
|
||||
LodDirection.SOUTH,
|
||||
LodDirection.NORTH,
|
||||
LodDirection.EAST,
|
||||
LodDirection.WEST };
|
||||
/** North, South, East, West */
|
||||
public static final LodDirection[] ADJ_DIRECTIONS = new LodDirection[] {
|
||||
LodDirection.EAST,
|
||||
LodDirection.WEST,
|
||||
LodDirection.SOUTH,
|
||||
LodDirection.NORTH };
|
||||
// private final int data3d;
|
||||
// private final int oppositeIndex;
|
||||
// private final int data2d;
|
||||
@@ -73,7 +117,7 @@ public enum LodDirection
|
||||
|
||||
|
||||
|
||||
private LodDirection(int p_i46016_3_, int p_i46016_4_, int p_i46016_5_, String p_i46016_6_, LodDirection.AxisDirection p_i46016_7_, LodDirection.Axis p_i46016_8_, Vec3i p_i46016_9_)
|
||||
LodDirection(int p_i46016_3_, int p_i46016_4_, int p_i46016_5_, String p_i46016_6_, LodDirection.AxisDirection p_i46016_7_, LodDirection.Axis p_i46016_8_, Vec3i p_i46016_9_)
|
||||
{
|
||||
// this.data3d = p_i46016_3_;
|
||||
// this.data2d = p_i46016_5_;
|
||||
@@ -227,9 +271,8 @@ public enum LodDirection
|
||||
{
|
||||
return this.axis;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static LodDirection byName(@Nullable String name)
|
||||
|
||||
public static LodDirection byName(String name)
|
||||
{
|
||||
return name == null ? null : BY_NAME.get(name.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
@@ -328,7 +371,7 @@ public enum LodDirection
|
||||
// return this.normal.getX() * f1 + this.normal.getZ() * f2 > 0.0F;
|
||||
// }
|
||||
|
||||
public static enum Axis implements Predicate<LodDirection>
|
||||
public enum Axis implements Predicate<LodDirection>
|
||||
{
|
||||
X("x")
|
||||
{
|
||||
@@ -381,12 +424,11 @@ public enum LodDirection
|
||||
}));
|
||||
private final String name;
|
||||
|
||||
private Axis(String name)
|
||||
Axis(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
public static LodDirection.Axis byName(String name)
|
||||
{
|
||||
return BY_NAME.get(name.toLowerCase(Locale.ROOT));
|
||||
@@ -419,7 +461,7 @@ public enum LodDirection
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean test(@Nullable LodDirection p_test_1_)
|
||||
public boolean test(LodDirection p_test_1_)
|
||||
{
|
||||
return p_test_1_ != null && p_test_1_.getAxis() == this;
|
||||
}
|
||||
@@ -443,7 +485,7 @@ public enum LodDirection
|
||||
public abstract double choose(double p_196051_1_, double p_196051_3_, double p_196051_5_);
|
||||
}
|
||||
|
||||
public static enum AxisDirection
|
||||
public enum AxisDirection
|
||||
{
|
||||
POSITIVE(1, "Towards positive"),
|
||||
NEGATIVE(-1, "Towards negative");
|
||||
@@ -451,7 +493,7 @@ public enum LodDirection
|
||||
private final int step;
|
||||
private final String name;
|
||||
|
||||
private AxisDirection(int newStep, String newName)
|
||||
AxisDirection(int newStep, String newName)
|
||||
{
|
||||
this.step = newStep;
|
||||
this.name = newName;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -31,20 +31,24 @@ package com.seibel.lod.core.enums.config;
|
||||
*/
|
||||
public enum BufferRebuildTimes
|
||||
{
|
||||
FREQUENT(1000, 500, 2500),
|
||||
CONSTANT(0, 0, 0, 1),
|
||||
|
||||
NORMAL(2000, 1000, 5000),
|
||||
FREQUENT(1000, 500, 2500, 1),
|
||||
|
||||
RARE(5000, 2000, 10000);
|
||||
NORMAL(2000, 1000, 5000, 4),
|
||||
|
||||
RARE(5000, 2000, 10000, 16);
|
||||
|
||||
public final int playerMoveTimeout;
|
||||
public final int renderedChunkTimeout;
|
||||
public final int chunkChangeTimeout;
|
||||
public final int playerMoveDistance;
|
||||
|
||||
BufferRebuildTimes(int playerMoveTimeout, int renderedChunkTimeout, int chunkChangeTimeout)
|
||||
BufferRebuildTimes(int playerMoveTimeout, int renderedChunkTimeout, int chunkChangeTimeout, int playerMoveDistance)
|
||||
{
|
||||
this.playerMoveTimeout = playerMoveTimeout;
|
||||
this.renderedChunkTimeout = renderedChunkTimeout;
|
||||
this.chunkChangeTimeout = chunkChangeTimeout;
|
||||
this.playerMoveDistance = playerMoveDistance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -36,9 +36,9 @@ package com.seibel.lod.core.enums.config;
|
||||
public enum DistanceGenerationMode
|
||||
{
|
||||
/**
|
||||
* Don't generate anything
|
||||
* Don't generate anything except just load in already existing chunks
|
||||
*/
|
||||
NONE((byte) 0),
|
||||
NONE((byte) 1),
|
||||
|
||||
/**
|
||||
* Only generate the biomes and use biome
|
||||
@@ -47,7 +47,7 @@ public enum DistanceGenerationMode
|
||||
* Doesn't generate height, everything is shown at sea level.
|
||||
* Multithreaded - Fastest (2-5 ms)
|
||||
*/
|
||||
BIOME_ONLY((byte) 1),
|
||||
BIOME_ONLY((byte) 2),
|
||||
|
||||
/**
|
||||
* Same as BIOME_ONLY, except instead
|
||||
@@ -55,7 +55,7 @@ public enum DistanceGenerationMode
|
||||
* different biome types (mountain, ocean, forest, etc.)
|
||||
* use predetermined heights to simulate having height data.
|
||||
*/
|
||||
BIOME_ONLY_SIMULATE_HEIGHT((byte) 2),
|
||||
BIOME_ONLY_SIMULATE_HEIGHT((byte) 3),
|
||||
|
||||
/**
|
||||
* Generate the world surface,
|
||||
@@ -63,7 +63,7 @@ public enum DistanceGenerationMode
|
||||
* or structures.
|
||||
* Multithreaded - Faster (10-20 ms)
|
||||
*/
|
||||
SURFACE((byte) 3),
|
||||
SURFACE((byte) 4),
|
||||
|
||||
/**
|
||||
* Generate everything except structures.
|
||||
@@ -71,7 +71,7 @@ public enum DistanceGenerationMode
|
||||
* since some features cause concurrentModification exceptions.
|
||||
* Multithreaded - Fast (15-20 ms)
|
||||
*/
|
||||
FEATURES((byte) 4),
|
||||
FEATURES((byte) 5),
|
||||
|
||||
/**
|
||||
* Ask the server to generate/load each chunk.
|
||||
@@ -80,8 +80,9 @@ public enum DistanceGenerationMode
|
||||
* are adding the mod on a pre-existing world.
|
||||
* Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms)
|
||||
*/
|
||||
FULL((byte) 5);
|
||||
FULL((byte) 6);
|
||||
|
||||
public static DistanceGenerationMode RENDERABLE = DistanceGenerationMode.BIOME_ONLY;
|
||||
|
||||
/**
|
||||
* The higher the number the more complete the generation is.
|
||||
@@ -92,4 +93,42 @@ public enum DistanceGenerationMode
|
||||
{
|
||||
this.complexity = complexity;
|
||||
}
|
||||
|
||||
// Note: return null if out of range
|
||||
public static DistanceGenerationMode previous(DistanceGenerationMode mode) {
|
||||
switch (mode) {
|
||||
case FULL:
|
||||
return DistanceGenerationMode.FEATURES;
|
||||
case FEATURES:
|
||||
return DistanceGenerationMode.SURFACE;
|
||||
case SURFACE:
|
||||
return DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
return DistanceGenerationMode.BIOME_ONLY;
|
||||
case BIOME_ONLY:
|
||||
return DistanceGenerationMode.NONE;
|
||||
case NONE:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: return null if out of range
|
||||
public static DistanceGenerationMode next(DistanceGenerationMode mode) {
|
||||
switch (mode) {
|
||||
case FEATURES:
|
||||
return DistanceGenerationMode.FULL;
|
||||
case SURFACE:
|
||||
return DistanceGenerationMode.FEATURES;
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
return DistanceGenerationMode.SURFACE;
|
||||
case BIOME_ONLY:
|
||||
return DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
|
||||
case NONE:
|
||||
return DistanceGenerationMode.BIOME_ONLY;
|
||||
case FULL:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-24
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2022 Tom Lee (TomTheFurry)
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -16,35 +17,33 @@
|
||||
* 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.core.wrapperInterfaces.block;
|
||||
|
||||
package com.seibel.lod.core.enums.config;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 11-17-2021
|
||||
* AUTO <br>
|
||||
* SMOOTH_DROPOFF <br>
|
||||
* PERFORMANCE_FOCUSED <br>
|
||||
* <br>
|
||||
* Determines how lod level drop off should be done
|
||||
*
|
||||
* @author Tom Lee
|
||||
* @version 7-1-2022
|
||||
*/
|
||||
public interface IBlockColorWrapper
|
||||
{
|
||||
//--------------//
|
||||
//Colors getters//
|
||||
//--------------//
|
||||
public enum DropoffQuality {
|
||||
|
||||
/** SMOOTH_DROPOFF when <128 lod view distance, or PERFORMANCE_FOCUSED otherwise */
|
||||
AUTO(-1),
|
||||
|
||||
public boolean hasColor();
|
||||
SMOOTH_DROPOFF(10),
|
||||
|
||||
public int getColor();
|
||||
PERFORMANCE_FOCUSED(0);
|
||||
|
||||
public final int fastModeSwitch;
|
||||
|
||||
//------------//
|
||||
//Tint getters//
|
||||
//------------//
|
||||
DropoffQuality(int fastModeSwitch) {
|
||||
this.fastModeSwitch = fastModeSwitch;
|
||||
}
|
||||
|
||||
public boolean hasTint();
|
||||
|
||||
public boolean hasGrassTint();
|
||||
|
||||
public boolean hasFolliageTint();
|
||||
|
||||
public boolean hasWaterTint();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -20,6 +20,7 @@
|
||||
package com.seibel.lod.core.enums.config;
|
||||
|
||||
/**
|
||||
* AUTO <br>
|
||||
* Near_First <br>
|
||||
* Far_First <br>
|
||||
* <br>
|
||||
@@ -27,11 +28,16 @@ package com.seibel.lod.core.enums.config;
|
||||
* outside the normal view distance.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 9-25-2021
|
||||
* @version 12-1-2021
|
||||
*/
|
||||
public enum GenerationPriority
|
||||
{
|
||||
/** NEAR_FIRST when connected to servers and BALANCED when on single player */
|
||||
AUTO,
|
||||
|
||||
NEAR_FIRST,
|
||||
|
||||
BALANCED,
|
||||
|
||||
FAR_FIRST
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -20,22 +20,51 @@
|
||||
package com.seibel.lod.core.enums.config;
|
||||
|
||||
/**
|
||||
* Buffer_Storage, Sub_Data, Buffer_Mapping
|
||||
* Auto, BUFFER_STORAGE_MAPPING, Buffer_Storage, Sub_Data, Buffer_Mapping, Data
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-21-2021
|
||||
* @version 12-1-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,
|
||||
/** Picks the best option based on the GPU the user has. */
|
||||
AUTO(false, false),
|
||||
|
||||
/** Fast rendering but will stutter when uploading. */
|
||||
DATA,
|
||||
/*
|
||||
*/
|
||||
BUFFER_STORAGE_MAPPING(true, true),
|
||||
|
||||
/**
|
||||
* Default for NVIDIA if OpenGL 4.5 is supported. <br>
|
||||
* Fast rendering, no stuttering.
|
||||
*/
|
||||
BUFFER_STORAGE(false, true),
|
||||
|
||||
/**
|
||||
* Backup option for NVIDIA. <br>
|
||||
* Fast rendering but may stutter when uploading.
|
||||
*/
|
||||
SUB_DATA(false, false),
|
||||
|
||||
/**
|
||||
* Default option for AMD/Intel. <br>
|
||||
* May end up storing buffers in System memory. <br>
|
||||
* Fast rending if in GPU memory, slow if in system memory, <br>
|
||||
* but won't stutter when uploading.
|
||||
*/
|
||||
BUFFER_MAPPING(true, false),
|
||||
|
||||
/**
|
||||
* Backup option for AMD/Intel. <br>
|
||||
* Fast rendering but may stutter when uploading.
|
||||
*/
|
||||
DATA(false, false);
|
||||
|
||||
public final boolean useEarlyMapping;
|
||||
public final boolean useBufferStorage;
|
||||
GpuUploadMethod(boolean useEarlyMapping, boolean useBufferStorage) {
|
||||
this.useEarlyMapping = useEarlyMapping;
|
||||
this.useBufferStorage = useBufferStorage;
|
||||
}
|
||||
|
||||
/** May end up storing buffers in System memory. Slower rendering but won't stutter when uploading. */
|
||||
BUFFER_MAPPING,
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.enums.config;
|
||||
|
||||
public enum LightGenerationMode
|
||||
{
|
||||
|
||||
// Fake in light values based on height maps
|
||||
FAST,
|
||||
|
||||
// Run the light engine though the chunk to generate proper light values
|
||||
FANCY
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 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.core.enums.config;
|
||||
|
||||
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.AbstractLodTemplate;
|
||||
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.CubicLodTemplate;
|
||||
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.DynamicLodTemplate;
|
||||
import com.seibel.lod.core.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,44 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.enums.config;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
public enum LoggerMode {
|
||||
DISABLED(Level.OFF, Level.OFF),
|
||||
LOG_ALL_TO_FILE(Level.ALL, Level.OFF),
|
||||
LOG_ERROR_TO_CHAT(Level.ALL, Level.ERROR),
|
||||
LOG_WARNING_TO_CHAT(Level.ALL, Level.WARN),
|
||||
LOG_INFO_TO_CHAT(Level.ALL, Level.INFO),
|
||||
LOG_DEBUG_TO_CHAT(Level.ALL, Level.DEBUG),
|
||||
LOG_ALL_TO_CHAT(Level.ALL, Level.ALL),
|
||||
LOG_ERROR_TO_CHAT_AND_FILE(Level.ERROR, Level.ERROR),
|
||||
LOG_WARNING_TO_CHAT_AND_FILE(Level.WARN, Level.WARN),
|
||||
LOG_INFO_TO_CHAT_AND_FILE(Level.INFO, Level.INFO),
|
||||
LOG_DEBUG_TO_CHAT_AND_FILE(Level.DEBUG, Level.DEBUG),
|
||||
LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE(Level.INFO, Level.WARN),
|
||||
LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE(Level.INFO, Level.ERROR),
|
||||
;
|
||||
public final Level levelForFile;
|
||||
public final Level levelForChat;
|
||||
LoggerMode(Level levelForFile, Level levelForChat) {
|
||||
this.levelForFile = levelForFile;
|
||||
this.levelForChat = levelForChat;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2022 Tom Lee (TomTheFurry)
|
||||
* Copyright (C) 2020-2022 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.core.enums.config;
|
||||
|
||||
/**
|
||||
* AUTO <br>
|
||||
* NAME_ONLY <br>
|
||||
* NAME_IP <br>
|
||||
* NAME_IP_PORT <br>
|
||||
* <br>
|
||||
* Determines how the multiplayer folders should be named.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 3-7-2022
|
||||
*/
|
||||
public enum ServerFolderNameMode
|
||||
{
|
||||
/**
|
||||
* NAME_IP for LAN connections <Br>
|
||||
* NAME_IP_PORT for all others
|
||||
*/
|
||||
AUTO,
|
||||
|
||||
/** Only use the server name */
|
||||
NAME_ONLY,
|
||||
|
||||
/**
|
||||
* {SERVER_NAME} IP {IP} <br>
|
||||
* Minecraft Server IP 192.168.1.40
|
||||
*/
|
||||
NAME_IP,
|
||||
|
||||
/**
|
||||
* {SERVER_NAME} IP {IP} <br>
|
||||
* Minecraft Server IP 192.168.1.40:25565
|
||||
*/
|
||||
NAME_IP_PORT,
|
||||
|
||||
/**
|
||||
* {SERVER_NAME} IP {IP} <br>
|
||||
* Minecraft Server IP 192.168.1.40:25565 GameVersion 1.16.5 <Br> <br>
|
||||
*
|
||||
* Not normally recommended, since the game version can change if the
|
||||
* server installs paper or some other jar. <br>
|
||||
* This is just here to provide backwards compatibility.
|
||||
*
|
||||
* TODO add this to config desc
|
||||
*/
|
||||
NAME_IP_PORT_MC_VERSION;
|
||||
|
||||
}
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.enums.config;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -31,15 +31,12 @@ package com.seibel.lod.core.enums.config;
|
||||
*/
|
||||
public enum VanillaOverdraw
|
||||
{
|
||||
/** Never draw LODs where a minecraft chunk could be. */
|
||||
/** Dont draw LODs where a minecraft chunk could be. Use Overdraw Offset to tweak the border thickness */
|
||||
NEVER,
|
||||
|
||||
/** Draw LODs over the farther minecraft chunks. */
|
||||
/** Draw LODs over the farther minecraft chunks. Dynamically decides the border thickness */
|
||||
DYNAMIC,
|
||||
|
||||
/** Draw LODs over all minecraft chunks. */
|
||||
ALWAYS,
|
||||
|
||||
/** Draw LODs over border chunks. */
|
||||
BORDER,
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -22,59 +22,98 @@ package com.seibel.lod.core.enums.config;
|
||||
/**
|
||||
* heightmap <br>
|
||||
* multi_lod <br>
|
||||
*
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 10-07-2021
|
||||
* @version 2022-3-26
|
||||
*/
|
||||
public enum VerticalQuality
|
||||
{
|
||||
LOW(
|
||||
new int[] { 2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1 }
|
||||
new int[] { 4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1 },
|
||||
2
|
||||
),
|
||||
|
||||
MEDIUM(
|
||||
new int[] { 4,
|
||||
4,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1 }
|
||||
new int[] { 6, 4, 3, 2, 2, 1, 1, 1, 1, 1, 1 },
|
||||
4
|
||||
),
|
||||
|
||||
HIGH(
|
||||
new int[] {
|
||||
8,
|
||||
8,
|
||||
4,
|
||||
4,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1 }
|
||||
new int[] { 8, 6, 4, 2, 2, 2, 2, 1, 1, 1, 1 },
|
||||
6
|
||||
),
|
||||
|
||||
ULTRA(
|
||||
new int[] { 16, 16, 8, 8, 4, 4, 4, 1, 1, 1, 1 },
|
||||
12
|
||||
);
|
||||
|
||||
public final int[] maxVerticalData;
|
||||
|
||||
VerticalQuality(int[] maxVerticalData)
|
||||
@Deprecated // Will find other ways to optimize
|
||||
public final int maxConnectedLods;
|
||||
|
||||
VerticalQuality(int[] maxVerticalData, int maxConnectedLods)
|
||||
{
|
||||
this.maxVerticalData = maxVerticalData;
|
||||
this.maxConnectedLods = maxConnectedLods;
|
||||
}
|
||||
|
||||
/** returns null if out of range */
|
||||
public static VerticalQuality previous(VerticalQuality mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ULTRA:
|
||||
return VerticalQuality.HIGH;
|
||||
case HIGH:
|
||||
return VerticalQuality.MEDIUM;
|
||||
case MEDIUM:
|
||||
return VerticalQuality.LOW;
|
||||
case LOW:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** returns null if out of range */
|
||||
public static VerticalQuality next(VerticalQuality mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case MEDIUM:
|
||||
return VerticalQuality.HIGH;
|
||||
case LOW:
|
||||
return VerticalQuality.MEDIUM;
|
||||
case HIGH:
|
||||
return VerticalQuality.ULTRA;
|
||||
case ULTRA:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value with the given name, case-insensitive. <br>
|
||||
* Returns null if no enums match the name. <br>
|
||||
* Similar to valueOf(String value)
|
||||
*/
|
||||
public static VerticalQuality getByName(String name)
|
||||
{
|
||||
switch (name.toUpperCase())
|
||||
{
|
||||
case "ULTRA":
|
||||
return VerticalQuality.ULTRA;
|
||||
case "HIGH":
|
||||
return VerticalQuality.HIGH;
|
||||
case "MEDIUM":
|
||||
return VerticalQuality.MEDIUM;
|
||||
case "LOW":
|
||||
return VerticalQuality.LOW;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -29,21 +29,41 @@ public enum DebugMode
|
||||
{
|
||||
/** LODs are rendered normally */
|
||||
OFF,
|
||||
|
||||
/** LOD draws in wireframe. */
|
||||
SHOW_WIREFRAME,
|
||||
|
||||
/** LOD colors are based on their detail */
|
||||
SHOW_DETAIL,
|
||||
|
||||
/** LOD colors are based on their detail, and draws in wireframe. */
|
||||
SHOW_DETAIL_WIREFRAME;
|
||||
SHOW_DETAIL_WIREFRAME,
|
||||
|
||||
/** LOD colors are based on their gen mode. */
|
||||
SHOW_GENMODE,
|
||||
|
||||
/** LOD colors are based on their gen mode, and draws in wireframe. */
|
||||
SHOW_GENMODE_WIREFRAME,
|
||||
|
||||
/** Only draw overlapping LOD quads. */
|
||||
SHOW_OVERLAPPING_QUADS,
|
||||
|
||||
/** Only draw overlapping LOD quads, and draws in wireframe. */
|
||||
SHOW_OVERLAPPING_QUADS_WIREFRAME;
|
||||
|
||||
/** used when cycling through the different modes */
|
||||
private DebugMode next;
|
||||
|
||||
static
|
||||
{
|
||||
OFF.next = SHOW_DETAIL;
|
||||
OFF.next = SHOW_WIREFRAME;
|
||||
SHOW_WIREFRAME.next = SHOW_DETAIL;
|
||||
SHOW_DETAIL.next = SHOW_DETAIL_WIREFRAME;
|
||||
SHOW_DETAIL_WIREFRAME.next = OFF;
|
||||
SHOW_DETAIL_WIREFRAME.next = SHOW_GENMODE;
|
||||
SHOW_GENMODE.next = SHOW_GENMODE_WIREFRAME;
|
||||
SHOW_GENMODE_WIREFRAME.next = SHOW_OVERLAPPING_QUADS;
|
||||
SHOW_OVERLAPPING_QUADS.next = SHOW_OVERLAPPING_QUADS_WIREFRAME;
|
||||
SHOW_OVERLAPPING_QUADS_WIREFRAME.next = OFF;
|
||||
}
|
||||
|
||||
/** returns the next debug mode */
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.enums.rendering;
|
||||
|
||||
/**
|
||||
* USE_DEFAULT_FOG_COLOR, <br>
|
||||
* USE_SKY_COLOR, <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-27-2021
|
||||
*/
|
||||
public enum FogColorMode
|
||||
{
|
||||
/** Fog uses Minecraft's fog color. */
|
||||
USE_WORLD_FOG_COLOR,
|
||||
|
||||
/**
|
||||
* Replicates the effect of the clear sky mod.
|
||||
* Making the fog blend in with the sky better
|
||||
* https://www.curseforge.com/minecraft/mc-mods/clear-skies
|
||||
* https://www.curseforge.com/minecraft/mc-mods/clear-skies-forge-port
|
||||
* For it to look good you need one of those mods
|
||||
*/
|
||||
USE_SKY_COLOR,
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -23,16 +23,11 @@ package com.seibel.lod.core.enums.rendering;
|
||||
* NEAR, FAR, or NEAR_AND_FAR.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-14-2021
|
||||
* @version 11-26-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
|
||||
}
|
||||
+10
-17
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -21,27 +21,20 @@ package com.seibel.lod.core.enums.rendering;
|
||||
|
||||
/**
|
||||
* USE_OPTIFINE_FOG_SETTING, <br>
|
||||
* NEVER_DRAW_FOG, <br>
|
||||
* ALWAYS_DRAW_FOG_FAST, <br>
|
||||
* ALWAYS_DRAW_FOG_FANCY <br>
|
||||
* FOG_ENABLED, <br>
|
||||
* FOG_DISABLED <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-3-2021
|
||||
* @version 11-27-2021
|
||||
*/
|
||||
public enum FogDrawOverride
|
||||
public enum FogDrawMode
|
||||
{
|
||||
/**
|
||||
* Use whatever Fog setting optifine is using.
|
||||
* If optifine isn't installed this defaults to ALWAYS_DRAW_FOG.
|
||||
*/
|
||||
OPTIFINE_SETTING,
|
||||
USE_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
|
||||
}
|
||||
FOG_ENABLED,
|
||||
FOG_DISABLED
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.enums.rendering;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Contains all configurable options related to fog.
|
||||
*
|
||||
* @version 2022-4-13
|
||||
*/
|
||||
public class FogSetting
|
||||
{
|
||||
/** a FogSetting object with 0 for every value */
|
||||
public static final FogSetting EMPTY = new FogSetting(0, 0, 0, 0,0, FogSetting.FogType.LINEAR);
|
||||
|
||||
|
||||
public final double start;
|
||||
public final double end;
|
||||
public final double min;
|
||||
public final double max;
|
||||
public final double density;
|
||||
public final FogType fogType;
|
||||
|
||||
public FogSetting(double start, double end, double min, double max, double density, FogType fogType)
|
||||
{
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.density = density;
|
||||
this.fogType = fogType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
FogSetting that = (FogSetting) o;
|
||||
return Double.compare(that.start, start) == 0 && Double.compare(that.end, end) == 0 && Double.compare(that.min, min) == 0 && Double.compare(that.max, max) == 0 && Double.compare(that.density, density) == 0 && fogType == that.fogType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(start, end, min, max, density, fogType);
|
||||
}
|
||||
|
||||
public enum FogType
|
||||
{
|
||||
LINEAR,
|
||||
EXPONENTIAL,
|
||||
EXPONENTIAL_SQUARED,
|
||||
// TEXTURE_BASED, // TODO: Impl this
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.enums.rendering;
|
||||
|
||||
/**
|
||||
* basic <br>
|
||||
* Ignore_Height <br>
|
||||
* Addition <br>
|
||||
* Max <br>
|
||||
* Multiply <br>
|
||||
* Inverse_Multiply <br>
|
||||
* Limited_Addition <br>
|
||||
* Multiply_Addition <br>
|
||||
* Inverse_Multiply_Addition <br>
|
||||
* Average <br>
|
||||
*
|
||||
* @author Leetom
|
||||
* @version 2022-4-14
|
||||
*/
|
||||
public enum HeightFogMixMode
|
||||
{
|
||||
BASIC,
|
||||
IGNORE_HEIGHT,
|
||||
ADDITION,
|
||||
MAX,
|
||||
MULTIPLY,
|
||||
INVERSE_MULTIPLY,
|
||||
LIMITED_ADDITION,
|
||||
MULTIPLY_ADDITION,
|
||||
INVERSE_MULTIPLY_ADDITION,
|
||||
AVERAGE,
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.enums.rendering;
|
||||
|
||||
public enum HeightFogMode
|
||||
{
|
||||
ABOVE_CAMERA(true, true, false),
|
||||
BELOW_CAMERA(true, false, true),
|
||||
ABOVE_AND_BELOW_CAMERA(true, true, true),
|
||||
ABOVE_SET_HEIGHT(false, true, false),
|
||||
BELOW_SET_HEIGHT(false, false, true),
|
||||
ABOVE_AND_BELOW_SET_HEIGHT(false, true, true);
|
||||
|
||||
public final boolean basedOnCamera;
|
||||
public final boolean above;
|
||||
public final boolean below;
|
||||
|
||||
HeightFogMode(boolean basedOnCamera, boolean above, boolean below)
|
||||
{
|
||||
this.basedOnCamera = basedOnCamera;
|
||||
this.above = above;
|
||||
this.below = below;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.enums.rendering;
|
||||
|
||||
public enum RendererType {
|
||||
DEFAULT,
|
||||
DEBUG,
|
||||
DISABLED,
|
||||
;
|
||||
|
||||
public static RendererType next(RendererType type) {
|
||||
switch (type) {
|
||||
case DEFAULT: return DEBUG;
|
||||
case DEBUG: return DISABLED;
|
||||
default: return DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
public static RendererType previous(RendererType type) {
|
||||
switch (type) {
|
||||
case DEFAULT: return DISABLED;
|
||||
case DEBUG: return DEFAULT;
|
||||
default: return DEBUG;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -19,8 +19,8 @@
|
||||
|
||||
package com.seibel.lod.core.handlers;
|
||||
|
||||
import com.seibel.lod.core.enums.rendering.FogQuality;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.enums.rendering.FogDrawMode;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.IBindable;
|
||||
|
||||
/**
|
||||
* A singleton used to get variables from methods
|
||||
@@ -35,24 +35,18 @@ import com.seibel.lod.core.objects.math.Mat4f;
|
||||
* different MC versions.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
* @version 3-5-2022
|
||||
*/
|
||||
public interface IReflectionHandler
|
||||
public interface IReflectionHandler extends IBindable
|
||||
{
|
||||
/** @returns the type of fog optifine is currently set to render. */
|
||||
public FogQuality getFogQuality();
|
||||
/** @returns Whether Optifine is set to render fog or not. */
|
||||
FogDrawMode getFogDrawMode();
|
||||
|
||||
/** @returns if Vivecraft is present. Attempts to find the "VRRenderer" class. */
|
||||
public boolean vivecraftPresent();
|
||||
boolean vivecraftPresent();
|
||||
|
||||
/**
|
||||
* Modifies the projection matrix's clip planes.
|
||||
* The projection matrix must be in column-major format.
|
||||
*
|
||||
* @param projectionMatrix The projection matrix to be modified.
|
||||
* @param newNearClipPlane the new near clip plane value.
|
||||
* @param newFarClipPlane the new far clip plane value.
|
||||
* @return The modified matrix.
|
||||
*/
|
||||
public Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane);
|
||||
/** @returns if Sodium (or a sodium like) mod is present. Attempts to find the "SodiumWorldRenderer" class. */
|
||||
boolean sodiumPresent();
|
||||
|
||||
boolean optifinePresent();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -21,24 +21,38 @@ package com.seibel.lod.core.handlers;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.logging.ConfigBasedLogger;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
|
||||
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||
import com.seibel.lod.core.objects.lod.LevelContainer;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.lod.LodRegion;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.objects.lod.VerticalLevelContainer;
|
||||
import com.seibel.lod.core.util.LodThreadFactory;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
import com.seibel.lod.core.logging.SpamReducedLogger;
|
||||
import com.seibel.lod.core.util.UnitBytes;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
|
||||
/**
|
||||
* This object handles creating LodRegions
|
||||
@@ -47,22 +61,31 @@ import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
*
|
||||
* @author James Seibel
|
||||
* @author Cola
|
||||
* @version 9-25-2021
|
||||
* @version 2022-3-30
|
||||
*/
|
||||
public class LodDimensionFileHandler
|
||||
{
|
||||
/** This is the dimension that owns this file handler */
|
||||
private LodDimension lodDimension;
|
||||
private static final ILodConfigWrapperSingleton config = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
public static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(LodDimensionFileHandler.class),
|
||||
() -> config.client().advanced().debugging().debugSwitch().getLogFileReadWriteEvent());
|
||||
|
||||
private final File dimensionDataSaveFolder;
|
||||
public static final boolean ENABLE_SAVE_THREAD_LOGGING = true;
|
||||
public static final boolean ENABLE_SAVE_REGION_LOGGING = false;
|
||||
|
||||
/** This is the dimension that owns this file handler */
|
||||
private final LodDimension lodDimension;
|
||||
|
||||
public final File dimensionDataSaveFolder;
|
||||
|
||||
/** lod */
|
||||
private static final String FILE_NAME_PREFIX = "lod";
|
||||
/** .txt */
|
||||
/** .xz */
|
||||
private static final String FILE_EXTENSION = ".xz";
|
||||
/** detail- */
|
||||
private static final String DETAIL_FOLDER_NAME_PREFIX = "detail-";
|
||||
|
||||
public static final String MULTIPLAYER_FOLDER_NAME = "Distant_Horizons_server_data";
|
||||
|
||||
/**
|
||||
* .tmp <br>
|
||||
* Added to the end of the file path when saving to prevent
|
||||
@@ -72,20 +95,29 @@ public class LodDimensionFileHandler
|
||||
*/
|
||||
private static final String TMP_FILE_EXTENSION = ".tmp";
|
||||
|
||||
/**
|
||||
* This is compression level set in XZ
|
||||
* lower values are faster
|
||||
* difference in size should be only 2x
|
||||
*/
|
||||
private static final int COMPRESSION_LEVEL = 1;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
public static final int LOD_SAVE_FILE_VERSION = 9;
|
||||
|
||||
/**
|
||||
* Allow saving asynchronously, but never try to save multiple regions
|
||||
* at a time
|
||||
*/
|
||||
private final ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
|
||||
|
||||
private final AtomicBoolean isFileWritingThreadRunning = new AtomicBoolean(false);
|
||||
private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(
|
||||
new LodThreadFactory(this.getClass().getSimpleName(), Thread.NORM_PRIORITY + 1));
|
||||
|
||||
private final ConcurrentHashMap<RegionPos, LodRegion> regionToSave = new ConcurrentHashMap<RegionPos, LodRegion>();
|
||||
|
||||
|
||||
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLodDimension)
|
||||
@@ -95,6 +127,66 @@ public class LodDimensionFileHandler
|
||||
|
||||
dimensionDataSaveFolder = newSaveFolder;
|
||||
lodDimension = newLodDimension;
|
||||
|
||||
checkForOldSaveStructure();
|
||||
}
|
||||
|
||||
private ReentrantLock mergeOldFileLock = new ReentrantLock();
|
||||
|
||||
private void checkForOldSaveStructure()
|
||||
{
|
||||
File file = new File(getFileBasePath());
|
||||
if (!file.exists())
|
||||
return;
|
||||
|
||||
File[] vertQualFiles = file.listFiles();
|
||||
for (File vertQualFile : vertQualFiles)
|
||||
{
|
||||
if (!vertQualFile.isDirectory())
|
||||
continue;
|
||||
|
||||
if (vertQualFile.getName().equals(VerticalQuality.HIGH.toString()) ||
|
||||
vertQualFile.getName().equals(VerticalQuality.MEDIUM.toString()) ||
|
||||
vertQualFile.getName().equals(VerticalQuality.LOW.toString()))
|
||||
{
|
||||
File[] subFiles = vertQualFile.listFiles();
|
||||
for (File subFile : subFiles)
|
||||
{
|
||||
if (!subFile.isDirectory())
|
||||
continue;
|
||||
|
||||
if (subFile.getName().equals(DistanceGenerationMode.FULL.toString()) ||
|
||||
subFile.getName().equals(DistanceGenerationMode.FEATURES.toString()) ||
|
||||
subFile.getName().equals(DistanceGenerationMode.SURFACE.toString()) ||
|
||||
subFile.getName().equals(DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString()) ||
|
||||
subFile.getName().equals(DistanceGenerationMode.BIOME_ONLY.toString()) ||
|
||||
subFile.getName().equals(DistanceGenerationMode.NONE.toString()))
|
||||
{
|
||||
LOGGER.info("Noticed old save structure files. Starting merge process...");
|
||||
LodDimensionOldFileStructureHandler oldFileStructHandler = new LodDimensionOldFileStructureHandler(this);
|
||||
if (mergeOldFileLock.tryLock())
|
||||
{
|
||||
// I got the lock to merge file.
|
||||
LOGGER.info("Updating VerticalQuality LOW...");
|
||||
oldFileStructHandler.mergeOldFileStructureForVertQuality(VerticalQuality.LOW);
|
||||
LOGGER.info("Updating VerticalQuality MEDIUM...");
|
||||
oldFileStructHandler.mergeOldFileStructureForVertQuality(VerticalQuality.MEDIUM);
|
||||
LOGGER.info("Updating VerticalQuality HIGH...");
|
||||
oldFileStructHandler.mergeOldFileStructureForVertQuality(VerticalQuality.HIGH);
|
||||
LOGGER.info("Update completed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Someone is already doing it. I just need to wait until they are done.
|
||||
mergeOldFileLock.lock();
|
||||
mergeOldFileLock.unlock();
|
||||
}
|
||||
LOGGER.info("Merge process completed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,127 +196,113 @@ public class LodDimensionFileHandler
|
||||
//================//
|
||||
|
||||
/**
|
||||
* Returns the LodRegion at the given coordinates.
|
||||
* Returns a new 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)
|
||||
public LodRegion loadRegionFromFile(byte detailLevel, RegionPos regionPos, 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--)
|
||||
// Get one from the region hot cache
|
||||
LodRegion region = regionToSave.get(regionPos);
|
||||
if (region != null && region.getMinDetailLevel() <= detailLevel &&
|
||||
region.getVerticalQuality().compareTo(verticalQuality) >= 0)
|
||||
return region; // The current hot cache to-be-saved region match our requirement.
|
||||
region = new LodRegion((byte) (LodUtil.REGION_DETAIL_LEVEL + 1), regionPos, verticalQuality);
|
||||
return loadRegionFromFile(detailLevel, region, verticalQuality);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LodRegion that is filled at the given coordinates.
|
||||
* Returns an empty region if the file doesn't exist.
|
||||
*/
|
||||
public LodRegion loadRegionFromFile(byte detailLevel, LodRegion region, VerticalQuality verticalQuality)
|
||||
{
|
||||
if (region.getVerticalQuality().compareTo(verticalQuality) < 0)
|
||||
{
|
||||
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.FULL;
|
||||
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.FULL)
|
||||
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();
|
||||
ClientApi.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();
|
||||
ClientApi.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)
|
||||
{
|
||||
ClientApi.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
|
||||
ClientApi.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + e.getMessage() + "]: ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}// for each detail level
|
||||
regionToSave.put(region.getRegionPos(), region); //FIXME: The hashMap key should prob be a {regionPos,VertQual} pair.
|
||||
region = new LodRegion((byte) (LodUtil.REGION_DETAIL_LEVEL + 1), region.getRegionPos(), verticalQuality);
|
||||
}
|
||||
int regionX = region.regionPosX;
|
||||
int regionZ = region.regionPosZ;
|
||||
|
||||
if (region.getMinDetailLevel() >= detailLevel)
|
||||
region.growTree(detailLevel);
|
||||
for (byte tempDetailLevel = (byte) (region.getMinDetailLevel() - 1); tempDetailLevel >= detailLevel; tempDetailLevel--)
|
||||
{
|
||||
|
||||
File file = getBestMatchingRegionFile(tempDetailLevel, regionX, regionZ, verticalQuality);
|
||||
if (file == null)
|
||||
{
|
||||
region.addLevelContainer(new VerticalLevelContainer(tempDetailLevel));
|
||||
continue; // Failed to find the file for this detail level. continue and try next one
|
||||
}
|
||||
|
||||
long fileSize = file.length();
|
||||
if (fileSize == 0)
|
||||
{
|
||||
region.addLevelContainer(new VerticalLevelContainer(tempDetailLevel));
|
||||
continue; // file is empty. Let's not try parsing empty files
|
||||
}
|
||||
try (FileInputStream fileInStream = new FileInputStream(file))
|
||||
{
|
||||
XZCompressorInputStream inputStream = new XZCompressorInputStream(fileInStream);
|
||||
int fileVersion;
|
||||
fileVersion = inputStream.read();
|
||||
|
||||
// check if this file can be read by this file handler
|
||||
if (fileVersion < 6)
|
||||
{
|
||||
// the file we are reading is an older version,
|
||||
// close the reader and delete the file.
|
||||
inputStream.close();
|
||||
file.delete();
|
||||
LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ")[" + tempDetailLevel + "]"
|
||||
+ " version found: " + fileVersion
|
||||
+ ", version requested: " + LOD_SAVE_FILE_VERSION
|
||||
+ ". File has been deleted.");
|
||||
// This should not break, but be continue to see whether other detail levels can be loaded or updated
|
||||
region.addLevelContainer(new VerticalLevelContainer(tempDetailLevel));
|
||||
}
|
||||
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();
|
||||
LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ")[" + tempDetailLevel + "]"
|
||||
+ " version found: " + fileVersion
|
||||
+ ", version requested: " + LOD_SAVE_FILE_VERSION
|
||||
+ " this region will not be written to in order to protect the newer file.");
|
||||
// This should not break, but be continue to see whether other detail levels can be loaded or updated
|
||||
region.addLevelContainer(new VerticalLevelContainer(tempDetailLevel));
|
||||
}
|
||||
else if (fileVersion < LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
LOGGER.info("Old LOD region file for region: (" + regionX + "," + regionZ + ")[" + tempDetailLevel + "]"
|
||||
+ " version found: " + fileVersion
|
||||
+ ", version requested: " + LOD_SAVE_FILE_VERSION
|
||||
+ ". File will be loaded and updated to new format in next save.");
|
||||
// this is old, but readable version
|
||||
// read and add the data to our region
|
||||
DataInputStream dataStream = new DataInputStream(inputStream);
|
||||
region.addLevelContainer(new VerticalLevelContainer(dataStream, fileVersion, tempDetailLevel));
|
||||
dataStream.close();
|
||||
inputStream.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.debug("Loading LOD region file for region: (" + regionX + "," + regionZ + ")[" + tempDetailLevel + "]");
|
||||
// this file is a readable version,
|
||||
// read and add the data to our region
|
||||
DataInputStream dataStream = new DataInputStream(inputStream);
|
||||
region.addLevelContainer(new VerticalLevelContainer(dataStream, LOD_SAVE_FILE_VERSION, tempDetailLevel));
|
||||
dataStream.close();
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ioEx)
|
||||
{
|
||||
LOGGER.error("LOD file read error. Unable to read xz compressed file [" + file + "]: ", ioEx);
|
||||
region.addLevelContainer(new VerticalLevelContainer(tempDetailLevel));
|
||||
}
|
||||
} // for each detail level
|
||||
|
||||
return region;
|
||||
}
|
||||
@@ -234,33 +312,226 @@ public class LodDimensionFileHandler
|
||||
// Save to File //
|
||||
//==============//
|
||||
|
||||
/** Save all dirty regions in this LodDimension to file */
|
||||
public void saveDirtyRegionsToFileAsync()
|
||||
{
|
||||
fileWritingThreadPool.execute(saveDirtyRegionsThread);
|
||||
}
|
||||
|
||||
private final Thread saveDirtyRegionsThread = new Thread(() ->
|
||||
public void saveDirect(int posX, int posZ, VerticalQuality vertQual, VerticalLevelContainer dataContainer)
|
||||
{
|
||||
File file = new File(getFileBasePath() + vertQual + File.separatorChar +
|
||||
DETAIL_FOLDER_NAME_PREFIX + dataContainer.detailLevel + File.separatorChar +
|
||||
FILE_NAME_PREFIX + "." + posX + "." + posZ + FILE_EXTENSION);
|
||||
if (file.exists())
|
||||
{
|
||||
LOGGER.warn("LOD file write warn. Unable to write [" + file + "] because the newer version file already exist! Skipping this position...");
|
||||
return;
|
||||
}
|
||||
if (!file.getParentFile().exists())
|
||||
file.getParentFile().mkdirs();
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < lodDimension.getWidth(); i++)
|
||||
file.createNewFile();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.error("LOD file write error. Unable to create parent directory for [" + file + "]: ", e);
|
||||
return;
|
||||
}
|
||||
try (FileOutputStream fileOutStream = new FileOutputStream(file))
|
||||
{
|
||||
XZCompressorOutputStream outputStream = new XZCompressorOutputStream(fileOutStream, COMPRESSION_LEVEL);
|
||||
// add the version of this file
|
||||
outputStream.write(LOD_SAVE_FILE_VERSION);
|
||||
// add each LodChunk to the file
|
||||
DataOutputStream dataStream = new DataOutputStream(outputStream);
|
||||
dataContainer.writeData(dataStream);
|
||||
dataStream.close();
|
||||
outputStream.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.error("LOD file write error. Unable to write to temp file [" + file + "]: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void addRegionsToSave(LodRegion r)
|
||||
{
|
||||
regionToSave.put(r.getRegionPos(), r);
|
||||
}
|
||||
|
||||
private final SpamReducedLogger ramLogger = new SpamReducedLogger(1);
|
||||
|
||||
public void dumpBufferMemoryUsage()
|
||||
{
|
||||
if (!ramLogger.canMaybeLog())
|
||||
return;
|
||||
ArrayList<LodRegion> regions = new ArrayList<LodRegion>(regionToSave.values());
|
||||
ramLogger.info("Dumping Ram Usage for file writer for {} with {} regions...",
|
||||
lodDimension.dimension.getDimensionName(), regions.size());
|
||||
int nonNullRegionCount = 0;
|
||||
int nonDirtiedRegionCount = 0;
|
||||
int writingRegionCount = 0;
|
||||
long totalUsage = 0;
|
||||
int[] detailCount = new int[LodUtil.DETAIL_OPTIONS];
|
||||
long[] detailUsage = new long[LodUtil.DETAIL_OPTIONS];
|
||||
for (LodRegion r : regions)
|
||||
{
|
||||
if (r == null)
|
||||
continue;
|
||||
nonNullRegionCount++;
|
||||
if (!r.needSaving)
|
||||
nonDirtiedRegionCount++;
|
||||
if (r.isWriting.get() != 0)
|
||||
writingRegionCount++;
|
||||
LevelContainer[] container = r.debugGetDataContainers().clone();
|
||||
if (container == null || container.length != LodUtil.DETAIL_OPTIONS)
|
||||
{
|
||||
for (int j = 0; j < lodDimension.getWidth(); j++)
|
||||
LOGGER.error("DumpRamUsage encountered an invalid region!");
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < LodUtil.DETAIL_OPTIONS; i++)
|
||||
{
|
||||
if (container[i] == null)
|
||||
continue;
|
||||
detailCount[i]++;
|
||||
long byteUsage = container[i].getRoughRamUsage();
|
||||
detailUsage[i] += byteUsage;
|
||||
totalUsage += byteUsage;
|
||||
}
|
||||
}
|
||||
ramLogger.info("================================================");
|
||||
ramLogger.info("Non Null Regions: [{}], Non-Dirtied Regions: [{}], Writing Regions: [{}], Bytes: [{}]",
|
||||
nonNullRegionCount, nonDirtiedRegionCount, writingRegionCount, new UnitBytes(totalUsage));
|
||||
ramLogger.info("------------------------------------------------");
|
||||
for (int i = 0; i < LodUtil.DETAIL_OPTIONS; i++)
|
||||
{
|
||||
ramLogger.info("DETAIL {}: Containers: [{}], Bytes: [{}]", i, detailCount[i], new UnitBytes(detailUsage[i]));
|
||||
}
|
||||
ramLogger.info("================================================");
|
||||
ramLogger.incLogTries();
|
||||
}
|
||||
|
||||
/** Save all dirty regions in this LodDimension to file */
|
||||
public void saveDirtyRegionsToFile(boolean blockUntilFinished)
|
||||
{
|
||||
// determine the regions to save
|
||||
for (int i = 0; i < lodDimension.getWidth(); i++)
|
||||
{
|
||||
for (int j = 0; j < lodDimension.getWidth(); j++)
|
||||
{
|
||||
LodRegion r = lodDimension.getRegionByArrayIndex(i, j);
|
||||
|
||||
// FIXME: Note that the isWriting is a crude attempt at syncing. It won't work.
|
||||
// It just reduces the chance of a race condition
|
||||
if (r != null && r.needSaving)
|
||||
{
|
||||
if (lodDimension.GetIsRegionDirty(i, j) && lodDimension.getRegionByArrayIndex(i, j) != null)
|
||||
{
|
||||
saveRegionToFile(lodDimension.getRegionByArrayIndex(i, j));
|
||||
lodDimension.SetIsRegionDirty(i, j, false);
|
||||
}
|
||||
regionToSave.put(r.getRegionPos(), r);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
// save the dimension data
|
||||
ClientApi.DIMENSION_FINDER.saveDimensionPlayerData(this.dimensionDataSaveFolder);
|
||||
trySaveRegionsToBeSaved();
|
||||
|
||||
// wait for the saving to finish if requested
|
||||
if (blockUntilFinished)
|
||||
{
|
||||
e.printStackTrace();
|
||||
if (ENABLE_SAVE_THREAD_LOGGING)
|
||||
LOGGER.info("Blocking until lod file save finishes!");
|
||||
|
||||
try
|
||||
{
|
||||
fileWritingThreadPool.shutdown();
|
||||
boolean worked = fileWritingThreadPool.awaitTermination(30, TimeUnit.SECONDS);
|
||||
if (!worked)
|
||||
LOGGER.error("File writing timed out! File data may not be saved correctly and may cause corruptions!!!");
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
LOGGER.error("File writing wait is interrupted! File data may not be saved correctly and may cause corruptions!!!: ", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName(), Thread.NORM_PRIORITY + 1));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void trySaveRegionsToBeSaved()
|
||||
{
|
||||
if (regionToSave.isEmpty())
|
||||
return;
|
||||
// Use Memory order Acquire to acquire any memory changes on getting this boolean
|
||||
// (Corresponding call is the this::writerMain(...)::...setRelease(false);)
|
||||
//boolean haventStarted = !isFileWritingThreadRunning.compareAndExchangeAcquire(false, true);
|
||||
// The above needs java 9!
|
||||
boolean haventStarted = isFileWritingThreadRunning.compareAndSet(false, true);
|
||||
|
||||
if (haventStarted)
|
||||
{
|
||||
// We acquired the atomic lock.
|
||||
fileWritingThreadPool.execute(this::writerMain);
|
||||
}
|
||||
}
|
||||
|
||||
private void writerMain()
|
||||
{
|
||||
// Use Memory order Relaxed as no additional memory changes needed to be visible.
|
||||
// (This is just a safety checks)
|
||||
// boolean isStarted = isFileWritingThreadRunning.getPlain();
|
||||
// The above needs java 9!
|
||||
boolean isStarted = isFileWritingThreadRunning.get();
|
||||
|
||||
if (!isStarted)
|
||||
throw new ConcurrentModificationException("WriterMain Triggered but the thead state is not started!?");
|
||||
|
||||
if (ENABLE_SAVE_THREAD_LOGGING)
|
||||
LOGGER.info("Lod File Writer started. To-be-written-regions: " + regionToSave.size());
|
||||
|
||||
Instant start = Instant.now();
|
||||
// Note: Since regionToSave is a ConcurrentHashMap, and the .values() return one that support concurrency,
|
||||
// this for loop should be safe and loop until all values are gone.
|
||||
while (!regionToSave.isEmpty())
|
||||
{
|
||||
for (LodRegion r : regionToSave.values())
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
if (r.isWriting.getAndIncrement() > 0)
|
||||
continue;
|
||||
//Check if the data has been swapped out right under me. Otherwise remove it from the entry
|
||||
if (!regionToSave.remove(r.getRegionPos(), r))
|
||||
continue;
|
||||
r.needSaving = false;
|
||||
Instant i = Instant.now();
|
||||
if (ENABLE_SAVE_REGION_LOGGING)
|
||||
LOGGER.info("Lod: Saving Region " + r.getRegionPos());
|
||||
saveRegionToFile(r);
|
||||
Instant j = Instant.now();
|
||||
Duration d = Duration.between(i, j);
|
||||
if (ENABLE_SAVE_REGION_LOGGING)
|
||||
LOGGER.info("Lod: Region " + r.getRegionPos() + " save finish. Took " + d);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("Lod: UNCAUGHT exception when saving region " + r.getRegionPos() + ": ", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
r.isWriting.decrementAndGet();
|
||||
}
|
||||
}
|
||||
}
|
||||
Instant end = Instant.now();
|
||||
|
||||
if (ENABLE_SAVE_THREAD_LOGGING)
|
||||
LOGGER.info("Lod File Writer completed. Took " + Duration.between(start, end));
|
||||
|
||||
// Use Memory order Release to release any memory changes on setting this boolean
|
||||
// (Corresponding call is the this::saveRegions(...)::...compareAndExchangeAcquire(false, true);)
|
||||
// isFileWritingThreadRunning.setRelease(false);
|
||||
// The above needs java 9!
|
||||
isFileWritingThreadRunning.set(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a specific region to disk.<br>
|
||||
@@ -274,121 +545,134 @@ public class LodDimensionFileHandler
|
||||
{
|
||||
for (byte detailLevel = region.getMinDetailLevel(); detailLevel <= LodUtil.REGION_DETAIL_LEVEL; detailLevel++)
|
||||
{
|
||||
String fileName = getFileNameAndPathForRegion(region.regionPosX, region.regionPosZ, region.getGenerationMode(), detailLevel, region.getVerticalQuality());
|
||||
// Get the old file
|
||||
File oldFile = getRegionFile(region.regionPosX, region.regionPosZ, detailLevel, region.getVerticalQuality());
|
||||
if (ENABLE_SAVE_REGION_LOGGING)
|
||||
LOGGER.debug("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] detail " + detailLevel + " to file.");
|
||||
|
||||
// if the fileName was null that means the folder is inaccessible
|
||||
// for some reason
|
||||
if (fileName == null)
|
||||
boolean isFileFullyGened = false;
|
||||
// make sure the file and folder exists
|
||||
if (!oldFile.exists())
|
||||
{
|
||||
ClientApi.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.");
|
||||
byte[] temp = region.getLevel(detailLevel).toDataString();
|
||||
|
||||
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();
|
||||
|
||||
try
|
||||
{
|
||||
// the file doesn't exist,
|
||||
// create it and the folder if need be
|
||||
if (!oldFile.getParentFile().exists())
|
||||
oldFile.getParentFile().mkdirs();
|
||||
oldFile.createNewFile();
|
||||
}
|
||||
else
|
||||
catch (IOException e)
|
||||
{
|
||||
// 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;
|
||||
int isFull = 0;
|
||||
try (XZCompressorInputStream inputStream = new XZCompressorInputStream(new FileInputStream(oldFile)))
|
||||
{
|
||||
fileVersion = inputStream.read();
|
||||
inputStream.skip(1);
|
||||
isFull = inputStream.read() & 0b10000000;
|
||||
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 ((temp[1] & 0b10000000) != 0b10000000 && isFull == 0b10000000)
|
||||
{
|
||||
// existing file is complete while new one is only partially generate
|
||||
// this can happen is for some reason loading failed
|
||||
// this doesn't fix the bug, but at least protects old data
|
||||
ClientApi.LOGGER.error("LOD file write error. Attempted to overwrite complete region with incomplete one [" + fileName + "]");
|
||||
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(temp);
|
||||
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();
|
||||
LOGGER.error("LOD file write error. Unable to create parent directory for [" + oldFile + "] error [" + e.getMessage() + "]: ");
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
else
|
||||
{
|
||||
ClientApi.LOGGER.error("LOD file write error. Unable to write to [" + fileName + "] error [" + e.getMessage() + "]: ");
|
||||
e.printStackTrace();
|
||||
// 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 (FileInputStream fileInStream = new FileInputStream(oldFile))
|
||||
{
|
||||
XZCompressorInputStream inputStream = new XZCompressorInputStream(fileInStream);
|
||||
fileVersion = inputStream.read();
|
||||
inputStream.skip(1);
|
||||
isFileFullyGened = (inputStream.read() & 0b10000000) != 0;
|
||||
inputStream.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.warn("LOD file write warning. Unable to read existing file [" + oldFile + "] version. Treating it as latest version. [" + e.getMessage() + "]: ");
|
||||
e.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.
|
||||
continue;
|
||||
}
|
||||
// if we got this far then we are good
|
||||
// to overwrite the old file
|
||||
}
|
||||
|
||||
// Now create a new temporary save file
|
||||
File tempFile = new File(oldFile.getPath() + TMP_FILE_EXTENSION);
|
||||
try (FileOutputStream fileOutStream = new FileOutputStream(tempFile))
|
||||
{
|
||||
XZCompressorOutputStream outputStream = new XZCompressorOutputStream(fileOutStream, 3);
|
||||
// add the version of this file
|
||||
outputStream.write(LOD_SAVE_FILE_VERSION);
|
||||
// add each LodChunk to the file
|
||||
DataOutputStream dataStream = new DataOutputStream(outputStream);
|
||||
boolean isNewDataFullyGened = region.getLevel(detailLevel).writeData(dataStream);
|
||||
dataStream.close();
|
||||
outputStream.close();
|
||||
|
||||
if (!isNewDataFullyGened && isFileFullyGened)
|
||||
{
|
||||
// existing file is complete while new one is only partially generate
|
||||
// this can happen is for some reason loading failed
|
||||
// this doesn't fix the bug, but at least protects old data
|
||||
LOGGER.error("LOD file write error. Attempted to overwrite complete region with incomplete one [" + oldFile + "]");
|
||||
try
|
||||
{
|
||||
tempFile.delete();
|
||||
}
|
||||
catch (SecurityException e)
|
||||
{
|
||||
// Failed to delete temp file... just continue.
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.error("LOD file write error. Unable to write to temp file [" + tempFile + "]: ", e);
|
||||
continue;
|
||||
}
|
||||
|
||||
// overwrite the old file with the new one
|
||||
try
|
||||
{
|
||||
Files.move(tempFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.error("LOD file write error. Unable to update file [" + oldFile + "]: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
public byte[] getHashFromFile(byte detailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
|
||||
/**
|
||||
* Returns the save folder used for this dimension.
|
||||
*
|
||||
* @throws RuntimeException if there was an error getting the folder
|
||||
*/
|
||||
private String getFileBasePath() throws RuntimeException
|
||||
{
|
||||
int regionX = regionPos.x;
|
||||
int regionZ = regionPos.z;
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, detailLevel, verticalQuality);
|
||||
try (InputStream is = Files.newInputStream(Paths.get(fileName))) {
|
||||
return org.apache.commons.codec.digest.DigestUtils.md5(is);
|
||||
}
|
||||
catch (IOException ioEx)
|
||||
try
|
||||
{
|
||||
ClientApi.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + ioEx.getMessage() + "]: ");
|
||||
ioEx.printStackTrace();
|
||||
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.warn("Unable to get the base save file path. Error: " + e.getMessage(), e);
|
||||
throw new RuntimeException("DistantHorizons Get Save File Path Failure");
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the name of the file that should contain the
|
||||
* region at the given x and z. <br>
|
||||
@@ -398,26 +682,29 @@ public class LodDimensionFileHandler
|
||||
* <p>
|
||||
* Returns null if there is an IO or security Exception.
|
||||
*/
|
||||
private String getFileNameAndPathForRegion(int regionX, int regionZ, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality)
|
||||
private File getRegionFile(int regionX, int regionZ, byte detail, VerticalQuality vertQuality)
|
||||
{
|
||||
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)
|
||||
{
|
||||
ClientApi.LOGGER.warn("Unable to get the filename for the region [" + regionX + ", " + regionZ + "], error: [" + e.getMessage() + "], stacktrace: ");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return new File(getFileBasePath() + vertQuality + File.separatorChar +
|
||||
DETAIL_FOLDER_NAME_PREFIX + detail + File.separatorChar +
|
||||
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION);
|
||||
}
|
||||
|
||||
/** Returns null if no file is found */
|
||||
private File getBestMatchingRegionFile(byte detailLevel, int regionX, int regionZ, VerticalQuality targetVertQuality)
|
||||
{
|
||||
// Search from least vertQuality to max vertQuality
|
||||
do
|
||||
{
|
||||
File file = getRegionFile(regionX, regionZ, detailLevel, targetVertQuality);
|
||||
if (file.exists())
|
||||
return file; // Found target file.
|
||||
|
||||
targetVertQuality = VerticalQuality.next(targetVertQuality);
|
||||
}
|
||||
while (targetVertQuality != null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,585 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.handlers;
|
||||
|
||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||
|
||||
import com.seibel.lod.core.api.ApiShared;
|
||||
import com.seibel.lod.core.handlers.dimensionFinder.PlayerData;
|
||||
import com.seibel.lod.core.handlers.dimensionFinder.SubDimCompare;
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilderConfig;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.logging.ConfigBasedLogger;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.lod.LodRegion;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Used to guess the world folder for the player's current dimension.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-3-31
|
||||
*/
|
||||
public class LodDimensionFinder
|
||||
{
|
||||
private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
public static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(LodDimensionFinder.class),
|
||||
() -> CONFIG.client().advanced().debugging().debugSwitch().getLogFileSubDimEvent());
|
||||
|
||||
/** Increasing this will increase accuracy but increase calculation time */
|
||||
private static final VerticalQuality VERTICAL_QUALITY_TO_TEST_WITH = VerticalQuality.LOW;
|
||||
|
||||
public static final String THREAD_NAME = "Sub-Dimension-Finder";
|
||||
public static final String DEFAULT_SAVE_DIMENSION_FOLDER = "_Default-Sub-Dimension";
|
||||
|
||||
private PlayerData playerData = new PlayerData(MC);
|
||||
private PlayerData firstSeenPlayerData = null;
|
||||
|
||||
private volatile LodDimension foundLodDimension = null;
|
||||
|
||||
/** If true the LodDimensionFileHelper is attempting to determine the folder for this dimension */
|
||||
private AtomicBoolean determiningWorldFolder = new AtomicBoolean(false);
|
||||
|
||||
|
||||
|
||||
public LodDimensionFinder()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Returns true if a LodDimension has been found */
|
||||
public boolean isDone()
|
||||
{
|
||||
return foundLodDimension != null;
|
||||
}
|
||||
|
||||
/** Returns the found LodDimension */
|
||||
public LodDimension getAndClearFoundLodDimension()
|
||||
{
|
||||
// clear the found dimension
|
||||
LodDimension returnDim = this.foundLodDimension;
|
||||
this.foundLodDimension = null;
|
||||
|
||||
return returnDim;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void AttemptToDetermineSubDimensionAsync(IDimensionTypeWrapper dimensionTypeWrapper)
|
||||
{
|
||||
// prevent multiple threads running at the same time
|
||||
if (determiningWorldFolder.getAndSet(true))
|
||||
return;
|
||||
|
||||
// run asynchronously since this could take a while
|
||||
Thread thread = new Thread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
// attempt to get the file handler
|
||||
File saveDir;
|
||||
if (CONFIG.client().multiplayer().getMultiDimensionRequiredSimilarity() == 0)
|
||||
{
|
||||
// only allow 1 sub dimension per world
|
||||
saveDir = getDefaultSubDimensionFolder(dimensionTypeWrapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
saveDir = attemptToDetermineSubDimensionFolder();
|
||||
}
|
||||
|
||||
if (saveDir == null)
|
||||
return;
|
||||
|
||||
foundLodDimension = new LodDimension(dimensionTypeWrapper, ApiShared.lodBuilder.defaultDimensionWidthInRegions, saveDir);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
ApiShared.LOGGER.error("Unable to set the dimension file handler for dimension type [" + dimensionTypeWrapper.getDimensionName() + "]. Error: " + e.getMessage(), e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// make sure we unlock this method
|
||||
determiningWorldFolder.set(false);
|
||||
}
|
||||
});
|
||||
thread.setName(THREAD_NAME);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default save folder if it exists
|
||||
* otherwise the first valid subDimension folder lexicographically.
|
||||
*
|
||||
* @throws IOException if the folder doesn't exist or can't be accessed
|
||||
*/
|
||||
public File getDefaultSubDimensionFolder(IDimensionTypeWrapper dimensionTypeWrapper) throws IOException
|
||||
{
|
||||
File subDimFolder = null;
|
||||
LOGGER.info("Attempting to determine default sub-dimension for [" + MC.getCurrentDimension().getDimensionName() + "]");
|
||||
|
||||
// move any old data folders if they exist
|
||||
File dimensionFolder = GetDimensionFolder(MC.getCurrentDimension(), "");
|
||||
moveOldSaveFoldersIfNecessary(dimensionFolder, MC.getCurrentDimension(), DEFAULT_SAVE_DIMENSION_FOLDER);
|
||||
|
||||
// check if a sub dimension folder exists
|
||||
if (dimensionFolder.listFiles() != null)
|
||||
{
|
||||
// at least one folder exists
|
||||
LOGGER.info("Potential Sub Dimension folders: [" + dimensionFolder.listFiles(File::isDirectory).length + "]");
|
||||
|
||||
|
||||
// search for a valid sub dimension folder
|
||||
File[] potentialSubDimFolders = dimensionFolder.listFiles();
|
||||
Arrays.sort(potentialSubDimFolders); // listFiles isn't necessarily sorted
|
||||
for (File potentialSubDim : potentialSubDimFolders)
|
||||
{
|
||||
if (isValidSubDimensionDirectory(potentialSubDim))
|
||||
{
|
||||
if (potentialSubDim.getName().equals(DEFAULT_SAVE_DIMENSION_FOLDER))
|
||||
{
|
||||
// use the default save folder if possible
|
||||
subDimFolder = potentialSubDim;
|
||||
break;
|
||||
}
|
||||
else if (subDimFolder == null)
|
||||
{
|
||||
// only get the first non-default sub folder
|
||||
subDimFolder = potentialSubDim;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no valid sub dimension was found, create a new one
|
||||
if (subDimFolder == null)
|
||||
{
|
||||
subDimFolder = GetDimensionFolder(dimensionTypeWrapper, DEFAULT_SAVE_DIMENSION_FOLDER);
|
||||
LOGGER.info("Default Sub Dimension not found. Creating: [" + DEFAULT_SAVE_DIMENSION_FOLDER + "]");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.info("Default Sub Dimension set to: [" + LodUtil.shortenString(subDimFolder.getName(), 8) + "...]");
|
||||
}
|
||||
|
||||
return subDimFolder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Currently this method checks a single chunk (where the player is)
|
||||
* and compares it against the same chunk position in the other dimension worlds to
|
||||
* guess which world the player is in.
|
||||
*
|
||||
* @throws IOException if the folder doesn't exist or can't be accessed
|
||||
*/
|
||||
public File attemptToDetermineSubDimensionFolder() throws IOException
|
||||
{
|
||||
if (firstSeenPlayerData == null)
|
||||
{
|
||||
firstSeenPlayerData = playerData;
|
||||
playerData = new PlayerData(MC);
|
||||
}
|
||||
|
||||
// relevant positions
|
||||
AbstractChunkPosWrapper playerChunkPos = FACTORY.createChunkPos(playerData.playerBlockPos);
|
||||
int startingBlockPosX = playerChunkPos.getMinBlockX();
|
||||
int startingBlockPosZ = playerChunkPos.getMinBlockZ();
|
||||
RegionPos playerRegionPos = new RegionPos(playerChunkPos);
|
||||
|
||||
|
||||
// chunk from the newly loaded dimension
|
||||
IChunkWrapper newlyLoadedChunk = MC.getWrappedClientWorld().tryGetChunk(playerChunkPos);
|
||||
// check if this chunk is valid to test
|
||||
if (!CanDetermineDimensionFolder(newlyLoadedChunk))
|
||||
return null;
|
||||
|
||||
// create a temporary dimension to store the test LOD
|
||||
LodDimension newlyLoadedDim = new LodDimension(MC.getCurrentDimension(), 1, null, false);
|
||||
newlyLoadedDim.move(playerRegionPos);
|
||||
newlyLoadedDim.regions.set(playerRegionPos.x, playerRegionPos.z, new LodRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerRegionPos, VERTICAL_QUALITY_TO_TEST_WITH));
|
||||
|
||||
// generate a LOD to test against
|
||||
boolean lodGenerated = ApiShared.lodBuilder.generateLodNodeFromChunk(newlyLoadedDim, newlyLoadedChunk, new LodBuilderConfig(DistanceGenerationMode.FULL), true, true);
|
||||
if (!lodGenerated)
|
||||
return null;
|
||||
|
||||
|
||||
// log the start of this attempt
|
||||
LOGGER.info("Attempting to determine sub-dimension for [" + MC.getCurrentDimension().getDimensionName() + "]");
|
||||
LOGGER.info("Player block pos in dimension: [" + playerData.playerBlockPos.getX() + "," + playerData.playerBlockPos.getY() + "," + playerData.playerBlockPos.getZ() + "]");
|
||||
|
||||
|
||||
// new chunk data
|
||||
long[][][] newChunkData = new long[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH][];
|
||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
|
||||
{
|
||||
long[] array = newlyLoadedDim.getRegion(playerRegionPos.x, playerRegionPos.z).getAllData(LodUtil.BLOCK_DETAIL_LEVEL, x + startingBlockPosX, z + startingBlockPosZ);
|
||||
newChunkData[x][z] = array;
|
||||
}
|
||||
}
|
||||
boolean newChunkHasData = !isDataEmpty(newChunkData);
|
||||
|
||||
// check if the chunk is actually empty
|
||||
if (!newChunkHasData)
|
||||
{
|
||||
if (newlyLoadedChunk.getHeight() != 0)
|
||||
{
|
||||
// the chunk isn't empty but the LOD is...
|
||||
|
||||
String message = "Error: the chunk at (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") has a height of [" + newlyLoadedChunk.getHeight() + "] but the LOD generated is empty!";
|
||||
LOGGER.error(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
String message = "Warning: The chunk at (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") is empty.";
|
||||
LOGGER.warn(message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// get every folder (world) we have for this dimension
|
||||
File dimensionFolder = GetDimensionFolder(newlyLoadedDim.dimension, "");
|
||||
// check if the folder exists
|
||||
if (dimensionFolder.listFiles() == null)
|
||||
{
|
||||
if (!dimensionFolder.exists())
|
||||
{
|
||||
// create the directory since it doesn't exist
|
||||
dimensionFolder.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// move any old data folders if they exist
|
||||
moveOldSaveFoldersIfNecessary(dimensionFolder, MC.getCurrentDimension(), UUID.randomUUID().toString());
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// compare each world with the newly loaded one
|
||||
SubDimCompare mostSimilarSubDim = null;
|
||||
|
||||
LOGGER.info("Potential Sub Dimension folders: [" + dimensionFolder.listFiles(File::isDirectory).length + "]");
|
||||
for (File testDimFolder : dimensionFolder.listFiles())
|
||||
{
|
||||
if (!testDimFolder.isDirectory())
|
||||
continue;
|
||||
|
||||
LOGGER.info("Testing sub dimension: [" + LodUtil.shortenString(testDimFolder.getName(), 8) + "]");
|
||||
|
||||
try
|
||||
{
|
||||
// get a LOD from this dimension folder
|
||||
LodDimension tempLodDim = new LodDimension(null, 1, null, false);
|
||||
tempLodDim.move(playerRegionPos);
|
||||
LodDimensionFileHandler tempFileHandler = new LodDimensionFileHandler(testDimFolder, tempLodDim);
|
||||
LodRegion testRegion = tempFileHandler.loadRegionFromFile(LodUtil.BLOCK_DETAIL_LEVEL, playerRegionPos, VERTICAL_QUALITY_TO_TEST_WITH);
|
||||
// get data from this LOD
|
||||
long[][][] testChunkData = new long[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH][];
|
||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
|
||||
{
|
||||
long[] array = testRegion.getAllData(LodUtil.BLOCK_DETAIL_LEVEL, x + startingBlockPosX, z + startingBlockPosZ);
|
||||
testChunkData[x][z] = array;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// get the player data for this dimension folder
|
||||
PlayerData testPlayerData = new PlayerData(testDimFolder);
|
||||
LOGGER.info("Last known player pos: [" + testPlayerData.playerBlockPos.getX() + "," + testPlayerData.playerBlockPos.getY() + "," + testPlayerData.playerBlockPos.getZ() + "]");
|
||||
|
||||
// check if the block positions are close
|
||||
int playerBlockDist = testPlayerData.playerBlockPos.getManhattanDistance(playerData.playerBlockPos);
|
||||
ApiShared.LOGGER.info("Player block position distance between saved sub dimension and first seen is [" + playerBlockDist + "]");
|
||||
|
||||
|
||||
// check if the chunk is actually empty
|
||||
if (isDataEmpty(testChunkData))
|
||||
{
|
||||
String message = "The test chunk for dimension folder [" + LodUtil.shortenString(testDimFolder.getName(), 8) + "] and chunk pos (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") is empty. This is expected if the position is outside the sub-dimension's generated area.";
|
||||
LOGGER.info(message);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// compare the two LODs
|
||||
int equalDataPoints = 0;
|
||||
int totalDataPointCount = 0;
|
||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
|
||||
{
|
||||
for (int y = 0; y < newChunkData[x][z].length; y++)
|
||||
{
|
||||
if (newChunkData[x][z][y] == testChunkData[x][z][y])
|
||||
{
|
||||
equalDataPoints++;
|
||||
}
|
||||
totalDataPointCount++;
|
||||
|
||||
if (!DataPointUtil.doesItExist(newChunkData[x][z][y]) || !DataPointUtil.doesItExist(testChunkData[x][z][y]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// determine if this world is closer to the newly loaded world
|
||||
SubDimCompare subDimCompare = new SubDimCompare(equalDataPoints, totalDataPointCount, playerBlockDist, testDimFolder);
|
||||
if (mostSimilarSubDim == null || subDimCompare.compareTo(mostSimilarSubDim) > 0)
|
||||
{
|
||||
mostSimilarSubDim = subDimCompare;
|
||||
}
|
||||
|
||||
LOGGER.info("Sub dimension [" + LodUtil.shortenString(testDimFolder.getName(), 8) + "...] is current dimension probability: " + LodUtil.shortenString(subDimCompare.getPercentEqual() + "", 5) + " (" + equalDataPoints + "/" + totalDataPointCount + ")");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// this sub dimension isn't formatted correctly
|
||||
// for now we are just assuming it is an unrelated file
|
||||
}
|
||||
}
|
||||
|
||||
// TODO if two sub dimensions contain the same LODs merge them
|
||||
|
||||
|
||||
|
||||
// the first seen player data is no longer needed, the sub dimension has been determined
|
||||
firstSeenPlayerData = null;
|
||||
|
||||
|
||||
if (mostSimilarSubDim != null && mostSimilarSubDim.isValidSubDim())
|
||||
{
|
||||
// we found a world folder that is similar, use it
|
||||
|
||||
LOGGER.info("Sub Dimension set to: [" + LodUtil.shortenString(mostSimilarSubDim.folder.getName(), 8) + "...] with an equality of [" + mostSimilarSubDim.getPercentEqual() + "]");
|
||||
return mostSimilarSubDim.folder;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no world folder was found, create a new one
|
||||
|
||||
double highestEqualityPercent = mostSimilarSubDim != null ? mostSimilarSubDim.getPercentEqual() : 0;
|
||||
|
||||
String newId = UUID.randomUUID().toString();
|
||||
String message = "No suitable sub dimension found. The highest equality was [" + LodUtil.shortenString(highestEqualityPercent + "", 5) + "]. Creating a new sub dimension with ID: " + LodUtil.shortenString(newId, 8) + "...";
|
||||
LOGGER.info(message);
|
||||
return GetDimensionFolder(newlyLoadedDim.dimension, newId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dimension folder with the specific ID if specified. <br>
|
||||
* If the worldId is empty or null this returns the dimension parent folder <br>
|
||||
* Example folder names: "dim_overworld/worldId", "dim_the_nether/worldId"
|
||||
*/
|
||||
public static File GetDimensionFolder(IDimensionTypeWrapper newDimensionType, String worldId)
|
||||
{
|
||||
// prevent null pointers
|
||||
if (worldId == null)
|
||||
worldId = "";
|
||||
|
||||
// make sure the ID is a valid path
|
||||
worldId = worldId.replaceAll(LodUtil.INVALID_FILE_CHARACTERS_REGEX, "");
|
||||
|
||||
try
|
||||
{
|
||||
if (MC.hasSinglePlayerServer())
|
||||
{
|
||||
// local world
|
||||
|
||||
IWorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(newDimensionType);
|
||||
return new File(serverWorld.getSaveFolder().getCanonicalFile().getPath() + File.separatorChar + "lod" + File.separatorChar + worldId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// multiplayer
|
||||
return new File(MC.getGameDirectory().getCanonicalFile().getPath() +
|
||||
File.separatorChar + "Distant_Horizons_server_data" + File.separatorChar + MC.getCurrentDimensionId() + File.separatorChar + worldId);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.error("Unable to get dimension folder for dimension [" + newDimensionType.getDimensionName() + "]", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Returns true if the given chunk is valid to test */
|
||||
public boolean CanDetermineDimensionFolder(IChunkWrapper chunk)
|
||||
{
|
||||
// we can only guess if the given chunk can be converted into a LOD
|
||||
return LodBuilder.canGenerateLodFromChunk(chunk);
|
||||
}
|
||||
|
||||
|
||||
/** Used for debugging, returns true if every data point is 0 */
|
||||
private static boolean isDataEmpty(long[][][] chunkData)
|
||||
{
|
||||
for (long[][] xArray : chunkData)
|
||||
{
|
||||
for (long[] zArray : xArray)
|
||||
{
|
||||
for (long dataPoint : zArray)
|
||||
{
|
||||
if (dataPoint != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Returns true if the given folder holds valid Lod Dimension data */
|
||||
public static boolean isValidSubDimensionDirectory(File potentialFolder)
|
||||
{
|
||||
if (!potentialFolder.isDirectory())
|
||||
// it needs to be a folder
|
||||
return false;
|
||||
|
||||
if (potentialFolder.listFiles() == null)
|
||||
// it needs to have folders in it
|
||||
return false;
|
||||
|
||||
// check if there is at least one VerticalQuality folder in this directory
|
||||
for (File internalFolder : potentialFolder.listFiles())
|
||||
{
|
||||
if (VerticalQuality.getByName(internalFolder.getName()) != null)
|
||||
{
|
||||
// one of the internal folders is a VerticalQuality folder
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves any folders from the old save location
|
||||
* (directly under the dimension type)
|
||||
* to a sub-dimension folder with the given name.
|
||||
*/
|
||||
private void moveOldSaveFoldersIfNecessary(File dimensionFolder, IDimensionTypeWrapper dimensionType, String subDimensionName) throws IOException
|
||||
{
|
||||
// if there are no files this will return null
|
||||
if (dimensionFolder.listFiles() == null)
|
||||
return;
|
||||
|
||||
|
||||
for (File folder : dimensionFolder.listFiles())
|
||||
{
|
||||
if (VerticalQuality.getByName(folder.getName()) != null)
|
||||
{
|
||||
// this is a LOD save folder
|
||||
// create a new sub dimension and move the data into it
|
||||
|
||||
File newDimension = GetDimensionFolder(dimensionType, subDimensionName);
|
||||
newDimension.mkdirs();
|
||||
|
||||
File oldDataNewPath = new File(newDimension.getPath() + File.separatorChar + folder.getName());
|
||||
Files.move(folder.toPath(), oldDataNewPath.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ignore this folder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public void updatePlayerData()
|
||||
{
|
||||
playerData.updateData(MC);
|
||||
}
|
||||
|
||||
/** saves any necessary player data to the given world folder */
|
||||
public void saveDimensionPlayerData(File worldFolder)
|
||||
{
|
||||
// get and create the file and path if they don't exist
|
||||
File file = PlayerData.getFileForDimensionFolder(worldFolder);
|
||||
if (!file.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.error("Unable to save player dimension data for world folder [" + worldFolder.getPath() + "].", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// determine the playerData
|
||||
IMinecraftClientWrapper mc = SingletonHandler.get(IMinecraftClientWrapper.class);
|
||||
PlayerData playerdata = new PlayerData(mc);
|
||||
|
||||
// write the data to file
|
||||
CommentedFileConfig toml = CommentedFileConfig.builder(file).build();
|
||||
playerdata.toTomlFile(toml);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.handlers;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import com.seibel.lod.core.api.ApiShared;
|
||||
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.objects.lod.VerticalLevelContainer;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
|
||||
public class LodDimensionOldFileStructureHandler
|
||||
{
|
||||
|
||||
/** This is the dimension that owns this file handler */
|
||||
private final File dimensionDataSaveFolder;
|
||||
private final LodDimensionFileHandler newFileHandler;
|
||||
|
||||
enum OldDistanceGenerationMode {
|
||||
NONE,
|
||||
BIOME_ONLY,
|
||||
BIOME_ONLY_SIMULATE_HEIGHT,
|
||||
SURFACE,
|
||||
FEATURES,
|
||||
FULL
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 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-";
|
||||
|
||||
private static final String RETIRED_OLD_STRUCT_POSTFIX = "-RETIRED-CAN-BE-DELETED";
|
||||
|
||||
public static final int LOD_SAVE_FILE_VERSION = 8;
|
||||
public static final ExecutorService mergerThreads = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
|
||||
private static class TempLodRegion {
|
||||
final VerticalLevelContainer[] containers;
|
||||
final VerticalQuality vertQual;
|
||||
final int posX;
|
||||
final int posZ;
|
||||
TempLodRegion(VerticalQuality vertQual, RegionPos pos) {
|
||||
this.vertQual = vertQual;
|
||||
posX = pos.x;
|
||||
posZ = pos.z;
|
||||
containers = new VerticalLevelContainer[LodUtil.REGION_DETAIL_LEVEL+1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public LodDimensionOldFileStructureHandler(LodDimensionFileHandler fileHandler)
|
||||
{
|
||||
dimensionDataSaveFolder = fileHandler.dimensionDataSaveFolder;
|
||||
newFileHandler = fileHandler;
|
||||
}
|
||||
|
||||
|
||||
private void loadGenModeToRegion(TempLodRegion region, OldDistanceGenerationMode genMode)
|
||||
{
|
||||
int regionX = region.posX;
|
||||
int regionZ = region.posZ;
|
||||
for (byte detail = LodUtil.REGION_DETAIL_LEVEL; detail >= 0; detail--) {
|
||||
File file = new File(getFileBasePath() + region.vertQual + File.separatorChar +
|
||||
genMode + File.separatorChar + DETAIL_FOLDER_NAME_PREFIX + detail + File.separatorChar +
|
||||
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION);
|
||||
if (!file.exists()) continue;
|
||||
if (!file.isFile()) continue;
|
||||
long fileSize = file.length();
|
||||
if (fileSize == 0) continue;
|
||||
|
||||
try (FileInputStream fileInStream = new FileInputStream(file))
|
||||
{
|
||||
XZCompressorInputStream inputStream = new XZCompressorInputStream(fileInStream);
|
||||
int fileVersion;
|
||||
fileVersion = inputStream.read();
|
||||
|
||||
// check if this file can be read by this file handler
|
||||
if (fileVersion < 6)
|
||||
{
|
||||
// the file we are reading is too old.
|
||||
// close the reader and delete the file.
|
||||
inputStream.close();
|
||||
ApiShared.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ")"
|
||||
+ " version found: " + fileVersion
|
||||
+ ", version requested: " + LOD_SAVE_FILE_VERSION
|
||||
+ ". this region file will not be read and merged into the new save structure.");
|
||||
continue;
|
||||
}
|
||||
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();
|
||||
ApiShared.LOGGER.info("Unexpected newer LOD region file for region: (" + regionX + "," + regionZ + ")"
|
||||
+ " version found: " + fileVersion
|
||||
+ ", version requested: " + LOD_SAVE_FILE_VERSION
|
||||
+ " this region file will not be read and merged into the new save structure.");
|
||||
continue;
|
||||
}
|
||||
else if (fileVersion < LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
ApiShared.LOGGER.debug("Old LOD region file for region: (" + regionX + "," + regionZ + ")"
|
||||
+ " version found: " + fileVersion
|
||||
+ ", version requested: " + LOD_SAVE_FILE_VERSION
|
||||
+ ". this region file be read, updated, and merged into the new save structure.");
|
||||
}
|
||||
VerticalLevelContainer data = new VerticalLevelContainer(new DataInputStream(inputStream), fileVersion, detail);
|
||||
if (region.containers[detail] == null) {
|
||||
region.containers[detail] = data;
|
||||
} else {
|
||||
region.containers[detail].addChunkOfData(data.dataContainer, 0, 0, data.size, data.size, false);
|
||||
}
|
||||
inputStream.close();
|
||||
}
|
||||
catch (IOException ioEx)
|
||||
{
|
||||
ApiShared.LOGGER.error("LOD file read error. Unable to read xz compressed file [" + file + "] error [" + ioEx.getMessage() + "]: ");
|
||||
ioEx.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveRegion(TempLodRegion region) {
|
||||
for (int detail=0; detail<=LodUtil.REGION_DETAIL_LEVEL; detail++) {
|
||||
if (region.containers[detail] == null) continue;
|
||||
newFileHandler.saveDirect(region.posX, region.posZ, region.vertQual, region.containers[detail]);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAndMergeAndSaveRegion(VerticalQuality verticalQuality, RegionPos regionPos)
|
||||
{
|
||||
ApiShared.LOGGER.info("Merging region "+regionPos+" at "+verticalQuality+"...");
|
||||
TempLodRegion region = new TempLodRegion(verticalQuality, regionPos);
|
||||
ApiShared.LOGGER.info("Reading data...");
|
||||
loadGenModeToRegion(region, OldDistanceGenerationMode.FULL);
|
||||
loadGenModeToRegion(region, OldDistanceGenerationMode.FEATURES);
|
||||
loadGenModeToRegion(region, OldDistanceGenerationMode.SURFACE);
|
||||
loadGenModeToRegion(region, OldDistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT);
|
||||
loadGenModeToRegion(region, OldDistanceGenerationMode.BIOME_ONLY);
|
||||
loadGenModeToRegion(region, OldDistanceGenerationMode.NONE);
|
||||
ApiShared.LOGGER.info("Writing data...");
|
||||
saveRegion(region);
|
||||
ApiShared.LOGGER.info("region "+regionPos+" at "+verticalQuality+" merged");
|
||||
}
|
||||
|
||||
|
||||
|
||||
private RegionPos parseFileName(String fileName) {
|
||||
if (!fileName.endsWith(FILE_EXTENSION)) return null;
|
||||
if (!fileName.startsWith(FILE_NAME_PREFIX)) return null;
|
||||
String[] array = fileName.split("\\."); // Array content: "lod", "-1", "1", ".xz"
|
||||
if (array.length!=4) return null;
|
||||
try {
|
||||
return new RegionPos(Integer.parseInt(array[1]), Integer.parseInt(array[2]));
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private HashSet<RegionPos> scanOldRegionFiles(VerticalQuality vertQual, OldDistanceGenerationMode genMode) {
|
||||
HashSet<RegionPos> result = new HashSet<RegionPos>();
|
||||
File baseBaseFolder = new File(getFileBasePath() + vertQual + File.separatorChar + genMode);
|
||||
if (!baseBaseFolder.exists()) return result;
|
||||
for (byte detail=0; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++) {
|
||||
File baseFolder = new File(getFileBasePath() + vertQual + File.separatorChar +
|
||||
genMode + File.separatorChar + DETAIL_FOLDER_NAME_PREFIX + detail);
|
||||
if (!baseFolder.exists()) continue;
|
||||
if (!baseFolder.isDirectory()) continue;
|
||||
File[] subFiles = baseFolder.listFiles();
|
||||
for (File subFile : subFiles) {
|
||||
if (!subFile.isFile()) continue;
|
||||
if (!subFile.canRead()) continue;
|
||||
RegionPos pos = parseFileName(subFile.getName());
|
||||
if (pos != null) result.add(pos);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void renameOldFileStructure(VerticalQuality vertQual, OldDistanceGenerationMode genMode) {
|
||||
File baseBaseFolder = new File(getFileBasePath() + vertQual + File.separatorChar + genMode);
|
||||
if (!baseBaseFolder.exists()) return;
|
||||
baseBaseFolder.renameTo(new File(getFileBasePath() + vertQual + File.separatorChar + genMode + RETIRED_OLD_STRUCT_POSTFIX));
|
||||
}
|
||||
|
||||
public void mergeOldFileStructureForVertQuality(VerticalQuality vertQual) {
|
||||
File baseFile = new File(getFileBasePath() + vertQual);
|
||||
if (!baseFile.exists()) return;
|
||||
if (!baseFile.isDirectory()) return;
|
||||
HashSet<RegionPos> totalPos = new HashSet<RegionPos>();
|
||||
totalPos.addAll(scanOldRegionFiles(vertQual, OldDistanceGenerationMode.NONE));
|
||||
totalPos.addAll(scanOldRegionFiles(vertQual, OldDistanceGenerationMode.BIOME_ONLY));
|
||||
totalPos.addAll(scanOldRegionFiles(vertQual, OldDistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT));
|
||||
totalPos.addAll(scanOldRegionFiles(vertQual, OldDistanceGenerationMode.SURFACE));
|
||||
totalPos.addAll(scanOldRegionFiles(vertQual, OldDistanceGenerationMode.FEATURES));
|
||||
totalPos.addAll(scanOldRegionFiles(vertQual, OldDistanceGenerationMode.FULL));
|
||||
ArrayList<Future<?>> futures = new ArrayList<Future<?>>();
|
||||
for (RegionPos pos : totalPos) {
|
||||
futures.add(mergerThreads.submit(() -> {
|
||||
loadAndMergeAndSaveRegion(vertQual, pos);
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
futures.forEach(t ->
|
||||
{
|
||||
try
|
||||
{
|
||||
t.get();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
renameOldFileStructure(vertQual, OldDistanceGenerationMode.NONE);
|
||||
renameOldFileStructure(vertQual, OldDistanceGenerationMode.BIOME_ONLY);
|
||||
renameOldFileStructure(vertQual, OldDistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT);
|
||||
renameOldFileStructure(vertQual, OldDistanceGenerationMode.SURFACE);
|
||||
renameOldFileStructure(vertQual, OldDistanceGenerationMode.FEATURES);
|
||||
renameOldFileStructure(vertQual, OldDistanceGenerationMode.FULL);
|
||||
}
|
||||
|
||||
private String getFileBasePath()
|
||||
{
|
||||
try
|
||||
{
|
||||
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
ApiShared.LOGGER.warn("Unable to get the base save file path. Error: " + e.getMessage(), e);
|
||||
throw new RuntimeException("DistantHorizons Get Save File Path Failure");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -25,27 +25,30 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.enums.rendering.FogQuality;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.enums.rendering.FogDrawMode;
|
||||
|
||||
/**
|
||||
* A singleton used to get variables from methods
|
||||
* where they are private or potentially absent.
|
||||
* Specifically the fog setting in Optifine or the
|
||||
* presence/absence of other mods.
|
||||
* For example: the fog setting in Optifine or the
|
||||
* presence/absence of Vivecraft.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
* @version 12-14-2021
|
||||
*/
|
||||
public class ReflectionHandler implements IReflectionHandler
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME + "-" + ReflectionHandler.class.getSimpleName());
|
||||
|
||||
private static ReflectionHandler instance;
|
||||
public static ReflectionHandler instance;
|
||||
|
||||
private Field ofFogField = null;
|
||||
private final Object mcOptionsObject;
|
||||
|
||||
private Boolean sodiumPresent = null;
|
||||
private boolean optifinePresent = false;
|
||||
|
||||
|
||||
|
||||
|
||||
private ReflectionHandler(Field[] optionFields, Object newMcOptionsObject)
|
||||
@@ -75,6 +78,8 @@ public class ReflectionHandler implements IReflectionHandler
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** finds the Optifine fog type field */
|
||||
private void setupFogField(Field[] optionFields)
|
||||
{
|
||||
@@ -83,6 +88,7 @@ public class ReflectionHandler implements IReflectionHandler
|
||||
{
|
||||
if (field.getName().equals("ofFogType"))
|
||||
{
|
||||
optifinePresent = true;
|
||||
ofFogField = field;
|
||||
return;
|
||||
}
|
||||
@@ -100,14 +106,14 @@ public class ReflectionHandler implements IReflectionHandler
|
||||
* @return the fog quality
|
||||
*/
|
||||
@Override
|
||||
public FogQuality getFogQuality()
|
||||
public FogDrawMode getFogDrawMode()
|
||||
{
|
||||
if (ofFogField == null)
|
||||
{
|
||||
// either optifine isn't installed,
|
||||
// the variable name was changed, or
|
||||
// the setup method wasn't called yet.
|
||||
return FogQuality.FANCY;
|
||||
return FogDrawMode.FOG_ENABLED;
|
||||
}
|
||||
|
||||
int returnNum = 0;
|
||||
@@ -129,12 +135,11 @@ public class ReflectionHandler implements IReflectionHandler
|
||||
// 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;
|
||||
case 1: // fast
|
||||
case 2: // fancy
|
||||
return FogDrawMode.FOG_ENABLED;
|
||||
case 3: // off
|
||||
return FogDrawMode.FOG_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,42 +160,34 @@ public class ReflectionHandler implements IReflectionHandler
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the projection matrix's clip planes.
|
||||
* The projection matrix must be in column-major format.
|
||||
*
|
||||
* @param projectionMatrix The projection matrix to be modified.
|
||||
* @param newNearClipPlane the new near clip plane value.
|
||||
* @param newFarClipPlane the new far clip plane value.
|
||||
* @return The modified matrix.
|
||||
*/
|
||||
@Override
|
||||
public Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane)
|
||||
public boolean optifinePresent()
|
||||
{
|
||||
// find the matrix values.
|
||||
float nearMatrixValue = -((newFarClipPlane + newNearClipPlane) / (newFarClipPlane - newNearClipPlane));
|
||||
float farMatrixValue = -((2 * newFarClipPlane * newNearClipPlane) / (newFarClipPlane - newNearClipPlane));
|
||||
|
||||
try
|
||||
{
|
||||
// TODO this was originally created before we had the Mat4f object,
|
||||
// so this doesn't need to be done with reflection anymore.
|
||||
// And should be moved to RenderUtil
|
||||
|
||||
// get the fields of the projectionMatrix
|
||||
Field[] fields = projectionMatrix.getClass().getDeclaredFields();
|
||||
// bypass the security protections on the fields that encode near and far plane values.
|
||||
fields[10].setAccessible(true);
|
||||
fields[11].setAccessible(true);
|
||||
// Change the values of the near and far plane.
|
||||
fields[10].set(projectionMatrix, nearMatrixValue);
|
||||
fields[11].set(projectionMatrix, farMatrixValue);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return projectionMatrix;
|
||||
return optifinePresent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean sodiumPresent()
|
||||
{
|
||||
// we don't want to run a potentially expensive
|
||||
// reflection search operation every time this method is called
|
||||
if (sodiumPresent == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class.forName("me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer");
|
||||
|
||||
sodiumPresent = true;
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
sodiumPresent = false;
|
||||
}
|
||||
}
|
||||
|
||||
return sodiumPresent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.handlers.dependencyInjection;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class takes care of tracking objects used in dependency injection.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 3-4-2022
|
||||
*/
|
||||
public class DependencyHandler
|
||||
{
|
||||
private final Map<Class<?>, Object> dependencies = new HashMap<Class<?>, Object>();
|
||||
private boolean bindingFinished = false;
|
||||
|
||||
|
||||
/**
|
||||
* Links the given implementation object to an interface so it can be referenced later.
|
||||
*
|
||||
* @param depenencyInterface The interface the implementation object should implement.
|
||||
* @param dependencyImplementation A object that implements the depenencyInterface interface.
|
||||
* @throws IllegalStateException if the implementation object doesn't implement
|
||||
* the interface or the interface has already been bound.
|
||||
*/
|
||||
public void bind(Class<?> depenencyInterface, Object dependencyImplementation) throws IllegalStateException
|
||||
{
|
||||
// only allow binding before the finishBinding method is called
|
||||
if (bindingFinished)
|
||||
{
|
||||
throw new IllegalStateException("The dependency [" + depenencyInterface.getSimpleName() + "] cannot be bound, Binding is finished for [" + this.getClass().getSimpleName() + "]. Make sure your bindings are happening before the [bindingFinished] method is being called.");
|
||||
}
|
||||
|
||||
|
||||
// make sure we haven't already bound this dependency
|
||||
if (dependencies.containsKey(depenencyInterface))
|
||||
{
|
||||
throw new IllegalStateException("The dependency [" + depenencyInterface.getSimpleName() + "] has already been bound.");
|
||||
}
|
||||
|
||||
|
||||
// make sure the given dependency implements the necessary interfaces
|
||||
boolean implementsInterface = checkIfClassImplements(dependencyImplementation.getClass(), depenencyInterface);
|
||||
boolean implementsBindable = checkIfClassImplements(dependencyImplementation.getClass(), IBindable.class);
|
||||
|
||||
// display any errors
|
||||
if (!implementsInterface)
|
||||
{
|
||||
throw new IllegalStateException("The dependency [" + dependencyImplementation.getClass().getSimpleName() + "] doesn't implement the interface [" + depenencyInterface.getSimpleName() + "].");
|
||||
}
|
||||
if (!implementsBindable)
|
||||
{
|
||||
throw new IllegalStateException("The dependency [" + dependencyImplementation.getClass().getSimpleName() + "] doesn't implement the interface [" + IBindable.class.getSimpleName() + "].");
|
||||
}
|
||||
|
||||
|
||||
dependencies.put(depenencyInterface, dependencyImplementation);
|
||||
}
|
||||
/**
|
||||
* Checks if classToTest (or one of its ancestors)
|
||||
* implements the given interface.
|
||||
*/
|
||||
private boolean checkIfClassImplements(Class<?> classToTest, Class<?> interfaceToLookFor)
|
||||
{
|
||||
// check the parent class (if applicable)
|
||||
if (classToTest.getSuperclass() != Object.class && classToTest.getSuperclass() != null)
|
||||
{
|
||||
if (checkIfClassImplements(classToTest.getSuperclass(), interfaceToLookFor))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check interfaces
|
||||
for (Class<?> implementationInterface : classToTest.getInterfaces())
|
||||
{
|
||||
// recurse to check interface parents if necessary
|
||||
if (implementationInterface.getInterfaces().length != 0)
|
||||
{
|
||||
if (checkIfClassImplements(implementationInterface, interfaceToLookFor))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (implementationInterface.equals(interfaceToLookFor))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a dependency of type T if one has been bound.
|
||||
* Returns null otherwise.
|
||||
*
|
||||
* @param <T> class of the dependency
|
||||
* (inferred from the objectClass parameter)
|
||||
* @param interfaceClass Interface of the dependency
|
||||
* @return the dependency of type T
|
||||
* @throws ClassCastException If the dependency isn't able to be cast to type T.
|
||||
* (this shouldn't normally happen, unless the bound object changed somehow)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends IBindable> T get(Class<?> interfaceClass) throws ClassCastException
|
||||
{
|
||||
// getting dependencies should only happen after everything has been bound
|
||||
if (!bindingFinished)
|
||||
{
|
||||
throw new IllegalStateException("Binding hasn't been finished for [" + this.getClass().getSimpleName() + "]. Make sure you are calling the [bindingFinished] method before calling [get].");
|
||||
}
|
||||
|
||||
return (T) dependencies.get(interfaceClass);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Should only be called after all Binds have been done.
|
||||
* Calls the delayedSetup method for each dependency. <br> <br>
|
||||
*
|
||||
* This is done so we can have circular dependencies.
|
||||
*/
|
||||
public void finishBinding()
|
||||
{
|
||||
// (yes technically the binding isn't finished,
|
||||
// but this needs to be set to "true" so we can use "get")
|
||||
bindingFinished = true;
|
||||
|
||||
for (Class<?> interfaceKey : dependencies.keySet())
|
||||
{
|
||||
IBindable concreteObject = get(interfaceKey);
|
||||
concreteObject.finishDelayedSetup();
|
||||
}
|
||||
}
|
||||
|
||||
/** returns whether the finishBinding method has been called */
|
||||
public boolean getBindingFinished()
|
||||
{
|
||||
return bindingFinished;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.handlers.dependencyInjection;
|
||||
|
||||
/**
|
||||
* Necessary for all singletons that can be dependency injected.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 3-4-2022
|
||||
*/
|
||||
public interface IBindable
|
||||
{
|
||||
/**
|
||||
* Finish initializing this object. <br> <br>
|
||||
*
|
||||
* Generally this should just used for getting other objects through
|
||||
* dependency injection and is specifically designed to allow
|
||||
* for circular references. <br><br>
|
||||
*
|
||||
* If no circular dependencies are required this method
|
||||
* doesn't have to be implemented.
|
||||
*/
|
||||
public default void finishDelayedSetup()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.handlers.dependencyInjection;
|
||||
|
||||
import com.seibel.lod.core.api.ApiShared;
|
||||
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IModAccessor;
|
||||
|
||||
/**
|
||||
* This class takes care of dependency injection for mods accessors. (for mod compatibility
|
||||
* support).
|
||||
*
|
||||
* This is basically the same as the SingletonHandler, except it can return null.
|
||||
* Getting null either means the mod isn't loaded in the game
|
||||
* or it hasn't been implemented for the given Minecraft version.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @author Leetom
|
||||
* @version 3-1-2022
|
||||
*/
|
||||
public class ModAccessorHandler
|
||||
{
|
||||
private static final DependencyHandler dependencyHandler = new DependencyHandler();
|
||||
|
||||
|
||||
/**
|
||||
* Links the given mod accessor to an interface so it can be referenced later.
|
||||
*
|
||||
* @param interfaceClass The interface the mod accessor should implement.
|
||||
* @param modAccessor An object that implements the interfaceClass interface.
|
||||
* @throws IllegalStateException if the mod accessor doesn't implement
|
||||
* the interface or the interface has already been bound.
|
||||
*/
|
||||
public static void bind(Class<? extends IModAccessor> interfaceClass, IModAccessor modAccessor)
|
||||
throws IllegalStateException
|
||||
{
|
||||
dependencyHandler.bind(interfaceClass, modAccessor);
|
||||
ApiShared.LOGGER.info("Registored mod comatibility accessor for " + modAccessor.getModName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mod accessor of type T if one has been bound.
|
||||
* Returns null otherwise.
|
||||
*
|
||||
* @param <T> class of the mod accessor
|
||||
* (inferred from the objectClass parameter)
|
||||
* @param interfaceClass Interface of the mod accessor
|
||||
* @return the dependency of type T
|
||||
* @throws ClassCastException If the mod accessor isn't able to be cast to type T.
|
||||
* (this shouldn't normally happen, unless the bound object changed somehow)
|
||||
*/
|
||||
public static <T extends IModAccessor> T get(Class<T> objectClass) throws ClassCastException
|
||||
{
|
||||
return dependencyHandler.get(objectClass);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Should only be called after all Binds have been done.
|
||||
* Calls the delayedSetup method for each dependency. <br> <br>
|
||||
*
|
||||
* This is done so we can have circular dependencies.
|
||||
*/
|
||||
public static void finishBinding()
|
||||
{
|
||||
dependencyHandler.finishBinding();
|
||||
}
|
||||
|
||||
/** returns whether the finishBinding method has been called */
|
||||
public static boolean bindingFinished()
|
||||
{
|
||||
return dependencyHandler.getBindingFinished();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.handlers.dependencyInjection;
|
||||
|
||||
/**
|
||||
* This class takes care of dependency injection
|
||||
* for singletons.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 3-5-2022
|
||||
*/
|
||||
public class SingletonHandler
|
||||
{
|
||||
private static final DependencyHandler dependencyHandler = new DependencyHandler();
|
||||
|
||||
|
||||
/**
|
||||
* Links the given implementation object to an interface so it can be referenced later.
|
||||
*
|
||||
* @param depenencyInterface The interface the implementation object should implement.
|
||||
* @param dependencyImplementation A object that implements the depenencyInterface interface.
|
||||
* @throws IllegalStateException if the implementation object doesn't implement
|
||||
* the interface or the interface has already been bound.
|
||||
*/
|
||||
public static void bind(Class<?> interfaceClass, Object singletonReference) throws IllegalStateException
|
||||
{
|
||||
dependencyHandler.bind(interfaceClass, singletonReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a dependency of type T if one has been bound.
|
||||
* Returns null otherwise.
|
||||
*
|
||||
* @param <T> class of the dependency
|
||||
* (inferred from the objectClass parameter)
|
||||
* @param interfaceClass Interface of the dependency
|
||||
* @return the dependency of type T
|
||||
* @throws NullPointerException If no dependency was bound.
|
||||
* @throws ClassCastException If the dependency isn't able to be cast to type T.
|
||||
* (this shouldn't normally happen, unless the bound object changed somehow)
|
||||
*/
|
||||
public static <T> T get(Class<T> interfaceClass) throws NullPointerException, ClassCastException
|
||||
{
|
||||
T foundObject = dependencyHandler.get(interfaceClass);
|
||||
|
||||
// throw an error if the given singleton doesn't exist.
|
||||
if (foundObject == null)
|
||||
{
|
||||
throw new NullPointerException("The singleton [" + interfaceClass.getSimpleName() + "] was never bound. If you are calling [bind], make sure it is happening before you call [get].");
|
||||
}
|
||||
|
||||
return foundObject;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Should only be called after all Binds have been done.
|
||||
* Calls the delayedSetup method for each dependency. <br> <br>
|
||||
*
|
||||
* This is done so we can have circular dependencies.
|
||||
*/
|
||||
public static void finishBinding()
|
||||
{
|
||||
dependencyHandler.finishBinding();
|
||||
}
|
||||
|
||||
/** returns whether the finishBinding method has been called */
|
||||
public static boolean getBindingFinished()
|
||||
{
|
||||
return dependencyHandler.getBindingFinished();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.handlers.dimensionFinder;
|
||||
|
||||
|
||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Data container for any player data we can use to differentiate one dimension from another.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-3-26
|
||||
*/
|
||||
public class PlayerData
|
||||
{
|
||||
public static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
private static final String playerDataFileName = "_playerData.toml";
|
||||
|
||||
|
||||
public static final String PLAYER_BLOCK_POS_X_PATH = "playerBlockPosX";
|
||||
public static final String PLAYER_BLOCK_POS_Y_PATH = "playerBlockPosY";
|
||||
public static final String PLAYER_BLOCK_POS_Z_PATH = "playerBlockPosZ";
|
||||
public AbstractBlockPosWrapper playerBlockPos;
|
||||
|
||||
// not implemented yet
|
||||
public static final String WORLD_SPAWN_POS_X_PATH = "worldSpawnBlockPosX";
|
||||
public static final String WORLD_SPAWN_POS_Y_PATH = "worldSpawnBlockPosY";
|
||||
public static final String WORLD_SPAWN_POS_Z_PATH = "worldSpawnBlockPosZ";
|
||||
/**
|
||||
* The client world has access to a spawn point, so this should be possible to fill in.
|
||||
* I'm not sure what this will look like for worlds that don't have a spawn point.
|
||||
*/
|
||||
public AbstractBlockPosWrapper worldSpawnPointBlockPos;
|
||||
|
||||
|
||||
|
||||
public PlayerData(IMinecraftClientWrapper mc)
|
||||
{
|
||||
updateData(mc);
|
||||
}
|
||||
|
||||
public PlayerData(File dimensionFolder)
|
||||
{
|
||||
File file = getFileForDimensionFolder(dimensionFolder);
|
||||
CommentedFileConfig toml = CommentedFileConfig.builder(file).build();
|
||||
|
||||
toml.load();
|
||||
|
||||
|
||||
// get the player block pos if it is specified
|
||||
if (toml.contains(PLAYER_BLOCK_POS_X_PATH)
|
||||
&& toml.contains(PLAYER_BLOCK_POS_Y_PATH)
|
||||
&& toml.contains(PLAYER_BLOCK_POS_Z_PATH))
|
||||
{
|
||||
int x = toml.getIntOrElse(PLAYER_BLOCK_POS_X_PATH, 0);
|
||||
int y = toml.getIntOrElse(PLAYER_BLOCK_POS_Y_PATH, 0);
|
||||
int z = toml.getIntOrElse(PLAYER_BLOCK_POS_Z_PATH, 0);
|
||||
this.playerBlockPos = FACTORY.createBlockPos(x, y, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.playerBlockPos = FACTORY.createBlockPos(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static File getFileForDimensionFolder(File file)
|
||||
{
|
||||
return new File(file.getPath() + File.separatorChar + playerDataFileName);
|
||||
}
|
||||
|
||||
|
||||
/** Should be called often to make sure this object is up to date with the player's info */
|
||||
public void updateData(IMinecraftClientWrapper mc)
|
||||
{
|
||||
if (mc.playerExists())
|
||||
{
|
||||
this.playerBlockPos = mc.getPlayerBlockPos();
|
||||
}
|
||||
}
|
||||
|
||||
/** Writes everything from this object to the file given. */
|
||||
public void toTomlFile(CommentedFileConfig toml)
|
||||
{
|
||||
if (playerBlockPos == null)
|
||||
{
|
||||
toml.remove(PLAYER_BLOCK_POS_X_PATH);
|
||||
toml.remove(PLAYER_BLOCK_POS_Y_PATH);
|
||||
toml.remove(PLAYER_BLOCK_POS_Z_PATH);
|
||||
} else {
|
||||
// player block pos
|
||||
toml.add(PLAYER_BLOCK_POS_X_PATH, playerBlockPos.getX());
|
||||
toml.add(PLAYER_BLOCK_POS_Y_PATH, playerBlockPos.getY());
|
||||
toml.add(PLAYER_BLOCK_POS_Z_PATH, playerBlockPos.getZ());
|
||||
}
|
||||
toml.save();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "PlayerBlockPos: [" + playerBlockPos.getX() + "," + playerBlockPos.getY() + "," + playerBlockPos.getZ() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.handlers.dimensionFinder;
|
||||
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Contains data used to compare different sub LodDimensions.
|
||||
* Sub Dimensions are the different folders under a dimension.
|
||||
* For example: "\Distant_Horizons_server_data\server_1\dim_the_nether\6fb97c01-e4c7-4634-87db-36b1e490e4c3"
|
||||
* is a sub dimension for the server "server_1" in the nether.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-3-26
|
||||
*/
|
||||
public class SubDimCompare implements Comparable<SubDimCompare>
|
||||
{
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
|
||||
|
||||
public int equalDataPoints = 0;
|
||||
public int totalDataPoints = 0;
|
||||
public int playerPosDist = 0;
|
||||
public File folder = null;
|
||||
|
||||
|
||||
public SubDimCompare(int newEqualDataPoints, int newTotalDataPoints, int newPlayerPosDistance, File newSubDimFolder)
|
||||
{
|
||||
this.equalDataPoints = newEqualDataPoints;
|
||||
this.totalDataPoints = newTotalDataPoints;
|
||||
this.playerPosDist = newPlayerPosDistance;
|
||||
|
||||
this.folder = newSubDimFolder;
|
||||
}
|
||||
|
||||
/** returns a number between 0 (not equal) and 1 (totally equal) */
|
||||
public double getPercentEqual()
|
||||
{
|
||||
return (double) equalDataPoints / (double) totalDataPoints;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull SubDimCompare other)
|
||||
{
|
||||
if (this.equalDataPoints != other.equalDataPoints)
|
||||
{
|
||||
// compare based on data points
|
||||
return Integer.compare(this.equalDataPoints, other.equalDataPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
// break ties based on player position
|
||||
return Integer.compare(this.playerPosDist, other.playerPosDist);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if this sub dimension is close enough to be considered a valid sub dimension */
|
||||
public boolean isValidSubDim()
|
||||
{
|
||||
double minimumSimilarityRequired = CONFIG.client().multiplayer().getMultiDimensionRequiredSimilarity();
|
||||
return this.getPercentEqual() >= minimumSimilarityRequired || this.playerPosDist <= 3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.logging;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.config.LoggerMode;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.message.Message;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigBasedLogger {
|
||||
public static final List<WeakReference<ConfigBasedLogger>> loggers
|
||||
= Collections.synchronizedList(new LinkedList<WeakReference<ConfigBasedLogger>>());
|
||||
public static synchronized void updateAll() {
|
||||
loggers.removeIf((logger) -> logger.get()==null);
|
||||
loggers.forEach((logger) -> {
|
||||
ConfigBasedLogger l = logger.get();
|
||||
if (l!=null) l.update();
|
||||
});
|
||||
}
|
||||
|
||||
private LoggerMode mode;
|
||||
private final Supplier<LoggerMode> getter;
|
||||
private final Logger logger;
|
||||
public ConfigBasedLogger(Logger logger, Supplier<LoggerMode> configQuery) {
|
||||
getter = configQuery;
|
||||
mode = getter.get();
|
||||
this.logger = logger;
|
||||
loggers.add(new WeakReference<>(this));
|
||||
}
|
||||
public void update() {
|
||||
mode = getter.get();
|
||||
}
|
||||
public boolean canMaybeLog() {return mode != LoggerMode.DISABLED;}
|
||||
|
||||
public void log(Level level, String str, Object... param) {
|
||||
|
||||
Message msg = logger.getMessageFactory().newMessage(str, param);
|
||||
String msgStr = msg.getFormattedMessage();
|
||||
if (mode.levelForFile.isLessSpecificThan(level)) {
|
||||
Level logLevel = level.isLessSpecificThan(Level.INFO) ? Level.INFO : level;
|
||||
if (param.length > 0 && param[param.length-1] instanceof Throwable)
|
||||
logger.log(logLevel, msgStr, (Throwable)param[param.length-1]);
|
||||
else logger.log(logLevel, msgStr);
|
||||
}
|
||||
if (mode.levelForChat.isLessSpecificThan(level)) {
|
||||
if (param.length > 0 && param[param.length-1] instanceof Throwable)
|
||||
ClientApi.logToChat(level, msgStr + "\nat\n" + Arrays.toString(((Throwable) param[param.length - 1]).getStackTrace()));
|
||||
else ClientApi.logToChat(level, msgStr);
|
||||
}
|
||||
}
|
||||
|
||||
public void error(String str, Object... param) {
|
||||
log(Level.ERROR, str, param);
|
||||
}
|
||||
public void warn(String str, Object... param) {
|
||||
log(Level.WARN, str, param);
|
||||
}
|
||||
public void info(String str, Object... param) {
|
||||
log(Level.INFO, str, param);
|
||||
}
|
||||
public void debug(String str, Object... param) {
|
||||
log(Level.DEBUG, str, param);
|
||||
}
|
||||
public void trace(String str, Object... param) {
|
||||
log(Level.TRACE, str, param);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.logging;
|
||||
|
||||
import com.seibel.lod.core.api.ApiShared;
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.config.LoggerMode;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.message.Message;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigBasedSpamLogger {
|
||||
public static final List<WeakReference<ConfigBasedSpamLogger>> loggers
|
||||
= Collections.synchronizedList(new LinkedList<WeakReference<ConfigBasedSpamLogger>>());
|
||||
public static synchronized void updateAll(boolean flush) {
|
||||
loggers.removeIf((logger) -> logger.get()==null);
|
||||
loggers.forEach((logger) -> {
|
||||
ConfigBasedSpamLogger l = logger.get();
|
||||
if (l!=null) l.update();
|
||||
if (l!=null && flush) l.reset();
|
||||
});
|
||||
}
|
||||
|
||||
private LoggerMode mode;
|
||||
private final Supplier<LoggerMode> getter;
|
||||
private final int maxLogCount;
|
||||
private final AtomicInteger logTries = new AtomicInteger(0);
|
||||
private final Logger logger;
|
||||
|
||||
public ConfigBasedSpamLogger(Logger logger, Supplier<LoggerMode> configQuery, int maxLogPerSec) {
|
||||
getter = configQuery;
|
||||
mode = getter.get();
|
||||
maxLogCount = maxLogPerSec;
|
||||
this.logger = logger;
|
||||
loggers.add(new WeakReference<>(this));
|
||||
}
|
||||
public void reset() {logTries.set(0);}
|
||||
public boolean canMaybeLog() {return mode != LoggerMode.DISABLED && logTries.get() < maxLogCount;}
|
||||
public void update() {
|
||||
mode = getter.get();
|
||||
}
|
||||
|
||||
public void log(Level level, String str, Object... param) {
|
||||
if (logTries.get() >= maxLogCount) return;
|
||||
|
||||
Message msg = logger.getMessageFactory().newMessage(str, param);
|
||||
String msgStr = msg.getFormattedMessage();
|
||||
if (mode.levelForFile.isLessSpecificThan(level)) {
|
||||
Level logLevel = level.isLessSpecificThan(Level.INFO) ? Level.INFO : level;
|
||||
if (param.length > 0 && param[param.length-1] instanceof Throwable)
|
||||
logger.log(logLevel, msgStr, (Throwable)param[param.length-1]);
|
||||
else logger.log(logLevel, msgStr);
|
||||
}
|
||||
if (mode.levelForChat.isLessSpecificThan(level)) {
|
||||
if (param.length > 0 && param[param.length - 1] instanceof Throwable)
|
||||
ClientApi.logToChat(level, msgStr + "\nat\n" + Arrays.toString(((Throwable) param[param.length - 1]).getStackTrace()));
|
||||
else ClientApi.logToChat(level, msgStr);
|
||||
}
|
||||
}
|
||||
|
||||
public void error(String str, Object... param) {
|
||||
log(Level.ERROR, str, param);
|
||||
}
|
||||
public void warn(String str, Object... param) {
|
||||
log(Level.WARN, str, param);
|
||||
}
|
||||
public void info(String str, Object... param) {
|
||||
log(Level.INFO, str, param);
|
||||
}
|
||||
public void debug(String str, Object... param) {
|
||||
log(Level.DEBUG, str, param);
|
||||
}
|
||||
public void trace(String str, Object... param) {
|
||||
log(Level.TRACE, str, param);
|
||||
}
|
||||
|
||||
public void incLogTries() {
|
||||
logTries.getAndIncrement();
|
||||
}
|
||||
|
||||
public void logInc(Level level, String str, Object... param) {
|
||||
if (logTries.getAndIncrement() >= maxLogCount) return;
|
||||
|
||||
Message msg = logger.getMessageFactory().newMessage(str, param);
|
||||
String msgStr = msg.getFormattedMessage();
|
||||
if (mode.levelForFile.isLessSpecificThan(level)) {
|
||||
Level logLevel = level.isLessSpecificThan(Level.INFO) ? Level.INFO : level;
|
||||
if (param.length > 0 && param[param.length-1] instanceof Throwable)
|
||||
logger.log(logLevel, msgStr, (Throwable)param[param.length-1]);
|
||||
else logger.log(logLevel, msgStr);
|
||||
}
|
||||
if (mode.levelForChat.isLessSpecificThan(level)) {
|
||||
if (param.length > 0 && param[param.length - 1] instanceof Throwable)
|
||||
ClientApi.logToChat(level, msgStr + "\nat\n" + Arrays.toString(((Throwable) param[param.length - 1]).getStackTrace()));
|
||||
else ClientApi.logToChat(level, msgStr);
|
||||
}
|
||||
}
|
||||
|
||||
public void errorInc(String str, Object... param) {
|
||||
logInc(Level.ERROR, str, param);
|
||||
}
|
||||
public void warnInc(String str, Object... param) {
|
||||
logInc(Level.WARN, str, param);
|
||||
}
|
||||
public void infoInc(String str, Object... param) {
|
||||
logInc(Level.INFO, str, param);
|
||||
}
|
||||
public void debugInc(String str, Object... param) {
|
||||
logInc(Level.DEBUG, str, param);
|
||||
}
|
||||
public void traceInc(String str, Object... param) {
|
||||
logInc(Level.TRACE, str, param);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.logging;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.seibel.lod.core.api.ApiShared;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
public class SpamReducedLogger {
|
||||
public static final List<WeakReference<SpamReducedLogger>> loggers
|
||||
= Collections.synchronizedList(new LinkedList<WeakReference<SpamReducedLogger>>());
|
||||
public static synchronized void flushAll() {
|
||||
loggers.removeIf((logger) -> logger.get()==null);
|
||||
loggers.forEach((logger) -> {
|
||||
SpamReducedLogger l = logger.get();
|
||||
if (l!=null) l.reset();
|
||||
});
|
||||
}
|
||||
|
||||
private final int maxLogCount;
|
||||
private final AtomicInteger logTries = new AtomicInteger(0);
|
||||
|
||||
public SpamReducedLogger(int maxLogPerSec) {
|
||||
maxLogCount = maxLogPerSec;
|
||||
loggers.add(new WeakReference<SpamReducedLogger>(this));
|
||||
}
|
||||
public void reset() {logTries.set(0);}
|
||||
public boolean canMaybeLog() {return logTries.get() < maxLogCount;}
|
||||
|
||||
public void log(Level level, String str, Object... param) {
|
||||
if (logTries.get() >= maxLogCount) return;
|
||||
ApiShared.LOGGER.log(level.isLessSpecificThan(Level.INFO) ? Level.INFO : level, str, param);
|
||||
}
|
||||
|
||||
public void error(String str, Object... param) {
|
||||
log(Level.ERROR, str, param);
|
||||
}
|
||||
public void warn(String str, Object... param) {
|
||||
log(Level.WARN, str, param);
|
||||
}
|
||||
public void info(String str, Object... param) {
|
||||
log(Level.INFO, str, param);
|
||||
}
|
||||
public void debug(String str, Object... param) {
|
||||
log(Level.DEBUG, str, param);
|
||||
}
|
||||
public void trace(String str, Object... param) {
|
||||
log(Level.TRACE, str, param);
|
||||
}
|
||||
|
||||
public void incLogTries() {
|
||||
logTries.getAndIncrement();
|
||||
}
|
||||
|
||||
public void logInc(Level level, String str, Object... param) {
|
||||
if (logTries.getAndIncrement() >= maxLogCount) return;
|
||||
ApiShared.LOGGER.log(level.isLessSpecificThan(Level.INFO) ? Level.INFO : level, str, param);
|
||||
}
|
||||
|
||||
public void errorInc(String str, Object... param) {
|
||||
logInc(Level.ERROR, str, param);
|
||||
}
|
||||
public void warnInc(String str, Object... param) {
|
||||
logInc(Level.WARN, str, param);
|
||||
}
|
||||
public void infoInc(String str, Object... param) {
|
||||
logInc(Level.INFO, str, param);
|
||||
}
|
||||
public void debugInc(String str, Object... param) {
|
||||
logInc(Level.DEBUG, str, param);
|
||||
}
|
||||
public void traceInc(String str, Object... param) {
|
||||
logInc(Level.TRACE, str, param);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.objects;
|
||||
|
||||
/*
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.BlockDetail;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
public class BlockBiomeCouple
|
||||
{
|
||||
public static final ConcurrentMap<BlockDetail, BlockBiomeCouple> noBiomeIstanceCache = new ConcurrentHashMap<>();
|
||||
public static ConcurrentMap<IBiomeWrapper, ConcurrentMap<BlockDetail, BlockBiomeCouple>> withBiomeIstanceCache = new ConcurrentHashMap<>();
|
||||
|
||||
String blockName;
|
||||
String biomeName;
|
||||
String coupleName;
|
||||
|
||||
IBiomeWrapper biomeColor;
|
||||
BlockDetail blockColor;
|
||||
|
||||
public static void addBlockBiomeToCache(IBlockColorWrapper blockColor){
|
||||
}
|
||||
|
||||
public static BlockBiomeCouple getBlockBiomeCouple(IBlockColorWrapper blockColor){
|
||||
if(noBiomeIstanceCache.containsKey(blockColor))
|
||||
{
|
||||
return noBiomeIstanceCache.get(blockColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockBiomeCouple couple = new BlockBiomeCouple(blockColor);
|
||||
noBiomeIstanceCache.put(blockColor,couple);
|
||||
return couple;
|
||||
}
|
||||
}
|
||||
|
||||
public static BlockBiomeCouple getBlockBiomeCouple(IBiomeWrapper biomeColor, IBlockColorWrapper blockColor){
|
||||
if(biomeColor == null)
|
||||
{
|
||||
return getBlockBiomeCouple(blockColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(withBiomeIstanceCache.containsKey(biomeColor))
|
||||
{
|
||||
withBiomeIstanceCache.put(biomeColor, new ConcurrentHashMap<>());
|
||||
}
|
||||
ConcurrentMap<IBlockColorWrapper, BlockBiomeCouple> blockToCoupleMap = withBiomeIstanceCache.get(biomeColor);
|
||||
if(blockToCoupleMap.containsKey(blockColor))
|
||||
{
|
||||
return blockToCoupleMap.get(blockColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockBiomeCouple couple = new BlockBiomeCouple(blockColor,biomeColor);
|
||||
blockToCoupleMap.put(blockColor,couple);
|
||||
return couple;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BlockBiomeCouple(IBlockColorWrapper blockColor)
|
||||
{
|
||||
this.biomeColor = null;
|
||||
this.blockColor = blockColor;
|
||||
biomeName = "";
|
||||
blockName = blockColor.getName();
|
||||
coupleName = blockName;
|
||||
}
|
||||
|
||||
public BlockBiomeCouple(IBlockColorWrapper blockColor, IBiomeWrapper biomeColor)
|
||||
{
|
||||
this.biomeColor = biomeColor;
|
||||
this.blockColor = blockColor;
|
||||
|
||||
if(biomeColor == null)
|
||||
biomeName = biomeColor.getName();
|
||||
else
|
||||
biomeName = "";
|
||||
|
||||
blockName = blockColor.getName();
|
||||
|
||||
coupleName = blockName + biomeName;
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof BlockBiomeCouple))
|
||||
return false;
|
||||
BlockBiomeCouple that = (BlockBiomeCouple) o;
|
||||
return Objects.equals(blockName, that.blockName) && Objects.equals(biomeName, that.biomeName);
|
||||
}
|
||||
|
||||
@Override public int hashCode()
|
||||
{
|
||||
return Objects.hash(blockName, biomeName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.objects;
|
||||
|
||||
public final class BoolType {
|
||||
public static final BoolType TRUE = new BoolType();
|
||||
public static final BoolType FALSE = null;
|
||||
private BoolType() {}
|
||||
}
|
||||
@@ -1,627 +0,0 @@
|
||||
/*
|
||||
* 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.core.objects;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.math.Vec3i;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
|
||||
/**
|
||||
* Similar to Minecraft's AxisAlignedBoundingBox.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 10-2-2021
|
||||
*/
|
||||
public class Box
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
|
||||
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 LodDirection[] DIRECTIONS = new LodDirection[] {
|
||||
LodDirection.UP,
|
||||
LodDirection.DOWN,
|
||||
LodDirection.WEST,
|
||||
LodDirection.EAST,
|
||||
LodDirection.NORTH,
|
||||
LodDirection.SOUTH };
|
||||
|
||||
/** North, South, East, West */
|
||||
public static final LodDirection[] ADJ_DIRECTIONS = new LodDirection[] {
|
||||
LodDirection.EAST,
|
||||
LodDirection.WEST,
|
||||
LodDirection.SOUTH,
|
||||
LodDirection.NORTH };
|
||||
|
||||
/** All the faces and vertices of a cube. This is used to extract the vertex from the column */
|
||||
@SuppressWarnings("serial")
|
||||
public static final Map<LodDirection, int[][]> DIRECTION_VERTEX_MAP = new HashMap<LodDirection, int[][]>()
|
||||
{{
|
||||
put(LodDirection.UP, new int[][] {
|
||||
{ 0, 1, 0 }, // 0
|
||||
{ 0, 1, 1 }, // 1
|
||||
{ 1, 1, 1 }, // 2
|
||||
|
||||
{ 0, 1, 0 }, // 0
|
||||
{ 1, 1, 1 }, // 2
|
||||
{ 1, 1, 0 } // 3
|
||||
});
|
||||
put(LodDirection.DOWN, new int[][] {
|
||||
{ 1, 0, 0 }, // 0
|
||||
{ 1, 0, 1 }, // 1
|
||||
{ 0, 0, 1 }, // 2
|
||||
|
||||
{ 1, 0, 0 }, // 0
|
||||
{ 0, 0, 1 }, // 2
|
||||
{ 0, 0, 0 } // 3
|
||||
});
|
||||
put(LodDirection.EAST, new int[][] {
|
||||
{ 1, 1, 0 }, // 0
|
||||
{ 1, 1, 1 }, // 1
|
||||
{ 1, 0, 1 }, // 2
|
||||
|
||||
{ 1, 1, 0 }, // 0
|
||||
{ 1, 0, 1 }, // 2
|
||||
{ 1, 0, 0 } }); // 3
|
||||
put(LodDirection.WEST, new int[][] {
|
||||
{ 0, 0, 0 }, // 0
|
||||
{ 0, 0, 1 }, // 1
|
||||
{ 0, 1, 1 }, // 2
|
||||
|
||||
{ 0, 0, 0 }, // 0
|
||||
{ 0, 1, 1 }, // 2
|
||||
{ 0, 1, 0 } // 3
|
||||
});
|
||||
put(LodDirection.SOUTH, new int[][] {
|
||||
{ 1, 0, 1 }, // 0
|
||||
{ 1, 1, 1 }, // 1
|
||||
{ 0, 1, 1 }, // 2
|
||||
|
||||
{ 1, 0, 1 }, // 0
|
||||
{ 0, 1, 1 }, // 2
|
||||
{ 0, 0, 1 } // 3
|
||||
});
|
||||
put(LodDirection.NORTH, new int[][] {
|
||||
{ 0, 0, 0 }, // 0
|
||||
{ 0, 1, 0 }, // 1
|
||||
{ 1, 1, 0 }, // 2
|
||||
|
||||
{ 0, 0, 0 }, // 0
|
||||
{ 1, 1, 0 }, // 2
|
||||
{ 1, 0, 0 } // 3
|
||||
});
|
||||
}};
|
||||
|
||||
|
||||
/**
|
||||
* This indicates which position is invariable in the DIRECTION_VERTEX_MAP.
|
||||
* Is used to extract the vertex
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static final Map<LodDirection, int[]> FACE_DIRECTION = new HashMap<LodDirection, int[]>()
|
||||
{{
|
||||
put(LodDirection.UP, new int[] { Y, MAX });
|
||||
put(LodDirection.DOWN, new int[] { Y, MIN });
|
||||
put(LodDirection.EAST, new int[] { X, MAX });
|
||||
put(LodDirection.WEST, new int[] { X, MIN });
|
||||
put(LodDirection.SOUTH, new int[] { Z, MAX });
|
||||
put(LodDirection.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
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static final Map<LodDirection, Vec3i> DIRECTION_NORMAL_MAP = new HashMap<LodDirection, Vec3i>()
|
||||
{{
|
||||
put(LodDirection.UP, LodDirection.UP.getNormal());
|
||||
put(LodDirection.DOWN, LodDirection.DOWN.getNormal());
|
||||
put(LodDirection.EAST, LodDirection.EAST.getNormal());
|
||||
put(LodDirection.WEST, LodDirection.WEST.getNormal());
|
||||
put(LodDirection.SOUTH, LodDirection.SOUTH.getNormal());
|
||||
put(LodDirection.NORTH, LodDirection.NORTH.getNormal());
|
||||
}};
|
||||
|
||||
/** We use this index for all array that are going to */
|
||||
@SuppressWarnings("serial")
|
||||
public static final Map<LodDirection, Integer> DIRECTION_INDEX = new HashMap<LodDirection, Integer>()
|
||||
{{
|
||||
put(LodDirection.UP, 0);
|
||||
put(LodDirection.DOWN, 1);
|
||||
put(LodDirection.EAST, 2);
|
||||
put(LodDirection.WEST, 3);
|
||||
put(LodDirection.SOUTH, 4);
|
||||
put(LodDirection.NORTH, 5);
|
||||
}};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static final Map<LodDirection, Integer> ADJ_DIRECTION_INDEX = new HashMap<LodDirection, Integer>()
|
||||
{{
|
||||
put(LodDirection.EAST, 0);
|
||||
put(LodDirection.WEST, 1);
|
||||
put(LodDirection.SOUTH, 2);
|
||||
put(LodDirection.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<LodDirection, int[]> adjHeight;
|
||||
public final Map<LodDirection, int[]> adjDepth;
|
||||
public final Map<LodDirection, byte[]> skyLights;
|
||||
public byte blockLight;
|
||||
|
||||
/** Holds if the given direction should be culled or not */
|
||||
public final boolean[] culling;
|
||||
|
||||
|
||||
/** creates an empty box */
|
||||
@SuppressWarnings("serial")
|
||||
public Box()
|
||||
{
|
||||
boxOffset = new int[3];
|
||||
boxWidth = new int[3];
|
||||
|
||||
colorMap = new int[6];
|
||||
skyLights = new HashMap<LodDirection, byte[]>()
|
||||
{{
|
||||
put(LodDirection.UP, new byte[1]);
|
||||
put(LodDirection.DOWN, new byte[1]);
|
||||
put(LodDirection.EAST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.WEST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.SOUTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.NORTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
}};
|
||||
adjHeight = new HashMap<LodDirection, int[]>()
|
||||
{{
|
||||
put(LodDirection.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
}};
|
||||
adjDepth = new HashMap<LodDirection, int[]>()
|
||||
{{
|
||||
put(LodDirection.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.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(LodDirection.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 (LodDirection lodDirection : DIRECTIONS)
|
||||
{
|
||||
if (!adjShadeDisabled[DIRECTION_INDEX.get(lodDirection)])
|
||||
colorMap[DIRECTION_INDEX.get(lodDirection)] = ColorUtil.applyShade(color, MC.getShade(lodDirection));
|
||||
else
|
||||
colorMap[DIRECTION_INDEX.get(lodDirection)] = color;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lodDirection of the face of which we want to get the color
|
||||
* @return color of the face
|
||||
*/
|
||||
public int getColor(LodDirection lodDirection)
|
||||
{
|
||||
if (CONFIG.client().advanced().debugging().getDebugMode() != DebugMode.SHOW_DETAIL)
|
||||
return colorMap[DIRECTION_INDEX.get(lodDirection)];
|
||||
else
|
||||
return ColorUtil.applyShade(color, MC.getShade(lodDirection));
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public byte getSkyLight(LodDirection lodDirection, int verticalIndex)
|
||||
{
|
||||
if(lodDirection == LodDirection.UP || lodDirection == LodDirection.DOWN)
|
||||
return skyLights.get(lodDirection)[0];
|
||||
else
|
||||
return skyLights.get(lodDirection)[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 (LodDirection lodDirection : ADJ_DIRECTIONS)
|
||||
{
|
||||
for (int i = 0; i < adjHeight.get(lodDirection).length; i++)
|
||||
{
|
||||
adjHeight.get(lodDirection)[i] = VOID_FACE;
|
||||
adjDepth.get(lodDirection)[i] = VOID_FACE;
|
||||
skyLights.get(lodDirection)[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** determine which faces should be culled */
|
||||
public void setUpCulling(int cullingDistance, AbstractBlockPosWrapper playerPos)
|
||||
{
|
||||
for (LodDirection lodDirection : DIRECTIONS)
|
||||
{
|
||||
if (lodDirection == LodDirection.DOWN || lodDirection == LodDirection.WEST || lodDirection == LodDirection.NORTH)
|
||||
culling[DIRECTION_INDEX.get(lodDirection)] = playerPos.get(lodDirection.getAxis()) > getFacePos(lodDirection) + cullingDistance;
|
||||
|
||||
else if (lodDirection == LodDirection.UP || lodDirection == LodDirection.EAST || lodDirection == LodDirection.SOUTH)
|
||||
culling[DIRECTION_INDEX.get(lodDirection)] = playerPos.get(lodDirection.getAxis()) < getFacePos(lodDirection) - cullingDistance;
|
||||
|
||||
culling[DIRECTION_INDEX.get(lodDirection)] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lodDirection 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(LodDirection lodDirection)
|
||||
{
|
||||
return culling[DIRECTION_INDEX.get(lodDirection)];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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<LodDirection, 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(LodDirection.DOWN)[0];
|
||||
if(DataPointUtil.doesItExist(singleAdjDataPoint))
|
||||
skyLights.get(LodDirection.DOWN)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||
else
|
||||
skyLights.get(LodDirection.DOWN)[0] = skyLights.get(LodDirection.UP)[0];
|
||||
//other sided
|
||||
//TODO clean some similar cases
|
||||
for (LodDirection lodDirection : ADJ_DIRECTIONS)
|
||||
{
|
||||
if (isCulled(lodDirection))
|
||||
continue;
|
||||
|
||||
long[] dataPoint = adjData.get(lodDirection);
|
||||
if (dataPoint == null || DataPointUtil.isVoid(dataPoint[0]))
|
||||
{
|
||||
adjHeight.get(lodDirection)[0] = maxY;
|
||||
adjDepth.get(lodDirection)[0] = minY;
|
||||
adjHeight.get(lodDirection)[1] = VOID_FACE;
|
||||
adjDepth.get(lodDirection)[1] = VOID_FACE;
|
||||
skyLights.get(lodDirection)[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(lodDirection)[0] = getMaxY();
|
||||
adjDepth.get(lodDirection)[0] = getMinY();
|
||||
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
adjDepth.get(lodDirection)[faceToDraw] = getMinY();
|
||||
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||
}
|
||||
faceToDraw++;
|
||||
toFinish = false;
|
||||
|
||||
// break since all the other data will be lower
|
||||
break;
|
||||
}
|
||||
else if (depth <= minY)
|
||||
{
|
||||
if (height >= maxY)
|
||||
{
|
||||
// the adj data is inside the current data
|
||||
// don't draw the face
|
||||
adjHeight.get(lodDirection)[0] = VOID_FACE;
|
||||
adjDepth.get(lodDirection)[0] = VOID_FACE;
|
||||
}
|
||||
else // 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(lodDirection)[0] = getMaxY();
|
||||
adjDepth.get(lodDirection)[0] = height;
|
||||
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
adjDepth.get(lodDirection)[faceToDraw] = height;
|
||||
skyLights.get(lodDirection)[faceToDraw] = 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(lodDirection)[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(lodDirection)[0] = getMaxY();
|
||||
}
|
||||
|
||||
adjDepth.get(lodDirection)[faceToDraw] = height;
|
||||
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||
faceToDraw++;
|
||||
adjHeight.get(lodDirection)[faceToDraw] = depth;
|
||||
firstFace = false;
|
||||
toFinish = true;
|
||||
toFinishIndex = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allAbove)
|
||||
{
|
||||
adjHeight.get(lodDirection)[0] = getMaxY();
|
||||
adjDepth.get(lodDirection)[0] = getMinY();
|
||||
skyLights.get(lodDirection)[0] = skyLights.get(LodDirection.UP)[0];
|
||||
faceToDraw++;
|
||||
}
|
||||
else if (toFinish)
|
||||
{
|
||||
adjDepth.get(lodDirection)[faceToDraw] = minY;
|
||||
if(toFinishIndex < dataPoint.length)
|
||||
{
|
||||
singleAdjDataPoint = dataPoint[toFinishIndex];
|
||||
if (DataPointUtil.doesItExist(singleAdjDataPoint))
|
||||
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||
else
|
||||
skyLights.get(lodDirection)[faceToDraw] = skyLights.get(LodDirection.UP)[0];
|
||||
}
|
||||
faceToDraw++;
|
||||
}
|
||||
|
||||
adjHeight.get(lodDirection)[faceToDraw] = VOID_FACE;
|
||||
adjDepth.get(lodDirection)[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 lodDirection that we want to check
|
||||
* @return position in the axis of the face
|
||||
*/
|
||||
public int getFacePos(LodDirection lodDirection)
|
||||
{
|
||||
return boxOffset[FACE_DIRECTION.get(lodDirection)[0]] + boxWidth[FACE_DIRECTION.get(lodDirection)[0]] * FACE_DIRECTION.get(lodDirection)[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if the given direction should be rendered.
|
||||
*/
|
||||
public boolean shouldRenderFace(LodDirection lodDirection, int adjIndex)
|
||||
{
|
||||
if (lodDirection == LodDirection.UP || lodDirection == LodDirection.DOWN)
|
||||
return adjIndex == 0;
|
||||
return !(adjHeight.get(lodDirection)[adjIndex] == VOID_FACE && adjDepth.get(lodDirection)[adjIndex] == VOID_FACE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param lodDirection 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(LodDirection lodDirection, int vertexIndex)
|
||||
{
|
||||
return boxOffset[X] + boxWidth[X] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][X];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lodDirection 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(LodDirection lodDirection, int vertexIndex)
|
||||
{
|
||||
return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lodDirection 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(LodDirection lodDirection, int vertexIndex, int adjIndex)
|
||||
{
|
||||
if (lodDirection == LodDirection.DOWN || lodDirection == LodDirection.UP)
|
||||
return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y];
|
||||
else
|
||||
{
|
||||
// this could probably be cleaned up more,
|
||||
// but it still works
|
||||
if (1 - DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y] == ADJACENT_HEIGHT_INDEX)
|
||||
return adjHeight.get(lodDirection)[adjIndex];
|
||||
else
|
||||
return adjDepth.get(lodDirection)[adjIndex];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lodDirection 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(LodDirection lodDirection, int vertexIndex)
|
||||
{
|
||||
return boxOffset[Z] + boxWidth[Z] * DIRECTION_VERTEX_MAP.get(lodDirection)[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];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -28,9 +28,9 @@ package com.seibel.lod.core.objects;
|
||||
*/
|
||||
public class MinDefaultMax<T>
|
||||
{
|
||||
public T minValue;
|
||||
public T defaultValue;
|
||||
public T maxValue;
|
||||
public final T minValue;
|
||||
public final T defaultValue;
|
||||
public final T maxValue;
|
||||
|
||||
public MinDefaultMax(T newMinValue, T newDefaultValue, T newMaxValue)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.objects;
|
||||
|
||||
|
||||
/**
|
||||
* Represents an IP and includes a couple helper methods.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 3-7-2022
|
||||
*/
|
||||
public class ParsedIp
|
||||
{
|
||||
/** can be used to find numeric IPs, IE: "192.168.1.19" */
|
||||
public static final String NUMERIC_IP_REGEX = "^[0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*(:[0-9]*)?$";
|
||||
|
||||
/**
|
||||
* Can be used to find if a numeric IP is a LAN IP
|
||||
*
|
||||
* Ip list source: <br>
|
||||
* https://networkengineering.stackexchange.com/questions/5825/why-192-168-for-local-addresses
|
||||
*/
|
||||
public static final String LAN_IP_REGEX = "(10|172\\.16|192\\.168).*";
|
||||
|
||||
|
||||
/** Examples: "192.168.1.19", "mc.hypixel.net", or "localhost" */
|
||||
public final String ip;
|
||||
/**
|
||||
* null if the ip isn't numeric (IE: "mc.hypixel.net" or "localhost") <br>
|
||||
* Example: "25586"
|
||||
*/
|
||||
public final String port;
|
||||
public final boolean isNumeric;
|
||||
|
||||
|
||||
/** parses a standard IP string */
|
||||
public ParsedIp(String fullIp)
|
||||
{
|
||||
fullIp = fullIp.trim();
|
||||
|
||||
isNumeric = fullIp.matches(NUMERIC_IP_REGEX);
|
||||
if (isNumeric)
|
||||
{
|
||||
// attempt to separate the IP and the Port
|
||||
String[] list = fullIp.split(":");
|
||||
if (list.length == 2)
|
||||
{
|
||||
// IP and Port successfully separated
|
||||
ip = list[0];
|
||||
port = list[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// this IP must not have a port
|
||||
ip = fullIp;
|
||||
port = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// text based IP, IE: "localhost"
|
||||
ip = fullIp;
|
||||
port = null;
|
||||
}
|
||||
}
|
||||
|
||||
public ParsedIp(String newIp, String newPort)
|
||||
{
|
||||
ip = newIp;
|
||||
port = newPort;
|
||||
isNumeric = ip.matches(NUMERIC_IP_REGEX);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** Returns if this IP is for a Local Area Network connection */
|
||||
public boolean isLan()
|
||||
{
|
||||
return ip.toLowerCase().equals("localhost") || ip.matches(LAN_IP_REGEX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return ip +
|
||||
// only print the ":port" if a port is present
|
||||
(port != null ? (":" + port) : "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.objects;
|
||||
|
||||
public final class Pos2D {
|
||||
public final int x;
|
||||
public final int y;
|
||||
public Pos2D(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -31,7 +31,6 @@ public class PosToGenerateContainer
|
||||
{
|
||||
private final int playerPosX;
|
||||
private final int playerPosZ;
|
||||
private final byte farMinDetail;
|
||||
private int nearSize;
|
||||
private int farSize;
|
||||
|
||||
@@ -39,14 +38,10 @@ public class PosToGenerateContainer
|
||||
private final int[][] nearPosToGenerate;
|
||||
private final int[][] farPosToGenerate;
|
||||
|
||||
|
||||
|
||||
|
||||
public PosToGenerateContainer(byte farMinDetail, int maxDataToGenerate, int playerPosX, int playerPosZ)
|
||||
public PosToGenerateContainer(int maxDataToGenerate, int playerPosX, int playerPosZ)
|
||||
{
|
||||
this.playerPosX = playerPosX;
|
||||
this.playerPosZ = playerPosZ;
|
||||
this.farMinDetail = farMinDetail;
|
||||
nearSize = 0;
|
||||
farSize = 0;
|
||||
nearPosToGenerate = new int[maxDataToGenerate][4];
|
||||
@@ -56,46 +51,24 @@ public class PosToGenerateContainer
|
||||
|
||||
|
||||
// TODO what is going on in this method?
|
||||
public void addPosToGenerate(byte detailLevel, int posX, int posZ)
|
||||
public void addNearPosToGenerate(byte detailLevel, int posX, int posZ, boolean sort)
|
||||
{
|
||||
int distance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ);
|
||||
// FIXME: This is a cast from double to int!!! OVERFLOW MAY HAPPEN!
|
||||
int distance = (int)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--;
|
||||
//We are introducing a position in the near array
|
||||
|
||||
index = nearSize;
|
||||
if (index == nearPosToGenerate.length) {
|
||||
if (Integer.compare(distance, nearPosToGenerate[index - 1][3]) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
index--;
|
||||
} else nearSize++;
|
||||
|
||||
if (sort) {
|
||||
while (index > 0 && Integer.compare(distance, nearPosToGenerate[index - 1][3]) <= 0)
|
||||
{
|
||||
nearPosToGenerate[index][0] = nearPosToGenerate[index - 1][0];
|
||||
nearPosToGenerate[index][1] = nearPosToGenerate[index - 1][1];
|
||||
@@ -103,16 +76,48 @@ public class PosToGenerateContainer
|
||||
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;
|
||||
}
|
||||
|
||||
// TODO what is going on in this method?
|
||||
public void addFarPosToGenerate(byte detailLevel, int posX, int posZ, boolean sort)
|
||||
{
|
||||
// FIXME: This is a cast from double to int!!! OVERFLOW MAY HAPPEN!
|
||||
int distance = (int)LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ);
|
||||
int index;
|
||||
|
||||
// We are introducing a position in the far array
|
||||
|
||||
index = farSize;
|
||||
if (index == farPosToGenerate.length) {
|
||||
if (Integer.compare(distance, farPosToGenerate[index - 1][3]) > 0) {
|
||||
return;
|
||||
}
|
||||
index--;
|
||||
} else farSize++;
|
||||
|
||||
if (sort) {
|
||||
while (index > 0 && Integer.compare(distance, farPosToGenerate[index - 1][3]) <= 0)
|
||||
{
|
||||
nearPosToGenerate[index][0] = detailLevel + 1;
|
||||
nearPosToGenerate[index][1] = posX;
|
||||
nearPosToGenerate[index][2] = posZ;
|
||||
nearPosToGenerate[index][3] = distance;
|
||||
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--;
|
||||
}
|
||||
}
|
||||
farPosToGenerate[index][0] = detailLevel + 1;
|
||||
farPosToGenerate[index][1] = posX;
|
||||
farPosToGenerate[index][2] = posZ;
|
||||
farPosToGenerate[index][3] = distance;
|
||||
}
|
||||
|
||||
public boolean isFull() {
|
||||
return nearSize == nearPosToGenerate.length && farSize == farPosToGenerate.length;
|
||||
}
|
||||
|
||||
|
||||
@@ -132,6 +137,16 @@ public class PosToGenerateContainer
|
||||
return farSize;
|
||||
}
|
||||
|
||||
public int getMaxNumberOfNearPos()
|
||||
{
|
||||
return nearPosToGenerate.length;
|
||||
}
|
||||
|
||||
public int getMaxNumberOfFarPos()
|
||||
{
|
||||
return farPosToGenerate.length;
|
||||
}
|
||||
|
||||
// TODO what does getNth mean? could the name be more descriptive or is it just a index?
|
||||
public int getNthDetail(int n, boolean near)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -21,7 +21,7 @@ package com.seibel.lod.core.objects;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.api.ApiShared;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
|
||||
@@ -60,7 +60,7 @@ public class PosToRenderContainer
|
||||
{
|
||||
// This is might be due to dimensions having a different width
|
||||
// when first loading in
|
||||
ClientApi.LOGGER.error("Unable to addPosToRender. numberOfPosToRender [" + numberOfPosToRender + "] detailLevel [" + detailLevel + "] Pos [" + posX + "," + posZ + "]");
|
||||
ApiShared.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;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -19,6 +19,9 @@
|
||||
|
||||
package com.seibel.lod.core.objects.lod;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A level container is a quad tree level
|
||||
*/
|
||||
@@ -39,9 +42,15 @@ public interface LevelContainer
|
||||
* @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
|
||||
* @return true if correctly changed, false otherwise
|
||||
*/
|
||||
boolean addVerticalData(long[] data, int posX, int posZ);
|
||||
boolean addVerticalData(long[] data, int posX, int posZ, boolean override);
|
||||
|
||||
/**
|
||||
* With this you can add a square of data to the level container
|
||||
* @return true if anything changed, false otherwise
|
||||
*/
|
||||
boolean addChunkOfData(long[] data, int posX, int posZ, int widthX, int widthZ, boolean override);
|
||||
|
||||
/**
|
||||
* With this you can add data to the level container
|
||||
@@ -59,6 +68,14 @@ public interface LevelContainer
|
||||
* @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[] getAllData(int posX, int posZ);
|
||||
|
||||
/**
|
||||
* With this you can get data from the level container
|
||||
@@ -81,7 +98,7 @@ public interface LevelContainer
|
||||
byte getDetailLevel();
|
||||
|
||||
|
||||
int getMaxVerticalData();
|
||||
int getVerticalSize();
|
||||
|
||||
/** Clears the dataPoint at the given array index */
|
||||
void clear(int posX, int posZ);
|
||||
@@ -99,17 +116,24 @@ public interface LevelContainer
|
||||
* @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
|
||||
* This will write the raw data with metadata to the output stream
|
||||
* @return isAllGenerated whether the data is all generated
|
||||
* @throws IOException
|
||||
*/
|
||||
byte[] toDataString();
|
||||
|
||||
boolean writeData(DataOutputStream output) throws IOException;
|
||||
|
||||
/**
|
||||
* This will give the data to save in the file
|
||||
* @return data as a String
|
||||
*/
|
||||
int getMaxNumberOfLods();
|
||||
|
||||
/**
|
||||
* This will return a ram usage estimation of this object
|
||||
* @return long as byte
|
||||
*/
|
||||
long getRoughRamUsage();
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -22,15 +22,14 @@ package com.seibel.lod.core.objects.lod;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.api.ApiShared;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
|
||||
/**
|
||||
* This stores all LODs for a given world.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @author Leonardo Amato
|
||||
* @version 9-27-2021
|
||||
* @version 2022-3-29
|
||||
*/
|
||||
public class LodWorld
|
||||
{
|
||||
@@ -44,7 +43,7 @@ public class LodWorld
|
||||
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 static final String NO_WORLD_LOADED = "";
|
||||
|
||||
|
||||
|
||||
@@ -65,25 +64,20 @@ public class LodWorld
|
||||
*/
|
||||
public void selectWorld(String newWorldName)
|
||||
{
|
||||
if (newWorldName.isEmpty())
|
||||
{
|
||||
deselectWorld();
|
||||
return;
|
||||
}
|
||||
|
||||
ApiShared.LOGGER.info("Selecting world {} while in world {}", newWorldName, worldName);
|
||||
if (worldName.equals(newWorldName))
|
||||
// don't recreate everything if we
|
||||
// didn't actually change worlds
|
||||
return;
|
||||
|
||||
deselectWorld();
|
||||
worldName = newWorldName;
|
||||
lodDimensions = new Hashtable<>();
|
||||
isWorldLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the worldName to "No world loaded"
|
||||
* and clear the lodDimensions Map. <br>
|
||||
* 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>
|
||||
@@ -91,7 +85,9 @@ public class LodWorld
|
||||
*/
|
||||
public void deselectWorld()
|
||||
{
|
||||
ApiShared.LOGGER.info("Deselecting world {}", worldName);
|
||||
worldName = NO_WORLD_LOADED;
|
||||
saveAllDimensions(true); // Make sure all dims are saved. This will block threads
|
||||
lodDimensions = null;
|
||||
isWorldLoaded = false;
|
||||
}
|
||||
@@ -105,8 +101,11 @@ public class LodWorld
|
||||
{
|
||||
if (lodDimensions == null)
|
||||
return;
|
||||
ApiShared.LOGGER.info("Adding dim {} to world {}", newDimension, worldName);
|
||||
|
||||
lodDimensions.put(newDimension.dimension, newDimension);
|
||||
LodDimension oldDim = lodDimensions.put(newDimension.dimension, newDimension);
|
||||
if (oldDim != null)
|
||||
oldDim.saveDirtyRegionsToFile(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +128,7 @@ public class LodWorld
|
||||
if (lodDimensions == null)
|
||||
return;
|
||||
|
||||
saveAllDimensions();
|
||||
saveAllDimensions(true); //block until saving is done
|
||||
|
||||
for (IDimensionTypeWrapper key : lodDimensions.keySet())
|
||||
lodDimensions.get(key).setRegionWidth(newRegionWidth);
|
||||
@@ -138,17 +137,35 @@ public class LodWorld
|
||||
/**
|
||||
* Requests all dimensions save any dirty regions they may have.
|
||||
*/
|
||||
public void saveAllDimensions()
|
||||
public void saveAllDimensions(boolean isBlocking)
|
||||
{
|
||||
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
|
||||
ClientApi.LOGGER.info("Saving LODs");
|
||||
ApiShared.LOGGER.info("Saving LODs");
|
||||
|
||||
for (IDimensionTypeWrapper key : lodDimensions.keySet())
|
||||
lodDimensions.get(key).saveDirtyRegionsToFileAsync();
|
||||
{
|
||||
lodDimensions.get(key).saveDirtyRegionsToFile(isBlocking);
|
||||
}
|
||||
//FIXME: This should block until file is saved.
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests all dimensions to shutdown
|
||||
*/
|
||||
public void shutdownAllDimensions()
|
||||
{
|
||||
if (lodDimensions == null)
|
||||
return;
|
||||
|
||||
// TODO: Add parallel shutdowns.
|
||||
for (IDimensionTypeWrapper key : lodDimensions.keySet())
|
||||
{
|
||||
lodDimensions.get(key).shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -19,8 +19,8 @@
|
||||
|
||||
package com.seibel.lod.core.objects.lod;
|
||||
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
@@ -82,10 +82,39 @@ public class RegionPos
|
||||
.offset(LodUtil.CHUNK_WIDTH / 2, 0, LodUtil.CHUNK_WIDTH / 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
// If the object is compared with itself then return true
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
// Check if o is an instance of RegionPos or not
|
||||
if (!(o instanceof RegionPos)) {
|
||||
return false;
|
||||
}
|
||||
RegionPos c = (RegionPos) o;
|
||||
return c.x==x &&c.z==z;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "(" + x + "," + z + ")";
|
||||
}
|
||||
|
||||
public static long asLong(int i, int j) {
|
||||
return (long)i & 0xFFFFFFFFL | ((long)j & 0xFFFFFFFFL) << 32;
|
||||
}
|
||||
public static int getX(long l) {
|
||||
return (int)(l & 0xFFFFFFFFL);
|
||||
}
|
||||
public static int getZ(long l) {
|
||||
return (int)(l >>> 32 & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Long.hashCode(asLong(x,z));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -19,32 +19,53 @@
|
||||
|
||||
package com.seibel.lod.core.objects.lod;
|
||||
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.util.*;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version ??
|
||||
*/
|
||||
public class VerticalLevelContainer implements LevelContainer
|
||||
{
|
||||
|
||||
private final short minHeight;
|
||||
public final byte detailLevel;
|
||||
public final int size;
|
||||
public final int maxVerticalData;
|
||||
public final int verticalSize;
|
||||
|
||||
public final long[] dataContainer;
|
||||
|
||||
//Currently these variable are not used. We are going to use them in the new data format
|
||||
public final int[] verticalDataContainer = null;
|
||||
public final int[] colorDataContainer = null;
|
||||
public final byte[] lightDataContainer = null;
|
||||
public final short[] positionDataContainer = null;
|
||||
|
||||
/*WE PROBABLY ARE GOING TO USE THIS IN THE FUTURE
|
||||
FOR NOW WE KEEP THE OLD SYSTEM
|
||||
public final short[] sectionVerticalSize = null;
|
||||
public final int[][] verticalDataContainer = null;
|
||||
public final int[][] colorDataContainer = null;
|
||||
public final byte[][] lightDataContainer = null;
|
||||
public final short[] positionDataContainer = null;
|
||||
*/
|
||||
|
||||
public VerticalLevelContainer(byte detailLevel)
|
||||
{
|
||||
this.detailLevel = detailLevel;
|
||||
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
maxVerticalData = DetailDistanceUtil.getMaxVerticalData(detailLevel);
|
||||
verticalSize = DetailDistanceUtil.getMaxVerticalData(detailLevel);
|
||||
dataContainer = new long[size * size * DetailDistanceUtil.getMaxVerticalData(detailLevel)];
|
||||
minHeight = SingletonHandler.get(IMinecraftClientWrapper.class).getWrappedClientWorld().getMinHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,31 +77,55 @@ public class VerticalLevelContainer implements LevelContainer
|
||||
@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;
|
||||
}
|
||||
for (int verticalIndex = 0; verticalIndex < verticalSize; verticalIndex++)
|
||||
dataContainer[posX * size * verticalSize + posZ * verticalSize + 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;
|
||||
dataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex] = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void forceWriteVerticalData(long[] data, int posX, int posZ)
|
||||
{
|
||||
int index = posX * size * verticalSize + posZ * verticalSize;
|
||||
if (verticalSize >= 0) System.arraycopy(data, 0, dataContainer, index + 0, verticalSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addVerticalData(long[] data, int posX, int posZ, boolean override)
|
||||
{
|
||||
int index = posX * size * verticalSize + posZ * verticalSize;
|
||||
int compare = DataPointUtil.compareDatapointPriority(data[0], dataContainer[index]);
|
||||
if (override) {
|
||||
if (compare<0) return false;
|
||||
} else {
|
||||
if (compare<=0) return false;
|
||||
}
|
||||
forceWriteVerticalData(data, posX, posZ);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addVerticalData(long[] data, int posX, int posZ)
|
||||
public boolean addChunkOfData(long[] data, int posX, int posZ, int widthX, int widthZ, boolean override)
|
||||
{
|
||||
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;
|
||||
boolean anyChange = false;
|
||||
if (posX+widthX > size || posZ+widthZ > size)
|
||||
throw new IndexOutOfBoundsException("addChunkOfData param not inside valid range");
|
||||
if (widthX*widthZ*verticalSize > data.length)
|
||||
throw new IndexOutOfBoundsException("addChunkOfData data array not long enough to contain the data to be copied");
|
||||
if (posX<0 || posZ<0 || widthX<0 || widthZ<0)
|
||||
throw new IndexOutOfBoundsException("addChunkOfData param is negative");
|
||||
|
||||
for (int ox=0; ox<widthX; ox++) {
|
||||
anyChange = DataPointUtil.mergeTwoDataArray(
|
||||
dataContainer, ((ox+posX)*size+posZ) * verticalSize,
|
||||
data, ox*widthX*verticalSize,
|
||||
widthZ, verticalSize, override);
|
||||
}
|
||||
return anyChange;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,21 +137,68 @@ public class VerticalLevelContainer implements LevelContainer
|
||||
@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];
|
||||
return dataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex];
|
||||
}
|
||||
|
||||
public short getPositionData(int posX, int posZ)
|
||||
{
|
||||
return positionDataContainer[posX * size + posZ];
|
||||
}
|
||||
|
||||
public int getVerticalData(int posX, int posZ, int verticalIndex)
|
||||
{
|
||||
return verticalDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex];
|
||||
}
|
||||
|
||||
public int getColorData(int posX, int posZ, int verticalIndex)
|
||||
{
|
||||
return colorDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex];
|
||||
}
|
||||
|
||||
public byte getLightData(int posX, int posZ, int verticalIndex)
|
||||
{
|
||||
return lightDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex];
|
||||
}
|
||||
|
||||
public void setPositionData(short positionData, int posX, int posZ)
|
||||
{
|
||||
positionDataContainer[posX * size + posZ] = positionData;
|
||||
}
|
||||
|
||||
public void setVerticalData(int verticalData, int posX, int posZ, int verticalIndex)
|
||||
{
|
||||
verticalDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex] = verticalData;
|
||||
}
|
||||
|
||||
public void setColorData(int colorData, int posX, int posZ, int verticalIndex)
|
||||
{
|
||||
colorDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex] = colorData;
|
||||
}
|
||||
|
||||
public void setLightData(byte lightData, int posX, int posZ, int verticalIndex)
|
||||
{
|
||||
lightDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex] = lightData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSingleData(int posX, int posZ)
|
||||
{
|
||||
return getData(posX, posZ, 0);
|
||||
return dataContainer[posX * size * verticalSize + posZ * verticalSize];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxVerticalData()
|
||||
public long[] getAllData(int posX, int posZ)
|
||||
{
|
||||
return maxVerticalData;
|
||||
long[] result = new long[verticalSize];
|
||||
int index = posX * size * verticalSize + posZ * verticalSize;
|
||||
System.arraycopy(dataContainer, index, result, 0, verticalSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVerticalSize()
|
||||
{
|
||||
return verticalSize;
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
@@ -117,124 +209,217 @@ public class VerticalLevelContainer implements LevelContainer
|
||||
@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] & 0b01111111;
|
||||
index++;
|
||||
size = 1 << (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;
|
||||
private long[] readDataVersion6(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
|
||||
int x = size * size * tempMaxVerticalData;
|
||||
byte[] data = new byte[x * Long.BYTES];
|
||||
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
|
||||
inputData.readFully(data);
|
||||
long[] result = new long[x];
|
||||
bb.asLongBuffer().get(result);
|
||||
patchVersion9Reorder(result);
|
||||
patchHeightAndDepth(result,-minHeight);
|
||||
return result;
|
||||
}
|
||||
private long[] readDataVersion7(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
|
||||
int x = size * size * tempMaxVerticalData;
|
||||
byte[] data = new byte[x * Long.BYTES];
|
||||
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
|
||||
inputData.readFully(data);
|
||||
long[] result = new long[x];
|
||||
bb.asLongBuffer().get(result);
|
||||
patchVersion9Reorder(result);
|
||||
patchHeightAndDepth(result, 64 - minHeight);
|
||||
return result;
|
||||
}
|
||||
|
||||
private long[] readDataVersion8(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
|
||||
int x = size * size * tempMaxVerticalData;
|
||||
byte[] data = new byte[x * Long.BYTES];
|
||||
short tempMinHeight = Short.reverseBytes(inputData.readShort());
|
||||
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
|
||||
inputData.readFully(data);
|
||||
long[] result = new long[x];
|
||||
bb.asLongBuffer().get(result);
|
||||
patchVersion9Reorder(result);
|
||||
if (tempMinHeight != minHeight) {
|
||||
patchHeightAndDepth(result,tempMinHeight - minHeight);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private long[] readDataVersion9(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
|
||||
int x = size * size * tempMaxVerticalData;
|
||||
byte[] data = new byte[x * Long.BYTES];
|
||||
short tempMinHeight = Short.reverseBytes(inputData.readShort());
|
||||
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
|
||||
inputData.readFully(data);
|
||||
long[] result = new long[x];
|
||||
bb.asLongBuffer().get(result);
|
||||
if (tempMinHeight != minHeight) {
|
||||
patchHeightAndDepth(result,tempMinHeight - minHeight);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void patchHeightAndDepth(long[] data, int offset) {
|
||||
for (int i=0; i<data.length; i++) {
|
||||
data[i] = DataPointUtil.shiftHeightAndDepth(data[i], (short)offset);
|
||||
}
|
||||
}
|
||||
|
||||
private static void patchVersion9Reorder(long[] data) {
|
||||
for (int i=0; i<data.length; i++) {
|
||||
data[i] = DataPointUtil.version9Reorder(data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public VerticalLevelContainer(DataInputStream inputData, int version, byte expectedDetailLevel) throws IOException {
|
||||
minHeight = SingletonHandler.get(IMinecraftClientWrapper.class).getWrappedClientWorld().getMinHeight();
|
||||
detailLevel = inputData.readByte();
|
||||
if (detailLevel != expectedDetailLevel)
|
||||
throw new IOException("Invalid Data: The expected detail level should be "+expectedDetailLevel+
|
||||
" but the data header say it's "+detailLevel);
|
||||
|
||||
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
int fileMaxVerticalData = inputData.readByte() & 0b01111111;
|
||||
long[] tempDataContainer = null;
|
||||
|
||||
switch (version) {
|
||||
case 6:
|
||||
tempDataContainer = readDataVersion6(inputData, fileMaxVerticalData);
|
||||
break;
|
||||
case 7:
|
||||
tempDataContainer = readDataVersion7(inputData, fileMaxVerticalData);
|
||||
break;
|
||||
case 8:
|
||||
tempDataContainer = readDataVersion8(inputData, fileMaxVerticalData);
|
||||
break;
|
||||
case 9:
|
||||
tempDataContainer = readDataVersion9(inputData, fileMaxVerticalData);
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
|
||||
int targetMaxVerticalData = DetailDistanceUtil.getMaxVerticalData(detailLevel);
|
||||
verticalSize = targetMaxVerticalData;
|
||||
dataContainer = DataPointUtil.changeMaxVertSize(tempDataContainer, fileMaxVerticalData, verticalSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelContainer expand()
|
||||
{
|
||||
return new VerticalLevelContainer((byte) (getDetailLevel() - 1));
|
||||
}
|
||||
|
||||
private static final ThreadLocal<long[][]> tLocalVerticalUpdateArrays = ThreadLocal.withInitial(() ->
|
||||
{
|
||||
return new long[LodUtil.DETAIL_OPTIONS - 1][];
|
||||
});
|
||||
|
||||
@Override
|
||||
public void updateData(LevelContainer lowerLevelContainer, int posX, int posZ)
|
||||
{
|
||||
//We reset the array
|
||||
long[] dataToMerge = ThreadMapUtil.getVerticalUpdateArray(detailLevel);
|
||||
long[][] verticalUpdateArrays = tLocalVerticalUpdateArrays.get();
|
||||
long[] dataToMerge = verticalUpdateArrays[detailLevel-1];
|
||||
int arrayLength = DetailDistanceUtil.getMaxVerticalData(detailLevel-1) * 4;
|
||||
if (dataToMerge == null || dataToMerge.length != arrayLength) {
|
||||
dataToMerge = new long[arrayLength];
|
||||
verticalUpdateArrays[detailLevel-1] = dataToMerge;
|
||||
} else Arrays.fill(dataToMerge, 0);
|
||||
|
||||
int lowerMaxVertical = dataToMerge.length / 4;
|
||||
int childPosX;
|
||||
int childPosZ;
|
||||
long[] data;
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
boolean anyDataExist = false;
|
||||
for (int x = 0; x <= 1; x++)
|
||||
{
|
||||
for (int z = 0; z <= 1; z++)
|
||||
{
|
||||
childPosX = 2 * posX + x;
|
||||
childPosZ = 2 * posZ + z;
|
||||
if (lowerLevelContainer.doesItExist(childPosX, childPosZ)) anyDataExist = true;
|
||||
for (int verticalIndex = 0; verticalIndex < lowerMaxVertical; verticalIndex++)
|
||||
dataToMerge[(z * 2 + x) * lowerMaxVertical + verticalIndex] = lowerLevelContainer.getData(childPosX, childPosZ, verticalIndex);
|
||||
}
|
||||
}
|
||||
data = DataPointUtil.mergeMultiData(dataToMerge, lowerMaxVertical, getMaxVerticalData());
|
||||
data = DataPointUtil.mergeMultiData(dataToMerge, lowerMaxVertical, getVerticalSize());
|
||||
if (!anyDataExist)
|
||||
throw new RuntimeException("Update data called but no child datapoint exist!");
|
||||
|
||||
addVerticalData(data, posX, posZ);
|
||||
if ((!DataPointUtil.doesItExist(data[0])) && anyDataExist)
|
||||
throw new RuntimeException("Update data called but higher level datapoint doesn't exist even though child data does exist!");
|
||||
|
||||
//FIXME: Disabled check if genMode for old data is already invalid due to having genMode 0.
|
||||
if (DataPointUtil.getGenerationMode(data[0]) != DataPointUtil.getGenerationMode(lowerLevelContainer.getSingleData(posX*2, posZ*2)))
|
||||
throw new RuntimeException("Update data called but higher level datapoint does not have the same GenerationMode as the top left corner child datapoint!");
|
||||
|
||||
forceWriteVerticalData(data, posX, posZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toDataString()
|
||||
{
|
||||
int index = 0;
|
||||
int x = size * size;
|
||||
int tempIndex;
|
||||
long current;
|
||||
public boolean writeData(DataOutputStream output) throws IOException {
|
||||
output.writeByte(detailLevel);
|
||||
output.writeByte((byte) verticalSize);
|
||||
output.writeByte((byte) (minHeight & 0xFF));
|
||||
output.writeByte((byte) ((minHeight >> 8) & 0xFF));
|
||||
boolean allGenerated = true;
|
||||
byte[] tempData = ThreadMapUtil.getSaveContainer(detailLevel);
|
||||
|
||||
tempData[index] = detailLevel;
|
||||
index++;
|
||||
tempData[index] = (byte) maxVerticalData;
|
||||
index++;
|
||||
int j;
|
||||
int x = size * size;
|
||||
for (int i = 0; i < x; i++)
|
||||
{
|
||||
for (j = 0; j < maxVerticalData; j++)
|
||||
for (int j = 0; j < verticalSize; j++)
|
||||
{
|
||||
current = dataContainer[i * maxVerticalData + j];
|
||||
for (tempIndex = 0; tempIndex < 8; tempIndex++)
|
||||
tempData[index + tempIndex] = (byte) (current >>> (8 * tempIndex));
|
||||
index += 8;
|
||||
long current = dataContainer[i * verticalSize + j];
|
||||
output.writeLong(Long.reverseBytes(current));
|
||||
}
|
||||
if(!DataPointUtil.doesItExist(dataContainer[i]))
|
||||
if (!DataPointUtil.doesItExist(dataContainer[i]))
|
||||
allGenerated = false;
|
||||
}
|
||||
if (allGenerated)
|
||||
tempData[1] |= 0b10000000;
|
||||
return tempData;
|
||||
return allGenerated;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public String toString()
|
||||
{
|
||||
/*
|
||||
String LINE_DELIMITER = "\n";
|
||||
String DATA_DELIMITER = " ";
|
||||
String SUBDATA_DELIMITER = ",";
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
stringBuilder.append(detailLevel);
|
||||
stringBuilder.append(DATA_DELIMITER);
|
||||
for (int x = 0; x < size; x++)
|
||||
stringBuilder.append(LINE_DELIMITER);
|
||||
for (int z = 0; z < size; z++)
|
||||
{
|
||||
for (int z = 0; z < size; z++)
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
//Converting the dataToHex
|
||||
stringBuilder.append(Long.toHexString(dataContainer[x][z][0]));
|
||||
stringBuilder.append(DATA_DELIMITER);
|
||||
for (int y = 0; y < verticalSize; y++) {
|
||||
//Converting the dataToHex
|
||||
stringBuilder.append(Long.toHexString(getData(x,z,y)));
|
||||
if (y != verticalSize) stringBuilder.append(SUBDATA_DELIMITER);
|
||||
}
|
||||
if (x != size) stringBuilder.append(DATA_DELIMITER);
|
||||
}
|
||||
if (z != size) stringBuilder.append(LINE_DELIMITER);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
*/
|
||||
return " ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxNumberOfLods()
|
||||
{
|
||||
return size * size * getMaxVerticalData();
|
||||
return size * size * getVerticalSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRoughRamUsage()
|
||||
{
|
||||
return dataContainer.length * Long.BYTES;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -161,41 +161,11 @@ public class Mat4f
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder stringbuilder = new StringBuilder();
|
||||
stringbuilder.append("Matrix4f:\n");
|
||||
stringbuilder.append(this.m00);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m01);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m02);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m03);
|
||||
stringbuilder.append("\n");
|
||||
stringbuilder.append(this.m10);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m11);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m12);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m13);
|
||||
stringbuilder.append("\n");
|
||||
stringbuilder.append(this.m20);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m21);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m22);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m23);
|
||||
stringbuilder.append("\n");
|
||||
stringbuilder.append(this.m30);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m31);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m32);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m33);
|
||||
stringbuilder.append("\n");
|
||||
return stringbuilder.toString();
|
||||
return "Matrix4f:\n" +
|
||||
this.m00 + " " + this.m01 + " " + this.m02 + " " + this.m03 + "\n" +
|
||||
this.m10 + " " + this.m11 + " " + this.m12 + " " + this.m13 + "\n" +
|
||||
this.m20 + " " + this.m21 + " " + this.m22 + " " + this.m23 + "\n" +
|
||||
this.m30 + " " + this.m31 + " " + this.m32 + " " + this.m33 + "\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -427,6 +397,7 @@ public class Mat4f
|
||||
/**
|
||||
* TODO: what kind of translation is this?
|
||||
* and how is this different from "multiplyTranslationMatrix"?
|
||||
* Answer: This is faster and direct (but only if this is pure translation matrix without rotate)
|
||||
*/
|
||||
public void translate(Vec3f vec)
|
||||
{
|
||||
@@ -553,4 +524,21 @@ public class Mat4f
|
||||
this.m13 = y;
|
||||
this.m23 = z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the values that store the clipping planes.
|
||||
* Formula for calculating matrix values is the same that OpenGL uses when making matrices.
|
||||
*
|
||||
* @param nearClip New near clipping plane value.
|
||||
* @param farClip New far clipping plane value.
|
||||
*/
|
||||
public void setClipPlanes(float nearClip,float farClip)
|
||||
{
|
||||
//convert to matrix values, formula copied from a textbook / openGL specification.
|
||||
float matNearClip = -((farClip + nearClip) / (farClip - nearClip));
|
||||
float matFarClip = -((2 * farClip * nearClip) / (farClip - nearClip));
|
||||
//set new values for the clip planes.
|
||||
this.m22 = matNearClip;
|
||||
this.m23 = matFarClip;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* Copyright (C) 2020-2022 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
|
||||
@@ -26,16 +26,18 @@ import com.google.common.collect.ImmutableList;
|
||||
* DefaultVertexFormats class.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
* @version 12-8-2021
|
||||
*/
|
||||
public class DefaultLodVertexFormats
|
||||
{
|
||||
public static final LodVertexFormatElement ELEMENT_POSITION = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.FLOAT, 3);
|
||||
public static final LodVertexFormatElement ELEMENT_COLOR = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.UBYTE, 4);
|
||||
public static final LodVertexFormatElement ELEMENT_UV = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.FLOAT, 2);
|
||||
public static final LodVertexFormatElement ELEMENT_LIGHT_MAP_UV = new LodVertexFormatElement(1, LodVertexFormatElement.DataType.SHORT, 2);
|
||||
public static final LodVertexFormatElement ELEMENT_NORMAL = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 3);
|
||||
public static final LodVertexFormatElement ELEMENT_PADDING = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 1);
|
||||
public static final LodVertexFormatElement ELEMENT_POSITION = new LodVertexFormatElement(3, LodVertexFormatElement.DataType.USHORT, 3, false);
|
||||
public static final LodVertexFormatElement ELEMENT_COLOR = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.UBYTE, 4, false);
|
||||
public static final LodVertexFormatElement ELEMENT_UV = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.FLOAT, 2, false);
|
||||
public static final LodVertexFormatElement ELEMENT_LIGHT_MAP_UV = new LodVertexFormatElement(1, LodVertexFormatElement.DataType.SHORT, 2, false);
|
||||
public static final LodVertexFormatElement ELEMENT_NORMAL = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 3, false);
|
||||
public static final LodVertexFormatElement ELEMENT_PADDING = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 1, true);
|
||||
|
||||
public static final LodVertexFormatElement ELEMENT_LIGHT = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.UBYTE, 1, false);
|
||||
|
||||
|
||||
public static final LodVertexFormat POSITION = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).build());
|
||||
@@ -45,4 +47,7 @@ public class DefaultLodVertexFormats
|
||||
public static final LodVertexFormat POSITION_COLOR_TEX = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).build());
|
||||
public static final LodVertexFormat POSITION_COLOR_TEX_LIGHTMAP = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).add(ELEMENT_LIGHT_MAP_UV).build());
|
||||
|
||||
public static final LodVertexFormat POSITION_COLOR_BLOCK_LIGHT_SKY_LIGHT = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder()
|
||||
.add(ELEMENT_POSITION).add(ELEMENT_PADDING).add(ELEMENT_LIGHT)
|
||||
.add(ELEMENT_COLOR).build());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 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.core.objects.opengl;
|
||||
|
||||
import com.seibel.lod.core.builders.lodBuilding.bufferBuilding.LodQuadBuilder;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
|
||||
public class LodBox
|
||||
{
|
||||
private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class);
|
||||
|
||||
public static void addBoxQuadsToBuilder(LodQuadBuilder builder, short xSize, short ySize, short zSize, short x,
|
||||
short y, short z, int color, byte skyLight, byte blockLight, long topData, long botData, long[][][] adjData,
|
||||
boolean[] adjFillBlack)
|
||||
{
|
||||
short maxX = (short) (x + xSize);
|
||||
short maxY = (short) (y + ySize);
|
||||
short maxZ = (short) (z + zSize);
|
||||
byte skyLightTop = skyLight;
|
||||
boolean isTransparent = ColorUtil.getAlpha(color)<255;
|
||||
boolean isTopTransparent = DataPointUtil.isTransparent(topData);
|
||||
boolean isBotTransparent = DataPointUtil.isTransparent(botData);
|
||||
byte skyLightBot = DataPointUtil.doesItExist(botData) ? DataPointUtil.getLightSky(botData) : 0;
|
||||
// Up direction case
|
||||
boolean skipTop = DataPointUtil.doesItExist(topData) && (
|
||||
(isTransparent && (DataPointUtil.getDepth(topData) == maxY)) ||
|
||||
(!isTransparent && (DataPointUtil.getDepth(topData) == maxY) && !isTopTransparent));
|
||||
boolean skipBot = DataPointUtil.doesItExist(botData) && (
|
||||
(isTransparent && (DataPointUtil.getDepth(botData) == maxY)) ||
|
||||
(!isTransparent && (DataPointUtil.getDepth(botData) == maxY) && !isBotTransparent));
|
||||
// DataPointUtil.getAlpha(singleAdjDataPoint)
|
||||
// == 255;
|
||||
//boolean skipBot = DataPointUtil.doesItExist(botData) && DataPointUtil.getHeight(botData) == y;// &&
|
||||
// DataPointUtil.getAlpha(singleAdjDataPoint)
|
||||
// == 255;
|
||||
|
||||
if (!skipTop)
|
||||
builder.addQuadUp(x, maxY, z, xSize, zSize, ColorUtil.applyShade(color, MC.getShade(LodDirection.UP)), skyLightTop, blockLight);
|
||||
if (!skipBot)
|
||||
builder.addQuadDown(x, y, z, xSize, zSize, ColorUtil.applyShade(color, MC.getShade(LodDirection.DOWN)), skyLightBot, blockLight);
|
||||
|
||||
//If the adj pos is at the same level we cull the faces normally, otherwise we divide the face in two and cull the two part separately
|
||||
|
||||
//NORTH face vertex creation
|
||||
{
|
||||
long[][] adjDataNorth = adjData[LodDirection.NORTH.ordinal() - 2];
|
||||
int adjOverlapNorth = adjFillBlack[LodDirection.NORTH.ordinal() - 2] ? ColorUtil.BLACK : ColorUtil.TRANSPARENT;
|
||||
if (adjDataNorth == null)
|
||||
{
|
||||
builder.addQuadAdj(LodDirection.NORTH, x, y, z, xSize, ySize, color, (byte) 15, blockLight);
|
||||
}
|
||||
else if (adjDataNorth.length == 1)
|
||||
{
|
||||
makeAdjQuads(builder, adjDataNorth[0], LodDirection.NORTH, x, y, z, xSize, ySize,
|
||||
color, adjOverlapNorth, skyLightTop, blockLight);
|
||||
}
|
||||
else
|
||||
{
|
||||
makeAdjQuads(builder, adjDataNorth[0], LodDirection.NORTH, x, y, z, (short) (xSize / 2), ySize,
|
||||
color, adjOverlapNorth, skyLightTop, blockLight);
|
||||
makeAdjQuads(builder, adjDataNorth[1], LodDirection.NORTH, (short) (x + xSize / 2), y, z, (short) (xSize / 2), ySize,
|
||||
color, adjOverlapNorth, skyLightTop, blockLight);
|
||||
}
|
||||
}
|
||||
|
||||
//SOUTH face vertex creation
|
||||
{
|
||||
long[][] adjDataSouth = adjData[LodDirection.SOUTH.ordinal() - 2];
|
||||
int adjOverlapSouth = adjFillBlack[LodDirection.SOUTH.ordinal() - 2] ? ColorUtil.BLACK : ColorUtil.TRANSPARENT;
|
||||
if (adjDataSouth == null)
|
||||
{
|
||||
builder.addQuadAdj(LodDirection.SOUTH, x, y, maxZ, xSize, ySize, color, (byte) 15, blockLight);
|
||||
}
|
||||
else if (adjDataSouth.length == 1)
|
||||
{
|
||||
makeAdjQuads(builder, adjDataSouth[0], LodDirection.SOUTH, x, y, maxZ, xSize, ySize,
|
||||
color, adjOverlapSouth, skyLightTop, blockLight);
|
||||
}
|
||||
else
|
||||
{
|
||||
makeAdjQuads(builder, adjDataSouth[0], LodDirection.SOUTH, x, y, maxZ, (short) (xSize / 2), ySize,
|
||||
color, adjOverlapSouth, skyLightTop, blockLight);
|
||||
|
||||
makeAdjQuads(builder, adjDataSouth[1], LodDirection.SOUTH, (short) (x + xSize / 2), y, maxZ, (short) (xSize / 2), ySize,
|
||||
color, adjOverlapSouth, skyLightTop, blockLight);
|
||||
}
|
||||
}
|
||||
|
||||
//WEST face vertex creation
|
||||
{
|
||||
long[][] adjDataWest = adjData[LodDirection.WEST.ordinal() - 2];
|
||||
int adjOverlapWest = adjFillBlack[LodDirection.WEST.ordinal() - 2] ? ColorUtil.BLACK : ColorUtil.TRANSPARENT;
|
||||
if (adjDataWest == null)
|
||||
{
|
||||
builder.addQuadAdj(LodDirection.WEST, x, y, z, zSize, ySize, color, (byte) 15, blockLight);
|
||||
}
|
||||
else if (adjDataWest.length == 1)
|
||||
{
|
||||
makeAdjQuads(builder, adjDataWest[0], LodDirection.WEST, x, y, z, zSize, ySize,
|
||||
color, adjOverlapWest, skyLightTop, blockLight);
|
||||
}
|
||||
else
|
||||
{
|
||||
makeAdjQuads(builder, adjDataWest[0], LodDirection.WEST, x, y, z, (short) (zSize / 2), ySize,
|
||||
color, adjOverlapWest, skyLightTop, blockLight);
|
||||
makeAdjQuads(builder, adjDataWest[1], LodDirection.WEST, x, y, (short) (z + zSize / 2), (short) (zSize / 2), ySize,
|
||||
color, adjOverlapWest, skyLightTop, blockLight);
|
||||
}
|
||||
}
|
||||
|
||||
//EAST face vertex creation
|
||||
{
|
||||
long[][] adjDataEast = adjData[LodDirection.EAST.ordinal() - 2];
|
||||
int adjOverlapEast = adjFillBlack[LodDirection.EAST.ordinal() - 2] ? ColorUtil.BLACK : ColorUtil.TRANSPARENT;
|
||||
if (adjData[LodDirection.EAST.ordinal() - 2] == null)
|
||||
{
|
||||
builder.addQuadAdj(LodDirection.EAST, maxX, y, z, zSize, ySize, color, (byte) 15, blockLight);
|
||||
}
|
||||
else if (adjDataEast.length == 1)
|
||||
{
|
||||
makeAdjQuads(builder, adjDataEast[0], LodDirection.EAST, maxX, y, z, zSize, ySize,
|
||||
color, adjOverlapEast, skyLightTop, blockLight);
|
||||
}
|
||||
else
|
||||
{
|
||||
makeAdjQuads(builder, adjDataEast[0], LodDirection.EAST, maxX, y, z, (short) (zSize / 2), ySize,
|
||||
color, adjOverlapEast, skyLightTop, blockLight);
|
||||
makeAdjQuads(builder, adjDataEast[1], LodDirection.EAST, maxX, y, (short) (z + zSize / 2), (short) (zSize / 2), ySize,
|
||||
color, adjOverlapEast, skyLightTop, blockLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void makeAdjQuads(LodQuadBuilder builder, long[] adjData, LodDirection direction, short x, short y,
|
||||
short z, short w0, short wy, int color, int overlapColor, byte upSkyLight, byte blockLight)
|
||||
{
|
||||
|
||||
boolean isTransparent = ColorUtil.getAlpha(color)<255;
|
||||
color = ColorUtil.applyShade(color, MC.getShade(direction));
|
||||
long[] dataPoint = adjData;
|
||||
if (dataPoint == null || DataPointUtil.isVoid(dataPoint[0]))
|
||||
{
|
||||
builder.addQuadAdj(direction, x, y, z, w0, wy, color, (byte) 15, blockLight);
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
boolean firstFace = true;
|
||||
boolean allAbove = true;
|
||||
short previousDepth = -1;
|
||||
byte nextSkyLight = upSkyLight;
|
||||
|
||||
// TODO transparency ocean floor fix
|
||||
// boolean isOpaque = ((colorMap[0] >> 24) & 0xFF) == 255;
|
||||
for (i = 0; i < dataPoint.length && DataPointUtil.doesItExist(adjData[i]) && !DataPointUtil.isVoid(adjData[i]); i++)
|
||||
{
|
||||
long adjPoint = adjData[i];
|
||||
boolean isAdjTransparent = DataPointUtil.isTransparent(adjPoint);
|
||||
// TODO transparency ocean floor fix
|
||||
if (!isTransparent && isAdjTransparent)
|
||||
continue;
|
||||
|
||||
short height = DataPointUtil.getHeight(adjPoint);
|
||||
short depth = DataPointUtil.getDepth(adjPoint);
|
||||
|
||||
// If the depth of said block is higher then our max Y, continue
|
||||
// Basically: y < maxY <= _____ height
|
||||
// _______&&: y < maxY <= depth
|
||||
if (y + wy <= depth)
|
||||
continue;
|
||||
// Now: depth < maxY
|
||||
allAbove = false;
|
||||
|
||||
if (height < y)
|
||||
{
|
||||
// Basically: _____ height < y < maxY
|
||||
// _______&&: depth ______ < y < maxY
|
||||
if (firstFace)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, y, z, w0, wy, color, DataPointUtil.getLightSky(adjPoint),
|
||||
blockLight);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Now: depth < height < y < previousDepth < maxY
|
||||
if (previousDepth == -1)
|
||||
throw new RuntimeException("Loop error");
|
||||
builder.addQuadAdj(direction, x, y, z, w0, (short) (previousDepth - y), color,
|
||||
DataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
previousDepth = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (depth <= y)
|
||||
{ // AND y <= height
|
||||
if (y + wy <= height)
|
||||
{
|
||||
// Basically: ________ y < maxY <= height
|
||||
// _______&&: depth <= y < maxY
|
||||
// The face is inside adj face completely.
|
||||
if (overlapColor != 0)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, y, z, w0, wy, overlapColor, (byte) 15, (byte) 15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Otherwise: ________ y <= Height < maxY
|
||||
// _______&&: depth <= y _________ < maxY
|
||||
// the adj data intersects the lower part of the current data
|
||||
if (height > y && overlapColor != 0)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, y, z, w0, (short) (height - y), overlapColor, (byte) 15, (byte) 15);
|
||||
}
|
||||
// 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)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, height, z, w0, (short) (y + wy - height), color,
|
||||
DataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Now: depth <= y <= height <= previousDepth < maxY
|
||||
if (previousDepth == -1)
|
||||
throw new RuntimeException("Loop error");
|
||||
if (previousDepth > height)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, height, z, w0, (short) (previousDepth - height), color,
|
||||
DataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
}
|
||||
previousDepth = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// In here always true: y < depth < maxY
|
||||
// _________________&&: y < _____ (height and maxY)
|
||||
|
||||
if (y + wy <= height)
|
||||
{
|
||||
// Basically: y _______ < maxY <= height
|
||||
// _______&&: y < depth < maxY
|
||||
// the adj data intersects the higher part of the current data
|
||||
if (overlapColor != 0)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, depth, z, w0, (short) (y + wy - depth), overlapColor, (byte) 15, (byte) 15);
|
||||
}
|
||||
// we start the creation of a new face
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise: y < _____ height < maxY
|
||||
// _______&&: y < depth ______ < maxY
|
||||
if (overlapColor != 0)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, depth, z, w0, (short) (height - depth), overlapColor, (byte) 15, (byte) 15);
|
||||
}
|
||||
if (firstFace)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, height, z, w0, (short) (y + wy - height), color,
|
||||
DataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Now: y < depth < height <= previousDepth < maxY
|
||||
if (previousDepth == -1)
|
||||
throw new RuntimeException("Loop error");
|
||||
if (previousDepth > height)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, height, z, w0, (short) (previousDepth - height), color,
|
||||
DataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
}
|
||||
previousDepth = -1;
|
||||
}
|
||||
}
|
||||
// set next top as current depth
|
||||
previousDepth = depth;
|
||||
firstFace = false;
|
||||
nextSkyLight = upSkyLight;
|
||||
if (i + 1 < adjData.length && DataPointUtil.doesItExist(adjData[i + 1]))
|
||||
nextSkyLight = DataPointUtil.getLightSky(adjData[i + 1]);
|
||||
}
|
||||
|
||||
if (allAbove)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, y, z, w0, wy, color, upSkyLight, blockLight);
|
||||
}
|
||||
else if (previousDepth != -1)
|
||||
{
|
||||
// We need to finish the last quad.
|
||||
builder.addQuadAdj(direction, x, y, z, w0, (short) (previousDepth - y), color, nextSkyLight,
|
||||
blockLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,547 +0,0 @@
|
||||
/*
|
||||
* 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.core.objects.opengl;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* A (almost) exact copy of Minecraft's
|
||||
* BufferBuilder object. <br>
|
||||
* Which allows for creating and filling
|
||||
* OpenGL buffers.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
*/
|
||||
public class LodBufferBuilder
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
public ByteBuffer buffer;
|
||||
|
||||
private final List<LodBufferBuilder.DrawState> vertexCounts = Lists.newArrayList();
|
||||
private int lastRenderedCountIndex = 0;
|
||||
private int totalRenderedBytes = 0;
|
||||
private int nextElementByte = 0;
|
||||
private int totalUploadedBytes = 0;
|
||||
private int vertices;
|
||||
@Nullable
|
||||
private LodVertexFormatElement currentElement;
|
||||
private int elementIndex;
|
||||
private int mode;
|
||||
private LodVertexFormat format;
|
||||
private boolean building;
|
||||
|
||||
|
||||
|
||||
|
||||
public LodBufferBuilder(int bufferSizeInBytes)
|
||||
{
|
||||
this.buffer = allocateByteBuffer(bufferSizeInBytes * 4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** originally from MC's GLAllocation class */
|
||||
private ByteBuffer allocateByteBuffer(int bufferSizeInBytes)
|
||||
{
|
||||
return ByteBuffer.allocateDirect(bufferSizeInBytes).order(ByteOrder.nativeOrder());
|
||||
}
|
||||
/** originally from MC's GLAllocation class */
|
||||
@SuppressWarnings("unused")
|
||||
private FloatBuffer allocateFloatBuffer(int bufferSizeInBytes)
|
||||
{
|
||||
return allocateByteBuffer(bufferSizeInBytes).asFloatBuffer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** make sure the buffer doesn't overflow when inserting new elements */
|
||||
private void ensureVertexCapacity()
|
||||
{
|
||||
this.ensureCapacity(this.format.getVertexSize());
|
||||
}
|
||||
private void ensureCapacity(int vertexSizeInBytes)
|
||||
{
|
||||
if (this.nextElementByte + vertexSizeInBytes > this.buffer.capacity())
|
||||
{
|
||||
int i = this.buffer.capacity();
|
||||
int j = i + roundUp(vertexSizeInBytes);
|
||||
//LOGGER.debug("Needed to grow BufferBuilder buffer: Old size {} bytes, new size {} bytes.", i, j);
|
||||
ByteBuffer bytebuffer = allocateByteBuffer(j);
|
||||
((Buffer) this.buffer).position(0);
|
||||
bytebuffer.put(this.buffer);
|
||||
((Buffer) bytebuffer).rewind();
|
||||
this.buffer = bytebuffer;
|
||||
}
|
||||
}
|
||||
private static int roundUp(int vertexSizeInBytes)
|
||||
{
|
||||
int i = 2097152; // 2 ^ 21
|
||||
if (vertexSizeInBytes == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vertexSizeInBytes < 0)
|
||||
{
|
||||
i *= -1;
|
||||
}
|
||||
|
||||
int j = vertexSizeInBytes % i;
|
||||
return j == 0 ? vertexSizeInBytes : vertexSizeInBytes + i - j;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* not currently needed sortQuads()
|
||||
// the x,y,z location is a blockPos
|
||||
public void sortQuads(float x, float y, float z)
|
||||
{
|
||||
((Buffer) this.buffer).clear();
|
||||
FloatBuffer floatbuffer = this.buffer.asFloatBuffer();
|
||||
int i = this.vertices / 4;
|
||||
float[] afloat = new float[i];
|
||||
|
||||
for (int j = 0; j < i; ++j)
|
||||
{
|
||||
afloat[j] = getQuadDistanceFromPlayer(floatbuffer, x, y, z, this.format.getIntegerSize(), this.totalRenderedBytes / 4 + j * this.format.getVertexSize());
|
||||
}
|
||||
|
||||
int[] aint = new int[i];
|
||||
|
||||
for (int k = 0; k < aint.length; aint[k] = k++)
|
||||
{
|
||||
}
|
||||
|
||||
IntArrays.mergeSort(aint, (p_227830_1_, p_227830_2_) ->
|
||||
{
|
||||
return Floats.compare(afloat[p_227830_2_], afloat[p_227830_1_]);
|
||||
});
|
||||
BitSet bitset = new BitSet();
|
||||
FloatBuffer floatbuffer1 = allocateFloatBuffer(this.format.getIntegerSize() * 4);
|
||||
|
||||
for (int l = bitset.nextClearBit(0); l < aint.length; l = bitset.nextClearBit(l + 1))
|
||||
{
|
||||
int i1 = aint[l];
|
||||
if (i1 != l)
|
||||
{
|
||||
this.limitToVertex(floatbuffer, i1);
|
||||
((Buffer) floatbuffer1).clear();
|
||||
floatbuffer1.put(floatbuffer);
|
||||
int j1 = i1;
|
||||
|
||||
for (int k1 = aint[i1]; j1 != l; k1 = aint[k1])
|
||||
{
|
||||
this.limitToVertex(floatbuffer, k1);
|
||||
FloatBuffer floatbuffer2 = floatbuffer.slice();
|
||||
this.limitToVertex(floatbuffer, j1);
|
||||
floatbuffer.put(floatbuffer2);
|
||||
bitset.set(j1);
|
||||
j1 = k1;
|
||||
}
|
||||
|
||||
this.limitToVertex(floatbuffer, l);
|
||||
((Buffer) floatbuffer1).flip();
|
||||
floatbuffer.put(floatbuffer1);
|
||||
}
|
||||
|
||||
bitset.set(l);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void limitToVertex(FloatBuffer p_227829_1_, int p_227829_2_)
|
||||
{
|
||||
int i = this.format.getIntegerSize() * 4;
|
||||
((Buffer) p_227829_1_).limit(this.totalRenderedBytes / 4 + (p_227829_2_ + 1) * i);
|
||||
((Buffer) p_227829_1_).position(this.totalRenderedBytes / 4 + p_227829_2_ * i);
|
||||
}
|
||||
*/
|
||||
|
||||
/* not curerntly needed getState()
|
||||
public LodBufferBuilder.State getState()
|
||||
{
|
||||
((Buffer) this.buffer).limit(this.nextElementByte);
|
||||
((Buffer) this.buffer).position(this.totalRenderedBytes);
|
||||
ByteBuffer bytebuffer = ByteBuffer.allocate(this.vertices * this.format.getVertexSize());
|
||||
bytebuffer.put(this.buffer);
|
||||
((Buffer) this.buffer).clear();
|
||||
return new LodBufferBuilder.State(bytebuffer, this.format);
|
||||
}
|
||||
*/
|
||||
|
||||
/* not currently needed getQuadDistanceFromPlayer()
|
||||
private static float getQuadDistanceFromPlayer(FloatBuffer floatBuffer, float x, float y, float z, int p_181665_4_, int p_181665_5_)
|
||||
{
|
||||
float f = floatBuffer.get(p_181665_5_ + p_181665_4_ * 0 + 0);
|
||||
float f1 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 0 + 1);
|
||||
float f2 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 0 + 2);
|
||||
float f3 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 1 + 0);
|
||||
float f4 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 1 + 1);
|
||||
float f5 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 1 + 2);
|
||||
float f6 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 2 + 0);
|
||||
float f7 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 2 + 1);
|
||||
float f8 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 2 + 2);
|
||||
float f9 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 3 + 0);
|
||||
float f10 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 3 + 1);
|
||||
float f11 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 3 + 2);
|
||||
float f12 = (f + f3 + f6 + f9) * 0.25F - x;
|
||||
float f13 = (f1 + f4 + f7 + f10) * 0.25F - y;
|
||||
float f14 = (f2 + f5 + f8 + f11) * 0.25F - z;
|
||||
return f12 * f12 + f13 * f13 + f14 * f14;
|
||||
}
|
||||
*/
|
||||
|
||||
/* not currently needed restoreState()
|
||||
public void restoreState(LodBufferBuilder.State bufferState)
|
||||
{
|
||||
((Buffer) bufferState.data).clear();
|
||||
int i = bufferState.data.capacity();
|
||||
this.ensureCapacity(i);
|
||||
((Buffer) this.buffer).limit(this.buffer.capacity());
|
||||
((Buffer) this.buffer).position(this.totalRenderedBytes);
|
||||
this.buffer.put(bufferState.data);
|
||||
((Buffer) this.buffer).clear();
|
||||
LodVertexFormat LodVertexFormat = bufferState.format;
|
||||
this.switchFormat(LodVertexFormat);
|
||||
this.vertices = i / LodVertexFormat.getVertexSize();
|
||||
this.nextElementByte = this.totalRenderedBytes + this.vertices * LodVertexFormat.getVertexSize();
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void switchFormat(LodVertexFormat newFormat)
|
||||
{
|
||||
format = newFormat;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//========================================//
|
||||
// methods for actually building a buffer //
|
||||
//========================================//
|
||||
|
||||
/**
|
||||
* @param openGlLodVertexFormat GL11.GL_QUADS, GL11.GL_TRIANGLES, etc.
|
||||
* @param LodVertexFormat
|
||||
*/
|
||||
public void begin(int openGlLodVertexFormat, LodVertexFormat LodVertexFormat)
|
||||
{
|
||||
if (this.building)
|
||||
{
|
||||
throw new IllegalStateException("Already building!");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.building = true;
|
||||
this.mode = openGlLodVertexFormat;
|
||||
this.switchFormat(LodVertexFormat);
|
||||
this.currentElement = LodVertexFormat.getElements().get(0);
|
||||
this.elementIndex = 0;
|
||||
((Buffer) this.buffer).clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void end()
|
||||
{
|
||||
if (!this.building)
|
||||
{
|
||||
throw new IllegalStateException("Not building!");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.building = false;
|
||||
this.vertexCounts.add(new LodBufferBuilder.DrawState(this.format, this.vertices, this.mode));
|
||||
this.totalRenderedBytes += this.vertices * this.format.getVertexSize();
|
||||
this.vertices = 0;
|
||||
this.currentElement = null;
|
||||
this.elementIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void putByte(int index, byte newByte)
|
||||
{
|
||||
this.buffer.put(this.nextElementByte + index, newByte);
|
||||
}
|
||||
|
||||
public void putShort(int index, short newShort)
|
||||
{
|
||||
this.buffer.putShort(this.nextElementByte + index, newShort);
|
||||
}
|
||||
|
||||
public void putFloat(int index, float newFloat)
|
||||
{
|
||||
this.buffer.putFloat(this.nextElementByte + index, newFloat);
|
||||
}
|
||||
|
||||
public void endVertex()
|
||||
{
|
||||
if (this.elementIndex != 0)
|
||||
{
|
||||
throw new IllegalStateException("Not filled all elements of the vertex");
|
||||
}
|
||||
else
|
||||
{
|
||||
++this.vertices;
|
||||
this.ensureVertexCapacity();
|
||||
}
|
||||
}
|
||||
|
||||
public void nextElement()
|
||||
{
|
||||
ImmutableList<LodVertexFormatElement> immutablelist = this.format.getElements();
|
||||
this.elementIndex = (this.elementIndex + 1) % immutablelist.size();
|
||||
this.nextElementByte += this.currentElement.getByteSize();
|
||||
LodVertexFormatElement LodVertexFormatelement = immutablelist.get(this.elementIndex);
|
||||
this.currentElement = LodVertexFormatelement;
|
||||
// if (LodVertexFormatelement.getUsage() == LodVertexFormatElement.Usage.PADDING)
|
||||
// {
|
||||
// this.nextElement();
|
||||
// }
|
||||
|
||||
// if (this.defaultColorSet && this.currentElement.getUsage() == LodVertexFormatElement.Usage.COLOR)
|
||||
// {
|
||||
// color(this.defaultR, this.defaultG, this.defaultB, this.defaultA);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
public LodBufferBuilder color(int red, int green, int blue, int alpha)
|
||||
{
|
||||
LodVertexFormatElement LodVertexFormatelement = this.currentElement();
|
||||
if (LodVertexFormatelement.getType() != LodVertexFormatElement.DataType.UBYTE)
|
||||
{
|
||||
throw new IllegalStateException("Color must be stored as a UBYTE");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.putByte(0, (byte) red);
|
||||
this.putByte(1, (byte) green);
|
||||
this.putByte(2, (byte) blue);
|
||||
this.putByte(3, (byte) alpha);
|
||||
this.nextElement();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public LodBufferBuilder vertex(float x, float y, float z)
|
||||
{
|
||||
if (this.currentElement().getType() != LodVertexFormatElement.DataType.FLOAT)
|
||||
{
|
||||
throw new IllegalStateException("Position verticies must be stored as a FLOAT");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.putFloat(0, x);
|
||||
this.putFloat(4, y);
|
||||
this.putFloat(8, z);
|
||||
this.nextElement();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* not currently needed fullVertex()
|
||||
* TODO James isn't sure about these names
|
||||
public void vertex(float blockPosX, float blockPosY, float blockPosZ,
|
||||
float red, float green, float blue, float alpha,
|
||||
float textureU, float textureV,
|
||||
int p_225588_10_, int p_225588_11_,
|
||||
float p_225588_12_, float p_225588_13_, float p_225588_14_)
|
||||
{
|
||||
if (this.defaultColorSet)
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
else if (this.fastFormat)
|
||||
{
|
||||
this.putFloat(0, blockPosX);
|
||||
this.putFloat(4, blockPosY);
|
||||
this.putFloat(8, blockPosZ);
|
||||
this.putByte(12, (byte) ((int) (red * 255.0F)));
|
||||
this.putByte(13, (byte) ((int) (green * 255.0F)));
|
||||
this.putByte(14, (byte) ((int) (blue * 255.0F)));
|
||||
this.putByte(15, (byte) ((int) (alpha * 255.0F)));
|
||||
this.putFloat(16, textureU);
|
||||
this.putFloat(20, textureV);
|
||||
int i;
|
||||
if (this.fullFormat)
|
||||
{
|
||||
this.putShort(24, (short) (p_225588_10_ & '\uffff'));
|
||||
this.putShort(26, (short) (p_225588_10_ >> 16 & '\uffff'));
|
||||
i = 28;
|
||||
}
|
||||
else
|
||||
{
|
||||
i = 24;
|
||||
}
|
||||
|
||||
this.putShort(i + 0, (short) (p_225588_11_ & '\uffff'));
|
||||
this.putShort(i + 2, (short) (p_225588_11_ >> 16 & '\uffff'));
|
||||
this.putByte(i + 4, IVertexConsumer.normalIntValue(p_225588_12_));
|
||||
this.putByte(i + 5, IVertexConsumer.normalIntValue(p_225588_13_));
|
||||
this.putByte(i + 6, IVertexConsumer.normalIntValue(p_225588_14_));
|
||||
this.nextElementByte += i + 8;
|
||||
this.endVertex();
|
||||
}
|
||||
else
|
||||
{
|
||||
super.vertex(blockPosX, blockPosY, blockPosZ, red, green, blue, alpha, textureU, textureV, p_225588_10_, p_225588_11_, p_225588_12_, p_225588_13_, p_225588_14_);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* James isn't sure what the difference between
|
||||
* using this method and just directly getting the buffer would be.
|
||||
* But this was what was being used before, so it will stay for now.
|
||||
*
|
||||
* If anyone figures out what is special about this, please replace this comment.
|
||||
*/
|
||||
public ByteBuffer getCleanedByteBuffer()
|
||||
{
|
||||
LodBufferBuilder.DrawState bufferbuilder$drawstate = this.vertexCounts.get(this.lastRenderedCountIndex++);
|
||||
((Buffer) this.buffer).position(this.totalUploadedBytes);
|
||||
this.totalUploadedBytes += bufferbuilder$drawstate.vertexCount() * bufferbuilder$drawstate.format().getVertexSize();
|
||||
((Buffer) this.buffer).limit(this.totalUploadedBytes);
|
||||
if (this.lastRenderedCountIndex == this.vertexCounts.size() && this.vertices == 0)
|
||||
{
|
||||
this.clear();
|
||||
}
|
||||
|
||||
ByteBuffer bytebuffer = this.buffer.slice();
|
||||
bytebuffer.order(this.buffer.order()); // FORGE: Fix incorrect byte order
|
||||
((Buffer) this.buffer).clear();
|
||||
return bytebuffer; // the original method also returned bufferbuilder$drawstate
|
||||
}
|
||||
|
||||
|
||||
public void clear()
|
||||
{
|
||||
if (this.totalRenderedBytes != this.totalUploadedBytes)
|
||||
{
|
||||
LOGGER.warn("Bytes mismatch " + this.totalRenderedBytes + " " + this.totalUploadedBytes);
|
||||
}
|
||||
|
||||
this.discard();
|
||||
}
|
||||
|
||||
public void discard()
|
||||
{
|
||||
this.totalRenderedBytes = 0;
|
||||
this.totalUploadedBytes = 0;
|
||||
this.nextElementByte = 0;
|
||||
this.vertexCounts.clear();
|
||||
this.lastRenderedCountIndex = 0;
|
||||
}
|
||||
|
||||
public LodVertexFormatElement currentElement()
|
||||
{
|
||||
if (this.currentElement == null)
|
||||
{
|
||||
throw new IllegalStateException("BufferBuilder not started");
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.currentElement;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean building()
|
||||
{
|
||||
return this.building;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// internal classes //
|
||||
//==================//
|
||||
|
||||
|
||||
public static final class DrawState
|
||||
{
|
||||
private final LodVertexFormat format;
|
||||
private final int vertexCount;
|
||||
private final int mode;
|
||||
|
||||
private DrawState(LodVertexFormat p_i225905_1_, int p_i225905_2_, int p_i225905_3_)
|
||||
{
|
||||
this.format = p_i225905_1_;
|
||||
this.vertexCount = p_i225905_2_;
|
||||
this.mode = p_i225905_3_;
|
||||
}
|
||||
|
||||
public LodVertexFormat format()
|
||||
{
|
||||
return this.format;
|
||||
}
|
||||
|
||||
public int vertexCount()
|
||||
{
|
||||
return this.vertexCount;
|
||||
}
|
||||
|
||||
public int mode()
|
||||
{
|
||||
return this.mode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Forge added methods
|
||||
public void putBulkData(ByteBuffer buffer)
|
||||
{
|
||||
ensureCapacity(buffer.limit() + this.format.getVertexSize());
|
||||
((Buffer) this.buffer).position(this.vertices * this.format.getVertexSize());
|
||||
this.buffer.put(buffer);
|
||||
this.vertices += buffer.limit() / this.format.getVertexSize();
|
||||
this.nextElementByte += buffer.limit();
|
||||
}
|
||||
|
||||
public LodVertexFormat getLodVertexFormat()
|
||||
{
|
||||
return this.format;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user