Compare commits

215 Commits

Author SHA1 Message Date
octarine-noise
d741338d46 minor cleanup 2020-02-17 15:55:37 +01:00
octarine-noise
01f697d2d3 un-bundle Mod Menu 2020-02-17 15:55:14 +01:00
octarine-noise
4c439dccd2 re-render chunks when configuration is changed 2020-02-17 12:40:21 +01:00
octarine-noise
df50f61b0d [WIP] initial Fabric port
major package refactoring
2020-02-05 13:43:54 +01:00
octarine-noise
2252fb3b42 [WIP] Optifine and Shaders fixes 2020-01-06 20:51:51 +01:00
octarine-noise
4efa831296 [WIP] more async texture loading
keep stitch() call in original method body, in case others want to mod it too
2020-01-06 18:15:17 +01:00
octarine-noise
a3d99c3076 [WIP] fix Mixin annotation processing 2020-01-05 16:33:12 +01:00
octarine-noise
c4ee768025 [WIP] async block texture reloading 2020-01-05 16:32:45 +01:00
octarine-noise
b4f18c1e1d switch to Gradle Kotlin DSL 2020-01-03 21:36:46 +01:00
octarine-noise
2a06c18884 [WIP] Roll all rendering parameters into a single context
+ split project into platform-dependent and -independent parts
2020-01-03 21:36:08 +01:00
octarine-noise
2ba99f40e7 Merge branch 'kotlin-1.12' into forge-1.14 2020-01-01 17:15:13 +01:00
octarine-noise
46cbe64328 [WIP] 1.14.4 port 2020-01-01 16:57:47 +01:00
octarine-noise
7b739c172f Bump version 2020-01-01 16:12:57 +01:00
octarine-noise
8319d721c7 Update to work with OptiFine_1.12.2_HD_U_F6_pre1 2020-01-01 15:15:44 +01:00
octarine-noise
1b0e93b050 Array bounds check for overlay layer 2020-01-01 15:14:19 +01:00
octarine-noise
1ea2b6b946 Model loading rework (1.14 prep)
remove unnecessary complexity
access sprites only at PostStitch
2019-12-30 17:35:52 +01:00
octarine-noise
558c9a2c34 Bump self and dependency versions 2019-12-21 15:10:32 +01:00
octarine-noise
f102bf4bda add Gradle wrapper 2019-12-21 15:10:09 +01:00
octarine-noise
f5cbf48dfa Try to cope with other mods wrapping IModel instances in a generic way 2019-12-21 15:09:51 +01:00
octarine-noise
d9cc03511a Cache round log neighborhood data for faster chunk re-rendering 2019-12-21 15:08:29 +01:00
octarine-noise
d0265483d2 Option to skip rendering of hidden extra leaves 2019-12-21 14:37:20 +01:00
octarine-noise
20da2a27bd Fully remove Optifine CTM code 2019-12-21 14:16:28 +01:00
octarine-noise
02509fa44d remove useless config option: distance limit 2019-09-04 11:29:50 +02:00
octarine-noise
6801304bd1 do not access TileEntities in unloaded chunks 2019-09-03 14:24:26 +02:00
octarine-noise
d96ac1c94c update Forge / Forgelin / Kotlin versions 2019-09-03 14:23:44 +02:00
octarine-noise
ac015b12df disable Optifine CTM support, fix custom colors 2019-04-13 09:37:57 +02:00
octarine-noise
71f0be0c93 fix shader mod integration 2019-03-02 13:27:39 +01:00
octarine-noise
bcc1b04e6b Optifine dev wrapper must be written in Java 2019-03-02 12:00:28 +01:00
octarine-noise
cbee4916ed Merge branch 'kotlin-1.10' into kotlin-1.12 2018-10-26 17:38:14 +02:00
octarine-noise
418d955f54 add Forgelin as mod dependency 2018-10-26 17:37:16 +02:00
octarine-noise
3121542c07 fix operand stack overflow in RenderChunk class transformation with recent Forge versions 2018-10-25 14:17:57 +02:00
octarine-noise
ab08457ae7 Merge branch 'kotlin-1.10' into kotlin-1.12 2018-10-25 14:15:06 +02:00
octarine-noise
5b1b35a891 update to latest recommended Forge 2018-10-25 14:14:30 +02:00
octarine-noise
787b3993b5 allow access to ClassWriter flags in class transformer context 2018-10-25 14:05:13 +02:00
octarine-noise
c593ff9bcb Merge branch 'kotlin-1.10' into kotlin-1.12 2018-10-25 13:46:29 +02:00
octarine-noise
afa619f838 use Forgelin 2018-10-25 11:11:14 +02:00
octarine-noise
e704a0af94 update to latest recommended Forge 2018-10-25 11:10:44 +02:00
octarine-noise
0997b83367 move values to properties file 2018-10-25 10:03:56 +02:00
octarine-noise
fe3030ef77 bump version to 2.1.11 2017-10-01 12:31:33 +02:00
octarine-noise
7aa510189a fix gray blocks with Optifine custom colors enabled 2017-09-30 13:37:43 +02:00
octarine-noise
36a3a38ff1 Merge branch 'kotlin-1.11.2' into kotlin-1.12 2017-09-10 01:18:47 +02:00
octarine-noise
7f4ee4b0a3 support MC versions 1.12 and 1.12.1 as well 2017-09-10 01:15:33 +02:00
octarine-noise
a4c6d1eecd Merge branch 'kotlin-1.10' into kotlin-1.11.2 2017-09-10 01:13:41 +02:00
octarine-noise
73223c15c3 bump version to 2.1.10 2017-09-10 01:12:32 +02:00
octarine-noise
b55d90802f fix broken serverside detection 2017-09-10 01:11:27 +02:00
octarine-noise
c5261216b2 Merge branch 'kotlin-1.11.2' into kotlin-1.12 2017-08-12 19:12:46 +02:00
octarine-noise
50399052b0 Merge branch 'kotlin-1.10' into kotlin-1.11.2 2017-08-12 18:57:10 +02:00
octarine-noise
e29d224df4 update to more recent Forge (GUI factory changes) 2017-08-12 18:19:25 +02:00
octarine-noise
3b7c44b062 bump version to 2.1.8 2017-08-12 18:15:18 +02:00
octarine-noise
32bf60492d add OptiFine custom color support 2017-08-12 17:58:24 +02:00
octarine-noise
9685971fd4 update lang file 2017-08-12 16:37:47 +02:00
octarine-noise
dacc63b11a minor code cleanup 2017-08-12 16:37:27 +02:00
octarine-noise
e20a3fbc54 move nVidia compatibility to a config option 2017-08-12 16:33:22 +02:00
octarine-noise
39df1951df add default model mappings for Lithos Core 2017-08-12 16:09:21 +02:00
octarine-noise
4761eb266f add support for block variants and multiple wood log bark textures 2017-08-12 15:59:22 +02:00
octarine-noise
25c302ecb6 add support for modded netherrack and mycelium
add support for NetherEx
2017-08-12 10:46:42 +02:00
octarine-noise
dec1f6ff03 Merge branch 'kotlin-1.11.2' into kotlin-1.12 2017-07-10 17:30:30 +02:00
octarine-noise
f145ff221e Merge branch 'kotlin-1.10' into kotlin-1.11.2 2017-07-10 17:27:34 +02:00
octarine-noise
c865c8e5ad bump version to 2.1.7 2017-07-10 17:20:32 +02:00
octarine-noise
47781cad91 fixed all rendering layer woes (hopefully) 2017-07-10 17:18:26 +02:00
octarine-noise
e329ce0270 remove unnecessary mirroring of round log endcap texture 2017-07-10 15:29:21 +02:00
octarine-noise
7cae04d7b4 fix round log texture glitch on non-nVidia cards 2017-07-10 15:28:52 +02:00
octarine-noise
0bbf206569 fix compatibility with OptiFine AA & AF 2017-07-10 15:14:07 +02:00
octarine-noise
55095e7252 Merge branch 'kotlin-1.11.2' into kotlin-1.12 2017-07-06 11:01:59 +02:00
octarine-noise
8eebb98c9d Merge branch 'kotlin-1.10' into kotlin-1.11.2 2017-07-06 11:00:32 +02:00
octarine-noise
2a8a9c2703 fix broken OptiFine integration 2017-07-06 10:59:52 +02:00
octarine-noise
7da89e24f1 update rubber integration for IC2 and TR 1.12 2017-07-06 10:12:46 +02:00
octarine-noise
fb078ab365 port to MC 1.12 2017-07-06 09:53:50 +02:00
octarine-noise
b5d87bb148 fix rubber tree integration to work with single resource loading pass 2017-07-06 09:10:38 +02:00
octarine-noise
6c98940d3e Merge branch 'kotlin-1.10' into kotlin-1.11.2 2017-07-05 15:09:28 +02:00
octarine-noise
813719c7f2 bump version to 2.1.6 2017-07-05 15:07:41 +02:00
octarine-noise
800fb4db9f add Better With Mods stumps to the log whitelist 2017-07-05 15:07:07 +02:00
octarine-noise
38b35c910b allow custom renderers to draw on multiple layers
fix extra leaves & fast graphics xray bug
2017-07-05 11:34:07 +02:00
octarine-noise
61076789db fix build errors after merge 2017-05-06 09:47:08 +02:00
octarine-noise
fde6c47ed3 Merge branch 'kotlin-1.10' into kotlin-1.11.2 2017-05-06 09:38:10 +02:00
octarine-noise
a9fba1a18e bump version to 2.1.5 2017-05-06 09:36:47 +02:00
octarine-noise
81ef954524 add support for multipart models 2017-05-06 09:36:32 +02:00
octarine-noise
674d22fdbb add AbyssalCraft grass blocks to default whitelist 2017-05-05 12:10:25 +02:00
octarine-noise
381b154413 change short grass override color to work better with dark textures 2017-05-05 12:08:55 +02:00
octarine-noise
568549e260 added population option to short grass as well (for non-full coverage) 2017-05-05 11:15:55 +02:00
octarine-noise
6d62cb9ac0 dev-only debug code for ASM transformer incompatibilities 2017-05-05 10:58:48 +02:00
octarine-noise
5bea5cde99 error reporting for unexpected registry misses 2017-05-05 10:57:34 +02:00
octarine-noise
28cead464e Merge branch 'kotlin-1.10' into kotlin-1.11.2 2017-04-09 12:07:25 +02:00
octarine-noise
8ffca417fb get rid of obfuscated namespace support, rely on FML deobfuscation instead 2017-04-09 11:30:35 +02:00
octarine-noise
8f9a35f40e bump version to 2.1.4 2017-04-09 10:26:56 +02:00
octarine-noise
ef90adf577 remove reeds and double plants from the crop list 2017-04-08 18:58:38 +02:00
octarine-noise
370e2bb38c don't render hidden faces on connected grass 2017-04-08 18:24:24 +02:00
octarine-noise
fefd5e5633 added compatibility with FoamFix anarchy parallel model baking 2017-04-08 17:58:54 +02:00
octarine-noise
bae81e8085 improve class transformer to work with SRG namespace as well 2017-04-08 17:57:52 +02:00
octarine-noise
479e4cadfa Switch to Kotlin 1.1.1, use typealias feature 2017-04-08 13:03:47 +02:00
octarine-noise
821d618395 update Forestry log support 2017-02-18 10:54:53 +01:00
octarine-noise
eb6058c4ee port to MC 1.11.2 2017-01-29 10:11:53 +01:00
octarine-noise
625a3bd543 change lang file names to lowercase 2016-12-22 00:34:42 +01:00
octarine-noise
37d091daed Merge branch 'kotlin-1.10' into kotlin-1.11 2016-12-22 00:28:58 +01:00
octarine-noise
d852faad96 bump version to 2.1.3 2016-12-22 00:01:14 +01:00
octarine-noise
da8d7ec237 add support for IC2 and TechReborn rubber logs and leaves 2016-12-21 23:57:28 +01:00
octarine-noise
31f64749b1 add some SideOnly annotations where appropriate 2016-12-21 19:54:49 +01:00
octarine-noise
66558932a9 move log block axis into the info object 2016-12-21 19:36:40 +01:00
octarine-noise
ceb3e5b116 port to MC 1.11 2016-12-11 17:02:54 +01:00
octarine-noise
1a1aa81c0f fixed leaf particle fallback type 2016-12-11 16:43:48 +01:00
octarine-noise
522fc1de33 create log directory if missing 2016-12-11 16:14:32 +01:00
octarine-noise
90c13a3a8e prefix log messages by mod name 2016-12-11 16:14:09 +01:00
octarine-noise
66b4df2850 bump version to 2.1.2 2016-12-05 00:32:15 +01:00
octarine-noise
7df142cf50 support for BOP flowering oak leaves
cleanup default models configs
2016-12-04 23:00:34 +01:00
octarine-noise
2b7582c5af fix modid mismatch between annotation and mcmod.info 2016-12-04 22:45:07 +01:00
octarine-noise
56e3dc5d24 fix color for connected grass blocks 2016-12-04 22:02:14 +01:00
octarine-noise
f7044e5225 update Aether II support 2016-12-04 20:19:05 +01:00
octarine-noise
59e4d0c602 fix saturation threshold not being respected for grass textures 2016-12-04 20:12:18 +01:00
octarine-noise
931dca6f3f add Agricultural Revolution log support
fixes #107
2016-12-04 19:21:15 +01:00
octarine-noise
c356c3ef57 defend against CME in Forestry leaf support module
fixes #106
2016-12-04 18:54:25 +01:00
octarine-noise
468d0f34b6 update Natura support
fixes #101
2016-12-04 18:50:02 +01:00
octarine-noise
6f42152cde get rid of some metadata warnings (future-proofing) 2016-12-04 18:47:25 +01:00
octarine-noise
44bfc93e1b preemptively create detail log file 2016-12-04 18:47:25 +01:00
octarine-noise
70591a484e null protection when fetching LeafInfo for leaf block 2016-12-04 18:47:24 +01:00
octarine-noise
fdc14595db add support for Forestry decorative leaves 2016-12-04 18:47:24 +01:00
Forstride
aa8226b46b Updated default block listings for Biomes O' Plenty support (#105)
Squashed 6 commits from branch kotlin-1.10 on Forstride/BetterFoliage

* Update CropDefault.cfg
* Update DirtDefault.cfg
* Update GrassBlocksDefault.cfg
* Update LeavesBlocksDefault.cfg
* Update LogBlocksDefault.cfg
* Update LilypadDefault.cfg
2016-12-04 18:46:25 +01:00
octarine-noise
ad78529d2a Merge branch 'kotlin-1.9.4' into kotlin-1.10 2016-09-17 08:33:33 +02:00
octarine-noise
8c922fd2e8 Merge branch 'kotlin-1.9' into kotlin-1.9.4 2016-09-13 21:12:29 +02:00
octarine-noise
9e9666f69f Merge branch 'kotlin-1.8' into kotlin-1.9 2016-09-13 21:08:36 +02:00
octarine-noise
2c0e95ba5b - revert to case-sensitive config
- fix handling of toggle lists
- ignore unknown options in config file
2016-09-13 20:36:35 +02:00
octarine-noise
85a4707494 automatically delete obsolete config properties 2016-09-05 22:45:00 +02:00
octarine-noise
f0a447bbbb switch to recent Forge and Kotlin versions 2016-09-05 22:43:30 +02:00
octarine-noise
4a4d39b523 Add support for Forestry logs
fixes #82
2016-09-05 22:40:13 +02:00
octarine-noise
e00ccd5919 bump version 2016-08-13 00:27:54 +02:00
octarine-noise
f032814d99 fix particle mapping
Forestry support
2016-08-11 11:56:52 +02:00
octarine-noise
62294bb2bb add support for Forestry leaves 2016-08-11 11:14:42 +02:00
octarine-noise
dec1ffd71c fix config change listener 2016-08-11 11:00:32 +02:00
octarine-noise
488078b50f rewrite model and texture detection
expose in mod configuration
2016-08-11 10:58:57 +02:00
octarine-noise
1bd353577f always push shader metadata for block models 2016-08-09 09:23:55 +02:00
octarine-noise
913496473d Merge branch 'kotlin-1.9.4' into kotlin-1.10 2016-07-28 12:18:58 +02:00
octarine-noise
a8e6c6c470 changed Kotlin version to 1.0.2, no more class relocation 2016-07-28 12:16:57 +02:00
octarine-noise
8bdb5ca8fd update modded block support 2016-07-28 12:16:09 +02:00
octarine-noise
5efc974133 Merge branch 'kotlin-1.9' into kotlin-1.9.4 2016-07-23 13:37:05 +02:00
octarine-noise
7f3617ef59 Merge branch 'kotlin-1.8' into kotlin-1.9 2016-07-23 13:32:31 +02:00
octarine-noise
400b965e02 use case insensitive names in config 2016-07-23 12:39:56 +02:00
octarine-noise
f96409f9a1 allow for custom column textures (preparation for rubber log support) 2016-07-23 12:01:28 +02:00
octarine-noise
2ca330fd29 added option to disable snow on extra leaves 2016-07-23 09:34:10 +02:00
octarine-noise
acf477d709 added korean localization 2016-07-23 09:32:11 +02:00
octarine-noise
97d5d6320c Merge pull request #62 from Ghostlyr/patch-1
Small inaccuracy in the russian localization
2016-06-28 00:43:51 +02:00
octarine-noise
6831050c77 port to 1.10 2016-06-27 23:38:37 +02:00
octarine-noise
70ec7e5289 use case insensitive names in config 2016-06-27 23:36:28 +02:00
octarine-noise
f66aabea67 port to 1.9.4 2016-06-21 01:28:43 +02:00
octarine-noise
0635f1e19e update to OptiFine H6 2016-06-15 20:54:58 +02:00
Ghostlyr
44a8abeb4b Small inaccuracy in the russian localization
Sorry, automatic translation =)
2016-06-07 00:32:10 +06:00
octarine-noise
b5af0fe1c5 upgrade to recent Forge and MCP mappings 2016-05-16 12:20:26 +02:00
octarine-noise
69db3d6608 Merge branch 'kotlin-1.8' into kotlin-1.9 2016-05-16 12:16:48 +02:00
octarine-noise
6903bd2de2 bump version 2016-05-16 12:12:43 +02:00
octarine-noise
9a544b1b53 support leaves that don't use the model minecraft:block/leaves 2016-05-16 12:05:54 +02:00
octarine-noise
bbd4df418c add support for blocks with submodels 2016-05-16 12:01:25 +02:00
octarine-noise
626bc69dad fix CTM textures
bump version to 2.0.10
2016-04-25 19:14:16 +02:00
octarine-noise
66ed1c098f do not use deprecated Block.canRenderInLayer() 2016-04-19 21:11:09 +02:00
octarine-noise
f37cb273f1 Merge branch 'kotlin-1.8' into kotlin-1.9 2016-04-19 21:09:52 +02:00
octarine-noise
a6cc354965 fix wrong height range used for algae 2016-04-19 20:56:28 +02:00
octarine-noise
8fe4346922 fix shallow water coral config option 2016-04-19 20:55:14 +02:00
octarine-noise
2f45abcd7c bump version to 2.0.9 2016-04-19 20:47:53 +02:00
octarine-noise
57cae957f8 keep round logs on the SOLID rendering layer 2016-04-19 20:23:07 +02:00
octarine-noise
befb64b8fc animated texture support for generated short grass 2016-04-19 20:21:17 +02:00
octarine-noise
a0aad5d608 fixed startup crash on null block in blockstate 2016-04-19 20:20:58 +02:00
octarine-noise
c0685d829b update to latest Forge and OptiFine
bump version to 2.0.8
2016-04-07 00:20:09 +02:00
octarine-noise
4209d1eea3 bump version to 2.0.7 2016-04-01 19:11:56 +02:00
octarine-noise
ac2001694b fix random display tick not firing 2016-04-01 19:11:28 +02:00
octarine-noise
50c4882855 fix config crash when accessing isOpaqueCube() before postInit 2016-04-01 19:08:06 +02:00
octarine-noise
07dc8888ef minor cosmetic tweaks in log renderer 2016-04-01 18:23:47 +02:00
octarine-noise
efebf29c64 bump version to 2.0.7 2016-04-01 18:21:49 +02:00
octarine-noise
983703133a Merge branch 'kotlin-1.8' into kotlin-1.9 2016-04-01 18:14:10 +02:00
octarine-noise
ee19120651 Copied from pull request #52 from Ghostlyr/kotlin-1.7.10
Add russian localization.
2016-04-01 18:12:27 +02:00
octarine-noise
cbaebfa26a fixed #50 2016-03-29 22:29:32 +02:00
octarine-noise
1ff5d45840 fixed X-ray glitch caused by Round Logs being reported as opaqe blocks 2016-03-29 22:28:49 +02:00
octarine-noise
7e667d483a fixed Log block endcap UV rotations 2016-03-29 22:14:59 +02:00
octarine-noise
6d5c03ba6a fix crash with OptiFine 1.9 (pre A series)
bump to 2.0.5
2016-03-26 09:28:54 +01:00
octarine-noise
f47aedf84d Merge branch 'kotlin-1.8' into kotlin-1.9 2016-03-26 08:08:58 +01:00
octarine-noise
6ee27c2a99 port to MC 1.9 2016-03-21 20:54:06 +01:00
octarine-noise
abf037d8a9 change the OptiFine dev tweaker to work with OF 1.8.x H5 and later 2016-03-21 20:51:37 +01:00
octarine-noise
c0be72bb37 get Optifine in the dev environment 2016-02-27 02:09:14 +01:00
octarine-noise
42c14790af Merge branch 'kotlin-1.8' into kotlin-1.8.8 2016-02-21 12:35:49 +01:00
octarine-noise
087e8d5685 fix Short Grass under snow 2016-02-21 12:34:00 +01:00
octarine-noise
aaca43fe2c Merge branch 'kotlin-1.8' into kotlin-1.8.8 2016-02-21 12:15:09 +01:00
octarine-noise
8e251dc038 make particle type definitions case insensitive 2016-02-21 12:12:55 +01:00
octarine-noise
1a6ffb251b bump version to 2.0.4 2016-02-21 11:57:59 +01:00
octarine-noise
efb49125b8 support Cooking Plus log blocks 2016-02-21 11:09:45 +01:00
octarine-noise
6032a120d8 support Plant Mega Pack log blocks 2016-02-21 11:08:44 +01:00
octarine-noise
f68f0e5edd Merge branch 'kotlin-1.8' into kotlin-1.8.8 2016-02-21 10:39:44 +01:00
octarine-noise
7393fed5fd Switch Kotlin version to 1.0.0 2016-02-21 10:37:37 +01:00
octarine-noise
41b080646a correctly apply smooth shading multipliers for AO data 2016-02-17 23:56:35 +01:00
octarine-noise
111f1b3907 fix custom texture search location 2016-02-13 11:56:48 +01:00
octarine-noise
662b4f50f0 Merge branch 'kotlin-1.8' into kotlin-1.8.8 2016-01-30 13:48:13 +01:00
octarine-noise
d9a042b356 protect against render buffer underruns 2016-01-30 13:44:33 +01:00
octarine-noise
ef574a13ac Merge branch 'kotlin-1.8' into kotlin-1.8.8 2016-01-30 13:05:30 +01:00
octarine-noise
b246c7acd0 simplified ModelDataInspector 2016-01-30 12:22:29 +01:00
octarine-noise
35ce80c602 added config option for rendering Round Logs with unknown axis
added Tangle Logs to the default whitelist
2016-01-30 12:20:13 +01:00
octarine-noise
4fdabbaf69 fixed lack of generic signature in 1.8 mappings 2016-01-30 11:57:34 +01:00
octarine-noise
0a6c433530 Merge branch 'kotlin-1.8' into kotlin-1.8.8 2016-01-30 00:58:41 +01:00
octarine-noise
3e6f98885f fixed & improved Round Log shading 2016-01-30 00:56:33 +01:00
octarine-noise
4720667f53 updated Thaumcraft Log block class 2016-01-30 00:52:35 +01:00
octarine-noise
5616a68f5a changed Kotlin version 2016-01-30 00:49:59 +01:00
octarine-noise
d1480ed3be improved Log block axis detection 2016-01-30 00:46:15 +01:00
octarine-noise
023e286fd4 Merge branch 'kotlin-1.8' into kotlin-1.8.8 2016-01-29 11:32:33 +01:00
octarine-noise
6c8f40f4e2 Optifine CTM support 2016-01-28 01:18:18 +01:00
octarine-noise
6306a5b03b fix accepted MC versions 2016-01-28 00:21:37 +01:00
octarine-noise
b0193bb108 update version 2016-01-18 14:31:22 +01:00
octarine-noise
eafd36b4b1 fixed OptiFine compatibility 2016-01-18 13:46:56 +01:00
octarine-noise
dac7033e18 Merge branch 'kotlin-1.8' into kotlin-1.8.8 2016-01-18 12:51:21 +01:00
octarine-noise
36c6b775db changed Extra Leaves brightness calculation 2016-01-18 12:50:08 +01:00
octarine-noise
0f985e996b port to MC 1.8.8 2016-01-18 12:47:23 +01:00
octarine-noise
e926f018e7 actually fix #36 2016-01-16 08:35:30 +01:00
octarine-noise
3716804ffb fix Round Log top and bottom texture rotation 2016-01-14 00:07:03 +01:00
octarine-noise
2d70de00e7 changed Round Log tapering rules 2016-01-13 23:12:04 +01:00
octarine-noise
9b717b549b fix for fix of #36 2016-01-13 23:00:19 +01:00
octarine-noise
81abad82e9 fixed #36 2016-01-13 22:55:07 +01:00
octarine-noise
601679f070 switch to new Forge and ForgeGradle version 2016-01-09 14:12:03 +01:00
octarine-noise
f0073b0f2a consolidate AccessTransformer 2016-01-09 14:11:28 +01:00
octarine-noise
5bdbb366eb remove nasty class visibility hack 2016-01-09 14:11:00 +01:00
octarine-noise
2e09f1f10a fixed language file typos 2016-01-09 13:36:55 +01:00
octarine-noise
8460103030 port to MC 1.8 2016-01-09 12:55:52 +01:00
octarine-noise
f44043bb0b first Kotlin version 2016-01-09 12:27:42 +01:00
259 changed files with 6525 additions and 4191 deletions

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="src" path="src/main/resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.springsource.ide.eclipse.gradle.classpathcontainer"/>
<classpathentry kind="output" path="bin"/>
</classpath>

10
.gitignore vendored
View File

@@ -1,5 +1,9 @@
.idea/
*.iml
*.ipr
*.iws
run/
.gradle/
.settings/
bin/
build/
libs/
classes/
temp/

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>BetterFoliage</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.springsource.ide.eclipse.gradle.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@@ -1,9 +1,4 @@
BetterFoliage
=============
Minecraft mod that alters the appearance of leaves &amp; grass
More info: http://www.minecraftforum.net/topic/2776217-better-foliage/
Download
========
[BetterFoliage 0.9.9-beta] (http://goo.gl/giuL8S) (MC 1.7.2 & 1.7.10)

View File

@@ -1,45 +0,0 @@
buildscript {
repositories {
mavenCentral()
maven {
name = "forge"
url = "http://files.minecraftforge.net/maven"
}
maven {
name = "sonatype"
url = "https://oss.sonatype.org/content/repositories/snapshots/"
}
}
dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT'
}
}
apply plugin: 'forge'
minecraft {
version = '1.7.2-10.12.2.1147'
}
jar.baseName = 'BetterFoliage'
group = 'com.github.octarine-noise'
version='0.9.10b'
processResources {
inputs.property "version", project.version
inputs.property "mcversion", project.minecraft.version
from(sourceSets.main.resources.srcDirs) {
include 'mcmod.info'
expand 'version':project.version, 'mcversion':project.minecraft.version
}
from(sourceSets.main.resources.srcDirs) {
exclude 'mcmod.info'
}
}
jar {
manifest {
attributes("FMLCorePlugin": "mods.betterfoliage.loader.BetterFoliageLoader", "FMLCorePluginContainsFMLMod": "mods.betterfoliage.BetterFoliage")
}
}

69
build.gradle.kts Normal file
View File

@@ -0,0 +1,69 @@
import net.fabricmc.loom.task.RemapJarTask
import org.ajoberstar.grgit.Grgit
plugins {
kotlin("jvm").version("1.3.60")
id("fabric-loom").version("0.2.6-SNAPSHOT")
id("org.ajoberstar.grgit").version("3.1.1")
}
apply(plugin = "org.ajoberstar.grgit")
val gitHash = (project.ext.get("grgit") as Grgit).head().abbreviatedId
val semVer = "${project.version}+$gitHash"
val jarName = "BetterFoliage-$semVer-Fabric-${properties["mcVersion"]}"
repositories {
maven("http://maven.fabricmc.net/")
maven("https://minecraft.curseforge.com/api/maven")
maven("http://maven.modmuss50.me/")
maven("https://grondag-repo.appspot.com").credentials { username = "guest"; password = "" }
maven("https://jitpack.io")
}
dependencies {
"minecraft"("com.mojang:minecraft:${properties["mcVersion"]}")
"mappings"("net.fabricmc:yarn:${properties["yarnMappings"]}:v2")
// basic Fabric stuff
"modImplementation"("net.fabricmc:fabric-loader:${properties["loaderVersion"]}")
"modImplementation"("net.fabricmc.fabric-api:fabric-api:${properties["fabricVersion"]}")
"modImplementation"("net.fabricmc:fabric-language-kotlin:${properties["fabricKotlinVersion"]}")
// configuration handling
"modImplementation"("io.github.prospector:modmenu:${properties["modMenuVersion"]}")
listOf("modImplementation", "include").forEach { configuration ->
configuration("me.shedaniel.cloth:config-2:${properties["clothConfigVersion"]}")
configuration("me.zeroeightsix:fiber:${properties["fiberVersion"]}")
}
// Canvas Renderer
// "modImplementation"("grondag:canvas:0.7.+")
// Optifabric
"modImplementation"("com.github.modmuss50:OptiFabric:df03dc2c22")
"implementation"("org.zeroturnaround:zt-zip:1.13")
}
sourceSets {
get("main").ext["refMap"] = "betterfoliage.refmap.json"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlin {
target.compilations.configureEach {
kotlinOptions.jvmTarget = "1.8"
kotlinOptions.freeCompilerArgs += listOf("-Xno-param-assertions", "-Xno-call-assertions")
}
}
tasks.getByName<ProcessResources>("processResources") {
filesMatching("fabric.mod.json") { expand(mutableMapOf("version" to semVer)) }
}
tasks.getByName<RemapJarTask>("remapJar") {
archiveName = "$jarName.jar"
}

21
gradle.properties Normal file
View File

@@ -0,0 +1,21 @@
org.gradle.jvmargs=-Xmx2G
org.gradle.daemon=false
group = com.github.octarine-noise
name = betterfoliage
jarName = BetterFoliage-Forge
version = 2.5.0
mcVersion = 1.14.4
yarnMappings=1.14.4+build.15
loaderVersion=0.7.3+build.176
fabricVersion=0.4.2+build.246-1.14
loomVersion=0.2.6-SNAPSHOT
kotlinVersion=1.3.60
fabricKotlinVersion=1.3.60+build.1
clothConfigVersion=1.8
modMenuVersion=1.7.6+build.115
fiberVersion=0.8.0-2

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

183
gradlew vendored Normal file
View File

@@ -0,0 +1,183 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## 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='"-Xmx64m" "-Xms64m"'
# 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 or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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=`expr $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"
exec "$JAVACMD" "$@"

100
gradlew.bat vendored Normal file
View File

@@ -0,0 +1,100 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@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="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_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

8
settings.gradle.kts Normal file
View File

@@ -0,0 +1,8 @@
pluginManagement {
repositories {
jcenter()
maven("https://maven.fabricmc.net/")
gradlePluginPortal()
}
}
rootProject.name = "betterfoliage"

View File

@@ -1,48 +0,0 @@
package mods.betterfoliage;
import java.io.File;
import java.util.Map;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.common.config.BetterFoliageConfig;
import org.apache.logging.log4j.Logger;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.network.NetworkCheckHandler;
import cpw.mods.fml.relauncher.Side;
@Mod(name=BetterFoliage.MOD_NAME, modid=BetterFoliage.MOD_ID, acceptedMinecraftVersions=BetterFoliage.MC_VERSIONS, guiFactory=BetterFoliage.GUI_FACTORY)
public class BetterFoliage {
public static final String MOD_ID = "BetterFoliage";
public static final String MOD_NAME = "Better Foliage";
public static final String MC_VERSIONS = "[1.7.2,1.7.10]";
public static final String GUI_FACTORY = "mods.betterfoliage.client.gui.ConfigGuiFactory";
@Mod.Instance
public static BetterFoliage instance;
public static BetterFoliageConfig config = new BetterFoliageConfig();
public static Logger log;
public static File configDir;
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
log = event.getModLog();
if (event.getSide() == Side.CLIENT) {
configDir = new File(event.getModConfigurationDirectory(), MOD_ID);
configDir.mkdir();
config.load(new File(configDir, "betterfoliage.cfg"));
BetterFoliageClient.preInit();
}
}
@NetworkCheckHandler
public boolean checkVersion(Map<String, String> mods, Side side) {
return true;
}
}

View File

@@ -1,120 +0,0 @@
package mods.betterfoliage.client;
import java.io.File;
import java.util.Map;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.impl.EntityFXFallingLeaves;
import mods.betterfoliage.client.render.impl.RenderBlockBetterAlgae;
import mods.betterfoliage.client.render.impl.RenderBlockBetterCactus;
import mods.betterfoliage.client.render.impl.RenderBlockBetterCoral;
import mods.betterfoliage.client.render.impl.RenderBlockBetterGrass;
import mods.betterfoliage.client.render.impl.RenderBlockBetterLeaves;
import mods.betterfoliage.client.render.impl.RenderBlockBetterLilypad;
import mods.betterfoliage.client.render.impl.RenderBlockBetterReed;
import mods.betterfoliage.client.resource.LeafGenerator;
import mods.betterfoliage.client.resource.LeafParticleTextures;
import mods.betterfoliage.client.resource.LeafTextureEnumerator;
import mods.betterfoliage.client.resource.ReedGenerator;
import mods.betterfoliage.client.resource.ShortGrassGenerator;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import com.google.common.collect.Maps;
import cpw.mods.fml.client.registry.RenderingRegistry;
import cpw.mods.fml.common.FMLCommonHandler;
public class BetterFoliageClient {
public static ResourceLocation missingTexture = new ResourceLocation("betterfoliage", "textures/blocks/missing_leaf.png");
public static Map<Integer, IRenderBlockDecorator> decorators = Maps.newHashMap();
public static LeafGenerator leafGenerator = new LeafGenerator();
public static LeafParticleTextures leafParticles = new LeafParticleTextures(0);
public static WindTracker wind = new WindTracker();
public static BlockMatcher leaves = new BlockMatcher();
public static BlockMatcher crops = new BlockMatcher();
public static BlockMatcher dirt = new BlockMatcher();
public static BlockMatcher grass = new BlockMatcher();
public static void preInit() {
FMLCommonHandler.instance().bus().register(new KeyHandler());
BetterFoliage.log.info("Registering renderers");
registerRenderer(new RenderBlockBetterLeaves());
registerRenderer(new RenderBlockBetterGrass());
registerRenderer(new RenderBlockBetterCactus());
registerRenderer(new RenderBlockBetterLilypad());
registerRenderer(new RenderBlockBetterReed());
registerRenderer(new RenderBlockBetterAlgae());
registerRenderer(new RenderBlockBetterCoral());
MinecraftForge.EVENT_BUS.register(wind);
FMLCommonHandler.instance().bus().register(wind);
leaves.load(new File(BetterFoliage.configDir, "classesLeaves.cfg"), new ResourceLocation("betterfoliage:classesLeavesDefault.cfg"));
MinecraftForge.EVENT_BUS.register(leaves);
crops.load(new File(BetterFoliage.configDir, "classesCrops.cfg"), new ResourceLocation("betterfoliage:classesCropsDefault.cfg"));
MinecraftForge.EVENT_BUS.register(crops);
dirt.load(new File(BetterFoliage.configDir, "classesDirt.cfg"), new ResourceLocation("betterfoliage:classesDirtDefault.cfg"));
MinecraftForge.EVENT_BUS.register(dirt);
grass.load(new File(BetterFoliage.configDir, "classesGrass.cfg"), new ResourceLocation("betterfoliage:classesGrassDefault.cfg"));
MinecraftForge.EVENT_BUS.register(grass);
BetterFoliage.log.info("Registering texture generators");
MinecraftForge.EVENT_BUS.register(leafGenerator);
MinecraftForge.EVENT_BUS.register(leafParticles);
MinecraftForge.EVENT_BUS.register(new LeafTextureEnumerator());
MinecraftForge.EVENT_BUS.register(new ReedGenerator("bf_reed_bottom", missingTexture, true));
MinecraftForge.EVENT_BUS.register(new ReedGenerator("bf_reed_top", missingTexture, false));
MinecraftForge.EVENT_BUS.register(new ShortGrassGenerator("bf_shortgrass", missingTexture, false));
MinecraftForge.EVENT_BUS.register(new ShortGrassGenerator("bf_shortgrass_snow", missingTexture, true));
ShadersModIntegration.init();
}
public static boolean isLeafTexture(TextureAtlasSprite icon) {
String resourceLocation = icon.getIconName();
if (resourceLocation.startsWith("forestry:leaves/")) return true;
return false;
}
public static int getRenderTypeOverride(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
// universal sign for DON'T RENDER ME!
if (original == -1) return original;
for (Map.Entry<Integer, IRenderBlockDecorator> entry : decorators.entrySet())
if (entry.getValue().isBlockAccepted(blockAccess, x, y, z, block, original))
return entry.getKey();
return original;
}
public static void onRandomDisplayTick(Block block, World world, int x, int y, int z) {
if (!BetterFoliage.config.fallingLeavesEnabled) return;
if (!leaves.matchesID(block) || !world.isAirBlock(x, y - 1, z)) return;
if (Math.random() > BetterFoliage.config.fallingLeavesChance.value) return;
Minecraft.getMinecraft().effectRenderer.addEffect(new EntityFXFallingLeaves(world, x, y, z));
}
public static void registerRenderer(IRenderBlockDecorator decorator) {
int renderId = RenderingRegistry.getNextAvailableRenderId();
decorators.put(renderId, decorator);
RenderingRegistry.registerBlockHandler(renderId, decorator);
MinecraftForge.EVENT_BUS.register(decorator);
decorator.init();
}
}

View File

@@ -1,83 +0,0 @@
package mods.betterfoliage.client;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Iterator;
import java.util.Set;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.common.util.Utils;
import net.minecraft.block.Block;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.world.WorldEvent;
import com.google.common.collect.Sets;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
public class BlockMatcher {
public Set<Class<?>> whiteList = Sets.newHashSet();
public Set<Class<?>> blackList = Sets.newHashSet();
public Set<Integer> blockIDs = Sets.newHashSet();
public void addClass(String className) {
try {
if (className.startsWith("-"))
blackList.add(Class.forName(className.substring(1)));
else
whiteList.add(Class.forName(className));
} catch(ClassNotFoundException e) {}
}
public boolean matchesClass(Block block) {
for (Class<?> clazz : blackList) if (clazz.isAssignableFrom(block.getClass())) return false;
for (Class<?> clazz : whiteList) if (clazz.isAssignableFrom(block.getClass())) return true;
return false;
}
public boolean matchesID(int blockId) {
return blockIDs.contains(blockId);
}
public boolean matchesID(Block block) {
return blockIDs.contains(Block.blockRegistry.getIDForObject(block));
}
public void load(File file, ResourceLocation defaults) {
if (!file.exists()) Utils.copyFromTextResource(defaults, file);
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
whiteList.clear();
blackList.clear();
String line = reader.readLine();
while(line != null) {
addClass(line.trim());
line = reader.readLine();
}
reader.close();
} catch (Exception e) {
BetterFoliage.log.warn(String.format("Error reading configuration: %s", file.getName()));
}
}
/** Caches block IDs on world load for fast lookup
* @param event
*/
@SuppressWarnings("unchecked")
@SubscribeEvent
public void handleWorldLoad(WorldEvent.Load event) {
if (!(event.world instanceof WorldClient)) return;
blockIDs.clear();
Iterator<Block> iter = Block.blockRegistry.iterator();
while (iter.hasNext()) {
Block block = iter.next();
if (matchesClass(block)) blockIDs.add(Block.blockRegistry.getIDForObject(block));
}
}
}

View File

@@ -1,27 +0,0 @@
package mods.betterfoliage.client;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.InputEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.ConfigGuiMain;
import net.minecraft.client.settings.KeyBinding;
@SideOnly(Side.CLIENT)
public class KeyHandler {
public static KeyBinding guiBinding;
public KeyHandler() {
guiBinding = new KeyBinding("key.betterfoliage.gui", 66, BetterFoliage.MOD_NAME);
ClientRegistry.registerKeyBinding(guiBinding);
}
@SubscribeEvent
public void handleKeyPress(InputEvent.KeyInputEvent event) {
if (guiBinding.isPressed()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiMain(null));
}
}

View File

@@ -1,85 +0,0 @@
package mods.betterfoliage.client;
import java.lang.reflect.Field;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.util.ResourceLocation;
/** Call hooks and helper methods for dealing with Shaders Mod.
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class ShadersModIntegration {
private static boolean hasShadersMod = false;
private static int tallGrassEntityData;
private static int leavesEntityData;
private static Field shadersEntityData;
private static Field shadersEntityDataIndex;
/** Hide constructor */
private ShadersModIntegration() {}
public static void init() {
tallGrassEntityData = Block.blockRegistry.getIDForObject(Blocks.tallgrass) & 0xFFFF | Blocks.tallgrass.getRenderType() << 16;
leavesEntityData = Block.blockRegistry.getIDForObject(Blocks.leaves) & 0xFFFF | Blocks.leaves.getRenderType() << 16;
try {
Class<?> classShaders = Class.forName("shadersmodcore.client.Shaders");
shadersEntityData = classShaders.getDeclaredField("entityData");
shadersEntityDataIndex = classShaders.getDeclaredField("entityDataIndex");
hasShadersMod = true;
} catch(Exception e) {
}
}
/** Signal start of grass-type quads
*/
public static void startGrassQuads() {
if (!hasShadersMod) return;
setShadersEntityData(tallGrassEntityData);
}
/** Signal start of leaf-type quads
*/
public static void startLeavesQuads() {
if (!hasShadersMod) return;
setShadersEntityData(leavesEntityData);
}
/** Change the entity data (containing block ID) for the currently rendered block.
* Quads drawn afterwards will have the altered data.
* @param data
*/
private static void setShadersEntityData(int data) {
try {
int[] entityData = (int[]) shadersEntityData.get(null);
int entityDataIndex = shadersEntityDataIndex.getInt(null);
entityData[(entityDataIndex * 2)] = data;
} catch (Exception e) {
}
}
/** Call hook from transformed ShadersMod class
* @param original entity data of currently rendered block
* @param block the block
* @return entity data to use
*/
public static int getBlockIdOverride(int original, Block block) {
if (BetterFoliageClient.leaves.matchesID(original & 0xFFFF)) return leavesEntityData;
if (BetterFoliageClient.crops.matchesID(original & 0xFFFF)) return tallGrassEntityData;
return original;
}
/**
* @param resource texture resource
* @return true if texture is a normal or specular map
*/
public static boolean isSpecialTexture(ResourceLocation resource) {
return resource.getResourcePath().toLowerCase().endsWith("_n.png") || resource.getResourcePath().toLowerCase().endsWith("_s.png");
}
}

View File

@@ -1,64 +0,0 @@
package mods.betterfoliage.client;
import java.util.Random;
import mods.betterfoliage.BetterFoliage;
import net.minecraft.client.Minecraft;
import net.minecraft.world.World;
import net.minecraftforge.event.world.WorldEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
public class WindTracker {
public Random random = new Random();
public double targetX;
public double targetZ;
public double currentX;
public double currentZ;
public long nextChange = 0;
public void changeWind(World world) {
long changeTime = 120;
nextChange = world.getWorldInfo().getWorldTime() + changeTime;
double direction = 2.0 * Math.PI * random.nextDouble();
double speed = Math.abs(random.nextGaussian()) * BetterFoliage.config.fallingLeavesWindStrength.value;
if (world.isRaining()) speed += Math.abs(random.nextGaussian()) * BetterFoliage.config.fallingLeavesStormStrength.value;
targetX = Math.cos(direction) * speed;
targetZ = Math.sin(direction) * speed;
}
@SubscribeEvent
public void handleWorldTick(ClientTickEvent event) {
if (event.phase != TickEvent.Phase.START) return;
World world = Minecraft.getMinecraft().theWorld;
if (world == null) return;
// change target wind speed
if (world.getWorldInfo().getWorldTime() >= nextChange) changeWind(world);
// change current wind speed
double changeRate = world.isRaining() ? 0.015 : 0.005;
double deltaX = targetX - currentX;
if (deltaX < -changeRate) deltaX = -changeRate;
if (deltaX > changeRate) deltaX = changeRate;
double deltaZ = targetZ - currentZ;
if (deltaZ < -changeRate) deltaZ = -changeRate;
if (deltaZ > changeRate) deltaZ = changeRate;
currentX += deltaX;
currentZ += deltaZ;
}
@SubscribeEvent
public void handleWorldLoad(WorldEvent.Load event) {
if (event.world.isRemote) changeWind(event.world);
}
}

View File

@@ -1,39 +0,0 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
import mods.betterfoliage.client.gui.widget.OptionIntegerWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class ConfigGuiAlgae extends ConfigGuiScreenBase {
public ConfigGuiAlgae(GuiScreen parent) {
super(parent);
int id = 10;
widgets.add(new OptionDoubleWidget(BetterFoliage.config.algaeHOffset, -100, -70, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.algaeSize, -100, -40, 200, 50, id++, id++, "message.betterfoliage.size", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.algaeHeightMin, -100, -10, 200, 50, id++, id++, "message.betterfoliage.minHeight", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.algaeHeightMax, -100, 20, 200, 50, id++, id++, "message.betterfoliage.maxHeight", "%.2f"));
widgets.add(new OptionIntegerWidget(BetterFoliage.config.algaeChance, -100, 50, 200, 50, id++, id++, "message.betterfoliage.algaeChance"));
}
@SuppressWarnings("unchecked")
@Override
public void addButtons(int x, int y) {
buttonList.add(new GuiButton(0, x - 50, y + 100, 100, 20, I18n.format("message.betterfoliage.back")));
}
@Override
protected void onButtonPress(int id) {
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
if (BetterFoliage.config.algaeHeightMin.value > BetterFoliage.config.algaeHeightMax.value) BetterFoliage.config.algaeHeightMin.value = BetterFoliage.config.algaeHeightMax.value;
}
}

View File

@@ -1,34 +0,0 @@
package mods.betterfoliage.client.gui;
import cpw.mods.fml.client.FMLClientHandler;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
import mods.betterfoliage.client.gui.widget.OptionIntegerWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
public class ConfigGuiCoral extends ConfigGuiScreenBase {
public ConfigGuiCoral(GuiScreen parent) {
super(parent);
int id = 10;
widgets.add(new OptionDoubleWidget(BetterFoliage.config.coralCrustSize, -100, -100, 200, 50, id++, id++, "message.betterfoliage.crustSize", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.coralVOffset, -100, -70, 200, 50, id++, id++, "message.betterfoliage.vOffset", "%.3f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.coralSize, -100, -40, 200, 50, id++, id++, "message.betterfoliage.size", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.coralHOffset, -100, -10, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
widgets.add(new OptionIntegerWidget(BetterFoliage.config.coralPopulation, -100, 20, 200, 50, id++, id++, "message.betterfoliage.coralPopulation"));
widgets.add(new OptionIntegerWidget(BetterFoliage.config.coralChance, -100, 50, 200, 50, id++, id++, "message.betterfoliage.coralChance"));
}
@SuppressWarnings("unchecked")
@Override
public void addButtons(int x, int y) {
buttonList.add(new GuiButton(0, x - 50, y + 80, 100, 20, I18n.format("message.betterfoliage.back")));
}
@Override
protected void onButtonPress(int id) {
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
}
}

View File

@@ -1,32 +0,0 @@
package mods.betterfoliage.client.gui;
import java.util.Set;
import com.google.common.collect.ImmutableSet;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import cpw.mods.fml.client.IModGuiFactory;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class ConfigGuiFactory implements IModGuiFactory {
public void initialize(Minecraft minecraftInstance) {
}
public Class<? extends GuiScreen> mainConfigGuiClass() {
return ConfigGuiMain.class;
}
public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() {
return ImmutableSet.<RuntimeOptionCategoryElement>of();
}
public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
return null;
}
}

View File

@@ -1,34 +0,0 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.FMLClientHandler;
public class ConfigGuiFallingLeaves extends ConfigGuiScreenBase {
public ConfigGuiFallingLeaves(GuiScreen parent) {
super(parent);
int id = 10;
widgets.add(new OptionDoubleWidget(BetterFoliage.config.fallingLeavesSize, -100, -100, 200, 50, id++, id++, "message.betterfoliage.size", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.fallingLeavesSpeed, -100, -70, 200, 50, id++, id++, "message.betterfoliage.speed", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.fallingLeavesWindStrength, -100, -40, 200, 50, id++, id++, "message.betterfoliage.windStrength", "%.1f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.fallingLeavesStormStrength, -100, -10, 200, 50, id++, id++, "message.betterfoliage.stormStrength", "%.1f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.fallingLeavesPerturb, -100, 20, 200, 50, id++, id++, "message.betterfoliage.fallingLeafPerturbation", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.fallingLeavesChance, -100, 50, 200, 50, id++, id++, "message.betterfoliage.fallingLeafChance", "%.3f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.fallingLeavesLifetime, -100, 80, 200, 50, id++, id++, "message.betterfoliage.fallingLeafLifetime", "%.2f"));
}
@SuppressWarnings("unchecked")
@Override
public void addButtons(int x, int y) {
buttonList.add(new GuiButton(0, x - 50, y + 110, 100, 20, I18n.format("message.betterfoliage.back")));
}
@Override
protected void onButtonPress(int id) {
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
}
}

View File

@@ -1,45 +0,0 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class ConfigGuiGrass extends ConfigGuiScreenBase {
public enum Button {CLOSE, GRASS_USE_GENERATED}
public ConfigGuiGrass(GuiScreen parent) {
super(parent);
int id = 10;
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassSize, -100, -70, 200, 50, id++, id++, "message.betterfoliage.size", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassHOffset, -100, -10, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassHeightMin, -100, 20, 200, 50, id++, id++, "message.betterfoliage.minHeight", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.grassHeightMax, -100, 50, 200, 50, id++, id++, "message.betterfoliage.maxHeight", "%.2f"));
}
@SuppressWarnings("unchecked")
@Override
public void addButtons(int x, int y) {
buttonList.add(new GuiButton(Button.CLOSE.ordinal(), x - 50, y + 80, 100, 20, I18n.format("message.betterfoliage.back")));
buttonList.add(new GuiButton(Button.GRASS_USE_GENERATED.ordinal(), x - 100, y - 40, 200, 20, ""));
}
protected void updateButtons() {
setButtonOptionBoolean(Button.GRASS_USE_GENERATED.ordinal(), "message.betterfoliage.genShortgrass", BetterFoliage.config.grassUseGenerated);
}
@Override
protected void onButtonPress(int id) {
if (id == Button.CLOSE.ordinal()) FMLClientHandler.instance().showGuiScreen(parent);
if (id == Button.GRASS_USE_GENERATED.ordinal()) BetterFoliage.config.grassUseGenerated = !BetterFoliage.config.grassUseGenerated;
if (BetterFoliage.config.grassHeightMin.value > BetterFoliage.config.grassHeightMax.value) BetterFoliage.config.grassHeightMin.value = BetterFoliage.config.grassHeightMax.value;
}
}

View File

@@ -1,42 +0,0 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class ConfigGuiLeaves extends ConfigGuiScreenBase {
public enum Button {CLOSE, LEAVES_OFFSET_MODE}
public ConfigGuiLeaves(GuiScreen parent) {
super(parent);
int id = 10;
widgets.add(new OptionDoubleWidget(BetterFoliage.config.leavesSize, -100, -70, 200, 50, id++, id++, "message.betterfoliage.size", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.leavesHOffset, -100, -10, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.leavesVOffset, -100, 20, 200, 50, id++, id++, "message.betterfoliage.vOffset", "%.3f"));
}
@SuppressWarnings("unchecked")
@Override
public void addButtons(int x, int y) {
buttonList.add(new GuiButton(Button.CLOSE.ordinal(), x - 50, y + 50, 100, 20, I18n.format("message.betterfoliage.back")));
buttonList.add(new GuiButton(Button.LEAVES_OFFSET_MODE.ordinal(), x - 100, y - 40, 200, 20, ""));
}
protected void updateButtons() {
setButtonOptionBoolean(Button.LEAVES_OFFSET_MODE.ordinal(), "message.betterfoliage.leavesMode", BetterFoliage.config.leavesSkew ? "message.betterfoliage.leavesSkew" : "message.betterfoliage.leavesTranslate");
}
@Override
protected void onButtonPress(int id) {
if (id == Button.CLOSE.ordinal()) FMLClientHandler.instance().showGuiScreen(parent);
if (id == Button.LEAVES_OFFSET_MODE.ordinal()) BetterFoliage.config.leavesSkew = !BetterFoliage.config.leavesSkew;
}
}

View File

@@ -1,34 +0,0 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
import mods.betterfoliage.client.gui.widget.OptionIntegerWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class ConfigGuiLilypad extends ConfigGuiScreenBase {
public ConfigGuiLilypad(GuiScreen parent) {
super(parent);
int id = 10;
widgets.add(new OptionDoubleWidget(BetterFoliage.config.lilypadHOffset, -100, -40, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
widgets.add(new OptionIntegerWidget(BetterFoliage.config.lilypadChance, -100, -10, 200, 50, id++, id++, "message.betterfoliage.flowerChance"));
}
@SuppressWarnings("unchecked")
@Override
public void addButtons(int x, int y) {
buttonList.add(new GuiButton(0, x - 50, y + 50, 100, 20, I18n.format("message.betterfoliage.back")));
}
@Override
protected void onButtonPress(int id) {
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
}
}

View File

@@ -1,97 +0,0 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.BetterFoliage;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class ConfigGuiMain extends ConfigGuiScreenBase {
public enum Button {CLOSE,
TOGGLE_LEAVES, CONFIG_LEAVES,
TOGGLE_FALLING_LEAVES, CONFIG_FALLING_LEAVES,
TOGGLE_GRASS, CONFIG_GRASS,
TOGGLE_CACTUS, CONFIG_CACTUS,
TOGGLE_LILYPAD, CONFIG_LILYPAD,
TOGGLE_REED, CONFIG_REED,
TOGGLE_ALGAE, CONFIG_ALGAE,
TOGGLE_CORAL, CONFIG_CORAL}
public ConfigGuiMain(GuiScreen parent) {
super(parent);
}
@SuppressWarnings("unchecked")
@Override
protected void addButtons(int x, int y) {
buttonList.add(new GuiButton(Button.CLOSE.ordinal(), x - 50, y + 110, 100, 20, I18n.format("message.betterfoliage.close")));
buttonList.add(new GuiButton(Button.TOGGLE_LEAVES.ordinal(), x - 100, y - 130, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_LEAVES.ordinal(), x + 60, y - 130, 40, 20, I18n.format("message.betterfoliage.config")));
buttonList.add(new GuiButton(Button.TOGGLE_FALLING_LEAVES.ordinal(), x - 100, y - 100, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_FALLING_LEAVES.ordinal(), x + 60, y - 100, 40, 20, I18n.format("message.betterfoliage.config")));
buttonList.add(new GuiButton(Button.TOGGLE_GRASS.ordinal(), x - 100, y - 70, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_GRASS.ordinal(), x + 60, y - 70, 40, 20, I18n.format("message.betterfoliage.config")));
buttonList.add(new GuiButton(Button.TOGGLE_CACTUS.ordinal(), x - 100, y - 40, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_CACTUS.ordinal(), x + 60, y - 40, 40, 20, I18n.format("message.betterfoliage.config")));
buttonList.add(new GuiButton(Button.TOGGLE_LILYPAD.ordinal(), x - 100, y - 10, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_LILYPAD.ordinal(), x + 60, y - 10, 40, 20, I18n.format("message.betterfoliage.config")));
buttonList.add(new GuiButton(Button.TOGGLE_REED.ordinal(), x - 100, y + 20, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_REED.ordinal(), x + 60, y + 20, 40, 20, I18n.format("message.betterfoliage.config")));
buttonList.add(new GuiButton(Button.TOGGLE_ALGAE.ordinal(), x - 100, y + 50, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_ALGAE.ordinal(), x + 60, y + 50, 40, 20, I18n.format("message.betterfoliage.config")));
buttonList.add(new GuiButton(Button.TOGGLE_CORAL.ordinal(), x - 100, y + 80, 150, 20, ""));
buttonList.add(new GuiButton(Button.CONFIG_CORAL.ordinal(), x + 60, y + 80, 40, 20, I18n.format("message.betterfoliage.config")));
}
protected void updateButtons() {
setButtonOptionBoolean(Button.TOGGLE_LEAVES.ordinal(), "message.betterfoliage.betterLeaves", BetterFoliage.config.leavesEnabled);
setButtonOptionBoolean(Button.TOGGLE_FALLING_LEAVES.ordinal(), "message.betterfoliage.fallingLeaves", BetterFoliage.config.fallingLeavesEnabled);
setButtonOptionBoolean(Button.TOGGLE_GRASS.ordinal(), "message.betterfoliage.betterGrass", BetterFoliage.config.grassEnabled);
setButtonOptionBoolean(Button.TOGGLE_CACTUS.ordinal(), "message.betterfoliage.betterCactus", BetterFoliage.config.cactusEnabled);
setButtonOptionBoolean(Button.TOGGLE_LILYPAD.ordinal(), "message.betterfoliage.betterLilypad", BetterFoliage.config.lilypadEnabled);
setButtonOptionBoolean(Button.TOGGLE_REED.ordinal(), "message.betterfoliage.betterReed", BetterFoliage.config.reedEnabled);
setButtonOptionBoolean(Button.TOGGLE_ALGAE.ordinal(), "message.betterfoliage.betterAlgae", BetterFoliage.config.algaeEnabled);
setButtonOptionBoolean(Button.TOGGLE_CORAL.ordinal(), "message.betterfoliage.betterCoral", BetterFoliage.config.coralEnabled);
((GuiButton) buttonList.get(Button.CONFIG_CACTUS.ordinal())).enabled = false;
}
@Override
protected void onButtonPress(int id) {
if (id == Button.CLOSE.ordinal()) {
BetterFoliage.config.save();
Minecraft.getMinecraft().renderGlobal.loadRenderers();
FMLClientHandler.instance().showGuiScreen(parent);
}
if (id == Button.TOGGLE_LEAVES.ordinal()) BetterFoliage.config.leavesEnabled = !BetterFoliage.config.leavesEnabled;
if (id == Button.TOGGLE_FALLING_LEAVES.ordinal()) BetterFoliage.config.fallingLeavesEnabled = !BetterFoliage.config.fallingLeavesEnabled;
if (id == Button.TOGGLE_GRASS.ordinal()) BetterFoliage.config.grassEnabled = !BetterFoliage.config.grassEnabled;
if (id == Button.TOGGLE_CACTUS.ordinal()) BetterFoliage.config.cactusEnabled = !BetterFoliage.config.cactusEnabled;
if (id == Button.TOGGLE_LILYPAD.ordinal()) BetterFoliage.config.lilypadEnabled = !BetterFoliage.config.lilypadEnabled;
if (id == Button.TOGGLE_REED.ordinal()) BetterFoliage.config.reedEnabled = !BetterFoliage.config.reedEnabled;
if (id == Button.TOGGLE_ALGAE.ordinal()) BetterFoliage.config.algaeEnabled = !BetterFoliage.config.algaeEnabled;
if (id == Button.TOGGLE_CORAL.ordinal()) BetterFoliage.config.coralEnabled = !BetterFoliage.config.coralEnabled;
if (id== Button.CONFIG_LEAVES.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiLeaves(this));
if (id== Button.CONFIG_FALLING_LEAVES.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiFallingLeaves(this));
if (id== Button.CONFIG_GRASS.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiGrass(this));
if (id== Button.CONFIG_LILYPAD.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiLilypad(this));
if (id== Button.CONFIG_REED.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiReed(this));
if (id== Button.CONFIG_ALGAE.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiAlgae(this));
if (id== Button.CONFIG_CORAL.ordinal()) FMLClientHandler.instance().showGuiScreen(new ConfigGuiCoral(this));
}
}

View File

@@ -1,38 +0,0 @@
package mods.betterfoliage.client.gui;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.gui.widget.OptionDoubleWidget;
import mods.betterfoliage.client.gui.widget.OptionIntegerWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class ConfigGuiReed extends ConfigGuiScreenBase {
public ConfigGuiReed(GuiScreen parent) {
super(parent);
int id = 10;
widgets.add(new OptionDoubleWidget(BetterFoliage.config.reedHOffset, -100, -70, 200, 50, id++, id++, "message.betterfoliage.hOffset", "%.3f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.reedHeightMin, -100, -40, 200, 50, id++, id++, "message.betterfoliage.minHeight", "%.2f"));
widgets.add(new OptionDoubleWidget(BetterFoliage.config.reedHeightMax, -100, -10, 200, 50, id++, id++, "message.betterfoliage.maxHeight", "%.2f"));
widgets.add(new OptionIntegerWidget(BetterFoliage.config.reedChance, -100, 20, 200, 50, id++, id++, "message.betterfoliage.reedChance"));
}
@SuppressWarnings("unchecked")
@Override
public void addButtons(int x, int y) {
buttonList.add(new GuiButton(0, x - 50, y + 50, 100, 20, I18n.format("message.betterfoliage.back")));
}
@Override
protected void onButtonPress(int id) {
if (id == 0) FMLClientHandler.instance().showGuiScreen(parent);
if (BetterFoliage.config.reedHeightMin.value > BetterFoliage.config.reedHeightMax.value) BetterFoliage.config.reedHeightMin.value = BetterFoliage.config.reedHeightMax.value;
}
}

View File

@@ -1,81 +0,0 @@
package mods.betterfoliage.client.gui;
import java.util.List;
import org.lwjgl.input.Keyboard;
import mods.betterfoliage.client.gui.widget.IOptionWidget;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.EnumChatFormatting;
import com.google.common.collect.Lists;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class ConfigGuiScreenBase extends GuiScreen {
protected GuiScreen parent;
protected List<IOptionWidget> widgets = Lists.newLinkedList();
public ConfigGuiScreenBase(GuiScreen parent) {
this.parent = parent;
}
@Override
public void drawScreen(int par1, int par2, float par3) {
this.drawDefaultBackground();
int x = width / 2;
int y = height / 2;
for (IOptionWidget widget : widgets) widget.drawStrings(this, fontRendererObj, x, y, 14737632, 16777120);
super.drawScreen(par1, par2, par3);
}
@SuppressWarnings("unchecked")
@Override
public void initGui() {
int x = width / 2;
int y = height / 2;
for (IOptionWidget widget : widgets) widget.addButtons(buttonList, x, y);
addButtons(x, y);
updateButtons();
}
protected void addButtons(int x, int y) {}
protected void updateButtons() {}
protected void onButtonPress(int id) {}
@Override
protected void actionPerformed(GuiButton button) {
super.actionPerformed(button);
for (IOptionWidget widget : widgets) widget.onAction(button.id, Keyboard.isKeyDown(42));
onButtonPress(button.id);
updateButtons();
}
@SuppressWarnings("unchecked")
protected void setButtonOptionBoolean(int id, String msgKey, boolean option) {
for (GuiButton button : (List<GuiButton>) buttonList) {
if (button.id == id) {
String optionText = option ? (EnumChatFormatting.GREEN + I18n.format("message.betterfoliage.optionOn")) : (EnumChatFormatting.RED + I18n.format("message.betterfoliage.optionOff"));
button.displayString = I18n.format(msgKey, optionText);
break;
}
}
}
@SuppressWarnings("unchecked")
protected void setButtonOptionBoolean(int id, String msgKey, String optionKey) {
for (GuiButton button : (List<GuiButton>) buttonList) {
if (button.id == id) {
button.displayString = I18n.format(msgKey, I18n.format(optionKey));
break;
}
}
}
}

View File

@@ -1,19 +0,0 @@
package mods.betterfoliage.client.gui.widget;
import java.util.List;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
@SideOnly(Side.CLIENT)
public interface IOptionWidget {
public void addButtons(List<GuiButton> buttonList, int xOffset, int yOffset);
public void drawStrings(GuiScreen screen, FontRenderer fontRenderer, int xOffset, int yOffset, int labelColor, int numColor);
public void onAction(int buttonId, boolean shiftPressed);
}

View File

@@ -1,53 +0,0 @@
package mods.betterfoliage.client.gui.widget;
import java.util.List;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import mods.betterfoliage.common.config.OptionDouble;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
@SideOnly(Side.CLIENT)
public class OptionDoubleWidget implements IOptionWidget {
public OptionDouble option;
public int x;
public int y;
public int width;
public int numWidth;
public int idDecrement;
public int idIncrement;
public String keyLabel;
public String formatString;
public OptionDoubleWidget(OptionDouble option, int x, int y, int width, int numWidth, int idDecrement, int idIncrement, String keyLabel, String formatString) {
this.option = option;
this.x = x;
this.y = y;
this.width = width;
this.numWidth = numWidth;
this.idDecrement = idDecrement;
this.idIncrement = idIncrement;
this.keyLabel = keyLabel;
this.formatString = formatString;
}
public void addButtons(List<GuiButton> buttonList, int xOffset, int yOffset) {
buttonList.add(new GuiButton(idDecrement, xOffset + x + width - numWidth - 40, yOffset + y, 20, 20, "-"));
buttonList.add(new GuiButton(idIncrement, xOffset + x + width - 20, yOffset + y, 20, 20, "+"));
}
public void drawStrings(GuiScreen screen, FontRenderer fontRenderer, int xOffset, int yOffset, int labelColor, int numColor) {
screen.drawString(fontRenderer, I18n.format(keyLabel), xOffset + x, yOffset + y + 5, labelColor);
screen.drawCenteredString(fontRenderer, String.format(formatString, option.value), xOffset + x + width - 20 - numWidth / 2, yOffset + y + 5, numColor);
}
public void onAction(int buttonId, boolean shiftPressed) {
if (buttonId == idDecrement) option.decrement(shiftPressed ? 5 :1);
if (buttonId == idIncrement) option.increment(shiftPressed ? 5 :1);
}
}

View File

@@ -1,50 +0,0 @@
package mods.betterfoliage.client.gui.widget;
import java.util.List;
import mods.betterfoliage.common.config.OptionInteger;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class OptionIntegerWidget implements IOptionWidget {
public OptionInteger option;
public int x;
public int y;
public int width;
public int numWidth;
public int idDecrement;
public int idIncrement;
public String keyLabel;
public OptionIntegerWidget(OptionInteger option, int x, int y, int width, int numWidth, int idDecrement, int idIncrement, String keyLabel) {
this.option = option;
this.x = x;
this.y = y;
this.width = width;
this.numWidth = numWidth;
this.idDecrement = idDecrement;
this.idIncrement = idIncrement;
this.keyLabel = keyLabel;
}
public void addButtons(List<GuiButton> buttonList, int xOffset, int yOffset) {
buttonList.add(new GuiButton(idDecrement, xOffset + x + width - numWidth - 40, yOffset + y, 20, 20, "-"));
buttonList.add(new GuiButton(idIncrement, xOffset + x + width - 20, yOffset + y, 20, 20, "+"));
}
public void drawStrings(GuiScreen screen, FontRenderer fontRenderer, int xOffset, int yOffset, int labelColor, int numColor) {
screen.drawString(fontRenderer, I18n.format(keyLabel), xOffset + x, yOffset + y + 5, labelColor);
screen.drawCenteredString(fontRenderer, Integer.toString(option.value), xOffset + x + width - 20 - numWidth / 2, yOffset + y + 5, numColor);
}
public void onAction(int buttonId, boolean shiftPressed) {
if (buttonId == idDecrement) option.decrement(shiftPressed ? 5 :1);
if (buttonId == idIncrement) option.increment(shiftPressed ? 5 :1);
}
}

View File

@@ -1,62 +0,0 @@
package mods.betterfoliage.client.render;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.util.IIcon;
/** Same as {@link RenderBlockAOBase}, but does not actually render anything.
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class FakeRenderBlockAOBase extends RenderBlockAOBase {
@Override
public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
saveShadingTopLeft(aoZNXYPP);
saveShadingTopRight(aoZNXYNP);
saveShadingBottomLeft(aoZNXYPN);
saveShadingBottomRight(aoZNXYNN);
}
@Override
public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
saveShadingTopLeft(aoZPXYNP);
saveShadingTopRight(aoZPXYPP);
saveShadingBottomLeft(aoZPXYNN);
saveShadingBottomRight(aoZPXYPN);
}
@Override
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
saveShadingTopLeft(aoXNYZPN);
saveShadingTopRight(aoXNYZPP);
saveShadingBottomLeft(aoXNYZNN);
saveShadingBottomRight(aoXNYZNP);
}
@Override
public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
saveShadingTopLeft(aoXPYZPP);
saveShadingTopRight(aoXPYZPN);
saveShadingBottomLeft(aoXPYZNP);
saveShadingBottomRight(aoXPYZNN);
}
@Override
public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) {
saveShadingTopLeft(aoYNXZNP);
saveShadingTopRight(aoYNXZPP);
saveShadingBottomLeft(aoYNXZNN);
saveShadingBottomRight(aoYNXZPN);
}
@Override
public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) {
saveShadingTopLeft(aoYPXZPP);
saveShadingTopRight(aoYPXZNP);
saveShadingBottomLeft(aoYPXZPN);
saveShadingBottomRight(aoYPXZNN);
}
}

View File

@@ -1,30 +0,0 @@
package mods.betterfoliage.client.render;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.world.IBlockAccess;
/** Block rendering handler that is only used under certain conditions
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public interface IRenderBlockDecorator extends ISimpleBlockRenderingHandler {
/** Initialize necessary helper values
*/
public void init();
/**
* @param blockAccess the world
* @param x
* @param y
* @param z
* @param block
* @param original renderType of the block
* @return true if this renderer should handle this block
*/
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original);
}

View File

@@ -1,52 +0,0 @@
package mods.betterfoliage.client.render;
import mods.betterfoliage.common.util.Utils;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** Holds an indexed set of textures
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class IconSet {
/** Icon array */
public IIcon[] icons = new IIcon[16];
/** Number of successfully loaded icons*/
public int numLoaded = 0;
/** Resource domain of icons */
String domain;
/** Format string of icon paths */
String path;
public IconSet(String domain, String path) {
this.domain = domain;
this.path = path;
}
public void registerIcons(IIconRegister register) {
numLoaded = 0;
for (int idx = 0; idx < 16; idx++) {
icons[idx] = null;
// if the path contains a domain, use that to check if the resource exists
String resolvedDomain = path.contains(":") ? new ResourceLocation(path).getResourceDomain() : domain;
String resolvedPath = String.format("textures/blocks/" + (path.contains(":") ? new ResourceLocation(path).getResourcePath() : path) + ".png", idx);
if (Utils.resourceExists(new ResourceLocation(resolvedDomain, resolvedPath)))
icons[numLoaded++] = register.registerIcon(domain + ":" + String.format(path, idx));
}
}
public IIcon get(int variation) {
return numLoaded == 0 ? null : icons[variation % numLoaded];
}
public boolean hasIcons() {
return numLoaded > 0;
}
}

View File

@@ -1,440 +0,0 @@
package mods.betterfoliage.client.render;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.init.Blocks;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraftforge.common.util.ForgeDirection;
import org.lwjgl.opengl.GL11;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** Block renderer base class. Stores calculated ambient occlusion light and color values when rendering
* block sides for later use.
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class RenderBlockAOBase extends RenderBlocks {
/** AO light and color values
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public static class ShadingValues {
public int passCounter = 0;
public int brightness;
public float red;
public float green;
public float blue;
public void setGray(float value) {
red = value; green = value; blue = value;
}
}
protected double[] uValues = new double[] {0.0, 16.0, 16.0, 0.0};
protected double[] vValues = new double[] {0.0, 0.0, 16.0, 16.0};
protected ForgeDirection[] faceDir1 = new ForgeDirection[] {ForgeDirection.WEST, ForgeDirection.WEST, ForgeDirection.WEST, ForgeDirection.EAST, ForgeDirection.SOUTH, ForgeDirection.NORTH};
protected ForgeDirection[] faceDir2 = new ForgeDirection[] {ForgeDirection.NORTH, ForgeDirection.SOUTH, ForgeDirection.UP, ForgeDirection.UP, ForgeDirection.UP, ForgeDirection.UP};
/** Random vector pool. Unit rotation vectors in the XZ plane, Y coord goes between [-1.0, 1.0].
* Filled at init time */
public Double3[] pRot = new Double3[64];
/** Pool of random double values. Filled at init time. */
public double[] pRand = new double[64];
public ShadingValues aoXPYZPP = new ShadingValues();
public ShadingValues aoXPYZPN = new ShadingValues();
public ShadingValues aoXPYZNP = new ShadingValues();
public ShadingValues aoXPYZNN = new ShadingValues();
public ShadingValues aoXNYZPP = new ShadingValues();
public ShadingValues aoXNYZPN = new ShadingValues();
public ShadingValues aoXNYZNP = new ShadingValues();
public ShadingValues aoXNYZNN = new ShadingValues();
public ShadingValues aoYPXZPP = new ShadingValues();
public ShadingValues aoYPXZPN = new ShadingValues();
public ShadingValues aoYPXZNP = new ShadingValues();
public ShadingValues aoYPXZNN = new ShadingValues();
public ShadingValues aoYNXZPP = new ShadingValues();
public ShadingValues aoYNXZPN = new ShadingValues();
public ShadingValues aoYNXZNP = new ShadingValues();
public ShadingValues aoYNXZNN = new ShadingValues();
public ShadingValues aoZPXYPP = new ShadingValues();
public ShadingValues aoZPXYPN = new ShadingValues();
public ShadingValues aoZPXYNP = new ShadingValues();
public ShadingValues aoZPXYNN = new ShadingValues();
public ShadingValues aoZNXYPP = new ShadingValues();
public ShadingValues aoZNXYPN = new ShadingValues();
public ShadingValues aoZNXYNP = new ShadingValues();
public ShadingValues aoZNXYNN = new ShadingValues();
// temporary shading values for a single face
public ShadingValues faceAOPP, faceAOPN, faceAONN, faceAONP;
/** Initialize random values */
public void init() {
List<Double3> perturbs = new ArrayList<Double3>(64);
for (int idx = 0; idx < 64; idx++) {
double angle = (double) idx * Math.PI * 2.0 / 64.0;
perturbs.add(new Double3(Math.cos(angle), Math.random() * 2.0 - 1.0, Math.sin(angle)));
pRand[idx] = Math.random();
}
Collections.shuffle(perturbs);
Iterator<Double3> iter = perturbs.iterator();
for (int idx = 0; idx < 64; idx++) pRot[idx] = iter.next();
}
/** Get a semi-random value depending on block position.
* @param x block X coord
* @param y block Y coord
* @param z block Z coord
* @param seed additional seed
* @return semirandom value
*/
protected int getSemiRandomFromPos(double x, double y, double z, int seed) {
long lx = MathHelper.floor_double(x);
long ly = MathHelper.floor_double(y);
long lz = MathHelper.floor_double(z);
long value = (lx * lx + ly * ly + lz * lz + lx * ly + ly * lz + lz * lx + seed * seed) & 63;
value = (3 * lx * value + 5 * ly * value + 7 * lz * value + 11 * seed) & 63;
return (int) value;
}
public void renderInventoryBlock(Block block, int metadata, int modelId, RenderBlocks renderer) {
renderStandardBlockAsItem(renderer, block, metadata, 1.0f);
}
public boolean shouldRender3DInInventory(int modelId) {
return true;
}
public int getRenderId() {
return 0;
}
protected void renderStandardBlockAsItem(RenderBlocks renderer, Block p_147800_1_, int p_147800_2_, float p_147800_3_) {
Tessellator tessellator = Tessellator.instance;
boolean flag = p_147800_1_ == Blocks.grass;
float f2;
float f3;
int k;
p_147800_1_.setBlockBoundsForItemRender();
renderer.setRenderBoundsFromBlock(p_147800_1_);
GL11.glRotatef(90.0F, 0.0F, 1.0F, 0.0F);
GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
tessellator.startDrawingQuads();
tessellator.setNormal(0.0F, -1.0F, 0.0F);
renderer.renderFaceYNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 0, p_147800_2_));
tessellator.draw();
if (flag && renderer.useInventoryTint)
{
k = p_147800_1_.getRenderColor(p_147800_2_);
f2 = (float)(k >> 16 & 255) / 255.0F;
f3 = (float)(k >> 8 & 255) / 255.0F;
float f4 = (float)(k & 255) / 255.0F;
GL11.glColor4f(f2 * p_147800_3_, f3 * p_147800_3_, f4 * p_147800_3_, 1.0F);
}
tessellator.startDrawingQuads();
tessellator.setNormal(0.0F, 1.0F, 0.0F);
renderer.renderFaceYPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 1, p_147800_2_));
tessellator.draw();
if (flag && renderer.useInventoryTint)
{
GL11.glColor4f(p_147800_3_, p_147800_3_, p_147800_3_, 1.0F);
}
tessellator.startDrawingQuads();
tessellator.setNormal(0.0F, 0.0F, -1.0F);
renderer.renderFaceZNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 2, p_147800_2_));
tessellator.draw();
tessellator.startDrawingQuads();
tessellator.setNormal(0.0F, 0.0F, 1.0F);
renderer.renderFaceZPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 3, p_147800_2_));
tessellator.draw();
tessellator.startDrawingQuads();
tessellator.setNormal(-1.0F, 0.0F, 0.0F);
renderer.renderFaceXNeg(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 4, p_147800_2_));
tessellator.draw();
tessellator.startDrawingQuads();
tessellator.setNormal(1.0F, 0.0F, 0.0F);
renderer.renderFaceXPos(p_147800_1_, 0.0D, 0.0D, 0.0D, renderer.getBlockIconFromSideAndMetadata(p_147800_1_, 5, p_147800_2_));
tessellator.draw();
GL11.glTranslatef(0.5F, 0.5F, 0.5F);
}
protected void setShadingForFace(ForgeDirection dir) {
if (dir == ForgeDirection.DOWN) {
// dir1 WEST, dir2 NORTH
faceAOPP = aoYNXZPP; faceAOPN = aoYNXZPN; faceAONN = aoYNXZNN; faceAONP = aoYNXZNP;
} else if (dir == ForgeDirection.UP) {
// dir1 WEST, dir2 SOUTH
faceAOPP = aoYPXZPP; faceAOPN = aoYPXZPN; faceAONN = aoYPXZNN; faceAONP = aoYPXZNP;
} else if (dir == ForgeDirection.NORTH) {
// dir1 WEST, dir2 UP
faceAOPP = aoZNXYNP; faceAOPN = aoZNXYNN; faceAONN = aoZNXYPN; faceAONP = aoZNXYPP;
} else if (dir == ForgeDirection.SOUTH) {
// dir1 EAST, dir2 UP
faceAOPP = aoZPXYPP; faceAOPN = aoZPXYPN; faceAONN = aoZPXYNN; faceAONP = aoZPXYNP;
} else if (dir == ForgeDirection.WEST) {
// dir1 SOUTH, dir2 UP
faceAOPP = aoXNYZPP; faceAOPN = aoXNYZNP; faceAONN = aoXNYZNN; faceAONP = aoXNYZPN;
} else if (dir == ForgeDirection.EAST) {
// dir1 NORTH, dir2 UP
faceAOPP = aoXPYZPN; faceAOPN = aoXPYZNN; faceAONN = aoXPYZNP; faceAONP = aoXPYZPP;
}
}
public void renderCrossedSideQuads(Double3 drawBase, ForgeDirection dir, double scale, double halfHeight, Double3 rendomVec, double offset, IIcon renderIcon, int uvRot, boolean noShading) {
Double3 facePP, faceNP, faceNormal, drawCenter;
if (dir == ForgeDirection.UP) {
// special case for block top, we'll be rendering a LOT of those
facePP = new Double3(-scale, 0.0, scale);
faceNP = new Double3(scale, 0.0, scale);
faceNormal = new Double3(0.0, halfHeight, 0.0);
drawCenter = drawBase.add(faceNormal);
if (rendomVec != null) {
drawCenter = drawBase.add(faceNormal).add(rendomVec.scaleAxes(-offset, 0.0, offset));
}
} else {
facePP = new Double3(faceDir1[dir.ordinal()]).add(new Double3(faceDir2[dir.ordinal()])).scale(scale);
faceNP = new Double3(faceDir1[dir.ordinal()]).inverse().add(new Double3(faceDir2[dir.ordinal()])).scale(scale);
faceNormal = new Double3(dir).scale(halfHeight);
drawCenter = drawBase.add(faceNormal);
if (rendomVec != null) {
drawCenter = drawCenter.add(new Double3(faceDir1[dir.ordinal()]).scale(rendomVec.x).scale(offset))
.add(new Double3(faceDir2[dir.ordinal()]).scale(rendomVec.z).scale(offset));
}
}
if (Minecraft.isAmbientOcclusionEnabled() && !noShading) {
setShadingForFace(dir);
renderQuadWithShading(renderIcon, drawCenter, facePP, faceNormal, uvRot, faceAOPP, faceAONN, faceAONN, faceAOPP);
renderQuadWithShading(renderIcon, drawCenter, facePP.inverse(), faceNormal, uvRot, faceAONN, faceAOPP, faceAOPP, faceAONN);
renderQuadWithShading(renderIcon, drawCenter, faceNP, faceNormal, uvRot, faceAONP, faceAOPN, faceAOPN, faceAONP);
renderQuadWithShading(renderIcon, drawCenter, faceNP.inverse(), faceNormal, uvRot, faceAOPN, faceAONP, faceAONP, faceAOPN);
} else {
renderQuad(renderIcon, drawCenter, facePP, faceNormal, uvRot);
renderQuad(renderIcon, drawCenter, facePP.inverse(), faceNormal, uvRot);
renderQuad(renderIcon, drawCenter, faceNP, faceNormal, uvRot);
renderQuad(renderIcon, drawCenter, faceNP.inverse(), faceNormal, uvRot);
}
}
protected void renderCrossedBlockQuadsTranslate(Double3 blockCenter, double halfSize, Double3 offsetVec, IIcon crossLeafIcon, int uvRot, boolean isAirTop, boolean isAirBottom) {
Double3 drawCenter = blockCenter;
if (offsetVec != null) drawCenter = drawCenter.add(offsetVec);
Double3 horz1 = new Double3(halfSize, 0.0, halfSize);
Double3 horz2 = new Double3(halfSize, 0.0, -halfSize);
Double3 vert1 = new Double3(0.0, halfSize * 1.41, 0.0);
renderCrossedBlockQuadsInternal(drawCenter, horz1, horz2, vert1, crossLeafIcon, uvRot, isAirTop, isAirBottom);
}
protected void renderCrossedBlockQuadsSkew(Double3 blockCenter, double halfSize, Double3 offsetVec1, Double3 offsetVec2, IIcon crossLeafIcon, int uvRot, boolean isAirTop, boolean isAirBottom) {
Double3 horz1 = new Double3(halfSize, 0.0, halfSize).add(offsetVec1);
Double3 horz2 = new Double3(halfSize, 0.0, -halfSize).add(offsetVec2);
Double3 vert1 = new Double3(0.0, halfSize * 1.41, 0.0);
renderCrossedBlockQuadsInternal(blockCenter, horz1, horz2, vert1, crossLeafIcon, uvRot, isAirTop, isAirBottom);
}
private void renderCrossedBlockQuadsInternal(Double3 drawCenter, Double3 horz1, Double3 horz2, Double3 vert1, IIcon crossLeafIcon, int uvRot, boolean isAirTop, boolean isAirBottom) {
if (Minecraft.isAmbientOcclusionEnabled()) {
renderQuadWithShading(crossLeafIcon, drawCenter, horz1, vert1, uvRot,
isAirTop ? aoYPXZPP : aoZPXYPP, isAirTop ? aoYPXZNN : aoXNYZPN, isAirBottom ? aoYNXZNN : aoXNYZNN, isAirBottom ? aoYNXZPP : aoZPXYPN);
renderQuadWithShading(crossLeafIcon, drawCenter, horz1.inverse(), vert1, uvRot,
isAirTop ? aoYPXZNN : aoZNXYNP, isAirTop ? aoYPXZPP : aoXPYZPP, isAirBottom ? aoYNXZPP : aoXPYZNP, isAirBottom ? aoYNXZNN : aoZNXYNN);
renderQuadWithShading(crossLeafIcon, drawCenter, horz2, vert1, uvRot,
isAirTop ? aoYPXZPN : aoXPYZPN, isAirTop ? aoYPXZNP : aoZPXYNP, isAirBottom ? aoYNXZNP : aoZPXYNN, isAirBottom ? aoYNXZPN : aoXPYZNN);
renderQuadWithShading(crossLeafIcon, drawCenter, horz2.inverse(), vert1, uvRot,
isAirTop ? aoYPXZNP : aoXNYZPP, isAirTop ? aoYPXZPN : aoZNXYPP, isAirBottom ? aoYNXZPN : aoZNXYPN, isAirBottom ? aoYNXZNP : aoXNYZNP);
} else {
renderQuad(crossLeafIcon, drawCenter, horz1, vert1, uvRot);
renderQuad(crossLeafIcon, drawCenter, horz1.inverse(), vert1, uvRot);
renderQuad(crossLeafIcon, drawCenter, horz2, vert1, uvRot);
renderQuad(crossLeafIcon, drawCenter, horz2.inverse(), vert1, uvRot);
}
}
@Override
public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceZNeg(block, x, y, z, icon);
saveShadingTopLeft(aoZNXYPP);
saveShadingTopRight(aoZNXYNP);
saveShadingBottomLeft(aoZNXYPN);
saveShadingBottomRight(aoZNXYNN);
}
@Override
public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceZPos(block, x, y, z, icon);
saveShadingTopLeft(aoZPXYNP);
saveShadingTopRight(aoZPXYPP);
saveShadingBottomLeft(aoZPXYNN);
saveShadingBottomRight(aoZPXYPN);
}
@Override
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceXNeg(block, x, y, z, icon);
saveShadingTopLeft(aoXNYZPN);
saveShadingTopRight(aoXNYZPP);
saveShadingBottomLeft(aoXNYZNN);
saveShadingBottomRight(aoXNYZNP);
}
@Override
public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceXPos(block, x, y, z, icon);
saveShadingTopLeft(aoXPYZPP);
saveShadingTopRight(aoXPYZPN);
saveShadingBottomLeft(aoXPYZNP);
saveShadingBottomRight(aoXPYZNN);
}
@Override
public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceYNeg(block, x, y, z, icon);
saveShadingTopLeft(aoYNXZNP);
saveShadingTopRight(aoYNXZPP);
saveShadingBottomLeft(aoYNXZNN);
saveShadingBottomRight(aoYNXZPN);
}
@Override
public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) {
super.renderFaceYPos(block, x, y, z, icon);
saveShadingTopLeft(aoYPXZPP);
saveShadingTopRight(aoYPXZNP);
saveShadingBottomLeft(aoYPXZPN);
saveShadingBottomRight(aoYPXZNN);
}
protected void saveShadingTopLeft(ShadingValues values) {
if (--values.passCounter != 0) return;
values.brightness = brightnessTopLeft;
values.red = colorRedTopLeft;
values.green = colorGreenTopLeft;
values.blue = colorBlueTopLeft;
}
protected void saveShadingTopRight(ShadingValues values) {
if (--values.passCounter != 0) return;
values.brightness = brightnessTopRight;
values.red = colorRedTopRight;
values.green = colorGreenTopRight;
values.blue = colorBlueTopRight;
}
protected void saveShadingBottomLeft(ShadingValues values) {
if (--values.passCounter != 0) return;
values.brightness = brightnessBottomLeft;
values.red = colorRedBottomLeft;
values.green = colorGreenBottomLeft;
values.blue = colorBlueBottomLeft;
}
protected void saveShadingBottomRight(ShadingValues values) {
if (--values.passCounter != 0) return;
values.brightness = brightnessBottomRight;
values.red = colorRedBottomRight;
values.green = colorGreenBottomRight;
values.blue = colorBlueBottomRight;
}
/** Set pass counter on all shading value objects.
* Used to collect AO values from a specific draw pass
* if the underlying renderer draws overlays
* @param value pass counter
*/
protected void setPassCounters(int value) {
aoXPYZPP.passCounter = value;
aoXPYZPN.passCounter = value;
aoXPYZNP.passCounter = value;
aoXPYZNN.passCounter = value;
aoXNYZPP.passCounter = value;
aoXNYZPN.passCounter = value;
aoXNYZNP.passCounter = value;
aoXNYZNN.passCounter = value;
aoYPXZPP.passCounter = value;
aoYPXZPN.passCounter = value;
aoYPXZNP.passCounter = value;
aoYPXZNN.passCounter = value;
aoYNXZPP.passCounter = value;
aoYNXZPN.passCounter = value;
aoYNXZNP.passCounter = value;
aoYNXZNN.passCounter = value;
aoZPXYPP.passCounter = value;
aoZPXYPN.passCounter = value;
aoZPXYNP.passCounter = value;
aoZPXYNN.passCounter = value;
aoZNXYPP.passCounter = value;
aoZNXYPN.passCounter = value;
aoZNXYNP.passCounter = value;
aoZNXYNN.passCounter = value;
}
/** Render textured quad
* @param icon texture to use
* @param center center of quad
* @param vec1 vector to the half-point of one of the sides
* @param vec2 vector to half-point of side next to vec1
* @param uvRot number of increments to rotate UV coordinates by
*/
protected void renderQuad(IIcon icon, Double3 center, Double3 vec1, Double3 vec2, int uvRot) {
Tessellator tessellator = Tessellator.instance;
tessellator.addVertexWithUV(center.x + vec1.x + vec2.x, center.y + vec1.y + vec2.y, center.z + vec1.z + vec2.z, icon.getInterpolatedU(uValues[uvRot & 3]), icon.getInterpolatedV(vValues[uvRot & 3]));
tessellator.addVertexWithUV(center.x - vec1.x + vec2.x, center.y - vec1.y + vec2.y, center.z - vec1.z + vec2.z, icon.getInterpolatedU(uValues[(uvRot + 1) & 3]), icon.getInterpolatedV(vValues[(uvRot + 1) & 3]));
tessellator.addVertexWithUV(center.x - vec1.x - vec2.x, center.y - vec1.y - vec2.y, center.z - vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 2) & 3]), icon.getInterpolatedV(vValues[(uvRot + 2) & 3]));
tessellator.addVertexWithUV(center.x + vec1.x - vec2.x, center.y + vec1.y - vec2.y, center.z + vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 3) & 3]), icon.getInterpolatedV(vValues[(uvRot + 3) & 3]));
}
/** Render textured quad using AO information
* @param icon texture to use
* @param center center of quad
* @param vec1 vector to the half-point of one of the sides
* @param vec2 vector to half-point of side next to vec1
* @param uvRot number of increments to rotate UV coordinates by
* @param aoPP AO values for vertex at (+vec1, +vec2)
* @param aoNP AO values for vertex at (-vec1, +vec2)
* @param aoNN AO values for vertex at (-vec1, -vec2)
* @param aoPN AO values for vertex at (+vec1, -vec2)
*/
protected void renderQuadWithShading(IIcon icon, Double3 center, Double3 vec1, Double3 vec2, int uvRot, ShadingValues aoPP, ShadingValues aoNP, ShadingValues aoNN, ShadingValues aoPN) {
Tessellator tessellator = Tessellator.instance;
tessellator.setBrightness(aoPP.brightness);
tessellator.setColorOpaque_F(aoPP.red, aoPP.green, aoPP.blue);
tessellator.addVertexWithUV(center.x + vec1.x + vec2.x, center.y + vec1.y + vec2.y, center.z + vec1.z + vec2.z, icon.getInterpolatedU(uValues[uvRot & 3]), icon.getInterpolatedV(vValues[uvRot & 3]));
tessellator.setBrightness(aoNP.brightness);
tessellator.setColorOpaque_F(aoNP.red, aoNP.green, aoNP.blue);
tessellator.addVertexWithUV(center.x - vec1.x + vec2.x, center.y - vec1.y + vec2.y, center.z - vec1.z + vec2.z, icon.getInterpolatedU(uValues[(uvRot + 1) & 3]), icon.getInterpolatedV(vValues[(uvRot + 1) & 3]));
tessellator.setBrightness(aoNN.brightness);
tessellator.setColorOpaque_F(aoNN.red, aoNN.green, aoNN.blue);
tessellator.addVertexWithUV(center.x - vec1.x - vec2.x, center.y - vec1.y - vec2.y, center.z - vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 2) & 3]), icon.getInterpolatedV(vValues[(uvRot + 2) & 3]));
tessellator.setBrightness(aoPN.brightness);
tessellator.setColorOpaque_F(aoPN.red, aoPN.green, aoPN.blue);
tessellator.addVertexWithUV(center.x + vec1.x - vec2.x, center.y + vec1.y - vec2.y, center.z + vec1.z - vec2.z, icon.getInterpolatedU(uValues[(uvRot + 3) & 3]), icon.getInterpolatedV(vValues[(uvRot + 3) & 3]));
}
protected int getBrightness(Block block, int x, int y, int z) {
return block.getMixedBrightnessForBlock(blockAccess, x, y, z);
}
}

View File

@@ -1,132 +0,0 @@
package mods.betterfoliage.client.render.impl;
import java.awt.Color;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class EntityFXFallingLeaves extends EntityFX {
protected static double[] cos = new double[64];
protected static double[] sin = new double[64];
static {
for (int idx = 0; idx < 64; idx++) {
cos[idx] = Math.cos(2.0 * Math.PI / 64.0 * idx);
sin[idx] = Math.sin(2.0 * Math.PI / 64.0 * idx);
}
}
public static float biomeBrightnessMultiplier = 0.5f;
public boolean wasOnGround = false;
public boolean isMirrored;
public int particleRotation = 0;
public boolean rotationPositive = true;
public EntityFXFallingLeaves(World world, int x, int y, int z) {
super(world, x + 0.5, y, z + 0.5);
particleMaxAge = MathHelper.floor_double((0.6 + 0.4 * rand.nextDouble()) * BetterFoliage.config.fallingLeavesLifetime.value * 20.0);
isMirrored = (rand.nextInt() & 1) == 1;
motionY = -BetterFoliage.config.fallingLeavesSpeed.value;
particleRotation = rand.nextInt(64);
particleScale = (float) BetterFoliage.config.fallingLeavesSize.value;
particleIcon = BetterFoliageClient.leafParticles.icons.get(rand.nextInt(1024));
Block block = world.getBlock(x, y, z);
IIcon blockIcon = block.getIcon(world, x, y, z, ForgeDirection.DOWN.ordinal());
calculateParticleColor(BetterFoliageClient.leafParticles.getColor(blockIcon), block.colorMultiplier(world, x, y, z));
}
@Override
public void onUpdate() {
super.onUpdate();
particleScale = (float) BetterFoliage.config.fallingLeavesSize.value;
if (rand.nextFloat() > 0.95f) rotationPositive = !rotationPositive;
if (particleAge > particleMaxAge - 20) particleAlpha = 0.05f * (particleMaxAge - particleAge);
if (onGround || wasOnGround) {
motionX = 0.0;
motionZ = 0.0;
motionZ = 0.0;
if (!wasOnGround) {
particleAge = Math.max(particleAge, particleMaxAge - 20);
}
wasOnGround = true;
} else {
motionX = (BetterFoliageClient.wind.currentX + cos[particleRotation] * BetterFoliage.config.fallingLeavesPerturb.value) * BetterFoliage.config.fallingLeavesSpeed.value;
motionZ = (BetterFoliageClient.wind.currentZ + sin[particleRotation] * BetterFoliage.config.fallingLeavesPerturb.value) * BetterFoliage.config.fallingLeavesSpeed.value;
motionY = -BetterFoliage.config.fallingLeavesSpeed.value;
particleRotation = (particleRotation + (rotationPositive ? 1 : -1)) & 63;
}
}
@Override
public void renderParticle(Tessellator tessellator, float partialTickTime, float rotX, float rotZ, float rotYZ, float rotXY, float rotXZ)
{
float minU = isMirrored ? particleIcon.getMinU() : particleIcon.getMaxU();
float maxU = isMirrored ? particleIcon.getMaxU() : particleIcon.getMinU();
float minV = particleIcon.getMinV();
float maxV = particleIcon.getMaxV();
float scale = 0.1F * this.particleScale;
Double3 center = new Double3(prevPosX + (posX - prevPosX) * partialTickTime - interpPosX,
prevPosY + (posY - prevPosY) * partialTickTime - interpPosY,
prevPosZ + (posZ - prevPosZ) * partialTickTime - interpPosZ);
Double3 vec1 = new Double3(rotX + rotXY, rotZ, rotYZ + rotXZ).scale(scale);
Double3 vec2 = new Double3(rotX - rotXY, -rotZ, rotYZ - rotXZ).scale(scale);
Double3 vec1Rot = vec1.scale(cos[particleRotation]).add(vec2.scale(sin[particleRotation]));
Double3 vec2Rot = vec1.scale(-sin[particleRotation]).add(vec2.scale(cos[particleRotation]));
tessellator.setColorRGBA_F(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha);
addVertex(tessellator, center.sub(vec1Rot), maxU, maxV);
addVertex(tessellator, center.sub(vec2Rot), maxU, minV);
addVertex(tessellator, center.add(vec1Rot), minU, minV);
addVertex(tessellator, center.add(vec2Rot), minU, maxV);
}
protected void addVertex(Tessellator tessellator, Double3 coord, double u, double v) {
tessellator.addVertexWithUV(coord.x, coord.y, coord.z, u, v);
}
/** Calculates and sets the color of the particle by blending the average color of the block texture with the current biome color
* Blending is done in HSB color space, weighted by the relative saturation of the colors
* @param textureAvgColor average color of the block texture
* @param blockColor biome color at the spawning block
*/
public void calculateParticleColor(int textureAvgColor, int blockColor) {
float[] hsbTexture = Color.RGBtoHSB((textureAvgColor >> 16) & 0xFF, (textureAvgColor >> 8) & 0xFF, textureAvgColor & 0xFF, null);
float[] hsbBlock = Color.RGBtoHSB((blockColor >> 16) & 0xFF, (blockColor >> 8) & 0xFF, blockColor & 0xFF, null);
float weightTex = hsbTexture[1] / (hsbTexture[1] + hsbBlock[1]);
float weightBlock = 1.0f - weightTex;
// avoid circular average for hue for performance reasons
// one of the color components should dominate anyway
float h = weightTex * hsbTexture[0] + weightBlock * hsbBlock[0];
float s = weightTex * hsbTexture[1] + weightBlock * hsbBlock[1];
float b = weightTex * hsbTexture[2] + weightBlock * hsbBlock[2] * biomeBrightnessMultiplier;
int particleColor = Color.HSBtoRGB(h, s, b);
particleBlue = (particleColor & 0xFF) / 256.0f;
particleGreen = ((particleColor >> 8) & 0xFF) / 256.0f;
particleRed = ((particleColor >> 16) & 0xFF) / 256.0f;
}
@Override
public int getFXLayer() {
return 1;
}
}

View File

@@ -1,85 +0,0 @@
package mods.betterfoliage.client.render.impl;
import java.util.Random;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.gen.NoiseGeneratorSimplex;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.event.world.WorldEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class RenderBlockBetterAlgae extends RenderBlockAOBase implements IRenderBlockDecorator {
public IconSet algaeIcons = new IconSet("bettergrassandleaves", "better_algae_%d");
public NoiseGeneratorSimplex noise;
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
if (!BetterFoliage.config.algaeEnabled) return false;
if (!(BetterFoliageClient.dirt.matchesID(block))) return false;
if (blockAccess.getBlock(x, y + 1, z).getMaterial() != Material.water) return false;
if (blockAccess.getBlock(x, y + 2, z).getMaterial() != Material.water) return false;
if (blockAccess.getBiomeGenForCoords(x, z).temperature < 0.4f) return false;
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x, z) + 1.0) * 32.0);
return terrainVariation < BetterFoliage.config.algaeChance.value;
}
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
// store world for later use
blockAccess = world;
// use original renderer for block breaking overlay
if (renderer.hasOverrideBlockTexture()) {
renderer.setRenderBoundsFromBlock(block);
renderer.renderStandardBlock(block, x, y, z);
return true;
}
// render dirt block
setPassCounters(1);
setRenderBoundsFromBlock(block);
renderStandardBlock(block, x, y, z);
int variation = getSemiRandomFromPos(x, y, z, 0);
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
IIcon renderIcon = algaeIcons.get(variation);
if (renderIcon == null) return true;
double scale = BetterFoliage.config.algaeSize.value * 0.5;
double halfHeight = 0.5 * (BetterFoliage.config.algaeHeightMin.value + pRand[heightVariation] * (BetterFoliage.config.algaeHeightMax.value - BetterFoliage.config.algaeHeightMin.value));
Tessellator.instance.setBrightness(getBrightness(block, x, y + 1, z));
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 - 0.125 * halfHeight, z + 0.5), ForgeDirection.UP, scale, halfHeight, pRot[variation], BetterFoliage.config.algaeHOffset.value, renderIcon, 0, false);
return true;
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
algaeIcons.registerIcons(event.map);
BetterFoliage.log.info(String.format("Found %d algae textures", algaeIcons.numLoaded));
}
@SubscribeEvent
public void handleWorldLoad(WorldEvent.Load event) {
noise = new NoiseGeneratorSimplex(new Random(event.world.getWorldInfo().getSeed() + 1));
}
}

View File

@@ -1,95 +0,0 @@
package mods.betterfoliage.client.render.impl;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.render.FakeRenderBlockAOBase;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.init.Blocks;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.util.ForgeDirection;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class RenderBlockBetterCactus extends FakeRenderBlockAOBase implements IRenderBlockDecorator {
public IIcon cactusRoundIcon;
public IconSet cactusSideIcons = new IconSet("bettergrassandleaves", "better_cactus_arm_%d");
/** Possible directions for cactus side growth*/
public static ForgeDirection[] cactusDirections = new ForgeDirection[] { ForgeDirection.NORTH, ForgeDirection.SOUTH, ForgeDirection.EAST, ForgeDirection.WEST};
/** Inner radius of cactus stem */
public static double cactusRadius = 0.4375;
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
return BetterFoliage.config.cactusEnabled && block == Blocks.cactus;
}
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
// store world for later use
blockAccess = world;
// use original renderer for block breaking overlay
if (renderer.hasOverrideBlockTexture()) {
renderer.renderBlockCactus(block, x, y, z);
return true;
}
// render cactus center
setPassCounters(1);
renderStandardBlock(block, x, y, z);
Double3 blockCenter = new Double3(x + 0.5, y + 0.5, z + 0.5);
Tessellator.instance.setBrightness(getBrightness(block,x, y, z));
renderCactusCore(block.getBlockTextureFromSide(ForgeDirection.UP.ordinal()),
block.getBlockTextureFromSide(ForgeDirection.NORTH.ordinal()),
blockCenter, 0);
// render side growth
ForgeDirection drawDirection = cactusDirections[getSemiRandomFromPos(x, y, z, 0) % 4];
int iconVariation = getSemiRandomFromPos(x, y, z, 1);
Double3 drawBase = blockCenter.add(new Double3(drawDirection).scale(cactusRadius));
Tessellator.instance.setBrightness(getBrightness(block, x, y, z));
if (cactusSideIcons.hasIcons()) renderCrossedSideQuads(drawBase, drawDirection, 0.5, 0.5, pRot[iconVariation], 0.2, cactusSideIcons.get(iconVariation), 0, false);
renderCrossedBlockQuadsSkew(blockCenter, 0.65,
pRot[iconVariation].scaleAxes(0.1, 0.0, 0.1),
pRot[(iconVariation + 1) & 63].scaleAxes(0.1, 0.0, 0.1),
cactusRoundIcon, iconVariation, false, false);
return true;
}
protected void renderCactusCore(IIcon topIcon, IIcon sideIcon, Double3 blockCenter, int sideUvRot) {
if (Minecraft.isAmbientOcclusionEnabled()) {
renderQuadWithShading(sideIcon, blockCenter.add(cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, -0.5), new Double3(0.0, 0.5, 0.0), sideUvRot, aoXPYZPN, aoXPYZPP, aoXPYZNP, aoXPYZNN);
renderQuadWithShading(sideIcon, blockCenter.add(-cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), new Double3(0.0, 0.5, 0.0), sideUvRot, aoXNYZPP, aoXNYZPN, aoXNYZNN, aoXNYZNP);
renderQuadWithShading(sideIcon, blockCenter.add(0.0, 0.0, cactusRadius), new Double3(0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot, aoZPXYPP, aoZPXYNP, aoZPXYNN, aoZPXYPN);
renderQuadWithShading(sideIcon, blockCenter.add(0.0, 0.0, -cactusRadius), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot, aoZNXYNP, aoZNXYPP, aoZNXYPN, aoZNXYNN);
renderQuadWithShading(topIcon, blockCenter.add(0.0, 0.5, 0.0), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), 0, aoYPXZNP, aoYPXZPP, aoYPXZPN, aoYPXZNN);
} else {
renderQuad(sideIcon, blockCenter.add(cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, -0.5), new Double3(0.0, 0.5, 0.0), sideUvRot);
renderQuad(sideIcon, blockCenter.add(-cactusRadius, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), new Double3(0.0, 0.5, 0.0), sideUvRot);
renderQuad(sideIcon, blockCenter.add(0.0, 0.0, cactusRadius), new Double3(0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot);
renderQuad(sideIcon, blockCenter.add(0.0, 0.0, -cactusRadius), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.5, 0.0), sideUvRot);
renderQuad(topIcon, blockCenter.add(0.0, 0.5, 0.0), new Double3(-0.5, 0.0, 0.0), new Double3(0.0, 0.0, 0.5), 0);
}
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
cactusRoundIcon = event.map.registerIcon("bettergrassandleaves:better_cactus");
cactusSideIcons.registerIcons(event.map);
BetterFoliage.log.info(String.format("Found %d cactus arm textures", cactusSideIcons.numLoaded));
}
}

View File

@@ -1,105 +0,0 @@
package mods.betterfoliage.client.render.impl;
import java.util.Random;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.init.Blocks;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.gen.NoiseGeneratorSimplex;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.event.world.WorldEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
public class RenderBlockBetterCoral extends RenderBlockAOBase implements IRenderBlockDecorator {
public IconSet coralCrustIcons = new IconSet("bettergrassandleaves", "better_crust_%d");
public IconSet coralCrossIcons = new IconSet("bettergrassandleaves", "better_coral_%d");
public NoiseGeneratorSimplex noise;
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
if (!BetterFoliage.config.coralEnabled) return false;
if (block != Blocks.sand) return false;
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x * 0.1, z * 0.1) + 1.0) * 32.0);
return terrainVariation < BetterFoliage.config.coralPopulation.value;
}
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
// store world for later use
blockAccess = world;
// use original renderer for block breaking overlay
if (renderer.hasOverrideBlockTexture()) {
renderer.setRenderBoundsFromBlock(block);
renderer.renderStandardBlock(block, x, y, z);
return true;
}
// render sand block
setPassCounters(1);
setRenderBoundsFromBlock(block);
renderStandardBlock(block, x, y, z);
Double3 blockCenter = new Double3(x + 0.5, y + 0.5, z + 0.5);
double offset = pRand[getSemiRandomFromPos(x, y, z, 6)] * BetterFoliage.config.coralVOffset.value;
double halfSize = BetterFoliage.config.coralSize.value * 0.5;
double halfCrustSize = BetterFoliage.config.coralCrustSize.value * 0.5;
Tessellator.instance.setBrightness(getBrightness(block, x, y, z));
for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) {
if (blockAccess.getBlock(x + dir.offsetX, y + dir.offsetY, z + dir.offsetZ).getMaterial() != Material.water) continue;
if (blockAccess.isAirBlock(x + dir.offsetX, y + dir.offsetY + 1, z + dir.offsetZ)) continue;
int variation = getSemiRandomFromPos(x, y, z, dir.ordinal());
if (variation < BetterFoliage.config.coralChance.value) {
IIcon crustIcon = coralCrustIcons.get(variation);
IIcon coralIcon = coralCrossIcons.get(variation);
if (crustIcon != null) renderCoralCrust(blockCenter, dir, offset, halfCrustSize, crustIcon, variation);
if (coralIcon != null) renderCrossedSideQuads(blockCenter.add(new Double3(dir).scale(0.5)), dir,
halfSize, halfSize,
pRot[variation], BetterFoliage.config.coralHOffset.value,
coralIcon, 0, false);
}
}
return true;
}
protected void renderCoralCrust(Double3 blockCenter, ForgeDirection dir, double offset, double scale, IIcon icon, int uvRot) {
Double3 face1 = new Double3(faceDir1[dir.ordinal()]).scale(scale);
Double3 face2 = new Double3(faceDir2[dir.ordinal()]).scale(scale);
Double3 drawCenter = blockCenter.add(new Double3(dir).scale(0.5 + offset));
if (Minecraft.isAmbientOcclusionEnabled()) {
setShadingForFace(dir);
renderQuadWithShading(icon, drawCenter, face1, face2, uvRot, faceAOPP, faceAONP, faceAONN, faceAOPN);
} else {
renderQuad(icon, drawCenter, face1, face2, uvRot);
}
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
coralCrustIcons.registerIcons(event.map);
coralCrossIcons.registerIcons(event.map);
BetterFoliage.log.info(String.format("Found %d coral crust textures", coralCrustIcons.numLoaded));
BetterFoliage.log.info(String.format("Found %d coral textures", coralCrossIcons.numLoaded));
}
@SubscribeEvent
public void handleWorldLoad(WorldEvent.Load event) {
noise = new NoiseGeneratorSimplex(new Random(event.world.getWorldInfo().getSeed() + 2));
}
}

View File

@@ -1,108 +0,0 @@
package mods.betterfoliage.client.render.impl;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.client.ShadersModIntegration;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.util.Double3;
import mods.betterfoliage.common.util.Utils;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.init.Blocks;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.util.ForgeDirection;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class RenderBlockBetterGrass extends RenderBlockAOBase implements IRenderBlockDecorator {
public IconSet grassIcons = new IconSet("bettergrassandleaves", "better_grass_long_%d");
public IconSet snowGrassIcons = new IconSet("bettergrassandleaves", "better_grass_snowed_%d");
public IconSet myceliumIcons = new IconSet("bettergrassandleaves", "better_mycel_%d");
public IIcon grassGenIcon;
public IIcon snowGrassGenIcon;
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
if (!BetterFoliage.config.grassEnabled) return false;
if (!(BetterFoliageClient.grass.matchesID(block) || block == Blocks.mycelium)) return false;
if (!blockAccess.isAirBlock(x, y + 1, z) && blockAccess.getBlock(x, y + 1, z) != Blocks.snow_layer) return false;
return true;
}
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
// store world for later use
blockAccess = world;
// use original renderer for block breaking overlay
if (renderer.hasOverrideBlockTexture()) {
renderer.setRenderBoundsFromBlock(block);
renderer.renderStandardBlock(block, x, y, z);
return true;
}
// render grass block
setPassCounters(1);
setRenderBoundsFromBlock(block);
if (block.getRenderType() == 0) {
renderStandardBlock(block, x, y, z);
} else {
ISimpleBlockRenderingHandler handler = Utils.getRenderingHandler(block.getRenderType());
handler.renderWorldBlock(world, x, y, z, block, block.getRenderType(), this);
}
int variation = getSemiRandomFromPos(x, y, z, 0);
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
boolean isSnowed = blockAccess.getBlock(x, y + 1, z) == Blocks.snow_layer;
IIcon renderIcon = null;
if (BetterFoliageClient.grass.matchesID(block)) {
if (BetterFoliage.config.grassUseGenerated) {
renderIcon = isSnowed ? snowGrassGenIcon : grassGenIcon;
} else {
renderIcon = isSnowed ? snowGrassIcons.get(variation) : grassIcons.get(variation);
}
} else if (block == Blocks.mycelium && !isSnowed) {
renderIcon = myceliumIcons.get(variation);
}
if (renderIcon == null) return true;
double scale = BetterFoliage.config.grassSize.value * 0.5;
double halfHeight = 0.5 * (BetterFoliage.config.grassHeightMin.value + pRand[heightVariation] * (BetterFoliage.config.grassHeightMax.value - BetterFoliage.config.grassHeightMin.value));
if (isSnowed) {
aoYPXZNN.setGray(0.9f); aoYPXZNP.setGray(0.9f); aoYPXZPN.setGray(0.9f); aoYPXZPP.setGray(0.9f);
Tessellator.instance.setColorOpaque(230, 230, 230);
}
// render short grass
ShadersModIntegration.startGrassQuads();
Tessellator.instance.setBrightness(getBrightness(block, x, y + 1, z));
Tessellator.instance.setColorOpaque_I(block.colorMultiplier(blockAccess, x, y, z));
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 + (isSnowed ? 0.0625 : 0.0), z + 0.5), ForgeDirection.UP, scale, halfHeight, pRot[variation], BetterFoliage.config.grassHOffset.value, renderIcon, 0, false);
return true;
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
grassIcons.registerIcons(event.map);
snowGrassIcons.registerIcons(event.map);
myceliumIcons.registerIcons(event.map);
grassGenIcon = event.map.registerIcon("bf_shortgrass:minecraft:tallgrass");
snowGrassGenIcon = event.map.registerIcon("bf_shortgrass_snow:minecraft:tallgrass");
BetterFoliage.log.info(String.format("Found %d short grass textures", grassIcons.numLoaded));
BetterFoliage.log.info(String.format("Found %d snowy grass textures", snowGrassIcons.numLoaded));
BetterFoliage.log.info(String.format("Found %d mycelium textures", myceliumIcons.numLoaded));
}
}

View File

@@ -1,106 +0,0 @@
package mods.betterfoliage.client.render.impl;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.util.Double3;
import mods.betterfoliage.common.util.Utils;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.init.Blocks;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.common.util.ForgeDirection;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class RenderBlockBetterLeaves extends RenderBlockAOBase implements IRenderBlockDecorator {
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
if (!BetterFoliage.config.leavesEnabled) return false;
if (original > 0 && original < 42) return false;
return BetterFoliageClient.leaves.matchesID(block) && !isBlockSurrounded(blockAccess, x, y, z);
}
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
// store world for later use
blockAccess = world;
// use original renderer for block breaking overlay
if (renderer.hasOverrideBlockTexture()) {
renderer.setRenderBoundsFromBlock(block);
renderer.renderStandardBlock(block, x, y, z);
return true;
}
// render leaves center
setPassCounters(1);
setRenderBoundsFromBlock(block);
if (block.getRenderType() == 0) {
renderStandardBlock(block, x, y, z);
} else {
ISimpleBlockRenderingHandler handler = Utils.getRenderingHandler(block.getRenderType());
handler.renderWorldBlock(world, x, y, z, block, block.getRenderType(), this);
}
// find generated texture to render with, assume the
// "true" texture of the block is the one on the north size
TextureAtlasSprite blockLeafIcon = null;
try {
blockLeafIcon = (TextureAtlasSprite) block.getIcon(world, x, y, z, ForgeDirection.NORTH.ordinal());
} catch (ClassCastException e) {
}
if (blockLeafIcon == null) {
BetterFoliage.log.debug(String.format("null leaf texture, x:%d, y:%d, z:%d, meta:%d, block:%s", x, y, z, blockAccess.getBlockMetadata(x, y, z), block.getClass().getName()));
return true;
}
IIcon crossLeafIcon = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(BetterFoliageClient.leafGenerator.domainName + ":" + blockLeafIcon.getIconName());
if (crossLeafIcon == null) {
return true;
}
int offsetVariation = getSemiRandomFromPos(x, y, z, 0);
int uvVariation = getSemiRandomFromPos(x, y, z, 1);
double halfSize = 0.5 * BetterFoliage.config.leavesSize.value;
boolean isAirTop = y == 255 || blockAccess.isAirBlock(x, y + 1, z);
boolean isAirBottom = y == 0 || blockAccess.isAirBlock(x, y - 1, z);
Tessellator.instance.setBrightness(isAirTop ? getBrightness(block, x, y + 1, z) : (isAirBottom ? getBrightness(block, x, y - 1, z) : getBrightness(block, x, y, z)));
Tessellator.instance.setColorOpaque_I(block.colorMultiplier(blockAccess, x, y, z));
if (BetterFoliage.config.leavesSkew) {
renderCrossedBlockQuadsSkew(new Double3(x + 0.5, y + 0.5, z + 0.5), halfSize,
pRot[offsetVariation].scaleAxes(BetterFoliage.config.leavesHOffset.value, BetterFoliage.config.leavesVOffset.value, BetterFoliage.config.leavesHOffset.value),
pRot[(offsetVariation + 1) & 63].scaleAxes(BetterFoliage.config.leavesHOffset.value, BetterFoliage.config.leavesVOffset.value, BetterFoliage.config.leavesHOffset.value),
crossLeafIcon, uvVariation, isAirTop, isAirBottom);
} else {
renderCrossedBlockQuadsTranslate(new Double3(x + 0.5, y + 0.5, z + 0.5), halfSize,
pRot[offsetVariation].scaleAxes(BetterFoliage.config.leavesHOffset.value, BetterFoliage.config.leavesVOffset.value, BetterFoliage.config.leavesHOffset.value),
crossLeafIcon, uvVariation, isAirTop, isAirBottom);
}
return true;
}
protected boolean isBlockSurrounded(IBlockAccess blockAccess, int x, int y, int z) {
if (isBlockNonSurrounding(blockAccess.getBlock(x + 1, y, z))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x - 1, y, z))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x, y + 1, z))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x, y - 1, z))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x, y, z + 1))) return false;
if (isBlockNonSurrounding(blockAccess.getBlock(x, y, z - 1))) return false;
return true;
}
protected boolean isBlockNonSurrounding(Block block) {
return block.getMaterial() == Material.air || block == Blocks.snow_layer;
}
}

View File

@@ -1,73 +0,0 @@
package mods.betterfoliage.client.render.impl;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.render.FakeRenderBlockAOBase;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.init.Blocks;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.util.ForgeDirection;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class RenderBlockBetterLilypad extends FakeRenderBlockAOBase implements IRenderBlockDecorator {
public IconSet lilypadFlowers = new IconSet("bettergrassandleaves", "better_lilypad_flower_%d");
public IconSet lilypadRoots = new IconSet("bettergrassandleaves", "better_lilypad_roots_%d");
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
return BetterFoliage.config.lilypadEnabled && block == Blocks.waterlily;
}
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
// store world for later use
blockAccess = world;
// use original renderer for block breaking overlay
if (renderer.hasOverrideBlockTexture()) {
renderer.renderBlockLilyPad(block, x, y, z);
return true;
}
// render lilypad block
renderBlockLilyPad(block, x, y, z);
int chanceVariation = getSemiRandomFromPos(x, y, z, 0);
int iconVariation = getSemiRandomFromPos(x, y, z, 1);
int offsetVariation = getSemiRandomFromPos(x, y, z, 2);
Tessellator.instance.setBrightness(getBrightness(block, x, y, z));
Tessellator.instance.setColorOpaque(255, 255, 255);
if (lilypadRoots.hasIcons()) renderCrossedSideQuads(new Double3(x + 0.5, y + 0.015, z + 0.5), ForgeDirection.DOWN,
0.2, 0.3,
null, 0.0,
lilypadRoots.get(iconVariation), 2,
true);
if (chanceVariation < BetterFoliage.config.lilypadChance.value && lilypadFlowers.hasIcons())
renderCrossedSideQuads(new Double3(x + 0.5, y + 0.02, z + 0.5), ForgeDirection.UP,
0.2, 0.3,
pRot[offsetVariation], BetterFoliage.config.lilypadHOffset.value,
lilypadFlowers.get(iconVariation), 0,
true);
return true;
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
lilypadFlowers.registerIcons(event.map);
lilypadRoots.registerIcons(event.map);
BetterFoliage.log.info(String.format("Found %d lilypad flower textures", lilypadFlowers.numLoaded));
BetterFoliage.log.info(String.format("Found %d lilypad root textures", lilypadRoots.numLoaded));
}
}

View File

@@ -1,93 +0,0 @@
package mods.betterfoliage.client.render.impl;
import java.util.Random;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.client.ShadersModIntegration;
import mods.betterfoliage.client.render.IRenderBlockDecorator;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.render.RenderBlockAOBase;
import mods.betterfoliage.common.util.Double3;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.gen.NoiseGeneratorSimplex;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.event.world.WorldEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class RenderBlockBetterReed extends RenderBlockAOBase implements IRenderBlockDecorator {
public IconSet reedBottomIcons = new IconSet("bf_reed_bottom", "bettergrassandleaves:better_reed_%d");
public IconSet reedTopIcons = new IconSet("bf_reed_top", "bettergrassandleaves:better_reed_%d");
public NoiseGeneratorSimplex noise;
public boolean isBlockAccepted(IBlockAccess blockAccess, int x, int y, int z, Block block, int original) {
if (!BetterFoliage.config.reedEnabled) return false;
if (!(BetterFoliageClient.dirt.matchesID(block))) return false;
if (blockAccess.getBlock(x, y + 1, z).getMaterial() != Material.water) return false;
if (!blockAccess.isAirBlock(x, y + 2, z)) return false;
if (blockAccess.getBiomeGenForCoords(x, z).temperature < 0.4f || blockAccess.getBiomeGenForCoords(x, z).rainfall < 0.4f) return false;
int terrainVariation = MathHelper.floor_double((noise.func_151605_a(x, z) + 1.0) * 32.0);
return terrainVariation < BetterFoliage.config.reedChance.value;
}
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
// store world for later use
blockAccess = world;
// use original renderer for block breaking overlay
if (renderer.hasOverrideBlockTexture()) {
renderer.setRenderBoundsFromBlock(block);
renderer.renderStandardBlock(block, x, y, z);
return true;
}
// render dirt block
setPassCounters(1);
setRenderBoundsFromBlock(block);
renderStandardBlock(block, x, y, z);
int iconVariation = getSemiRandomFromPos(x, y, z, 0);
int heightVariation = getSemiRandomFromPos(x, y, z, 1);
IIcon bottomIcon = reedBottomIcons.get(iconVariation);
IIcon topIcon = reedTopIcons.get(iconVariation);
if (bottomIcon == null || topIcon == null) return true;
double quarterHeight = 0.25 * (BetterFoliage.config.reedHeightMin.value + pRand[heightVariation] * (BetterFoliage.config.reedHeightMax.value - BetterFoliage.config.reedHeightMin.value));
Tessellator.instance.setBrightness(getBrightness(block, x, y + 2, z));
Tessellator.instance.setColorOpaque(255, 255, 255);
// render reeds
ShadersModIntegration.startGrassQuads();
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0, z + 0.5), ForgeDirection.UP, 0.5, quarterHeight, pRot[iconVariation], BetterFoliage.config.reedHOffset.value, bottomIcon, 0, true);
renderCrossedSideQuads(new Double3(x + 0.5, y + 1.0 + 2.0 * quarterHeight, z + 0.5), ForgeDirection.UP, 0.5, quarterHeight, pRot[iconVariation], BetterFoliage.config.reedHOffset.value, topIcon, 0, true);
return true;
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
reedBottomIcons.registerIcons(event.map);
reedTopIcons.registerIcons(event.map);
BetterFoliage.log.info(String.format("Found %d reed textures", reedBottomIcons.numLoaded));
}
@SubscribeEvent
public void handleWorldLoad(WorldEvent.Load event) {
noise = new NoiseGeneratorSimplex(new Random(event.world.getWorldInfo().getSeed()));
}
}

View File

@@ -1,92 +0,0 @@
package mods.betterfoliage.client.resource;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.common.util.Utils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.TextureStitchEvent;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** Base class for texture generators. Registers itself as a domain resource manager for the duration of block texture stitching.
* @author octarine-noise
*
*/
@SideOnly(Side.CLIENT)
public abstract class BlockTextureGenerator implements IResourceManager {
/** Resource domain name of generated textures */
public String domainName;
/** Resource location for fallback texture (if the generation process fails) */
public ResourceLocation missingResource;
/** Texture atlas for block textures used in the current run */
public TextureMap blockTextures;
public BlockTextureGenerator(String domainName, ResourceLocation missingResource) {
this.domainName = domainName;
this.missingResource = missingResource;
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
blockTextures = event.map;
Map<String, IResourceManager> domainManagers = Utils.getDomainResourceManagers();
if (domainManagers == null) {
BetterFoliage.log.warn("Failed to inject texture generator");
return;
}
domainManagers.put(domainName, this);
}
@SubscribeEvent
public void endTextureReload(TextureStitchEvent.Post event) {
blockTextures = null;
if (event.map.getTextureType() != 0) return;
// don't leave a mess
Map<String, IResourceManager> domainManagers = Utils.getDomainResourceManagers();
if (domainManagers != null) domainManagers.remove(domainName);
}
public Set<String> getResourceDomains() {
return ImmutableSet.<String>of(domainName);
}
public List<IResource> getAllResources(ResourceLocation resource) throws IOException {
return ImmutableList.<IResource>of(getResource(resource));
}
public IResource getMissingResource() throws IOException {
return Minecraft.getMinecraft().getResourceManager().getResource(missingResource);
}
public ResourceLocation unwrapResource(ResourceLocation wrapped) {
return new ResourceLocation(wrapped.getResourcePath().substring(16));
}
protected static int blendRGB(int rgbOrig, int rgbBlend, int weightOrig, int weightBlend) {
int r = (((rgbOrig >> 16) & 0xFF) * weightOrig + ((rgbBlend >> 16) & 0xFF) * weightBlend) / (weightOrig + weightBlend);
int g = (((rgbOrig >> 8) & 0xFF) * weightOrig + ((rgbBlend >> 8) & 0xFF) * weightBlend) / (weightOrig + weightBlend);
int b = ((rgbOrig & 0xFF) * weightOrig + (rgbBlend & 0xFF) * weightBlend) / (weightOrig + weightBlend);
int a = (rgbOrig >> 24) & 0xFF;
int result = (int) (a << 24 | r << 16 | g << 8 | b);
return result;
}
}

View File

@@ -1,51 +0,0 @@
package mods.betterfoliage.client.resource;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.data.IMetadataSection;
/** {@link IResource} for a {@link BufferedImage}
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class BufferedImageResource implements IResource {
/** Raw PNG data*/
protected byte[] data = null;
public BufferedImageResource(BufferedImage image) {
// create PNG image
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", baos);
data = baos.toByteArray();
} catch (IOException e) {
}
}
@Override
public InputStream getInputStream() {
return data == null ? null : new ByteArrayInputStream(data);
}
@Override
public boolean hasMetadata() {
return false;
}
@Override
public IMetadataSection getMetadata(String var1) {
return null;
}
}

View File

@@ -1,68 +0,0 @@
package mods.betterfoliage.client.resource;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.client.ShadersModIntegration;
import net.minecraft.client.Minecraft;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.TextureStitchEvent.Post;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class LeafGenerator extends LeafGeneratorBase {
/** Name of the default alpha mask to use */
public static String defaultMask = "default";
public LeafGenerator() {
super("bf_leaves", "betterfoliage", "textures/blocks/%s/%s", "betterfoliage:textures/blocks/leafmask_%d_%s.png", BetterFoliageClient.missingTexture);
}
@Override
protected BufferedImage generateLeaf(ResourceLocation originalWithDirs) throws IOException, TextureGenerationException {
// load normal leaf texture
BufferedImage origImage = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(originalWithDirs).getInputStream());
if (origImage.getWidth() != origImage.getHeight()) throw new TextureGenerationException();
int size = origImage.getWidth();
// tile leaf texture 2x2
BufferedImage overlayIcon = new BufferedImage(size * 2, size * 2, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D graphics = overlayIcon.createGraphics();
graphics.drawImage(origImage, 0, 0, null);
graphics.drawImage(origImage, 0, size, null);
graphics.drawImage(origImage, size, 0, null);
graphics.drawImage(origImage, size, size, null);
// overlay mask alpha on texture
if (!ShadersModIntegration.isSpecialTexture(originalWithDirs)) {
// load alpha mask of appropriate size
BufferedImage maskImage = loadLeafMaskImage(defaultMask, size * 2);
int scale = size * 2 / maskImage.getWidth();
for (int x = 0; x < overlayIcon.getWidth(); x++) for (int y = 0; y < overlayIcon.getHeight(); y++) {
long origPixel = overlayIcon.getRGB(x, y) & 0xFFFFFFFFl;
long maskPixel = maskImage.getRGB(x / scale, y / scale) & 0xFF000000l | 0x00FFFFFF;
overlayIcon.setRGB(x, y, (int) (origPixel & maskPixel));
}
}
return overlayIcon;
}
@Override
@SubscribeEvent
public void endTextureReload(Post event) {
super.endTextureReload(event);
if (event.map.getTextureType() != 0) return;
BetterFoliage.log.info(String.format("Found %d pre-drawn leaf textures", drawnCounter));
BetterFoliage.log.info(String.format("Generated %d leaf textures", generatedCounter));
}
}

View File

@@ -1,119 +0,0 @@
package mods.betterfoliage.client.resource;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import mods.betterfoliage.client.resource.LeafTextureEnumerator.LeafTextureFoundEvent;
import mods.betterfoliage.common.util.Utils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.TextureStitchEvent.Pre;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** Texture generator base class for textures based on leaf blocks.
* Supports loading from resource packs instead of generating if available.
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public abstract class LeafGeneratorBase extends BlockTextureGenerator {
@SideOnly(Side.CLIENT)
public static class TextureGenerationException extends Exception {
private static final long serialVersionUID = 7339757761980002651L;
};
/** Format string of pre-drawn texture location */
public String handDrawnLocationFormat;
/** Format string of alpha mask location */
public String maskImageLocationFormat;
/** Resource domain name of pre-drawn textures */
public String nonGeneratedDomain;
/** Number of textures generated in the current run */
public int generatedCounter = 0;
/** Number of pre-drawn textures found in the current run */
public int drawnCounter = 0;
public LeafGeneratorBase(String domain, String nonGeneratedDomain, String handDrawnLocationFormat, String maskImageLocationFormat, ResourceLocation missingResource) {
super(domain, missingResource);
this.nonGeneratedDomain = nonGeneratedDomain;
this.handDrawnLocationFormat = handDrawnLocationFormat;
this.maskImageLocationFormat = maskImageLocationFormat;
}
@Override
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
ResourceLocation originalNoDirs = unwrapResource(resourceLocation);
ResourceLocation originalWithDirs = new ResourceLocation(originalNoDirs.getResourceDomain(), "textures/blocks/" + originalNoDirs.getResourcePath());
// check for provided texture
ResourceLocation handDrawnLocation = new ResourceLocation(nonGeneratedDomain, String.format(handDrawnLocationFormat, originalNoDirs.getResourceDomain(), originalNoDirs.getResourcePath()));
if (Utils.resourceExists(handDrawnLocation)) {
drawnCounter++;
return resourceManager.getResource(handDrawnLocation);
}
// generate our own
if (!Utils.resourceExists(originalWithDirs)) return getMissingResource();
BufferedImage result;
try {
result = generateLeaf(originalWithDirs);
} catch (TextureGenerationException e) {
return getMissingResource();
}
generatedCounter++;
return new BufferedImageResource(result);
}
protected abstract BufferedImage generateLeaf(ResourceLocation originalWithDirs) throws IOException, TextureGenerationException;
/** Loads the alpha mask of the given type and size. If a mask of the exact size can not be found,
* will try to load progressively smaller masks down to 16x16
* @param type mask type
* @param size texture size
* @return alpha mask
*/
protected BufferedImage loadLeafMaskImage(String type, int size) {
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
IResource maskResource = null;
while (maskResource == null && size >= 1) {
try {
maskResource = resourceManager.getResource(new ResourceLocation(String.format(maskImageLocationFormat, size, type)));
} catch (Exception e) {}
size /= 2;
}
try {
return maskResource == null ? null : ImageIO.read(maskResource.getInputStream());
} catch (IOException e) {
return null;
}
}
@Override
@SubscribeEvent
public void handleTextureReload(Pre event) {
super.handleTextureReload(event);
if (event.map.getTextureType() != 0) return;
generatedCounter = 0;
drawnCounter = 0;
}
@SubscribeEvent
public void handleRegisterTexture(LeafTextureFoundEvent event) {
event.blockTextures.registerIcon(new ResourceLocation(domainName, event.icon.getIconName()).toString());
}
}

View File

@@ -1,93 +0,0 @@
package mods.betterfoliage.client.resource;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Map;
import javax.imageio.ImageIO;
import mods.betterfoliage.client.render.IconSet;
import mods.betterfoliage.client.resource.LeafTextureEnumerator.LeafTextureFoundEvent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.TextureStitchEvent;
import com.google.common.collect.Maps;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** Holds the texture for the falling leaf particles, and stores average texture color values for leaf textures
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class LeafParticleTextures {
/** Icons for leaf particles */
public IconSet icons = new IconSet("betterfoliage", "falling_leaf_default_%d");
/** Map of average color values */
public Map<IIcon, Integer> colors = Maps.newHashMap();
/** Default color value */
public int defaultColor = 0x208040;
public LeafParticleTextures(int defaultColor) {
this.defaultColor = defaultColor;
}
public int getColor(IIcon icon) {
Integer result = colors.get(icon);
return result == null ? defaultColor : result;
}
/** Calculate average color value (in HSB color space) for a texture and store it in the map.
* @param icon texture
*/
protected void addAtlasTexture(TextureAtlasSprite icon) {
ResourceLocation locationNoDirs = new ResourceLocation(icon.getIconName());
ResourceLocation locationWithDirs = new ResourceLocation(locationNoDirs.getResourceDomain(), String.format("textures/blocks/%s.png", locationNoDirs.getResourcePath()));
try {
BufferedImage image = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(locationWithDirs).getInputStream());
int numOpaque = 0;
float sumHueX = 0.0f;
float sumHueY = 0.0f;
float sumSaturation = 0.0f;
float sumBrightness = 0.0f;
for (int x = 0; x < image.getWidth(); x++) for (int y = 0; y < image.getHeight(); y++) {
int pixel = image.getRGB(x, y);
int alpha = (pixel >> 24) & 0xFF;
float[] hsbVals = Color.RGBtoHSB((pixel >> 16) & 0xFF, (pixel >> 8) & 0xFF, pixel & 0xFF, null);
if (alpha == 255) {
numOpaque++;
sumHueX += Math.cos((hsbVals[0] - 0.5) * 2.0 * Math.PI);
sumHueY += Math.sin((hsbVals[0] - 0.5) * 2.0 * Math.PI);
sumSaturation += hsbVals[1];
sumBrightness += hsbVals[2];
}
}
// average hue as usual for circular values - transform average unit vector back to polar angle
float avgHue = (float) (Math.atan2(sumHueY, sumHueX) / (2.0 * Math.PI) + 0.5);
colors.put(icon, Color.HSBtoRGB(avgHue, sumSaturation / numOpaque, sumBrightness / numOpaque));
} catch (IOException e) {
}
}
@SubscribeEvent
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
colors.clear();
icons.registerIcons(event.map);
}
@SubscribeEvent
public void handleRegisterTexture(LeafTextureFoundEvent event) {
addAtlasTexture(event.icon);
}
}

View File

@@ -1,107 +0,0 @@
package mods.betterfoliage.client.resource;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import mods.betterfoliage.BetterFoliage;
import mods.betterfoliage.client.BetterFoliageClient;
import mods.betterfoliage.common.util.Utils;
import mods.betterfoliage.loader.DeobfHelper;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.util.IIcon;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.MinecraftForge;
import com.google.common.collect.Sets;
import cpw.mods.fml.common.eventhandler.Event;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/** Enumerates all leaf textures at stitch time and emits an event for each.
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public class LeafTextureEnumerator implements IIconRegister {
/**{@link Event} that is emitted for each texture belonging to a leaf block.
* @author octarine-noise
*/
@SideOnly(Side.CLIENT)
public static class LeafTextureFoundEvent extends Event {
public TextureMap blockTextures;
public TextureAtlasSprite icon;
private LeafTextureFoundEvent(TextureMap blockTextures, TextureAtlasSprite icon) {
super();
this.blockTextures = blockTextures;
this.icon = icon;
}
}
/** Texture atlas for block textures used in the current run */
public TextureMap blockTextures;
/** Leaf blocks register their textures here.
* @return the originally registered {@link IIcon} already in the atlas
*/
public IIcon registerIcon(String resourceLocation) {
TextureAtlasSprite original = blockTextures.getTextureExtry(resourceLocation);
MinecraftForge.EVENT_BUS.post(new LeafTextureFoundEvent(blockTextures, original));
BetterFoliage.log.debug(String.format("Found leaf texture: %s", resourceLocation));
return original;
}
/** Iterates through all leaf blocks in the registry and makes them register
* their textures to "sniff out" all leaf textures.
* @param event
*/
@SubscribeEvent
@SuppressWarnings("unchecked")
public void handleTextureReload(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
blockTextures = event.map;
BetterFoliage.log.info("Reloading leaf textures");
// register simple block textures
Iterator<Block> iter = Block.blockRegistry.iterator();
while(iter.hasNext()) {
Block block = iter.next();
if (BetterFoliageClient.leaves.matchesClass(block)) {
BetterFoliage.log.debug(String.format("Inspecting leaf block: %s", block.getClass().getName()));
block.registerBlockIcons(this);
}
}
// enumerate all registered textures, find leaf textures among them
Map<String, TextureAtlasSprite> mapAtlas = null;
mapAtlas = Utils.getField(blockTextures, DeobfHelper.transformElementSearge("mapRegisteredSprites"), Map.class);
if (mapAtlas == null) mapAtlas = Utils.getField(blockTextures, "mapRegisteredSprites", Map.class);
if (mapAtlas == null) {
BetterFoliage.log.warn("Failed to reflect texture atlas, textures may be missing");
} else {
Set<TextureAtlasSprite> foundLeafTextures = Sets.newHashSet();
for (TextureAtlasSprite icon : mapAtlas.values())
if (BetterFoliageClient.isLeafTexture(icon)) foundLeafTextures.add(icon);
for (TextureAtlasSprite icon : foundLeafTextures) {
BetterFoliage.log.debug(String.format("Found non-block-registered leaf texture: %s", icon.getIconName()));
MinecraftForge.EVENT_BUS.post(new LeafTextureFoundEvent(blockTextures, icon));
}
}
}
@SubscribeEvent
public void endTextureReload(TextureStitchEvent.Post event) {
if (event.map.getTextureType() != 0) return;
blockTextures = null;
}
}

View File

@@ -1,44 +0,0 @@
package mods.betterfoliage.client.resource;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
@SideOnly(Side.CLIENT)
public class ReedGenerator extends BlockTextureGenerator {
public boolean isBottom;
public ReedGenerator(String domainName, ResourceLocation missingResource, boolean isBottom) {
super(domainName, missingResource);
this.isBottom = isBottom;
}
@Override
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
ResourceLocation originalNoDirs = unwrapResource(resourceLocation);
ResourceLocation originalWithDirs = new ResourceLocation(originalNoDirs.getResourceDomain(), "textures/blocks/" + originalNoDirs.getResourcePath());
// load full texture
BufferedImage origImage = ImageIO.read(resourceManager.getResource(originalWithDirs).getInputStream());
// draw half texture
BufferedImage result = new BufferedImage(origImage.getWidth(), origImage.getHeight() / 2, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D graphics = result.createGraphics();
graphics.drawImage(origImage, 0, isBottom ? -origImage.getHeight() / 2 : 0, null);
return new BufferedImageResource(result);
}
}

View File

@@ -1,56 +0,0 @@
package mods.betterfoliage.client.resource;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import mods.betterfoliage.client.ShadersModIntegration;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
@SideOnly(Side.CLIENT)
public class ShortGrassGenerator extends BlockTextureGenerator {
protected boolean isSnowed = false;
protected int snowOriginalWeight = 2;
protected int snowWhiteWeight = 3;
public ShortGrassGenerator(String domainName, ResourceLocation missingResource, boolean isSnowed) {
super(domainName, missingResource);
this.isSnowed = isSnowed;
}
@Override
public IResource getResource(ResourceLocation resourceLocation) throws IOException {
IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
ResourceLocation originalNoDirs = unwrapResource(resourceLocation);
ResourceLocation originalWithDirs = new ResourceLocation(originalNoDirs.getResourceDomain(), "textures/blocks/" + originalNoDirs.getResourcePath());
// load full texture
BufferedImage origImage = ImageIO.read(resourceManager.getResource(originalWithDirs).getInputStream());
// draw bottom half of texture
BufferedImage result = new BufferedImage(origImage.getWidth(), origImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D graphics = result.createGraphics();
graphics.drawImage(origImage, 0, 3 * origImage.getHeight() / 8, null);
// blend with white if snowed
if (isSnowed && !ShadersModIntegration.isSpecialTexture(originalWithDirs)) {
for (int x = 0; x < result.getWidth(); x++) for (int y = 0; y < result.getHeight(); y++) {
result.setRGB(x, y, blendRGB(result.getRGB(x, y), 0xFFFFFF, 2, 3));
}
}
return new BufferedImageResource(result);
}
}

View File

@@ -1,130 +0,0 @@
package mods.betterfoliage.common.config;
public class BetterFoliageConfig extends ConfigBase {
@CfgElement(category="leaves", key="enabled")
public boolean leavesEnabled = true;
@CfgElement(category="leaves", key="skewMode")
public boolean leavesSkew = false;
@CfgElement(category="grass", key="enabled")
public boolean grassEnabled = true;
@CfgElement(category="grass", key="useGenerated")
public boolean grassUseGenerated = false;
@CfgElement(category="cactus", key="enabled")
public boolean cactusEnabled = true;
@CfgElement(category="lilypad", key="enabled")
public boolean lilypadEnabled = true;
@CfgElement(category="reed", key="enabled")
public boolean reedEnabled = true;
@CfgElement(category="algae", key="enabled")
public boolean algaeEnabled = true;
@CfgElement(category="coral", key="enabled")
public boolean coralEnabled = true;
@CfgElement(category="fallingLeaves", key="enabled")
public boolean fallingLeavesEnabled = true;
@CfgElement(category="leaves", key="horizontalOffset")
public OptionDouble leavesHOffset = new OptionDouble(0.0, 0.4, 0.025, 0.2);
@CfgElement(category="leaves", key="verticalOffset")
public OptionDouble leavesVOffset = new OptionDouble(0.0, 0.4, 0.025, 0.1);
@CfgElement(category="leaves", key="size")
public OptionDouble leavesSize = new OptionDouble(0.75, 1.8, 0.05, 1.4);
@CfgElement(category="grass", key="horizontalOffset")
public OptionDouble grassHOffset = new OptionDouble(0.0, 0.4, 0.025, 0.2);
@CfgElement(category="grass", key="heightMin")
@Limit(max="grassHeightMax")
public OptionDouble grassHeightMin = new OptionDouble(0.1, 1.5, 0.05, 0.8);
@CfgElement(category="grass", key="heightMax")
public OptionDouble grassHeightMax = new OptionDouble(0.1, 1.5, 0.05, 1.0);
@CfgElement(category="grass", key="size")
public OptionDouble grassSize = new OptionDouble(0.5, 1.5, 0.05, 1.0);
@CfgElement(category="lilypad", key="horizontalOffset")
public OptionDouble lilypadHOffset = new OptionDouble(0.0, 0.25, 0.025, 0.1);
@CfgElement(category="lilypad", key="chance")
public OptionInteger lilypadChance = new OptionInteger(0, 64, 1, 16);
@CfgElement(category="reed", key="horizontalOffset")
public OptionDouble reedHOffset = new OptionDouble(0.0, 0.25, 0.025, 0.1);
@CfgElement(category="reed", key="heightMin")
@Limit(max="reedHeightMax")
public OptionDouble reedHeightMin = new OptionDouble(1.5, 3.5, 0.1, 2.0);
@CfgElement(category="reed", key="heightMax")
public OptionDouble reedHeightMax = new OptionDouble(1.5, 3.5, 0.1, 2.5);
@CfgElement(category="reed", key="chance")
public OptionInteger reedChance = new OptionInteger(0, 64, 1, 32);
@CfgElement(category="algae", key="horizontalOffset")
public OptionDouble algaeHOffset = new OptionDouble(0.0, 0.25, 0.025, 0.1);
@CfgElement(category="algae", key="size")
public OptionDouble algaeSize = new OptionDouble(0.5, 1.5, 0.05, 1.0);
@CfgElement(category="algae", key="heightMin")
@Limit(max="algaeHeightMax")
public OptionDouble algaeHeightMin = new OptionDouble(0.1, 1.5, 0.05, 0.5);
@CfgElement(category="algae", key="heightMax")
public OptionDouble algaeHeightMax = new OptionDouble(0.1, 1.5, 0.05, 1.0);
@CfgElement(category="algae", key="chance")
public OptionInteger algaeChance = new OptionInteger(0, 64, 1, 48);
@CfgElement(category="coral", key="population")
public OptionInteger coralPopulation = new OptionInteger(0, 64, 1, 32);
@CfgElement(category="coral", key="chance")
public OptionInteger coralChance = new OptionInteger(0, 64, 1, 32);
@CfgElement(category="coral", key="verticalOffset")
public OptionDouble coralVOffset = new OptionDouble(0.0, 0.25, 0.025, 0.1);
@CfgElement(category="coral", key="horizontalOffset")
public OptionDouble coralHOffset = new OptionDouble(0.0, 0.4, 0.025, 0.2);
@CfgElement(category="coral", key="crustSize")
public OptionDouble coralCrustSize = new OptionDouble(0.75, 1.75, 0.05, 1.4);
@CfgElement(category="coral", key="size")
public OptionDouble coralSize = new OptionDouble(0.25, 1.0, 0.05, 0.7);
@CfgElement(category="fallingLeaves", key="speed")
public OptionDouble fallingLeavesSpeed = new OptionDouble(0.01, 0.15, 0.01, 0.05);
@CfgElement(category="fallingLeaves", key="windStrength")
public OptionDouble fallingLeavesWindStrength = new OptionDouble(0.1, 2.0, 0.1, 0.5);
@CfgElement(category="fallingLeaves", key="stormStrength")
public OptionDouble fallingLeavesStormStrength = new OptionDouble(0.1, 2.0, 0.1, 0.8);
@CfgElement(category="fallingLeaves", key="size")
public OptionDouble fallingLeavesSize = new OptionDouble(0.25, 1.5, 0.05, 0.75);
@CfgElement(category="fallingLeaves", key="chance")
public OptionDouble fallingLeavesChance = new OptionDouble(0.005, 1.0, 0.005, 0.05);
@CfgElement(category="fallingLeaves", key="perturbation")
public OptionDouble fallingLeavesPerturb = new OptionDouble(0.05, 1.0, 0.05, 0.25);
@CfgElement(category="fallingLeaves", key="lifetime")
public OptionDouble fallingLeavesLifetime = new OptionDouble(1.0, 10.0, 0.1, 5.0);
}

View File

@@ -1,137 +0,0 @@
package mods.betterfoliage.common.config;
import java.io.File;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
/** Config base class using annotations
* @author octarine-noise
*/
public class ConfigBase {
/** Annotates a field linked to a config file property
* @author octarine-noise
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public static @interface CfgElement {
String category();
String key();
String comment() default "";
}
/** Declares a min/max limit on another field
* @author octarine-noise
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public static @interface Limit {
String min() default "";
String max() default "";
}
protected Configuration config;
public void load(File configFile) {
config = new Configuration(configFile);
config.load();
for (Field field : getClass().getDeclaredFields()) {
CfgElement annot = field.getAnnotation(CfgElement.class);
if (annot == null) continue;
field.setAccessible(true);
if (field.getType().equals(boolean.class) || field.getType().equals(Boolean.class)) {
try {
Property prop = config.get(annot.category(), annot.key(), field.getBoolean(this));
field.setBoolean(this, prop.getBoolean(field.getBoolean(this)));
} catch (Exception e) {
}
} else if (field.getType().equals(OptionInteger.class)) {
try {
OptionInteger option = (OptionInteger) field.get(this);
Property prop = config.get(annot.category(), annot.key(), option.value);
option.value = prop.getInt(option.value);
} catch (Exception e) {
}
} else if (field.getType().equals(OptionDouble.class)) {
try {
OptionDouble option = (OptionDouble) field.get(this);
Property prop = config.get(annot.category(), annot.key(), option.value);
option.value = prop.getDouble(option.value);
} catch (Exception e) {
}
}
}
validateLimits();
if (config.hasChanged()) config.save();
}
protected void validateLimits() {
for (Field fieldThis : getClass().getDeclaredFields()) {
Limit annot = fieldThis.getAnnotation(Limit.class);
if (annot == null) continue;
try {
Field fieldMin = annot.min().isEmpty() ? null : getClass().getDeclaredField(annot.min());
Field fieldMax = annot.max().isEmpty() ? null : getClass().getDeclaredField(annot.max());
fieldThis.setAccessible(true);
fieldMin.setAccessible(true);
fieldMax.setAccessible(true);
if (fieldThis.getType().equals(OptionInteger.class)) {
OptionInteger optionThis = (OptionInteger) fieldThis.get(this);
OptionInteger optionMin = fieldMin == null ? null : (OptionInteger) fieldMin.get(this);
OptionInteger optionMax = fieldMax == null ? null : (OptionInteger) fieldMax.get(this);
if (optionMin != null) optionThis.value = Math.max(optionThis.value, optionMin.value);
if (optionMax != null) optionThis.value = Math.min(optionThis.value, optionMax.value);
} else if (fieldThis.getType().equals(OptionDouble.class)) {
OptionDouble optionThis = (OptionDouble) fieldThis.get(this);
OptionDouble optionMin = fieldMin == null ? null : (OptionDouble) fieldMin.get(this);
OptionDouble optionMax = fieldMax == null ? null : (OptionDouble) fieldMax.get(this);
if (optionMin != null) optionThis.value = Math.max(optionThis.value, optionMin.value);
if (optionMax != null) optionThis.value = Math.min(optionThis.value, optionMax.value);
}
} catch (Exception e) {}
}
}
public void save() {
for (Field field : getClass().getDeclaredFields()) {
CfgElement annot = field.getAnnotation(CfgElement.class);
if (annot == null) continue;
field.setAccessible(true);
if (field.getType().equals(boolean.class) || field.getType().equals(Boolean.class)) {
try {
Property prop = config.get(annot.category(), annot.key(), field.getBoolean(this));
prop.set(field.getBoolean(this));
} catch (Exception e) {
}
} else if (field.getType().equals(OptionInteger.class)) {
try {
OptionInteger option = (OptionInteger) field.get(this);
Property prop = config.get(annot.category(), annot.key(), option.value);
prop.set(option.value);
} catch (Exception e) {
}
} else if (field.getType().equals(OptionDouble.class)) {
try {
OptionDouble option = (OptionDouble) field.get(this);
Property prop = config.get(annot.category(), annot.key(), option.value);
prop.set(option.value);
} catch (Exception e) {
}
}
}
config.save();
}
}

View File

@@ -1,26 +0,0 @@
package mods.betterfoliage.common.config;
public class OptionDouble {
public double min;
public double max;
public double step;
public double value;
public OptionDouble(double min, double max, double step, double value) {
this.min = min;
this.max = max;
this.step = step;
this.value = value;
}
public void increment(int times) {
value += times * step;
if (value > max) value = max;
}
public void decrement(int times) {
value -= times * step;
if (value < min) value = min;
}
}

View File

@@ -1,26 +0,0 @@
package mods.betterfoliage.common.config;
public class OptionInteger {
public int min;
public int max;
public int step;
public int value;
public OptionInteger(int min, int max, int step, int value) {
this.min = min;
this.max = max;
this.step = step;
this.value = value;
}
public void increment(int times) {
value += times * step;
if (value > max) value = max;
}
public void decrement(int times) {
value -= times * step;
if (value < min) value = min;
}
}

View File

@@ -1,49 +0,0 @@
package mods.betterfoliage.common.util;
import net.minecraftforge.common.util.ForgeDirection;
/** Immutable 3D vector of double precision.
* @author octarine-noise
*/
public class Double3 {
public final double x;
public final double y;
public final double z;
public Double3(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Double3(ForgeDirection dir) {
this.x = dir.offsetX;
this.y = dir.offsetY;
this.z = dir.offsetZ;
}
public Double3 add(Double3 other) {
return new Double3(x + other.x, y + other.y, z + other.z);
}
public Double3 sub(Double3 other) {
return new Double3(x - other.x, y - other.y, z - other.z);
}
public Double3 add(double x, double y, double z) {
return new Double3(this.x + x, this.y + y, this.z + z);
}
public Double3 scaleAxes(double sx, double sy, double sz) {
return new Double3(x * sx, y * sy, z * sz);
}
public Double3 scale(double s) {
return new Double3(x * s, y * s, z * s);
}
public Double3 inverse() {
return new Double3(-x, -y, -z);
}
}

View File

@@ -1,111 +0,0 @@
package mods.betterfoliage.common.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.Map;
import com.google.common.base.Charsets;
import mods.betterfoliage.loader.DeobfHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.SimpleReloadableResourceManager;
import net.minecraft.util.ResourceLocation;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import cpw.mods.fml.client.registry.RenderingRegistry;
public class Utils {
/** Hide constructor */
private Utils() {}
/**
* @return (({@link SimpleReloadableResourceManager}) Minecraft.getMinecraft().getResourceManager()).domainResourceManagers
*/
@SuppressWarnings("unchecked")
public static Map<String, IResourceManager> getDomainResourceManagers() {
IResourceManager manager = Minecraft.getMinecraft().getResourceManager();
Map<String, IResourceManager> result = getField(manager, DeobfHelper.transformElementSearge("domainResourceManagers"), Map.class);
if (result == null) result = getField(manager, "domainResourceManagers", Map.class);
return result;
}
@SuppressWarnings("unchecked")
public static <T> T getField(Object target, String fieldName, Class<T> resultClass) {
try {
Field field = target.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return (T) field.get(target);
} catch (Exception e) {
return null;
}
}
@SuppressWarnings("unchecked")
public static <T> T getStaticField(Class<?> clazz, String fieldName, Class<T> resultClass) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return (T) field.get(null);
} catch (Exception e) {
return null;
}
}
/** Retrieve a specific rendering handler from the registry
* @param renderType render type of block
* @return {@link ISimpleBlockRenderingHandler} if defined, null otherwise
*/
@SuppressWarnings("unchecked")
public static ISimpleBlockRenderingHandler getRenderingHandler(int renderType) {
try {
Field field = RenderingRegistry.class.getDeclaredField("INSTANCE");
field.setAccessible(true);
RenderingRegistry inst = (RenderingRegistry) field.get(null);
field = RenderingRegistry.class.getDeclaredField("blockRenderers");
field.setAccessible(true);
return ((Map<Integer, ISimpleBlockRenderingHandler>) field.get(inst)).get(renderType);
} catch (Exception e) {
return null;
}
}
/** Check for the existence of a {@link IResource}
* @param resourceLocation
* @return true if the resource exists
*/
public static boolean resourceExists(ResourceLocation resourceLocation) {
try {
IResource resource = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation);
if (resource != null) return true;
} catch (IOException e) {
}
return false;
}
/** Copy a text file from a resource to the filesystem
* @param resourceLocation resource location of text file
* @param target target file
*/
public static void copyFromTextResource(ResourceLocation resourceLocation, File target) {
try {
IResource defaults = Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation);
BufferedReader reader = new BufferedReader(new InputStreamReader(defaults.getInputStream(), Charsets.UTF_8));
FileWriter writer = new FileWriter(target);
String line = reader.readLine();
while(line != null) {
writer.write(line + System.lineSeparator());
line = reader.readLine();
}
reader.close();
writer.close();
} catch(IOException e) {
}
}
}

View File

@@ -1,29 +0,0 @@
package mods.betterfoliage.loader;
import java.util.Map;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
@IFMLLoadingPlugin.TransformerExclusions({"mods.betterfoliage.loader"})
public class BetterFoliageLoader implements IFMLLoadingPlugin {
public String[] getASMTransformerClass() {
return new String[] {"mods.betterfoliage.loader.BetterFoliageTransformer"};
}
public String getModContainerClass() {
return null;
}
public String getSetupClass() {
return null;
}
public void injectData(Map<String, Object> data) {
}
public String getAccessTransformerClass() {
return null;
}
}

View File

@@ -1,79 +0,0 @@
package mods.betterfoliage.loader;
import net.minecraft.launchwrapper.IClassTransformer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import com.google.common.collect.ImmutableList;
import cpw.mods.fml.relauncher.FMLInjectionData;
public class BetterFoliageTransformer implements IClassTransformer {
protected Iterable<MethodTransformerBase> transformers = ImmutableList.<MethodTransformerBase>of(
new TransformRenderBlockOverride(),
new TransformShaderModBlockOverride(),
new TransformRandomDisplayTick()
);
protected Logger logger = LogManager.getLogger(getClass().getSimpleName());
public BetterFoliageTransformer() {
String mcVersion = FMLInjectionData.data()[4].toString();
if (!ImmutableList.<String>of("1.7.2", "1.7.10").contains(mcVersion))
logger.warn(String.format("Unsupported Minecraft version %s", mcVersion));
DeobfHelper.init();
}
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
// ???
if (basicClass == null) return null;
// read class
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(basicClass);
classReader.accept(classNode, 0);
boolean hasTransformed = false;
for (MethodTransformerBase transformer : transformers) {
// try to find specified method in class
if (!transformedName.equals(transformer.getClassName())) continue;
logger.debug(String.format("Found class: %s -> %s", name, transformedName));
for (MethodNode methodNode : classNode.methods) {
logger.trace(String.format("Checking method: %s, sig: %s", methodNode.name, methodNode.desc));
Boolean isObfuscated = null;
if (methodNode.name.equals(DeobfHelper.transformElementName(transformer.getMethodName())) && methodNode.desc.equals(DeobfHelper.transformSignature(transformer.getSignature()))) {
isObfuscated = true;
} else if (methodNode.name.equals(transformer.getMethodName()) && methodNode.desc.equals(transformer.getSignature())) {
isObfuscated = false;
}
if (isObfuscated != null) {
// transform
hasTransformed = true;
try {
transformer.transform(methodNode, isObfuscated);
logger.info(String.format("%s: SUCCESS", transformer.getLogMessage()));
} catch (Exception e) {
logger.info(String.format("%s: FAILURE", transformer.getLogMessage()));
}
break;
}
}
}
// return result
ClassWriter writer = new ClassWriter(0);
if (hasTransformed) classNode.accept(writer);
return !hasTransformed ? basicClass : writer.toByteArray();
}
}

View File

@@ -1,84 +0,0 @@
package mods.betterfoliage.loader;
import java.util.Map;
import com.google.common.collect.Maps;
import cpw.mods.fml.relauncher.FMLInjectionData;
public class DeobfHelper {
private static Map<String, String> obfClasses = Maps.newHashMap();
private static Map<String, String> obfElements = Maps.newHashMap();
private static Map<String, String> srgElements = Maps.newHashMap();
public static void init() {
String mcVersion = FMLInjectionData.data()[4].toString();
srgElements.put("domainResourceManagers", "field_110548_a");
srgElements.put("mapRegisteredSprites", "field_110574_e");
if ("1.7.2".equals(mcVersion)) {
obfClasses.put("net/minecraft/client/renderer/RenderBlocks", "ble");
obfClasses.put("net/minecraft/world/IBlockAccess", "afx");
obfClasses.put("net/minecraft/block/Block", "ahu");
obfClasses.put("net/minecraft/client/multiplayer/WorldClient", "biz");
obfClasses.put("net/minecraft/world/World", "afn");
obfElements.put("blockAccess", "a");
obfElements.put("renderBlockByRenderType", "b");
obfElements.put("mapRegisteredSprites", "bpr");
obfElements.put("doVoidFogParticles", "C");
} else if ("1.7.10".equals(mcVersion)) {
obfClasses.put("net/minecraft/client/renderer/RenderBlocks", "blm");
obfClasses.put("net/minecraft/world/IBlockAccess", "ahl");
obfClasses.put("net/minecraft/block/Block", "aji");
obfClasses.put("net/minecraft/client/multiplayer/WorldClient", "bjf");
obfClasses.put("net/minecraft/world/World", "ahb");
obfElements.put("blockAccess", "a");
obfElements.put("renderBlockByRenderType", "b");
obfElements.put("mapRegisteredSprites", "bpr");
obfElements.put("doVoidFogParticles", "C");
}
}
/** Transform a class name from MCP to obfuscated names.
* @param className MCP name
* @return obfuscated name
*/
public static String transformClassName(String className) {
return obfClasses.containsKey(className) ? obfClasses.get(className) : className;
}
/** Transform a method or field name from MCP to obfuscated names.
* @param elementName MCP name
* @return obfuscated name
*/
public static String transformElementName(String elementName) {
return obfElements.containsKey(elementName) ? obfElements.get(elementName) : elementName;
}
/** Transform a method or field name from MCP to SRG names.
* @param elementName MCP name
* @return SRG name
*/
public static String transformElementSearge(String elementName) {
return srgElements.containsKey(elementName) ? srgElements.get(elementName) : elementName;
}
/** Transform an ASM signature from MCP to obfuscated names.
* @param signature MCP signature
* @return obfuscated signature
*/
public static String transformSignature(String signature) {
String result = signature;
boolean hasChanged = false;
do {
hasChanged = false;
for (Map.Entry<String, String> entry : obfClasses.entrySet()) if (result.contains("L" + entry.getKey() + ";")) {
result = result.replace("L" + entry.getKey() + ";", "L" + entry.getValue() + ";");
hasChanged = true;
}
} while(hasChanged);
return result;
}
}

View File

@@ -1,144 +0,0 @@
package mods.betterfoliage.loader;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
/** Base class for class transformers operating on a single method.
* @author octarine-noise
*/
public abstract class MethodTransformerBase {
/** Instruction node filter
* @author octarine-noise
*/
public static interface IInstructionMatch {
public boolean matches(AbstractInsnNode node);
}
/**
* @return MCP name of the class to transform
*/
public abstract String getClassName();
/**
* @return MCP name of the method to transform
*/
public abstract String getMethodName();
/**
* @return ASM signature of the method to transform using MCP names
*/
public abstract String getSignature();
/**
* @return Log message to write when method is found
*/
public abstract String getLogMessage();
/** Transform method node
* @param method method node
* @param isObfuscated true for obfuscated environment
*/
public abstract void transform(MethodNode method, boolean isObfuscated);
/** Transform a class name from MCP to obfuscated names if necessary.
* @param className MCP name
* @param isObfuscated true for obfuscated environment
* @return transformed name
*/
protected static String className(String className, boolean isObfuscated) {
return isObfuscated ? DeobfHelper.transformClassName(className) : className;
}
/** Transform a method or field name from MCP to obfuscated names if necessary.
* @param fieldName MCP name
* @param isObfuscated true for obfuscated environment
* @return transformed name
*/
protected static String element(String fieldName, boolean isObfuscated) {
return isObfuscated ? DeobfHelper.transformElementName(fieldName) : fieldName;
}
/** Transform an ASM signature from MCP to obfuscated names if necessary.
* @param signature MCP signature
* @param isObfuscated true for obfuscated environment
* @return transformed signature
*/
protected static String signature(String signature, boolean isObfuscated) {
return isObfuscated ? DeobfHelper.transformSignature(signature) : signature;
}
/** Find the next instruction node in an instruction list starting from a given node, matching a given filter
* @param start start node
* @param match filter
* @return instruction node if found, null otherwise
*/
protected AbstractInsnNode findNext(AbstractInsnNode start, IInstructionMatch match) {
AbstractInsnNode current = start;
while(current != null) {
if (match.matches(current)) break;
current = current.getNext();
}
return current;
}
/** Find the previous instruction node in a list starting from a given node, matching a given filter
* @param start start node
* @param match filter
* @return instruction node if found, null otherwise
*/
protected AbstractInsnNode findPrevious(AbstractInsnNode start, IInstructionMatch match) {
AbstractInsnNode current = start;
while(current != null) {
if (match.matches(current)) break;
current = current.getPrevious();
}
return current;
}
/**
* @return an instruction node filter matching any invoke instruction
*/
protected IInstructionMatch matchInvokeAny() {
return new IInstructionMatch() {
public boolean matches(AbstractInsnNode node) {
return node instanceof MethodInsnNode;
}
};
}
/**
* @return an instruction node filter matching the given opcode
*/
protected IInstructionMatch matchOpcode(final int opcode) {
return new IInstructionMatch() {
public boolean matches(AbstractInsnNode node) {
return node.getOpcode() == opcode;
}
};
}
/** Insert a list of instruction nodes in a list after a given node
* @param insnList instruction list
* @param node start node
* @param added instructions to add
*/
protected void insertAfter(InsnList insnList, AbstractInsnNode node, AbstractInsnNode... added) {
InsnList listAdd = new InsnList();
for (AbstractInsnNode inst : added) listAdd.add(inst);
insnList.insert(node, listAdd);
}
/** Insert a list of instruction nodes in a list before a given node
* @param insnList instruction list
* @param node start node
* @param added instructions to add
*/
protected void insertBefore(InsnList insnList, AbstractInsnNode node, AbstractInsnNode... added) {
InsnList listAdd = new InsnList();
for (AbstractInsnNode inst : added) listAdd.add(inst);
insnList.insertBefore(node, listAdd);
}
}

View File

@@ -1,43 +0,0 @@
package mods.betterfoliage.loader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
public class TransformRandomDisplayTick extends MethodTransformerBase {
@Override
public String getClassName() {
return "net.minecraft.client.multiplayer.WorldClient";
}
@Override
public String getMethodName() {
return "doVoidFogParticles";
}
@Override
public String getSignature() {
return "(III)V";
}
@Override
public String getLogMessage() {
return "Applying random display tick call hook";
}
@Override
public void transform(MethodNode method, boolean obf) {
AbstractInsnNode endLoop = findNext(method.instructions.getFirst(), matchOpcode(Opcodes.IINC));
insertBefore(method.instructions, endLoop,
new VarInsnNode(Opcodes.ALOAD, 10),
new VarInsnNode(Opcodes.ALOAD, 0),
new VarInsnNode(Opcodes.ILOAD, 7),
new VarInsnNode(Opcodes.ILOAD, 8),
new VarInsnNode(Opcodes.ILOAD, 9),
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "onRandomDisplayTick", signature("(Lnet/minecraft/block/Block;Lnet/minecraft/world/World;III)V", obf))
);
}
}

View File

@@ -1,49 +0,0 @@
package mods.betterfoliage.loader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
public class TransformRenderBlockOverride extends MethodTransformerBase {
@Override
public String getClassName() {
return "net.minecraft.client.renderer.RenderBlocks";
}
@Override
public String getMethodName() {
return "renderBlockByRenderType";
}
@Override
public String getSignature() {
return "(Lnet/minecraft/block/Block;III)Z";
}
@Override
public String getLogMessage() {
return "Applying RenderBlocks.renderBlockByRenderType() render type override";
}
@Override
public void transform(MethodNode method, boolean obf) {
AbstractInsnNode invokeGetRenderType = findNext(method.instructions.getFirst(), matchInvokeAny());
AbstractInsnNode storeRenderType = findNext(invokeGetRenderType, matchOpcode(Opcodes.ISTORE));
insertAfter(method.instructions, storeRenderType,
new VarInsnNode(Opcodes.ALOAD, 0),
new FieldInsnNode(Opcodes.GETFIELD, className("net/minecraft/client/renderer/RenderBlocks", obf), element("blockAccess", obf), signature("Lnet/minecraft/world/IBlockAccess;", obf)),
new VarInsnNode(Opcodes.ILOAD, 2),
new VarInsnNode(Opcodes.ILOAD, 3),
new VarInsnNode(Opcodes.ILOAD, 4),
new VarInsnNode(Opcodes.ALOAD, 1),
new VarInsnNode(Opcodes.ILOAD, 5),
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/BetterFoliageClient", "getRenderTypeOverride", signature("(Lnet/minecraft/world/IBlockAccess;IIILnet/minecraft/block/Block;I)I", obf)),
new VarInsnNode(Opcodes.ISTORE, 5)
);
}
}

View File

@@ -1,40 +0,0 @@
package mods.betterfoliage.loader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
public class TransformShaderModBlockOverride extends MethodTransformerBase {
@Override
public String getClassName() {
return "shadersmodcore.client.Shaders";
}
@Override
public String getMethodName() {
return "pushEntity";
}
@Override
public String getSignature() {
return "(Lnet/minecraft/client/renderer/RenderBlocks;Lnet/minecraft/block/Block;III)V";
}
@Override
public String getLogMessage() {
return "Applying Shaders.pushEntity() block id override";
}
@Override
public void transform(MethodNode method, boolean obf) {
AbstractInsnNode arrayStore = findNext(method.instructions.getFirst(), matchOpcode(Opcodes.IASTORE));
insertAfter(method.instructions, arrayStore.getPrevious(),
new VarInsnNode(Opcodes.ALOAD, 1),
new MethodInsnNode(Opcodes.INVOKESTATIC, "mods/betterfoliage/client/ShadersModIntegration", "getBlockIdOverride", signature("(ILnet/minecraft/block/Block;)I", obf))
);
}
}

View File

@@ -0,0 +1,44 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.Hooks;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Random;
@Mixin(Block.class)
public class MixinBlock {
private static final String shouldSideBeRendered = "shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)Z";
private static final String getVoxelShape = "Lnet/minecraft/block/BlockState;getCullShape(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)Lnet/minecraft/util/shape/VoxelShape;";
private static final String randomDisplayTick = "randomDisplayTick(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Ljava/util/Random;)V";
/**
* Override the {@link VoxelShape} used for the neighbor block in {@link Block}.shouldSideBeRendered().
*
* This way log blocks can be made to not block rendering, without altering any {@link Block} or
* {@link BlockState} properties with potential gameplay ramifications.
*/
@Redirect(method = shouldSideBeRendered, at = @At(value = "INVOKE", target = getVoxelShape, ordinal = 1))
private static VoxelShape getVoxelShapeOverride(BlockState state, BlockView reader, BlockPos pos, Direction dir) {
return Hooks.getVoxelShapeOverride(state, reader, pos, dir);
}
/**
* Inject a callback to call for every random display tick. Used for adding custom particle effects to blocks.
*/
@Inject(method = randomDisplayTick, at = @At("HEAD"))
void onRandomDisplayTick(BlockState state, World world, BlockPos pos, Random rnd, CallbackInfo ci) {
// Hooks.onRandomDisplayTick(state.getBlock(), state, world, pos, rnd);
}
}

View File

@@ -0,0 +1,17 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.BlockModelsReloadCallback;
import net.minecraft.client.render.block.BlockModels;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(BlockModels.class)
public class MixinBlockModels {
@Inject(method = "reload()V", at = @At("RETURN"))
void onReload(CallbackInfo ci) {
BlockModelsReloadCallback.EVENT.invoker().reloadBlockModels((BlockModels) (Object) this);
}
}

View File

@@ -0,0 +1,27 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.Hooks;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/**
* Mixin to override the result of {@link BlockState}.getAmbientOcclusionLightValue().
*
* Needed to avoid excessive darkening of Round Logs at the corners, now that they are not full blocks.
*/
@Mixin(BlockState.class)
@SuppressWarnings({"UnnecessaryQualifiedMemberReference", "deprecation"})
public class MixinBlockState {
private static final String callFrom = "Lnet/minecraft/block/BlockState;getAmbientOcclusionLightLevel(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;)F";
private static final String callTo = "Lnet/minecraft/block/Block;getAmbientOcclusionLightLevel(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;)F";
@Redirect(method = callFrom, at = @At(value = "INVOKE", target = callTo))
float getAmbientOcclusionValue(Block block, BlockState state, BlockView reader, BlockPos pos) {
return Hooks.getAmbientOcclusionLightValueOverride(block.getAmbientOcclusionLightLevel(state, reader, pos), state);
}
}

View File

@@ -0,0 +1,23 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.ClientChunkLoadCallback;
import net.minecraft.client.world.ClientChunkManager;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.PacketByteBuf;
import net.minecraft.world.World;
import net.minecraft.world.chunk.WorldChunk;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ClientChunkManager.class)
public class MixinClientChunkManager {
private static final String onLoadChunkFromPacket = "loadChunkFromPacket(Lnet/minecraft/world/World;IILnet/minecraft/util/PacketByteBuf;Lnet/minecraft/nbt/CompoundTag;IZ)Lnet/minecraft/world/chunk/WorldChunk;";
@Inject(method = onLoadChunkFromPacket, at = @At(value = "RETURN", ordinal = 2))
void onLoadChunkFromPacket(World world, int chunkX, int chunkZ, PacketByteBuf data, CompoundTag nbt, int updatedSectionsBits, boolean clearOld, CallbackInfoReturnable<WorldChunk> ci) {
ClientChunkLoadCallback.EVENT.invoker().loadChunk(ci.getReturnValue());
}
}

View File

@@ -0,0 +1,19 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.ClientChunkLoadCallback;
import net.minecraft.world.chunk.WorldChunk;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(targets = {"net.minecraft.client.world.ClientChunkManager$ClientChunkMap"})
public class MixinClientChunkManagerChunkMap {
private static final String onSetAndCompare = "method_20183(ILnet/minecraft/world/chunk/WorldChunk;Lnet/minecraft/world/chunk/WorldChunk;)Lnet/minecraft/world/chunk/WorldChunk;";
@Inject(method = onSetAndCompare, at = @At("HEAD"))
void onSetAndCompare(int i, WorldChunk oldChunk, WorldChunk newChunk, CallbackInfoReturnable<WorldChunk> ci) {
ClientChunkLoadCallback.EVENT.invoker().unloadChunk(oldChunk);
}
}

View File

@@ -0,0 +1,50 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.ClientWorldLoadCallback;
import mods.betterfoliage.Hooks;
import net.minecraft.block.BlockState;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.profiler.Profiler;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.level.LevelInfo;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Random;
@Mixin(ClientWorld.class)
public class MixinClientWorld {
private static final String ctor = "<init>(Lnet/minecraft/client/network/ClientPlayNetworkHandler;Lnet/minecraft/world/level/LevelInfo;Lnet/minecraft/world/dimension/DimensionType;ILnet/minecraft/util/profiler/Profiler;Lnet/minecraft/client/render/WorldRenderer;)V";
private static final String scheduleBlockRender = "scheduleBlockRender(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;)V";
private static final String rendererNotify = "Lnet/minecraft/client/render/WorldRenderer;method_21596(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Lnet/minecraft/block/BlockState;)V";
private static final String worldDisplayTick = "randomBlockDisplayTick(IIIILjava/util/Random;ZLnet/minecraft/util/math/BlockPos$Mutable;)V";
private static final String blockDisplayTick = "Lnet/minecraft/block/Block;randomDisplayTick(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Ljava/util/Random;)V";
/**
* Inject callback to get notified of client-side blockstate changes.
* Used to invalidate caches in the {@link mods.betterfoliage.chunk.ChunkOverlayManager}
*/
@Inject(method = scheduleBlockRender, at = @At(value = "HEAD"))
void onClientBlockChanged(BlockPos pos, BlockState oldState, BlockState newState, CallbackInfo ci) {
Hooks.onClientBlockChanged((ClientWorld) (Object) this, pos, oldState, newState);
}
@Inject(method = ctor, at = @At("RETURN"))
void onClientWorldCreated(ClientPlayNetworkHandler netHandler, LevelInfo levelInfo, DimensionType dimensionType, int i, Profiler profiler, WorldRenderer worldRenderer, CallbackInfo ci) {
ClientWorldLoadCallback.EVENT.invoker().loadWorld((ClientWorld) (Object) this);
}
/**
* Inject a callback to call for every random display tick. Used for adding custom particle effects to blocks.
*/
@Inject(method = worldDisplayTick, at = @At(value = "INVOKE", target = blockDisplayTick))
void onRandomDisplayTick(int xCenter, int yCenter, int zCenter, int radius, Random random, boolean spawnBarrierParticles, BlockPos.Mutable mutable, CallbackInfo ci) {
Hooks.onRandomDisplayTick((ClientWorld) (Object) this, mutable);
}
}

View File

@@ -0,0 +1,28 @@
package mods.betterfoliage.mixin;
import mods.betterfoliage.ModelLoadingCallback;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.util.ModelIdentifier;
import net.minecraft.resource.ResourceManager;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static net.minecraft.client.render.model.ModelLoader.MISSING;
@Mixin(ModelLoader.class)
public class MixinModelLoader {
@Shadow @Final private ResourceManager resourceManager;
@Inject(at = @At("HEAD"), method = "addModel")
private void addModelHook(ModelIdentifier id, CallbackInfo info) {
// use the same trick fabric-api does to get around the no-mixins-in-constructors policy
if (id == MISSING) {
ModelLoadingCallback.EVENT.invoker().beginLoadModels((ModelLoader) (Object) this, resourceManager);
}
}
}

View File

@@ -0,0 +1,88 @@
package mods.betterfoliage
import me.zeroeightsix.fiber.JanksonSettings
import mods.betterfoliage.chunk.ChunkOverlayManager
import mods.betterfoliage.config.BlockConfig
import mods.betterfoliage.config.MainConfig
import mods.betterfoliage.render.block.vanilla.*
import mods.betterfoliage.render.particle.LeafParticleRegistry
import mods.betterfoliage.render.particle.RisingSoulParticle
import mods.betterfoliage.resource.discovery.BakedModelReplacer
import mods.betterfoliage.resource.generated.GeneratedBlockTexturePack
import net.fabricmc.api.ClientModInitializer
import net.fabricmc.fabric.api.resource.ResourceManagerHelper
import net.fabricmc.loader.api.FabricLoader
import net.minecraft.client.MinecraftClient
import net.minecraft.resource.ResourceType
import net.minecraft.util.Identifier
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.simple.SimpleLogger
import org.apache.logging.log4j.util.PropertiesUtil
import java.io.File
import java.io.PrintStream
import java.util.*
object BetterFoliage : ClientModInitializer {
const val MOD_ID = "betterfoliage"
var logger = LogManager.getLogger()
var logDetail = SimpleLogger(
"BetterFoliage",
Level.DEBUG,
false, false, true, false,
"yyyy-MM-dd HH:mm:ss",
null,
PropertiesUtil(Properties()),
PrintStream(File(FabricLoader.getInstance().gameDirectory, "logs/betterfoliage.log").apply {
parentFile.mkdirs()
if (!exists()) createNewFile()
})
)
val configFile get() = File(FabricLoader.getInstance().configDirectory, "BetterFoliage.json")
val config = MainConfig().apply {
if (configFile.exists()) JanksonSettings().deserialize(fiberNode, configFile.inputStream())
else JanksonSettings().serialize(fiberNode, configFile.outputStream(), false)
}
val blockConfig = BlockConfig()
val generatedPack = GeneratedBlockTexturePack(Identifier(MOD_ID, "generated"), "betterfoliage-generated", "Better Foliage", "Generated leaf textures", logDetail)
val modelReplacer = BakedModelReplacer()
override fun onInitializeClient() {
// Register generated resource pack
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(generatedPack)
MinecraftClient.getInstance().resourcePackContainerManager.addCreator(generatedPack.finder)
// Add standard block support
modelReplacer.discoverers.add(StandardLeafDiscovery)
modelReplacer.discoverers.add(StandardGrassDiscovery)
modelReplacer.discoverers.add(StandardLogDiscovery)
modelReplacer.discoverers.add(StandardCactusDiscovery)
modelReplacer.discoverers.add(LilyPadDiscovery)
modelReplacer.discoverers.add(DirtDiscovery)
modelReplacer.discoverers.add(SandDiscovery)
modelReplacer.discoverers.add(MyceliumDiscovery)
modelReplacer.discoverers.add(NetherrackDiscovery)
// Init overlay layers
ChunkOverlayManager.layers.add(RoundLogOverlayLayer)
// Init singletons
LeafParticleRegistry
NormalLeavesModel.Companion
GrassBlockModel.Companion
RoundLogModel.Companion
CactusModel.Companion
LilypadModel.Companion
DirtModel.Companion
SandModel.Companion
MyceliumModel.Companion
NetherrackModel.Companion
RisingSoulParticle.Companion
}
}

View File

@@ -0,0 +1,29 @@
package mods.betterfoliage
// Optifine
//val OptifineClassTransformer = ClassRefOld<Any>("optifine.OptiFineClassTransformer")
//val BlockPosM = ClassRefOld<Any>("net.optifine.BlockPosM")
//object ChunkCacheOF : ClassRefOld<Any>("net.optifine.override.ChunkCacheOF") {
// val chunkCache = FieldRefOld(this, "chunkCache", ChunkRendererRegion)
//}
//object RenderEnv : ClassRefOld<Any>("net.optifine.render.RenderEnv") {
// val reset = MethodRefOld(this, "reset", void, BlockState, BlockPos)
//}
// Optifine custom colors
//val IColorizer = ClassRefOld<Any>("net.optifine.CustomColors\$IColorizer")
//object CustomColors : ClassRefOld<Any>("net.optifine.CustomColors") {
// val getColorMultiplier = MethodRefOld(this, "getColorMultiplier", int, BakedQuad, BlockState, ExtendedBlockView, BlockPos, RenderEnv)
//}
// Optifine shaders
//object SVertexBuilder : ClassRefOld<Any>("net.optifine.shaders.SVertexBuilder") {
// val pushState = MethodRefOld(this, "pushEntity", void, BlockState, BlockPos, ExtendedBlockView, BufferBuilder)
// val pushNum = MethodRefOld(this, "pushEntity", void, long)
// val pop = MethodRefOld(this, "popEntity", void)
//}

View File

@@ -0,0 +1,72 @@
package mods.betterfoliage
import net.fabricmc.fabric.api.event.Event
import net.fabricmc.fabric.api.event.EventFactory
import net.minecraft.client.render.block.BlockModels
import net.minecraft.client.render.model.ModelLoader
import net.minecraft.client.world.ClientWorld
import net.minecraft.resource.ResourceManager
import net.minecraft.world.chunk.WorldChunk
interface ClientChunkLoadCallback {
fun loadChunk(chunk: WorldChunk)
fun unloadChunk(chunk: WorldChunk)
companion object {
@JvmField val EVENT: Event<ClientChunkLoadCallback> = EventFactory.createArrayBacked(ClientChunkLoadCallback::class.java) { listeners ->
object : ClientChunkLoadCallback {
override fun loadChunk(chunk: WorldChunk) { listeners.forEach { it.loadChunk(chunk) } }
override fun unloadChunk(chunk: WorldChunk) { listeners.forEach { it.unloadChunk(chunk) } }
}
}
}
}
interface ClientWorldLoadCallback {
fun loadWorld(world: ClientWorld)
companion object {
@JvmField val EVENT : Event<ClientWorldLoadCallback> = EventFactory.createArrayBacked(ClientWorldLoadCallback::class.java) { listeners ->
object : ClientWorldLoadCallback {
override fun loadWorld(world: ClientWorld) { listeners.forEach { it.loadWorld(world) } }
}
}
}
}
/**
* Event fired after [BlockModels.reload] finishes.
*/
interface BlockModelsReloadCallback {
fun reloadBlockModels(blockModels: BlockModels)
companion object {
@JvmField val EVENT: Event<BlockModelsReloadCallback> = EventFactory.createArrayBacked(BlockModelsReloadCallback::class.java) { listeners ->
object : BlockModelsReloadCallback {
override fun reloadBlockModels(blockModels: BlockModels) {
listeners.forEach { it.reloadBlockModels(blockModels) }
}
}
}
}
}
/**
* Event fired when the [ModelLoader] first starts loading models.
*
* This happens during the constructor, so BEWARE!
* Try to avoid any interaction until the block texture atlas starts stitching.
*/
interface ModelLoadingCallback {
fun beginLoadModels(loader: ModelLoader, manager: ResourceManager)
companion object {
@JvmField val EVENT: Event<ModelLoadingCallback> = EventFactory.createArrayBacked(ModelLoadingCallback::class.java) { listeners ->
object : ModelLoadingCallback {
override fun beginLoadModels(loader: ModelLoader, manager: ResourceManager) {
listeners.forEach { it.beginLoadModels(loader, manager) }
}
}
}
}
}

View File

@@ -0,0 +1,67 @@
@file:JvmName("Hooks")
package mods.betterfoliage
import mods.betterfoliage.chunk.ChunkOverlayManager
import mods.betterfoliage.render.particle.FallingLeafParticle
import mods.betterfoliage.render.particle.RisingSoulParticle
import mods.betterfoliage.render.block.vanilla.LeafKey
import mods.betterfoliage.render.block.vanilla.RoundLogKey
import mods.betterfoliage.util.offset
import mods.betterfoliage.util.plus
import mods.betterfoliage.util.randomD
import net.minecraft.block.BlockRenderLayer
import net.minecraft.block.BlockRenderLayer.CUTOUT
import net.minecraft.block.BlockRenderLayer.CUTOUT_MIPPED
import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.client.MinecraftClient
import net.minecraft.client.world.ClientWorld
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
fun getAmbientOcclusionLightValueOverride(original: Float, state: BlockState): Float {
if (BetterFoliage.config.enabled &&
BetterFoliage.config.roundLogs.enabled &&
BetterFoliage.modelReplacer.getTyped<RoundLogKey>(state) != null
) return BetterFoliage.config.roundLogs.dimming.toFloat()
return original
}
fun getUseNeighborBrightnessOverride(original: Boolean, state: BlockState): Boolean {
return original || (BetterFoliage.config.enabled && BetterFoliage.config.roundLogs.enabled && BetterFoliage.blockConfig.logBlocks.matchesClass(state.block));
}
fun onClientBlockChanged(worldClient: ClientWorld, pos: BlockPos, oldState: BlockState, newState: BlockState) {
ChunkOverlayManager.onBlockChange(worldClient, pos)
}
fun onRandomDisplayTick(world: ClientWorld, pos: BlockPos) {
val state = world.getBlockState(pos)
if (BetterFoliage.config.enabled &&
BetterFoliage.config.risingSoul.enabled &&
state.block == Blocks.SOUL_SAND &&
world.isAir(pos + Direction.UP.offset) &&
Math.random() < BetterFoliage.config.risingSoul.chance) {
RisingSoulParticle(world, pos).addIfValid()
}
if (BetterFoliage.config.enabled &&
BetterFoliage.config.fallingLeaves.enabled &&
world.isAir(pos + Direction.DOWN.offset) &&
randomD() < BetterFoliage.config.fallingLeaves.chance) {
BetterFoliage.modelReplacer.getTyped<LeafKey>(state)?.let { key ->
FallingLeafParticle(world, pos, key).addIfValid()
}
}
}
fun getVoxelShapeOverride(state: BlockState, reader: BlockView, pos: BlockPos, dir: Direction): VoxelShape {
if (BetterFoliage.modelReplacer[state] is RoundLogKey) {
return VoxelShapes.empty()
}
return state.getCullShape(reader, pos, dir)
}

View File

@@ -0,0 +1,55 @@
package mods.betterfoliage.chunk
import mods.betterfoliage.util.Int3
import mods.betterfoliage.util.allDirections
import mods.betterfoliage.util.offset
import mods.betterfoliage.util.plus
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.client.MinecraftClient
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.ExtendedBlockView
import net.minecraft.world.biome.Biome
/**
* Represents the block being rendered. Has properties and methods to query the neighborhood of the block in
* block-relative coordinates.
*/
interface BlockCtx {
val world: ExtendedBlockView
val pos: BlockPos
fun offset(dir: Direction) = offset(dir.offset)
fun offset(offset: Int3): BlockCtx
val state: BlockState get() = world.getBlockState(pos)
fun state(dir: Direction) = world.getBlockState(pos + dir.offset)
fun state(offset: Int3) = world.getBlockState(pos + offset)
val biome: Biome get() = world.getBiome(pos)
val isNormalCube: Boolean get() = state.isSimpleFullBlock(world, pos)
fun shouldSideBeRendered(side: Direction) = Block.shouldDrawSide(state, world, pos, side)
fun isNeighborSolid(dir: Direction) = offset(dir).let { it.state.isSideSolidFullSquare(it.world, it.pos, dir.opposite) }
fun model(dir: Direction) = state(dir).let { MinecraftClient.getInstance().blockRenderManager.getModel(it)!! }
fun model(offset: Int3) = state(offset).let { MinecraftClient.getInstance().blockRenderManager.getModel(it)!! }
}
open class BasicBlockCtx(
override val world: ExtendedBlockView,
override val pos: BlockPos
) : BlockCtx {
override val state = world.getBlockState(pos)
override fun offset(offset: Int3) = BasicBlockCtx(world, pos + offset)
fun cache() = CachedBlockCtx(world, pos)
}
open class CachedBlockCtx(world: ExtendedBlockView, pos: BlockPos) : BasicBlockCtx(world, pos) {
var neighbors = Array<BlockState>(6) { world.getBlockState(pos + allDirections[it].offset) }
override var biome: Biome = world.getBiome(pos)
override fun state(dir: Direction) = neighbors[dir.ordinal]
}

View File

@@ -0,0 +1,50 @@
package mods.betterfoliage.chunk
import net.minecraft.util.math.BlockPos
import net.minecraft.world.BlockView
import net.minecraft.world.ExtendedBlockView
import net.minecraft.world.LightType
/**
* Delegating [IBlockAccess] that fakes a _modified_ location to return values from a _target_ location.
* All other locations are handled normally.
*
* @param[original] the [IBlockAccess] that is delegated to
*/
@Suppress("NOTHING_TO_INLINE", "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS", "HasPlatformType")
open class OffsetBlockView(open val original: BlockView, val modded: BlockPos, val target: BlockPos) : BlockView {
inline fun actualPos(pos: BlockPos) = if (pos != null && pos.x == modded.x && pos.y == modded.y && pos.z == modded.z) target else pos
override fun getBlockState(pos: BlockPos) = original.getBlockState(actualPos(pos))
override fun getBlockEntity(pos: BlockPos) = original.getBlockEntity(actualPos(pos))
override fun getFluidState(pos: BlockPos) = original.getFluidState(actualPos(pos))
}
@Suppress("NOTHING_TO_INLINE", "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS", "HasPlatformType")
class OffsetExtBlockView(val original: ExtendedBlockView, val modded: BlockPos, val target: BlockPos) : ExtendedBlockView by original {
inline fun actualPos(pos: BlockPos) = if (pos != null && pos.x == modded.x && pos.y == modded.y && pos.z == modded.z) target else pos
override fun getBlockState(pos: BlockPos) = original.getBlockState(actualPos(pos))
override fun getBlockEntity(pos: BlockPos) = original.getBlockEntity(actualPos(pos))
override fun getFluidState(pos: BlockPos) = original.getFluidState(actualPos(pos))
override fun getLightLevel(type: LightType, pos: BlockPos) = original.getLightLevel(type, actualPos(pos))
override fun getLightmapIndex(pos: BlockPos, light: Int) = original.getLightmapIndex(actualPos(pos), light)
override fun getBiome(pos: BlockPos) = original.getBiome(actualPos(pos))
}
/**
* Temporarily replaces the [IBlockReader] used by this [BlockContext] and the corresponding [ExtendedRenderBlocks]
* to use an [OffsetEnvBlockReader] while executing this lambda.
*
* @param[modded] the _modified_ location
* @param[target] the _target_ location
* @param[func] the lambda to execute
*/
//inline fun <reified T> BlockContext.withOffset(modded: Int3, target: Int3, func: () -> T): T {
// val original = reader!!
// reader = OffsetEnvBlockReader(original, pos + modded, pos + target)
// val result = func()
// reader = original
// return result
//}

View File

@@ -0,0 +1,124 @@
package mods.betterfoliage.chunk
import mods.betterfoliage.*
import mods.betterfoliage.util.YarnHelper
import mods.betterfoliage.util.get
import net.minecraft.client.render.chunk.ChunkRendererRegion
import net.minecraft.client.world.ClientWorld
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.ChunkPos
import net.minecraft.world.ExtendedBlockView
import net.minecraft.world.ViewableWorld
import net.minecraft.world.World
import net.minecraft.world.chunk.WorldChunk
import net.minecraft.world.dimension.DimensionType
import java.util.*
import kotlin.collections.List
import kotlin.collections.MutableMap
import kotlin.collections.associateWith
import kotlin.collections.forEach
import kotlin.collections.mutableListOf
import kotlin.collections.mutableMapOf
import kotlin.collections.set
// net.minecraft.world.chunk.WorldChunk.world
val WorldChunk_world = YarnHelper.requiredField<World>("net.minecraft.class_2818", "field_12858", "Lnet/minecraft/class_1937;")
// net.minecraft.client.render.chunk.ChunkRendererRegion.world
val ChunkRendererRegion_world = YarnHelper.requiredField<World>("net.minecraft.class_853", "field_4490", "Lnet/minecraft/class_1937;")
val ExtendedBlockView.dimType: DimensionType get() = when {
this is ViewableWorld -> dimension.type
this is ChunkRendererRegion -> this[ChunkRendererRegion_world]!!.dimension.type
// this.isInstance(ChunkCacheOF) -> this[ChunkCacheOF.chunkCache]!!.dimType
else -> throw IllegalArgumentException("DimensionType of world with class ${this::class.qualifiedName} cannot be determined!")
}
/**
* Represents some form of arbitrary non-persistent data that can be calculated and cached for each block position
*/
interface ChunkOverlayLayer<T> {
fun calculate(ctx: BlockCtx): T
fun onBlockUpdate(world: ExtendedBlockView, pos: BlockPos)
}
/**
* Query, lazy calculation and lifecycle management of multiple layers of chunk overlay data.
*/
object ChunkOverlayManager : ClientChunkLoadCallback, ClientWorldLoadCallback {
init {
ClientWorldLoadCallback.EVENT.register(this)
ClientChunkLoadCallback.EVENT.register(this)
}
val chunkData = IdentityHashMap<DimensionType, MutableMap<ChunkPos, ChunkOverlayData>>()
val layers = mutableListOf<ChunkOverlayLayer<*>>()
/**
* Get the overlay data for a given layer and position
*
* @param layer Overlay layer to query
* @param reader World to use if calculation of overlay value is necessary
* @param pos Block position
*/
fun <T> get(layer: ChunkOverlayLayer<T>, ctx: BlockCtx): T? {
val data = chunkData[ctx.world.dimType]?.get(ChunkPos(ctx.pos)) ?: return null
data.get(layer, ctx.pos).let { value ->
if (value !== ChunkOverlayData.UNCALCULATED) return value
val newValue = layer.calculate(ctx)
data.set(layer, ctx.pos, newValue)
return newValue
}
}
/**
* Clear the overlay data for a given layer and position
*
* @param layer Overlay layer to clear
* @param pos Block position
*/
fun <T> clear(dimension: DimensionType, layer: ChunkOverlayLayer<T>, pos: BlockPos) {
chunkData[dimension]?.get(ChunkPos(pos))?.clear(layer, pos)
}
fun onBlockChange(world: ClientWorld, pos: BlockPos) {
if (chunkData[world.dimType]?.containsKey(ChunkPos(pos)) == true) {
layers.forEach { layer -> layer.onBlockUpdate(world, pos) }
}
}
override fun loadChunk(chunk: WorldChunk) {
chunk[WorldChunk_world]!!.dimType.let { dim ->
val data = chunkData[dim] ?: mutableMapOf<ChunkPos, ChunkOverlayData>().apply { chunkData[dim] = this }
data.let { chunks ->
// check for existence first because Optifine fires a TON of these
if (chunk.pos !in chunks.keys) chunks[chunk.pos] = ChunkOverlayData(layers)
}
}
}
override fun unloadChunk(chunk: WorldChunk) {
chunk[WorldChunk_world]!!.dimType.let { dim ->
chunkData[dim]?.remove(chunk.pos)
}
}
override fun loadWorld(world: ClientWorld) {
val dim = world.dimType
// chunkData.keys.forEach { if (it == dim) chunkData[dim] = mutableMapOf() else chunkData.remove(dim)}
}
}
class ChunkOverlayData(layers: List<ChunkOverlayLayer<*>>) {
val BlockPos.isValid: Boolean get() = y in validYRange
val rawData = layers.associateWith { emptyOverlay() }
fun <T> get(layer: ChunkOverlayLayer<T>, pos: BlockPos): T? = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.get(pos.y) as T? else null
fun <T> set(layer: ChunkOverlayLayer<T>, pos: BlockPos, data: T) = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.set(pos.y, data) else null
fun <T> clear(layer: ChunkOverlayLayer<T>, pos: BlockPos) = if (pos.isValid) rawData[layer]?.get(pos.x and 15)?.get(pos.z and 15)?.set(pos.y, UNCALCULATED) else null
companion object {
val UNCALCULATED = object {}
fun emptyOverlay() = Array(16) { Array(16) { Array<Any?>(256) { UNCALCULATED }}}
val validYRange = 0 until 256
}
}

View File

@@ -0,0 +1,36 @@
package mods.betterfoliage.config
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.util.Invalidator
import mods.betterfoliage.resource.discovery.ConfigurableBlockMatcher
import mods.betterfoliage.resource.discovery.ModelTextureListConfiguration
import net.minecraft.resource.ResourceManager
import net.minecraft.util.Identifier
class BlockConfig {
private val list = mutableListOf<Any>()
val leafBlocks = blocks("leaves_blocks_default.cfg")
val leafModels = models("leaves_models_default.cfg")
val grassBlocks = blocks("grass_blocks_default.cfg")
val grassModels = models("grass_models_default.cfg")
val mycelium = blocks("mycelium_blocks_default.cfg")
// val dirt = blocks("dirt_default.cfg")
val crops = blocks("crop_default.cfg")
val logBlocks = blocks("log_blocks_default.cfg")
val logModels = models("log_models_default.cfg")
val sand = blocks("sand_default.cfg")
val lilypad = blocks("lilypad_default.cfg")
val cactus = blocks("cactus_default.cfg")
val netherrack = blocks("netherrack_blocks_default.cfg")
private fun blocks(cfgName: String) = ConfigurableBlockMatcher(BetterFoliage.logDetail, Identifier(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) }
private fun models(cfgName: String) = ModelTextureListConfiguration(BetterFoliage.logDetail, Identifier(BetterFoliage.MOD_ID, cfgName)).apply { list.add(this) }
fun reloadConfig(manager: ResourceManager) {
list.forEach { when(it) {
is ConfigurableBlockMatcher -> it.readDefaults(manager)
is ModelTextureListConfiguration -> it.readDefaults(manager)
} }
}
}

View File

@@ -0,0 +1,137 @@
package mods.betterfoliage.config
import me.shedaniel.clothconfig2.api.AbstractConfigListEntry
import me.shedaniel.clothconfig2.api.ConfigEntryBuilder
import me.shedaniel.clothconfig2.gui.entries.SubCategoryListEntry
import me.zeroeightsix.fiber.builder.ConfigValueBuilder
import me.zeroeightsix.fiber.tree.ConfigLeaf
import me.zeroeightsix.fiber.tree.ConfigNode
import me.zeroeightsix.fiber.tree.ConfigValue
import net.minecraft.client.resource.language.I18n
import java.util.*
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
const val MAX_LINE_LEN = 30
sealed class DelegatingConfigNode<N: ConfigLeaf>(val fiberNode: N) {
abstract fun createClothNode(names: List<String>): AbstractConfigListEntry<*>
}
abstract class DelegatingConfigValue<T>(fiberNode: ConfigValue<T>) : DelegatingConfigNode<ConfigValue<T>>(fiberNode), ReadOnlyProperty<DelegatingConfigGroup, T>
open class DelegatingConfigGroup(fiberNode: ConfigNode) : DelegatingConfigNode<ConfigNode>(fiberNode) {
val children = mutableListOf<DelegatingConfigNode<*>>()
override fun createClothNode(names: List<String>): SubCategoryListEntry {
val builder = ConfigEntryBuilder.create()
.startSubCategory(names.joinToString(".").translate())
.setTooltip(*names.joinToString(".").translateTooltip())
.setExpended(false)
children.forEach { builder.add(it.createClothNode(names + it.fiberNode.name!!)) }
return builder.build()
}
operator fun get(name: String) = children.find { it.fiberNode.name == name }
}
interface DelegatingConfigGroupFactory<T> {
operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T>
}
fun <T: DelegatingConfigGroup> subNode(factory: (ConfigNode)->T) = object : DelegatingConfigGroupFactory<T> {
override operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T> {
val childNode = ConfigNode(property.name, null)
val configGroup = factory(childNode)
parent.fiberNode.items.add(childNode)
parent.children.add(configGroup)
return object : ReadOnlyProperty<DelegatingConfigGroup, T> {
override fun getValue(thisRef: DelegatingConfigGroup, property: KProperty<*>) = configGroup
}
}
}
interface DelegatingConfigValueFactory<T> {
fun createFiberNode(parent: ConfigNode, name: String): ConfigValue<T>
fun createClothNode(node: ConfigValue<T>, names: List<String>): AbstractConfigListEntry<T>
operator fun provideDelegate(parent: DelegatingConfigGroup, property: KProperty<*>): ReadOnlyProperty<DelegatingConfigGroup, T> {
return object : DelegatingConfigValue<T>(createFiberNode(parent.fiberNode, property.name)) {
override fun createClothNode(names: List<String>) = createClothNode(fiberNode, names)
override fun getValue(thisRef: DelegatingConfigGroup, property: KProperty<*>) = fiberNode.value!!
}.apply { parent.children.add(this) }
}
}
fun String.translate() = I18n.translate(this)
fun String.translateTooltip(lineLength: Int = MAX_LINE_LEN) = ("$this.tooltip").translate().let { tooltip ->
tooltip.splitToSequence(" ").fold(mutableListOf("")) { tooltips, word ->
if (tooltips.last().length + word.length < lineLength) {
tooltips[tooltips.lastIndex] += "$word "
} else {
tooltips.add("$word ")
}
tooltips
}.map { it.trim() }.toTypedArray()
}
fun boolean(
default: Boolean,
langKey: (List<String>)->String = { it.joinToString(".") },
valueOverride: (Boolean)->Boolean = { it }
) = object : DelegatingConfigValueFactory<Boolean> {
override fun createFiberNode(parent: ConfigNode, name: String) = ConfigValueBuilder(Boolean::class.java)
.withName(name)
.withParent(parent)
.withDefaultValue(default)
.build()
override fun createClothNode(node: ConfigValue<Boolean>, names: List<String>) = ConfigEntryBuilder.create()
.startBooleanToggle(langKey(names).translate(), node.value!!)
.setTooltip(langKey(names).let { if (I18n.hasTranslation("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
.setSaveConsumer { node.value = valueOverride(it) }
.build()
}
fun integer(
default: Int, min: Int, max: Int,
langKey: (List<String>)->String = { it.joinToString(".") },
valueOverride: (Int)->Int = { it }
) = object : DelegatingConfigValueFactory<Int> {
override fun createFiberNode(parent: ConfigNode, name: String) = ConfigValueBuilder(Int::class.java)
.withName(name)
.withParent(parent)
.withDefaultValue(default)
.constraints().minNumerical(min).maxNumerical(max).finish()
.build()
override fun createClothNode(node: ConfigValue<Int>, names: List<String>) = ConfigEntryBuilder.create()
.startIntField(langKey(names).translate(), node.value!!)
.setTooltip(langKey(names).let { if (I18n.hasTranslation("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
.setMin(min).setMax(max)
.setSaveConsumer { node.value = valueOverride(it) }
.build()
}
fun double(
default: Double, min: Double, max: Double,
langKey: (List<String>)->String = { it.joinToString(".") },
valueOverride: (Double)->Double = { it }
) = object : DelegatingConfigValueFactory<Double> {
override fun createFiberNode(parent: ConfigNode, name: String) = ConfigValueBuilder(Double::class.java)
.withName(name)
.withParent(parent)
.withDefaultValue(default)
.constraints().minNumerical(min).maxNumerical(max).finish()
.build()
override fun createClothNode(node: ConfigValue<Double>, names: List<String>) = ConfigEntryBuilder.create()
.startDoubleField(langKey(names).translate(), node.value!!)
.setTooltip(langKey(names).let { if (I18n.hasTranslation("$it.tooltip")) Optional.of(it.translateTooltip()) else Optional.empty() })
.setMin(min).setMax(max)
.setSaveConsumer { node.value = valueOverride(it) }
.build()
}
val recurring = { names: List<String> -> "${names.first()}.${names.last()}" }
fun fakeCategory(name: String) = { names: List<String> ->
(listOf(names.first(), name) + names.drop(1)).joinToString(".")
}

View File

@@ -0,0 +1,154 @@
package mods.betterfoliage.config
import me.zeroeightsix.fiber.tree.ConfigNode
import java.util.*
interface PopulationConfigData {
val enabled: Boolean
val population: Int
fun enabled(random: Random) = random.nextInt(64) < population && enabled
}
fun population(default: Int) = integer(default, min = 0, max = 64, langKey = recurring)
class MainConfig : DelegatingConfigGroup(ConfigNode("root", null)) {
val enabled by boolean(true, langKey = fakeCategory("global"))
val nVidia by boolean(true, langKey = fakeCategory("global"))
val leaves by subNode { LeavesConfig(it) }
val shortGrass by subNode { ShortGrassConfig(it) }
val connectedGrass by subNode { ConnectedGrassConfig(it) }
val roundLogs by subNode { RoundLogConfig(it) }
val cactus by subNode { CactusConfig(it) }
val lilypad by subNode { LilypadConfig(it) }
val reed by subNode { ReedConfig(it) }
val algae by subNode { AlgaeConfig(it) }
val coral by subNode { CoralConfig(it) }
val netherrack by subNode { NetherrackConfig(it) }
val fallingLeaves by subNode { FallingLeavesConfig(it) }
val risingSoul by subNode { RisingSoulConfig(it) }
}
class LeavesConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
val enabled by boolean(true, langKey = recurring)
val snowEnabled by boolean(true)
val dense by boolean(false)
val hideInternal by boolean(true)
val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring)
val vOffset by double(0.1, min = 0.0, max = 0.4, langKey = recurring)
val size by double(1.4, min = 0.75, max = 2.5, langKey = recurring)
}
class ShortGrassConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {
override val enabled by boolean(true, langKey = recurring)
val myceliumEnabled by boolean(true)
val snowEnabled by boolean(true)
val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring)
val heightMin by double(0.6, min = 0.1, max = 2.5, langKey = recurring)
val heightMax by double(0.6, min = 0.1, max = 2.5, langKey = recurring) { it.coerceAtLeast(heightMin) }
val size by double(1.0, min = 0.5, max = 1.5, langKey = recurring)
override val population by population(64)
val useGenerated by boolean(false)
val shaderWind by boolean(true, langKey = recurring)
val saturationThreshold by double(0.1, min = 0.0, max = 1.0)
}
class ConnectedGrassConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
val enabled by boolean(true, langKey = recurring)
val snowEnabled by boolean(true)
}
class RoundLogConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
val enabled by boolean(true, langKey = recurring)
val defaultY by boolean(false)
val connectSolids by boolean(false)
val lenientConnect by boolean(true)
val connectPerpendicular by boolean(true)
val connectGrass by boolean(true)
val radiusSmall by double(0.25, min = 0.0, max = 0.5)
val radiusLarge by double(0.25, min = 0.0, max = 0.5) { it.coerceAtLeast(radiusSmall) }
val dimming by double(0.7, min = 0.0, max = 1.0)
val zProtection by double(0.99, min = 0.9, max = 1.0)
}
class CactusConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
val enabled by boolean(true, langKey = recurring)
val size by double(1.3, min = 0.5, max = 1.5, langKey = recurring)
val sizeVariation by double(0.1, min = 0.0, max = 0.5)
val hOffset by double(0.1, min = 0.0, max = 0.5, langKey = recurring)
}
class LilypadConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {
override val enabled by boolean(true, langKey = recurring)
val hOffset by double(0.1, min = 0.0, max = 0.25, langKey = recurring)
override val population by population(16)
}
class ReedConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {
override val enabled by boolean(true, langKey = recurring)
val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring)
val heightMin by double(1.7, min = 1.5, max = 3.0, langKey = recurring)
val heightMax by double(2.2, min = 1.5, max = 3.0, langKey = recurring) { it.coerceAtLeast(heightMin) }
override val population by population(32)
val minBiomeTemp by double(0.4, min = 0.0, max = 2.0)
val minBiomeRainfall by double(0.4, min = 0.0, max = 1.0)
val shaderWind by boolean(true, langKey = recurring)
}
class AlgaeConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {
override val enabled by boolean(true, langKey = recurring)
val hOffset by double(0.1, min = 0.0, max = 0.4, langKey = recurring)
val size by double(1.0, min = 0.5, max = 1.5, langKey = recurring)
val heightMin by double(0.5, min = 0.1, max = 1.0, langKey = recurring)
val heightMax by double(0.5, min = 0.1, max = 1.0, langKey = recurring) { it.coerceAtLeast(heightMin) }
override val population by population(48)
val shaderWind by boolean(true, langKey = recurring)
}
class CoralConfig(node: ConfigNode) : DelegatingConfigGroup(node), PopulationConfigData {
override val enabled by boolean(true, langKey = recurring)
val shallowWater by boolean(false)
val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring)
val vOffset by double(0.1, min = 0.0, max = 0.4, langKey = recurring)
val size by double(0.7, min = 0.5, max = 1.5, langKey = recurring)
val crustSize by double(1.4, min = 0.5, max = 1.5)
val chance by integer(32, min = 0, max = 64)
override val population by population(48)
}
class NetherrackConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
val enabled by boolean(true, langKey = recurring)
val hOffset by double(0.2, min = 0.0, max = 0.4, langKey = recurring)
val size by double(1.0, min = 0.5, max = 1.5, langKey = recurring)
val heightMin by double(0.6, min = 0.5, max = 1.5, langKey = recurring)
val heightMax by double(0.8, min = 0.5, max = 1.5, langKey = recurring) { it.coerceAtLeast(heightMin) }
}
class FallingLeavesConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
val enabled by boolean(true, langKey = recurring)
val speed by double(0.05, min = 0.01, max = 0.15)
val windStrength by double(0.5, min = 0.1, max = 2.0)
val stormStrength by double(0.8, min = 0.1, max = 2.0) { it.coerceAtLeast(windStrength) }
val size by double(0.75, min = 0.25, max = 1.5)
val chance by double(0.05, min = 0.001, max = 1.0)
val perturb by double(0.25, min = 0.01, max = 1.0)
val lifetime by double(7.5, min = 1.0, max = 15.0)
}
class RisingSoulConfig(node: ConfigNode) : DelegatingConfigGroup(node) {
val enabled by boolean(true, langKey = recurring)
val chance by double(0.02, min = 0.001, max = 1.0)
val perturb by double(0.05, min = 0.01, max = 0.25)
val headSize by double(1.0, min = 0.25, max = 1.5)
val trailSize by double(0.75, min = 0.25, max = 1.5)
val opacity by double(0.5, min = 0.05, max = 1.0)
val sizeDecay by double(0.97, min = 0.5, max = 1.0)
val opacityDecay by double(0.97, min = 0.5, max = 1.0)
val lifetime by double(4.0, min = 1.0, max = 15.0)
val trailLength by integer(48, min = 2, max = 128)
val trailDensity by integer(3, min = 1, max = 16)
}

View File

@@ -0,0 +1,110 @@
package mods.betterfoliage.integration
/*
val TextureLeaves = ClassRefOld<Any>("forestry.arboriculture.models.TextureLeaves")
val TextureLeaves_leafTextures = FieldRefOld(TextureLeaves, "leafTextures", Map)
val TextureLeaves_plain = FieldRefOld(TextureLeaves, "plain", Identifier)
val TextureLeaves_fancy = FieldRefOld(TextureLeaves, "fancy", Identifier)
val TextureLeaves_pollinatedPlain = FieldRefOld(TextureLeaves, "pollinatedPlain", Identifier)
val TextureLeaves_pollinatedFancy = FieldRefOld(TextureLeaves, "pollinatedFancy", Identifier)
val TileLeaves = ClassRefOld<Any>("forestry.arboriculture.tiles.TileLeaves")
val TileLeaves_getLeaveSprite = MethodRefOld(TileLeaves, "getLeaveSprite", Identifier, boolean)
val PropertyWoodType = ClassRefOld<Any>("forestry.arboriculture.blocks.PropertyWoodType")
val IWoodType = ClassRefOld<Any>("forestry.api.arboriculture.IWoodType")
val IWoodType_barkTex = MethodRefOld(IWoodType, "getBarkTexture", String)
val IWoodType_heartTex = MethodRefOld(IWoodType, "getHeartTexture", String)
val PropertyTreeType = ClassRefOld<Any>("forestry.arboriculture.blocks.PropertyTreeType")
val IAlleleTreeSpecies = ClassRefOld<Any>("forestry.api.arboriculture.IAlleleTreeSpecies")
val ILeafSpriteProvider = ClassRefOld<Any>("forestry.api.arboriculture.ILeafSpriteProvider")
val TreeDefinition = ClassRefOld<Any>("forestry.arboriculture.genetics.TreeDefinition")
val IAlleleTreeSpecies_getLeafSpriteProvider = MethodRefOld(IAlleleTreeSpecies, "getLeafSpriteProvider", ILeafSpriteProvider)
val TreeDefinition_species = FieldRefOld(TreeDefinition, "species", IAlleleTreeSpecies)
val ILeafSpriteProvider_getSprite = MethodRefOld(ILeafSpriteProvider, "getSprite", Identifier, boolean, boolean)
object ForestryIntegration {
init {
}
}
*/
/*
object ForestryLeafDiscovery : HasLogger, AsyncSpriteProvider<ModelLoader>, ModelRenderRegistry<LeafInfo> {
override val logger = BetterFoliage.logDetail
var idToValue = emptyMap<Identifier, LeafInfo>()
override fun get(state: BlockState, world: BlockView, pos: BlockPos): LeafInfo? {
// check variant property (used in decorative leaves)
state.entries.entries.find {
PropertyTreeType.isInstance(it.key) && TreeDefinition.isInstance(it.value)
} ?.let {
val species = it.value[TreeDefinition_species]!!
val spriteProvider = species[IAlleleTreeSpecies_getLeafSpriteProvider]()
val textureLoc = spriteProvider[ILeafSpriteProvider_getSprite](false, MinecraftClient.isFancyGraphicsEnabled())
return idToValue[textureLoc]
}
// extract leaf texture information from TileEntity
val tile = world.getBlockEntity(pos) ?: return null
if (!TileLeaves.isInstance(tile)) return null
val textureLoc = tile[TileLeaves_getLeaveSprite](MinecraftClient.isFancyGraphicsEnabled())
return idToValue[textureLoc]
}
override fun setup(manager: ResourceManager, bakeryF: CompletableFuture<ModelLoader>, atlasFuture: AtlasFuture): StitchPhases {
val futures = mutableMapOf<Identifier, CompletableFuture<LeafInfo>>()
return StitchPhases(
discovery = bakeryF.thenRunAsync {
val allLeaves = TextureLeaves_leafTextures.getStatic()
allLeaves!!.entries.forEach { (type, leaves) ->
log("base leaf type $type")
leaves!!
listOf(
leaves[TextureLeaves_plain], leaves[TextureLeaves_pollinatedPlain],
leaves[TextureLeaves_fancy], leaves[TextureLeaves_pollinatedFancy]
).forEach { textureLocation ->
futures[textureLocation!!] = defaultRegisterLeaf(textureLocation, atlasFuture)
}
}
},
cleanup = atlasFuture.runAfter {
idToValue = futures.mapValues { it.value.get() }
}
)
}
}
object ForestryLogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
override val logger = BetterFoliage.logDetail
override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
// respect class list to avoid triggering on fences, stairs, etc.
if (!BetterFoliageMod.blockConfig.logBlocks.matchesClass(ctx.state.block)) return null
// find wood type property
val woodType = ctx.state.entries.entries.find {
PropertyWoodType.isInstance(it.key) && IWoodType.isInstance(it.value)
}
if (woodType != null) {
logger.log(Level.DEBUG, "ForestryLogRegistry: block state ${ctx.state}")
logger.log(Level.DEBUG, "ForestryLogRegistry: variant ${woodType.value}")
// get texture names for wood type
val bark = woodType.value[IWoodType_barkTex]()
val heart = woodType.value[IWoodType_heartTex]()
logger.log(Level.DEBUG, "ForestryLogSupport: textures [heart=$heart, bark=$bark]")
val heartSprite = atlas.sprite(heart)
val barkSprite = atlas.sprite(bark)
return atlas.mapAfter {
SimpleColumnInfo(AsyncLogDiscovery.getAxis(ctx.state), heartSprite.get(), heartSprite.get(), listOf(barkSprite.get()))
}
}
return null
}
}
*/

View File

@@ -0,0 +1,29 @@
package mods.betterfoliage.integration
import io.github.prospector.modmenu.api.ModMenuApi
import me.shedaniel.clothconfig2.api.ConfigBuilder
import me.zeroeightsix.fiber.JanksonSettings
import mods.betterfoliage.BetterFoliage
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.resource.language.I18n
import java.util.function.Function
object ModMenu : ModMenuApi {
override fun getModId() = BetterFoliage.MOD_ID
override fun getConfigScreenFactory() = Function { screen: Screen ->
val builder = ConfigBuilder.create()
.setParentScreen(screen)
.setTitle(I18n.translate("betterfoliage.title"))
BetterFoliage.config.createClothNode(listOf("betterfoliage")).value.forEach { rootOption ->
builder.getOrCreateCategory("main").addEntry(rootOption)
}
builder.savingRunnable = Runnable {
JanksonSettings().serialize(BetterFoliage.config.fiberNode, BetterFoliage.configFile.outputStream(), false)
BetterFoliage.modelReplacer.invalidate()
MinecraftClient.getInstance().worldRenderer.reload()
}
builder.build()
}
}

View File

@@ -0,0 +1,139 @@
package mods.betterfoliage.integration
object IC2RubberIntegration {
// val BlockRubWood = ClassRefOld<Any>("ic2.core.block.BlockRubWood")
init {
// if (ModList.get().isLoaded("ic2") && allAvailable(BlockRubWood)) {
// BetterFoliage.log(Level.INFO, "IC2 rubber support initialized")
// LogRegistry.registries.add(IC2LogDiscovery)
// BetterFoliage.blockSprites.providers.add(IC2LogDiscovery)
// }
}
}
object TechRebornRubberIntegration {
// val BlockRubberLog = ClassRefOld<Any>("techreborn.blocks.BlockRubberLog")
init {
// if (ModList.get().isLoaded("techreborn") && allAvailable(BlockRubberLog)) {
// BetterFoliage.log(Level.INFO, "TechReborn rubber support initialized")
// LogRegistry.registries.add(TechRebornLogDiscovery)
// BetterFoliage.blockSprites.providers.add(TechRebornLogDiscovery)
// }
}
}
/*
class RubberLogInfo(
axis: Axis?,
val spotDir: Direction,
topTexture: Sprite,
bottomTexture: Sprite,
val spotTexture: Sprite,
sideTextures: List<Sprite>
) : SimpleColumnInfo(axis, topTexture, bottomTexture, sideTextures) {
override val side: QuadIconResolver = { ctx: CombinedContext, idx: Int, quad: Quad ->
val worldFace = (if ((idx and 1) == 0) SOUTH else EAST).rotate(ctx.modelRotation)
if (worldFace == spotDir) spotTexture else {
val sideIdx = if (this.sideTextures.size > 1) (ctx.semiRandom(1) + dirToIdx[worldFace.ordinal]) % this.sideTextures.size else 0
this.sideTextures[sideIdx]
}
}
}
object IC2LogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
override val logger = BetterFoliage.logDetail
override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
// check for proper block class, existence of ModelBlock, and "state" blockstate property
if (!IC2RubberIntegration.BlockRubWood.isInstance(ctx.state.block)) return null
val blockLoc = ctx.models.firstOrNull() as Pair<JsonUnbakedModel, Identifier> ?: return null
val type = ctx.state.entries.entries.find { it.key.getName() == "state" }?.value?.toString() ?: return null
// logs with no rubber spot
if (blockLoc.derivesFrom(Identifier("block/cube_column"))) {
val axis = when(type) {
"plain_y" -> Axis.Y
"plain_x" -> Axis.X
"plain_z" -> Axis.Z
else -> null
}
val textureNames = listOf("end", "side").map { blockLoc.first.resolveTexture(it) }
if (textureNames.any { it == "missingno" }) return null
log("IC2LogSupport: block state ${ctx.state.toString()}")
log("IC2LogSupport: axis=$axis, end=${textureNames[0]}, side=${textureNames[1]}")
val endSprite = atlas.sprite(textureNames[0])
val sideSprite = atlas.sprite(textureNames[1])
return atlas.mapAfter {
SimpleColumnInfo(axis, endSprite.get(), endSprite.get(), listOf(sideSprite.get()))
}
}
// logs with rubber spot
val spotDir = when(type) {
"dry_north", "wet_north" -> NORTH
"dry_south", "wet_south" -> SOUTH
"dry_west", "wet_west" -> WEST
"dry_east", "wet_east" -> EAST
else -> null
}
val textureNames = listOf("up", "down", "north", "south").map { blockLoc.first.resolveTexture(it) }
if (textureNames.any { it == "missingno" }) return null
log("IC2LogSupport: block state ${ctx.state.toString()}")
log("IC2LogSupport: spotDir=$spotDir, up=${textureNames[0]}, down=${textureNames[1]}, side=${textureNames[2]}, spot=${textureNames[3]}")
val upSprite = atlas.sprite(textureNames[0])
val downSprite = atlas.sprite(textureNames[1])
val sideSprite = atlas.sprite(textureNames[2])
val spotSprite = atlas.sprite(textureNames[3])
return if (spotDir != null) atlas.mapAfter {
RubberLogInfo(Axis.Y, spotDir, upSprite.get(), downSprite.get(), spotSprite.get(), listOf(sideSprite.get()))
} else atlas.mapAfter {
SimpleColumnInfo(Axis.Y, upSprite.get(), downSprite.get(), listOf(sideSprite.get()))
}
}
}
object TechRebornLogDiscovery : ModelDiscovery<ColumnTextureInfo>() {
override val logger = BetterFoliage.logDetail
override fun processModel(ctx: ModelDiscoveryContext, atlas: AtlasFuture): CompletableFuture<ColumnTextureInfo>? {
// check for proper block class, existence of ModelBlock
if (!TechRebornRubberIntegration.BlockRubberLog.isInstance(ctx.state.block)) return null
val blockLoc = ctx.models.map { it as? Pair<JsonUnbakedModel, Identifier> }.firstOrNull() ?: return null
val hasSap = ctx.state.entries.entries.find { it.key.getName() == "hassap" }?.value as? Boolean ?: return null
val sapSide = ctx.state.entries.entries.find { it.key.getName() == "sapside" }?.value as? Direction ?: return null
log("$logName: block state ${ctx.state}")
if (hasSap) {
val textureNames = listOf("end", "side", "sapside").map { blockLoc.first.resolveTexture(it) }
log("$logName: spotDir=$sapSide, end=${textureNames[0]}, side=${textureNames[2]}, spot=${textureNames[3]}")
if (textureNames.all { it != "missingno" }) {
val endSprite = atlas.sprite(textureNames[0])
val sideSprite = atlas.sprite(textureNames[1])
val sapSprite = atlas.sprite(textureNames[2])
return atlas.mapAfter {
RubberLogInfo(Axis.Y, sapSide, endSprite.get(), endSprite.get(), sapSprite.get(), listOf(sideSprite.get()))
}
}
} else {
val textureNames = listOf("end", "side").map { blockLoc.first.resolveTexture(it) }
log("$logName: end=${textureNames[0]}, side=${textureNames[1]}")
if (textureNames.all { it != "missingno" }) {
val endSprite = atlas.sprite(textureNames[0])
val sideSprite = atlas.sprite(textureNames[1])
return atlas.mapAfter {
SimpleColumnInfo(Axis.Y, endSprite.get(), endSprite.get(), listOf(sideSprite.get()))
}
}
}
return null
}
}
*/

View File

@@ -0,0 +1,130 @@
package mods.betterfoliage.render
import mods.betterfoliage.util.Double3
import net.minecraft.client.MinecraftClient
import net.minecraft.client.particle.ParticleTextureSheet
import net.minecraft.client.particle.SpriteBillboardParticle
import net.minecraft.client.render.BufferBuilder
import net.minecraft.client.render.Camera
import net.minecraft.client.texture.Sprite
import net.minecraft.world.World
import kotlin.math.cos
import kotlin.math.sin
abstract class AbstractParticle(world: World, x: Double, y: Double, z: Double) : SpriteBillboardParticle(world, x, y, z) {
companion object {
// @JvmStatic val sin = Array(64) { idx -> Math.sin(PI2 / 64.0 * idx) }
// @JvmStatic val cos = Array(64) { idx -> Math.cos(PI2 / 64.0 * idx) }
}
val billboardRot = Pair(Double3.zero, Double3.zero)
val currentPos = Double3.zero
val prevPos = Double3.zero
val velocity = Double3.zero
override fun tick() {
super.tick()
currentPos.setTo(x, y, z)
prevPos.setTo(prevPosX, prevPosY, prevPosZ)
velocity.setTo(velocityX, velocityY, velocityZ)
update()
x = currentPos.x; y = currentPos.y; z = currentPos.z;
velocityX = velocity.x; velocityY = velocity.y; velocityZ = velocity.z;
}
/** Render the particle. */
abstract fun render(worldRenderer: BufferBuilder, partialTickTime: Float)
/** Update particle on world tick. */
abstract fun update()
/** True if the particle is renderable. */
abstract val isValid: Boolean
/** Add the particle to the effect renderer if it is valid. */
fun addIfValid() { if (isValid) MinecraftClient.getInstance().particleManager.addParticle(this) }
override fun buildGeometry(buffer: BufferBuilder, camera: Camera, tickDelta: Float, rotX: Float, rotZ: Float, rotYZ: Float, rotXY: Float, rotXZ: Float) {
billboardRot.first.setTo(rotX + rotXY, rotZ, rotYZ + rotXZ)
billboardRot.second.setTo(rotX - rotXY, -rotZ, rotYZ - rotXZ)
render(buffer, tickDelta)
}
/**
* Render a particle quad.
*
* @param[tessellator] the [Tessellator] instance to use
* @param[partialTickTime] partial tick time
* @param[currentPos] render position
* @param[prevPos] previous tick position for interpolation
* @param[size] particle size
* @param[rotation] viewpoint-dependent particle rotation (64 steps)
* @param[sprite] particle texture
* @param[isMirrored] mirror particle texture along V-axis
* @param[alpha] aplha blending
*/
fun renderParticleQuad(worldRenderer: BufferBuilder,
partialTickTime: Float,
currentPos: Double3 = this.currentPos,
prevPos: Double3 = this.prevPos,
size: Double = scale.toDouble(),
rotation: Double = 0.0,
sprite: Sprite = this.sprite,
isMirrored: Boolean = false,
alpha: Float = this.colorAlpha) {
val minU = (if (isMirrored) sprite.minU else sprite.maxU).toDouble()
val maxU = (if (isMirrored) sprite.maxU else sprite.minU).toDouble()
val minV = sprite.minV.toDouble()
val maxV = sprite.maxV.toDouble()
val center = currentPos.copy().sub(prevPos).mul(partialTickTime.toDouble()).add(prevPos).sub(cameraX, cameraY, cameraZ)
val cosRotation = cos(rotation); val sinRotation = sin(rotation)
val v1 = Double3.weight(billboardRot.first, cosRotation * size, billboardRot.second, sinRotation * size)
val v2 = Double3.weight(billboardRot.first, -sinRotation * size, billboardRot.second, cosRotation * size)
val renderBrightness = this.getColorMultiplier(partialTickTime)
val brHigh = renderBrightness shr 16 and 65535
val brLow = renderBrightness and 65535
worldRenderer
.vertex(center.x - v1.x, center.y - v1.y, center.z - v1.z)
.texture(maxU, maxV)
.color(colorRed, colorGreen, colorBlue, alpha)
.texture(brHigh, brLow)
.next()
worldRenderer
.vertex(center.x - v2.x, center.y - v2.y, center.z - v2.z)
.texture(maxU, minV)
.color(colorRed, colorGreen, colorBlue, alpha)
.texture(brHigh, brLow)
.next()
worldRenderer
.vertex(center.x + v1.x, center.y + v1.y, center.z + v1.z)
.texture(minU, minV)
.color(colorRed, colorGreen, colorBlue, alpha)
.texture(brHigh, brLow)
.next()
worldRenderer
.vertex(center.x + v2.x, center.y + v2.y, center.z + v2.z)
.texture(minU, maxV)
.color(colorRed, colorGreen, colorBlue, alpha)
.texture(brHigh, brLow)
.next()
}
override fun getType() = ParticleTextureSheet.PARTICLE_SHEET_OPAQUE
fun setColor(color: Int) {
colorBlue = (color and 255) / 256.0f
colorGreen = ((color shr 8) and 255) / 256.0f
colorRed = ((color shr 16) and 255) / 256.0f
}
}

View File

@@ -0,0 +1,12 @@
package mods.betterfoliage.render
import net.minecraft.block.Blocks
import net.minecraft.block.Material
import net.minecraft.world.biome.Biome
val DIRT_BLOCKS = listOf(Blocks.DIRT, Blocks.COARSE_DIRT)
val SAND_BLOCKS = listOf(Blocks.SAND, Blocks.RED_SAND)
val SALTWATER_BIOMES = listOf(Biome.Category.BEACH, Biome.Category.OCEAN)
val SNOW_MATERIALS = listOf(Material.SNOW, Material.SNOW_BLOCK)

View File

@@ -0,0 +1,48 @@
package mods.betterfoliage.render
import mods.betterfoliage.*
import mods.betterfoliage.util.ThreadLocalDelegate
import net.minecraft.block.BlockState
import net.minecraft.client.MinecraftClient
import net.minecraft.client.render.model.BakedQuad
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction.UP
import org.apache.logging.log4j.Level
/**
* Integration for OptiFine custom block colors.
*/
/*
@Suppress("UNCHECKED_CAST")
object OptifineCustomColors {
val isColorAvailable = allAvailable(CustomColors, CustomColors.getColorMultiplier)
init {
BetterFoliage.log(Level.INFO, "Optifine custom color support is ${if (isColorAvailable) "enabled" else "disabled" }")
}
val renderEnv by ThreadLocalDelegate { OptifineRenderEnv() }
val fakeQuad = BakedQuad(IntArray(0), 1, UP, null)
fun getBlockColor(ctx: CombinedContext): Int {
val ofColor = if (isColorAvailable && MinecraftClient.getInstance().options.reflectDeclaredField<Boolean>("ofCustomColors") == true) {
renderEnv.reset(ctx.state, ctx.pos)
CustomColors.getColorMultiplier.invokeStatic(fakeQuad, ctx.state, ctx.world, ctx.pos, renderEnv.wrapped) as? Int
} else null
return if (ofColor == null || ofColor == -1) ctx.lightingCtx.color else ofColor
}
}
class OptifineRenderEnv {
val wrapped: Any = RenderEnv.element!!.getDeclaredConstructor(BlockState.element, BlockPos.element).let {
it.isAccessible = true
it.newInstance(null, null)
}
fun reset(state: BlockState, pos: BlockPos) {
RenderEnv.reset.invoke(wrapped, state, pos)
}
}
*/

View File

@@ -0,0 +1,64 @@
package mods.betterfoliage.render
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.util.get
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockRenderType.MODEL
import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.util.math.BlockPos
import net.minecraft.world.ExtendedBlockView
import org.apache.logging.log4j.Level.INFO
/**
* Integration for ShadersMod.
*/
/*
object ShadersModIntegration {
@JvmStatic val isAvailable = allAvailable(SVertexBuilder, SVertexBuilder.pushState, SVertexBuilder.pushNum, SVertexBuilder.pop)
val defaultLeaves = Blocks.OAK_LEAVES.defaultState
val defaultGrass = Blocks.TALL_GRASS.defaultState
/**
* Called from transformed ShadersMod code.
* @see mods.betterfoliage.loader.BetterFoliageTransformer
*/
@JvmStatic fun getBlockStateOverride(state: BlockState, world: ExtendedBlockView, pos: BlockPos): BlockState {
// if (LeafRegistry[state, world, pos] != null) return defaultLeaves
if (BetterFoliage.blockConfig.crops.matchesClass(state.block)) return defaultGrass
return state
}
init {
BetterFoliage.log(INFO, "ShadersMod integration is ${if (isAvailable) "enabled" else "disabled" }")
}
inline fun renderAs(ctx: CombinedContext, renderType: BlockRenderType, enabled: Boolean = true, func: ()->Unit) =
renderAs(ctx, ctx.state, renderType, enabled, func)
/** Quads rendered inside this block will use the given block entity data in shader programs. */
inline fun renderAs(ctx: CombinedContext, state: BlockState, renderType: BlockRenderType, enabled: Boolean = true, func: ()->Unit) {
if (isAvailable && enabled) {
val buffer = ctx.renderCtx.renderBuffer
val sVertexBuilder = buffer[BufferBuilder_sVertexBuilder]
SVertexBuilder.pushState.invoke(sVertexBuilder!!, ctx.state, ctx.pos, ctx.world, buffer)
func()
SVertexBuilder.pop.invoke(sVertexBuilder)
} else {
func()
}
}
/** Quads rendered inside this block will behave as tallgrass blocks in shader programs. */
inline fun grass(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) =
renderAs(ctx, defaultGrass, MODEL, enabled, func)
/** Quads rendered inside this block will behave as leaf blocks in shader programs. */
inline fun leaves(ctx: CombinedContext, enabled: Boolean = true, func: ()->Unit) =
renderAs(ctx, defaultLeaves, MODEL, enabled, func)
}
*/

View File

@@ -0,0 +1,94 @@
package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.render.lighting.withLighting
import mods.betterfoliage.render.lighting.grassTuftLighting
import mods.betterfoliage.render.lighting.roundLeafLighting
import mods.betterfoliage.util.Atlas
import mods.betterfoliage.resource.discovery.*
import mods.betterfoliage.resource.model.*
import mods.betterfoliage.util.*
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
import net.minecraft.block.BlockState
import net.minecraft.block.CactusBlock
import net.minecraft.client.render.model.BakedModel
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction.DOWN
import net.minecraft.world.ExtendedBlockView
import java.util.*
import java.util.function.Consumer
import java.util.function.Supplier
interface CactusKey : BlockRenderKey {
val cactusTop: Identifier
val cactusBottom: Identifier
val cactusSide: Identifier
}
object StandardCactusDiscovery : ConfigurableModelDiscovery() {
override val logger = BetterFoliage.logDetail
override val matchClasses = SimpleBlockMatcher(CactusBlock::class.java)
override val modelTextures = listOf(ModelTextureList("block/cactus", "top", "bottom", "side"))
override fun processModel(state: BlockState, textures: List<String>, atlas: Consumer<Identifier>): BlockRenderKey? {
val sprites = textures.map { Identifier(it) }
return CactusModel.Key(sprites[0], sprites[1], sprites[2])
}
}
class CactusModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(wrapped), FabricBakedModel {
val crossModels by cactusCrossModels.delegate(key)
val armModels by cactusArmModels.delegate(key)
val armLighting = horizontalDirections.map { grassTuftLighting(it) }
val crossLighting = roundLeafLighting()
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
(wrapped as FabricBakedModel).emitBlockQuads(blockView, state, pos, randomSupplier, context)
if (!BetterFoliage.config.enabled || !BetterFoliage.config.cactus.enabled) return
val random = randomSupplier.get()
val armSide = random.nextInt() and 3
context.withLighting(armLighting[armSide]) {
it.accept(armModels[armSide][random])
}
context.withLighting(crossLighting) {
it.accept(crossModels[random])
}
}
data class Key(
override val cactusTop: Identifier,
override val cactusBottom: Identifier,
override val cactusSide: Identifier
) : CactusKey {
override fun replace(model: BakedModel, state: BlockState) = CactusModel(this, meshifyStandard(model, state))
}
companion object {
val cactusCrossSprite by SpriteDelegate(Atlas.BLOCKS) {
Identifier(BetterFoliage.MOD_ID, "blocks/better_cactus")
}
val cactusArmSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
Identifier(BetterFoliage.MOD_ID, "blocks/better_cactus_arm_$idx")
}
val cactusArmModels = LazyMap(BetterFoliage.modelReplacer) { key: CactusKey ->
val shapes = BetterFoliage.config.cactus.let { tuftShapeSet(0.8, 0.8, 0.8, 0.2) }
val models = tuftModelSet(shapes, Color.white.asInt) { cactusArmSprites[randomI()] }
horizontalDirections.map { side ->
models.transform { move(0.0625 to DOWN).rotate(Rotation.fromUp[side.ordinal]) }.buildTufts()
}.toTypedArray()
}
val cactusCrossModels = LazyMap(BetterFoliage.modelReplacer) { key: CactusKey ->
val models = BetterFoliage.config.cactus.let { config ->
crossModelsRaw(64, config.size, 0.0, 0.0)
.transform { rotateZ(randomD(-config.sizeVariation, config.sizeVariation)) }
}
crossModelsTextured(models, Color.white.asInt, true) { cactusCrossSprite }
}
}
}

View File

@@ -0,0 +1,101 @@
package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.chunk.BasicBlockCtx
import mods.betterfoliage.render.DIRT_BLOCKS
import mods.betterfoliage.render.SALTWATER_BIOMES
import mods.betterfoliage.render.lighting.withLighting
import mods.betterfoliage.render.lighting.grassTuftLighting
import mods.betterfoliage.render.lighting.reedLighting
import mods.betterfoliage.render.lighting.renderMasquerade
import mods.betterfoliage.util.Atlas
import mods.betterfoliage.resource.discovery.BlockRenderKey
import mods.betterfoliage.resource.discovery.ModelDiscoveryBase
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
import mods.betterfoliage.resource.generated.CenteredSprite
import mods.betterfoliage.resource.model.*
import mods.betterfoliage.util.*
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
import net.minecraft.block.BlockRenderLayer
import net.minecraft.block.BlockState
import net.minecraft.block.Material
import net.minecraft.client.MinecraftClient
import net.minecraft.client.render.model.BakedModel
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction.UP
import net.minecraft.world.ExtendedBlockView
import java.util.*
import java.util.function.Consumer
import java.util.function.Supplier
object DirtKey : BlockRenderKey {
override fun replace(model: BakedModel, state: BlockState) = DirtModel(meshifyStandard(model, state))
}
object DirtDiscovery : ModelDiscoveryBase() {
override val logger = BetterFoliage.logDetail
override fun processModel(ctx: ModelDiscoveryContext, atlas: Consumer<Identifier>) =
if (ctx.state.block in DIRT_BLOCKS) DirtKey else null
}
class DirtModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
val algaeLighting = grassTuftLighting(UP)
val reedLighting = reedLighting()
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
if (!BetterFoliage.config.enabled) return super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
val ctx = BasicBlockCtx(blockView, pos)
val stateUp = ctx.offset(UP).state
val keyUp = BetterFoliage.modelReplacer[stateUp]
val isWater = stateUp.material == Material.WATER
val isDeepWater = isWater && ctx.offset(Int3(2 to UP)).state.material == Material.WATER
val isShallowWater = isWater && ctx.offset(Int3(2 to UP)).state.isAir
val isSaltWater = isWater && ctx.biome.category in SALTWATER_BIOMES
if (BetterFoliage.config.connectedGrass.enabled && keyUp is GrassKey) {
val grassBaseModel = (ctx.model(UP) as WrappedBakedModel).wrapped
context.renderMasquerade(grassBaseModel, blockView, stateUp, pos, randomSupplier, context)
} else {
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
}
val random = randomSupplier.get()
if (BetterFoliage.config.algae.enabled(random) && isDeepWater) {
context.withLighting(algaeLighting) {
it.accept(algaeModels[random])
}
} else if (BetterFoliage.config.reed.enabled(random) && isShallowWater && !isSaltWater) {
context.withLighting(reedLighting) {
it.accept(reedModels[random])
}
}
}
companion object {
val algaeSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
Identifier(BetterFoliage.MOD_ID, "blocks/better_algae_$idx")
}
val reedSprites by SpriteSetDelegate(Atlas.BLOCKS,
idFunc = { idx -> Identifier(BetterFoliage.MOD_ID, "blocks/better_reed_$idx") },
idRegister = { id -> CenteredSprite(id, aspectHeight = 2).register(BetterFoliage.generatedPack) }
)
val algaeModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
val shapes = BetterFoliage.config.algae.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
tuftModelSet(shapes, Color.white.asInt) { algaeSprites[randomI()] }
.withOpposites()
.build(BlockRenderLayer.CUTOUT_MIPPED, flatLighting = false)
}
val reedModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
val shapes = BetterFoliage.config.reed.let { tuftShapeSet(2.0, it.heightMin, it.heightMax, it.hOffset) }
tuftModelSet(shapes, Color.white.asInt) { reedSprites[randomI()] }
.withOpposites()
.build(BlockRenderLayer.CUTOUT_MIPPED, flatLighting = false)
}
}
}

View File

@@ -0,0 +1,121 @@
package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.chunk.BasicBlockCtx
import mods.betterfoliage.render.SNOW_MATERIALS
import mods.betterfoliage.render.lighting.withLighting
import mods.betterfoliage.render.lighting.grassTuftLighting
import mods.betterfoliage.util.Atlas
import mods.betterfoliage.resource.discovery.*
import mods.betterfoliage.resource.model.*
import mods.betterfoliage.util.*
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
import net.minecraft.block.BlockRenderLayer
import net.minecraft.block.BlockState
import net.minecraft.client.render.model.BakedModel
import net.minecraft.tag.BlockTags
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction.*
import net.minecraft.world.ExtendedBlockView
import java.util.*
import java.util.function.Consumer
import java.util.function.Supplier
interface GrassKey : BlockRenderKey {
val grassTopTexture: Identifier
/**
* Color to use for Short Grass rendering instead of the biome color.
*
* Value is null if the texture is mostly grey (the saturation of its average color is under a configurable limit),
* the average color of the texture otherwise.
*/
val overrideColor: Int?
}
object StandardGrassDiscovery : ConfigurableModelDiscovery() {
override val logger = BetterFoliage.logDetail
override val matchClasses: ConfigurableBlockMatcher get() = BetterFoliage.blockConfig.grassBlocks
override val modelTextures: List<ModelTextureList> get() = BetterFoliage.blockConfig.grassModels.modelList
override fun processModel(state: BlockState, textures: List<String>, atlas: Consumer<Identifier>): BlockRenderKey? {
val grassId = Identifier(textures[0])
log(" block state $state")
log(" texture $grassId")
return GrassBlockModel.Key(grassId, getAndLogColorOverride(grassId, Atlas.BLOCKS, BetterFoliage.config.shortGrass.saturationThreshold))
}
}
class GrassBlockModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(wrapped), FabricBakedModel {
val tuftNormal by grassTuftMeshesNormal.delegate(key)
val tuftSnowed by grassTuftMeshesSnowed.delegate(key)
val fullBlock by grassFullBlockMeshes.delegate(key)
val tuftLighting = grassTuftLighting(UP)
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
if (!BetterFoliage.config.enabled) return super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
val ctx = BasicBlockCtx(blockView, pos)
val stateBelow = ctx.state(DOWN)
val stateAbove = ctx.state(UP)
val isSnowed = stateAbove.material in SNOW_MATERIALS
val connected = BetterFoliage.config.connectedGrass.enabled &&
(!isSnowed || BetterFoliage.config.connectedGrass.snowEnabled) && (
BlockTags.DIRT_LIKE.contains(stateBelow.block) ||
BetterFoliage.modelReplacer.getTyped<GrassKey>(stateBelow) != null
)
val random = randomSupplier.get()
if (connected) {
context.meshConsumer().accept(if (isSnowed) snowFullBlockMeshes[random] else fullBlock[random])
} else {
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
}
if (BetterFoliage.config.shortGrass.enabled(random) && !ctx.isNeighborSolid(UP)) {
context.withLighting(tuftLighting) {
it.accept(if (isSnowed) tuftSnowed[random] else tuftNormal[random])
}
}
}
data class Key(
override val grassTopTexture: Identifier,
override val overrideColor: Int?
) : GrassKey {
override fun replace(model: BakedModel, state: BlockState) = GrassBlockModel(this, meshifyStandard(model, state))
}
companion object {
val grassTuftSpritesNormal by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
Identifier(BetterFoliage.MOD_ID, "blocks/better_grass_long_$idx")
}
val grassTuftSpritesSnowed by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
Identifier(BetterFoliage.MOD_ID, "blocks/better_grass_long_$idx")
}
val grassTuftShapes = LazyMap(BetterFoliage.modelReplacer) { key: GrassKey ->
BetterFoliage.config.shortGrass.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
}
val grassTuftMeshesNormal = LazyMap(BetterFoliage.modelReplacer) { key: GrassKey ->
tuftModelSet(grassTuftShapes[key], key.overrideColor) { idx -> grassTuftSpritesNormal[randomI()] }
.withOpposites()
.build(BlockRenderLayer.CUTOUT_MIPPED, flatLighting = false)
}
val grassTuftMeshesSnowed = LazyMap(BetterFoliage.modelReplacer) { key: GrassKey ->
tuftModelSet(grassTuftShapes[key], Color.white.asInt) { idx -> grassTuftSpritesSnowed[randomI()] }
.withOpposites()
.build(BlockRenderLayer.CUTOUT_MIPPED, flatLighting = false)
}
val grassFullBlockMeshes = LazyMap(BetterFoliage.modelReplacer) { key: GrassKey ->
Array(64) { fullCubeTextured(key.grassTopTexture, key.overrideColor) }
}
val snowFullBlockMeshes by LazyInvalidatable(BetterFoliage.modelReplacer) {
Array(64) { fullCubeTextured(Identifier("block/snow"), Color.white.asInt) }
}
}
}

View File

@@ -0,0 +1,116 @@
package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.chunk.BasicBlockCtx
import mods.betterfoliage.render.SNOW_MATERIALS
import mods.betterfoliage.render.lighting.withLighting
import mods.betterfoliage.render.lighting.roundLeafLighting
import mods.betterfoliage.render.particle.LeafParticleRegistry
import mods.betterfoliage.util.Atlas
import mods.betterfoliage.resource.discovery.*
import mods.betterfoliage.resource.generated.GeneratedLeafSprite
import mods.betterfoliage.resource.model.*
import mods.betterfoliage.util.*
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
import net.minecraft.block.BlockRenderLayer.CUTOUT_MIPPED
import net.minecraft.block.BlockState
import net.minecraft.client.render.model.BakedModel
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction.*
import net.minecraft.world.ExtendedBlockView
import java.util.*
import java.util.function.Consumer
import java.util.function.Supplier
interface LeafKey : BlockRenderKey {
val roundLeafTexture: Identifier
/** Type of the leaf block (configurable by user). */
val leafType: String
/** Average color of the round leaf texture. */
val overrideColor: Int?
}
object StandardLeafDiscovery : ConfigurableModelDiscovery() {
override val logger = BetterFoliage.logDetail
override val matchClasses: ConfigurableBlockMatcher get() = BetterFoliage.blockConfig.leafBlocks
override val modelTextures: List<ModelTextureList> get() = BetterFoliage.blockConfig.leafModels.modelList
override fun processModel(state: BlockState, textures: List<String>, atlas: Consumer<Identifier>) =
defaultRegisterLeaf(Identifier(textures[0]), atlas)
}
fun HasLogger.defaultRegisterLeaf(sprite: Identifier, atlas: Consumer<Identifier>): BlockRenderKey? {
val leafType = LeafParticleRegistry.typeMappings.getType(sprite) ?: "default"
val leafId = GeneratedLeafSprite(sprite, leafType).register(BetterFoliage.generatedPack)
atlas.accept(leafId)
log(" leaf texture $sprite")
log(" particle $leafType")
return NormalLeavesModel.Key(
leafId, leafType,
getAndLogColorOverride(leafId, Atlas.BLOCKS, BetterFoliage.config.shortGrass.saturationThreshold)
)
}
fun HasLogger.getAndLogColorOverride(sprite: Identifier, atlas: Atlas, threshold: Double): Int? {
val hsb = resourceManager.averageImageColorHSB(sprite, atlas)
return if (hsb.saturation >= threshold) {
log(" brightness ${hsb.brightness}")
log(" saturation ${hsb.saturation} >= ${threshold}, using texture color")
hsb.copy(brightness = 0.9f.coerceAtMost(hsb.brightness * 2.0f)).asColor
} else {
log(" saturation ${hsb.saturation} < ${threshold}, using block color")
null
}
}
class NormalLeavesModel(val key: Key, wrapped: BakedModel) : WrappedBakedModel(wrapped), FabricBakedModel {
val leafNormal by leafModelsNormal.delegate(key)
val leafSnowed by leafModelsSnowed.delegate(key)
val leafLighting = roundLeafLighting()
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
if (!BetterFoliage.config.enabled || !BetterFoliage.config.leaves.enabled) return
val ctx = BasicBlockCtx(blockView, pos)
val stateAbove = ctx.state(UP)
val isSnowed = stateAbove.material in SNOW_MATERIALS
val random = randomSupplier.get()
context.withLighting(leafLighting) {
it.accept(leafNormal[random])
if (isSnowed) it.accept(leafSnowed[random])
}
}
data class Key(
override val roundLeafTexture: Identifier,
override val leafType: String,
override val overrideColor: Int?
) : LeafKey {
override fun replace(model: BakedModel, state: BlockState) = NormalLeavesModel(this, meshifyStandard(model, state, renderLayerOverride = CUTOUT_MIPPED))
}
companion object {
val leafSpritesSnowed by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
Identifier(BetterFoliage.MOD_ID, "blocks/better_leaves_snowed_$idx")
}
val leafModelsBase = LazyMap(BetterFoliage.modelReplacer) { key: LeafKey ->
BetterFoliage.config.leaves.let { crossModelsRaw(64, it.size, it.hOffset, it.vOffset) }
}
val leafModelsNormal = LazyMap(BetterFoliage.modelReplacer) { key: LeafKey ->
crossModelsTextured(leafModelsBase[key], key.overrideColor, true) { Atlas.BLOCKS.atlas[key.roundLeafTexture]!! }
}
val leafModelsSnowed = LazyMap(BetterFoliage.modelReplacer) { key: LeafKey ->
crossModelsTextured(leafModelsBase[key], Color.white.asInt, false) { leafSpritesSnowed[it] }
}
}
}

View File

@@ -0,0 +1,68 @@
package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.util.Atlas
import mods.betterfoliage.resource.discovery.BlockRenderKey
import mods.betterfoliage.resource.discovery.ModelDiscoveryBase
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
import mods.betterfoliage.resource.discovery.RenderKeyFactory
import mods.betterfoliage.resource.model.*
import mods.betterfoliage.util.LazyInvalidatable
import mods.betterfoliage.util.get
import mods.betterfoliage.util.semiRandom
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.client.render.model.BakedModel
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction.DOWN
import net.minecraft.world.ExtendedBlockView
import java.util.*
import java.util.function.Consumer
import java.util.function.Supplier
object LilypadKey : BlockRenderKey {
override fun replace(model: BakedModel, state: BlockState) = LilypadModel(meshifyStandard(model, state))
}
object LilyPadDiscovery : ModelDiscoveryBase() {
override val logger = BetterFoliage.logDetail
override fun processModel(ctx: ModelDiscoveryContext, atlas: Consumer<Identifier>) =
if (ctx.state.block == Blocks.LILY_PAD) LilypadKey else null
}
class LilypadModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
if (!BetterFoliage.config.enabled || !BetterFoliage.config.lilypad.enabled) return
val random = randomSupplier.get()
context.meshConsumer().accept(lilypadRootModels[random])
if (random.nextInt(64) < BetterFoliage.config.lilypad.population) {
context.meshConsumer().accept(lilypadFlowerModels[random])
}
}
companion object {
val lilypadRootSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
Identifier(BetterFoliage.MOD_ID, "blocks/better_lilypad_roots_$idx")
}
val lilypadFlowerSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
Identifier(BetterFoliage.MOD_ID, "blocks/better_lilypad_flower_$idx")
}
val lilypadRootModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
val shapes = tuftShapeSet(1.0, 1.0, 1.0, BetterFoliage.config.lilypad.hOffset)
tuftModelSet(shapes, Color.white.asInt) { lilypadRootSprites[it] }
.transform { move(2.0 to DOWN) }
.buildTufts()
}
val lilypadFlowerModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
val shapes = tuftShapeSet(0.5, 0.5, 0.5, BetterFoliage.config.lilypad.hOffset)
tuftModelSet(shapes, Color.white.asInt) { lilypadFlowerSprites[it] }
.transform { move(1.0 to DOWN) }
.buildTufts()
}
}
}

View File

@@ -0,0 +1,63 @@
package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.render.lighting.withLighting
import mods.betterfoliage.render.lighting.grassTuftLighting
import mods.betterfoliage.util.Atlas
import mods.betterfoliage.resource.discovery.BlockRenderKey
import mods.betterfoliage.resource.discovery.ModelDiscoveryBase
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
import mods.betterfoliage.resource.discovery.RenderKeyFactory
import mods.betterfoliage.resource.model.*
import mods.betterfoliage.util.*
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.client.render.model.BakedModel
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction.UP
import net.minecraft.world.ExtendedBlockView
import java.util.*
import java.util.function.Consumer
import java.util.function.Supplier
object MyceliumKey : BlockRenderKey {
override fun replace(model: BakedModel, state: BlockState) = MyceliumModel(meshifyStandard(model, state))
}
object MyceliumDiscovery : ModelDiscoveryBase() {
override val logger = BetterFoliage.logDetail
val myceliumBlocks = listOf(Blocks.MYCELIUM)
override fun processModel(ctx: ModelDiscoveryContext, atlas: Consumer<Identifier>) =
if (ctx.state.block in myceliumBlocks) MyceliumKey else null
}
class MyceliumModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
val tuftLighting = grassTuftLighting(UP)
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
val random = randomSupplier.get()
if (BetterFoliage.config.enabled &&
BetterFoliage.config.shortGrass.let { it.myceliumEnabled && random.nextInt(64) < it.population } &&
blockView.getBlockState(pos + UP.offset).isAir
) {
context.withLighting(tuftLighting) {
it.accept(myceliumTuftModels[random])
}
}
}
companion object {
val myceliumTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
Identifier(BetterFoliage.MOD_ID, "blocks/better_mycel_$idx")
}
val myceliumTuftModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
val shapes = BetterFoliage.config.shortGrass.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
tuftModelSet(shapes, Color.white.asInt) { idx -> myceliumTuftSprites[randomI()] }.buildTufts()
}
}
}

View File

@@ -0,0 +1,66 @@
package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.render.lighting.withLighting
import mods.betterfoliage.render.lighting.grassTuftLighting
import mods.betterfoliage.util.Atlas
import mods.betterfoliage.resource.discovery.BlockRenderKey
import mods.betterfoliage.resource.discovery.ModelDiscoveryBase
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
import mods.betterfoliage.resource.discovery.RenderKeyFactory
import mods.betterfoliage.resource.model.*
import mods.betterfoliage.util.*
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
import net.minecraft.block.BlockRenderLayer
import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.client.render.model.BakedModel
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction.DOWN
import net.minecraft.world.ExtendedBlockView
import java.util.*
import java.util.function.Consumer
import java.util.function.Supplier
object NetherrackKey : BlockRenderKey {
override fun replace(model: BakedModel, state: BlockState) = NetherrackModel(meshifyStandard(model, state))
}
object NetherrackDiscovery : ModelDiscoveryBase() {
override val logger = BetterFoliage.logDetail
val netherrackBlocks = listOf(Blocks.NETHERRACK)
override fun processModel(ctx: ModelDiscoveryContext, atlas: Consumer<Identifier>) =
if (ctx.state.block in netherrackBlocks) NetherrackKey else null
}
class NetherrackModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
val tuftLighting = grassTuftLighting(DOWN)
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
if (BetterFoliage.config.enabled &&
BetterFoliage.config.netherrack.enabled &&
blockView.getBlockState(pos + DOWN.offset).isAir
) {
val random = randomSupplier.get()
context.withLighting(tuftLighting) {
it.accept(netherrackTuftModels[random])
}
}
}
companion object {
val netherrackTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
Identifier(BetterFoliage.MOD_ID, "blocks/better_netherrack_$idx")
}
val netherrackTuftModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
val shapes = BetterFoliage.config.netherrack.let { tuftShapeSet(it.size, it.heightMin, it.heightMax, it.hOffset) }
tuftModelSet(shapes, Color.white.asInt) { netherrackTuftSprites[randomI()] }
.transform { rotate(Rotation.fromUp[DOWN.ordinal]).rotateUV(2) }
.withOpposites()
.build(BlockRenderLayer.CUTOUT_MIPPED, flatLighting = false)
}
}
}

View File

@@ -0,0 +1,84 @@
package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.render.column.*
import mods.betterfoliage.util.Atlas
import mods.betterfoliage.resource.discovery.*
import mods.betterfoliage.resource.model.meshifyStandard
import mods.betterfoliage.util.LazyMap
import mods.betterfoliage.util.get
import mods.betterfoliage.util.tryDefault
import net.minecraft.block.BlockState
import net.minecraft.block.LogBlock
import net.minecraft.client.render.model.BakedModel
import net.minecraft.client.texture.SpriteAtlasTexture
import net.minecraft.util.Identifier
import net.minecraft.util.math.Direction.Axis
import java.util.function.Consumer
object RoundLogOverlayLayer : ColumnRenderLayer() {
override fun getColumnKey(state: BlockState) = BetterFoliage.modelReplacer.getTyped<ColumnBlockKey>(state)
override val connectSolids: Boolean get() = BetterFoliage.config.roundLogs.connectSolids
override val lenientConnect: Boolean get() = BetterFoliage.config.roundLogs.lenientConnect
override val defaultToY: Boolean get() = BetterFoliage.config.roundLogs.defaultY
}
object StandardLogDiscovery : ConfigurableModelDiscovery() {
override val logger = BetterFoliage.logDetail
override val matchClasses: ConfigurableBlockMatcher get() = BetterFoliage.blockConfig.logBlocks
override val modelTextures: List<ModelTextureList> get() = BetterFoliage.blockConfig.logModels.modelList
override fun processModel(state: BlockState, textures: List<String>, atlas: Consumer<Identifier>): BlockRenderKey? {
val axis = getAxis(state)
log(" axis $axis")
return RoundLogModel.Key(axis, Identifier(textures[0]), Identifier(textures[1]))
}
fun getAxis(state: BlockState): Axis? {
val axis = tryDefault(null) { state.get(LogBlock.AXIS).toString() } ?:
state.entries.entries.find { it.key.getName().toLowerCase() == "axis" }?.value?.toString()
return when (axis) {
"x" -> Axis.X
"y" -> Axis.Y
"z" -> Axis.Z
else -> null
}
}
}
interface RoundLogKey : ColumnBlockKey, BlockRenderKey {
val barkSprite: Identifier
val endSprite: Identifier
}
class RoundLogModel(val key: Key, wrapped: BakedModel) : ColumnModelBase(wrapped) {
override val enabled: Boolean get() = BetterFoliage.config.enabled && BetterFoliage.config.roundLogs.enabled
override val overlayLayer: ColumnRenderLayer get() = RoundLogOverlayLayer
override val connectPerpendicular: Boolean get() = BetterFoliage.config.roundLogs.connectPerpendicular
val modelSet by modelSets.delegate(key)
override fun getMeshSet(axis: Axis, quadrant: Int) = modelSet
data class Key(
override val axis: Axis?,
override val barkSprite: Identifier,
override val endSprite: Identifier
) : RoundLogKey {
override fun replace(model: BakedModel, state: BlockState) = RoundLogModel(this, meshifyStandard(model, state))
}
companion object {
val modelSets = LazyMap(BetterFoliage.modelReplacer) { key: Key ->
val barkSprite = Atlas.BLOCKS.atlas[key.barkSprite]!!
val endSprite = Atlas.BLOCKS.atlas[key.endSprite]!!
BetterFoliage.config.roundLogs.let { config ->
ColumnMeshSet(
config.radiusSmall, config.radiusLarge, config.zProtection,
key.axis ?: Axis.Y,
barkSprite, barkSprite,
endSprite, endSprite
)
}
}
}
}

View File

@@ -0,0 +1,96 @@
package mods.betterfoliage.render.block.vanilla
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.chunk.CachedBlockCtx
import mods.betterfoliage.render.SALTWATER_BIOMES
import mods.betterfoliage.render.SAND_BLOCKS
import mods.betterfoliage.render.lighting.withLighting
import mods.betterfoliage.render.lighting.grassTuftLighting
import mods.betterfoliage.util.Atlas
import mods.betterfoliage.resource.discovery.BlockRenderKey
import mods.betterfoliage.resource.discovery.ModelDiscoveryBase
import mods.betterfoliage.resource.discovery.ModelDiscoveryContext
import mods.betterfoliage.resource.model.*
import mods.betterfoliage.util.*
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
import net.minecraft.block.BlockRenderLayer.CUTOUT_MIPPED
import net.minecraft.block.BlockState
import net.minecraft.block.Material
import net.minecraft.client.render.model.BakedModel
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction.UP
import net.minecraft.world.ExtendedBlockView
import java.util.*
import java.util.function.Consumer
import java.util.function.Supplier
object SandKey : BlockRenderKey {
override fun replace(model: BakedModel, state: BlockState) = SandModel(meshifyStandard(model, state))
}
object SandDiscovery : ModelDiscoveryBase() {
override val logger = BetterFoliage.logDetail
override fun processModel(ctx: ModelDiscoveryContext, atlas: Consumer<Identifier>) =
if (ctx.state.block in SAND_BLOCKS) SandKey else null
}
class SandModel(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
val coralLighting = allDirections.map { grassTuftLighting(it) }.toTypedArray()
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
val ctx = CachedBlockCtx(blockView, pos)
val random = randomSupplier.get()
if (!BetterFoliage.config.enabled || !BetterFoliage.config.coral.enabled(random)) return
if (ctx.biome.category !in SALTWATER_BIOMES) return
allDirections.filter { random.nextInt(64) < BetterFoliage.config.coral.chance }.forEach { face ->
val isWater = ctx.state(face).material == Material.WATER
val isDeepWater = isWater && ctx.offset(face).state(UP).material == Material.WATER
if (isDeepWater) context.withLighting(coralLighting[face]) {
it.accept(coralCrustModels[face][random])
it.accept(coralTuftModels[face][random])
}
}
}
companion object {
// val sandModel by LazyInvalidatable(BetterFoliage.modelReplacer) {
// Array(64) { fullCubeTextured(Identifier("block/sand"), Color.white.asInt) }
// }
val coralTuftSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
Identifier(BetterFoliage.MOD_ID, "blocks/better_coral_$idx")
}
val coralCrustSprites by SpriteSetDelegate(Atlas.BLOCKS) { idx ->
Identifier(BetterFoliage.MOD_ID, "blocks/better_crust_$idx")
}
val coralTuftModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
val shapes = BetterFoliage.config.coral.let { tuftShapeSet(it.size, 1.0, 1.0, it.hOffset) }
allDirections.map { face ->
tuftModelSet(shapes, Color.white.asInt) { coralTuftSprites[randomI()] }
.transform { rotate(Rotation.fromUp[face]) }
.withOpposites()
.build(CUTOUT_MIPPED)
}.toTypedArray()
}
val coralCrustModels by LazyInvalidatable(BetterFoliage.modelReplacer) {
allDirections.map { face ->
Array(64) { idx ->
listOf(horizontalRectangle(x1 = -0.5, x2 = 0.5, z1 = -0.5, z2 = 0.5, y = 0.0)
.scale(BetterFoliage.config.coral.crustSize)
.move(0.5 + randomD(0.01, BetterFoliage.config.coral.vOffset) to UP)
.rotate(Rotation.fromUp[face])
.mirrorUV(randomB(), randomB()).rotateUV(randomI(max = 4))
.sprite(coralCrustSprites[idx]).colorAndIndex(null)
).build(CUTOUT_MIPPED)
}
}.toTypedArray()
}
}
}

View File

@@ -0,0 +1,158 @@
package mods.betterfoliage.render.column
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.*
import mods.betterfoliage.resource.model.*
import mods.betterfoliage.util.Double3
import mods.betterfoliage.util.Rotation
import net.minecraft.block.BlockRenderLayer.SOLID
import net.minecraft.client.texture.Sprite
import net.minecraft.util.math.Direction.*
/**
* Collection of dynamically generated meshes used to render rounded columns.
*/
class ColumnMeshSet(
radiusSmall: Double,
radiusLarge: Double,
zProtection: Double,
val axis: Axis,
val spriteLeft: Sprite,
val spriteRight: Sprite,
val spriteTop: Sprite,
val spriteBottom: Sprite
) {
protected fun sideRounded(radius: Double, yBottom: Double, yTop: Double): List<Quad> {
val halfRadius = radius * 0.5
return listOf(
// left side of the diagonal
verticalRectangle(0.0, 0.5, 0.5 - radius, 0.5, yBottom, yTop).clampUV(minU = 0.0, maxU = 0.5 - radius),
verticalRectangle(0.5 - radius, 0.5, 0.5 - halfRadius, 0.5 - halfRadius, yBottom, yTop).clampUV(minU = 0.5 - radius),
// right side of the diagonal
verticalRectangle(0.5 - halfRadius, 0.5 - halfRadius, 0.5, 0.5 - radius, yBottom, yTop).clampUV(maxU = radius - 0.5),
verticalRectangle(0.5, 0.5 - radius, 0.5, 0.0, yBottom, yTop).clampUV(minU = radius - 0.5, maxU = 0.0)
)
}
protected fun sideRoundedTransition(radiusBottom: Double, radiusTop: Double, yBottom: Double, yTop: Double): List<Quad> {
val ySplit = 0.5 * (yBottom + yTop)
val modelTop = sideRounded(radiusTop, yBottom, yTop)
val modelBottom = sideRounded(radiusBottom, yBottom, yTop)
return (modelBottom zip modelTop).map { (quadBottom, quadTop) ->
Quad.mix(quadBottom, quadTop) { vBottom, vTop -> if (vBottom.xyz.y < ySplit) vBottom.copy() else vTop.copy() }
}
}
protected fun sideSquare(yBottom: Double, yTop: Double) = listOf(
verticalRectangle(0.0, 0.5, 0.5, 0.5, yBottom, yTop).clampUV(minU = 0.0),
verticalRectangle(0.5, 0.5, 0.5, 0.0, yBottom, yTop).clampUV(maxU = 0.0)
)
protected fun lidRounded(radius: Double, y: Double, isBottom: Boolean) = Array(4) { quadrant ->
val rotation = baseRotation(axis) + quadrantRotations[quadrant]
val v1 = Vertex(Double3(0.0, y, 0.0), UV(0.0, 0.0))
val v2 = Vertex(Double3(0.0, y, 0.5), UV(0.0, 0.5))
val v3 = Vertex(Double3(0.5 - radius, y, 0.5), UV(0.5 - radius, 0.5))
val v4 = Vertex(Double3(0.5 - radius * 0.5, y, 0.5 - radius * 0.5), UV(0.5, 0.5))
val v5 = Vertex(Double3(0.5, y, 0.5 - radius), UV(0.5, 0.5 - radius))
val v6 = Vertex(Double3(0.5, y, 0.0), UV(0.5, 0.0))
listOf(Quad(v1, v2, v3, v4), Quad(v1, v4, v5, v6))
.map { it.cycleVertices(if (isBottom xor BetterFoliage.config.nVidia) 0 else 1) }
.map { it.rotate(rotation).rotateUV(quadrant) }
.map { it.sprite(if (isBottom) spriteBottom else spriteTop).colorAndIndex(Color.white.asInt) }
.map { if (isBottom) it.flipped else it }
}
protected fun lidSquare(y: Double, isBottom: Boolean) = Array(4) { quadrant ->
val rotation = baseRotation(axis) + quadrantRotations[quadrant]
listOf(
horizontalRectangle(x1 = 0.0, x2 = 0.5, z1 = 0.0, z2 = 0.5, y = y).clampUV(minU = 0.0, minV = 0.0)
.rotate(rotation).rotateUV(quadrant)
.sprite(if (isBottom) spriteBottom else spriteTop).colorAndIndex(Color.white.asInt)
.let { if (isBottom) it.flipped else it }
)
}
protected val zProtectionScale = zProtection.let { Double3(it, 1.0, it) }
protected fun List<Quad>.extendTop(size: Double) = map { q -> q.clampUV(minV = 0.5 - size).transformV { v ->
if (v.xyz.y > 0.501) v.copy(xyz = v.xyz * zProtectionScale) else v }
}
protected fun List<Quad>.extendBottom(size: Double) = map { q -> q.clampUV(maxV = -0.5 + size).transformV { v ->
if (v.xyz.y < -0.501) v.copy(xyz = v.xyz * zProtectionScale) else v }
}
protected fun List<Quad>.buildSides(quadsPerSprite: Int) = Array(4) { quadrant ->
val rotation = baseRotation(axis) + quadrantRotations[quadrant]
this.map { it.rotate(rotation).colorAndIndex(Color.white.asInt) }
.mapIndexed { idx, q -> if (idx % (2 * quadsPerSprite) >= quadsPerSprite) q.sprite(spriteRight) else q.sprite(spriteLeft) }
.build(SOLID, flatLighting = false)
}
companion object {
fun baseRotation(axis: Axis) = when(axis) {
Axis.X -> Rotation.fromUp[EAST.ordinal]
Axis.Y -> Rotation.fromUp[UP.ordinal]
Axis.Z -> Rotation.fromUp[SOUTH.ordinal]
}
val quadrantRotations = Array(4) { Rotation.rot90[UP.ordinal] * it }
}
//
// Mesh definitions
// 4-element arrays hold prebuild meshes for each of the rotations around the axis
//
val sideSquare = sideSquare(-0.5, 0.5).buildSides(quadsPerSprite = 1)
val sideRoundSmall = sideRounded(radiusSmall, -0.5, 0.5).buildSides(quadsPerSprite = 2)
val sideRoundLarge = sideRounded(radiusLarge, -0.5, 0.5).buildSides(quadsPerSprite = 2)
val sideExtendTopSquare = sideSquare(0.5, 0.5 + radiusLarge).extendTop(radiusLarge).buildSides(quadsPerSprite = 1)
val sideExtendTopRoundSmall = sideRounded(radiusSmall, 0.5, 0.5 + radiusLarge).extendTop(radiusLarge).buildSides(quadsPerSprite = 2)
val sideExtendTopRoundLarge = sideRounded(radiusLarge, 0.5, 0.5 + radiusLarge).extendTop(radiusLarge).buildSides(quadsPerSprite = 2)
val sideExtendBottomSquare = sideSquare(-0.5 - radiusLarge, -0.5).extendBottom(radiusLarge).buildSides(quadsPerSprite = 1)
val sideExtendBottomRoundSmall = sideRounded(radiusSmall, -0.5 - radiusLarge, -0.5).extendBottom(radiusLarge).buildSides(quadsPerSprite = 2)
val sideExtendBottomRoundLarge = sideRounded(radiusLarge, -0.5 - radiusLarge, -0.5).extendBottom(radiusLarge).buildSides(quadsPerSprite = 2)
val lidTopSquare = lidSquare(0.5, false).build(SOLID, flatLighting = false)
val lidTopRoundSmall = lidRounded(radiusSmall, 0.5, false).build(SOLID, flatLighting = false)
val lidTopRoundLarge = lidRounded(radiusLarge, 0.5, false).build(SOLID, flatLighting = false)
val lidBottomSquare = lidSquare(-0.5, true).build(SOLID, flatLighting = false)
val lidBottomRoundSmall = lidRounded(radiusSmall, -0.5, true).build(SOLID, flatLighting = false)
val lidBottomRoundLarge = lidRounded(radiusLarge, -0.5, true).build(SOLID, flatLighting = false)
val transitionTop = sideRoundedTransition(radiusLarge, radiusSmall, -0.5, 0.5).buildSides(quadsPerSprite = 2)
val transitionBottom = sideRoundedTransition(radiusSmall, radiusLarge, -0.5, 0.5).buildSides(quadsPerSprite = 2)
//
// Helper fuctions for lids (block ends)
//
fun flatTop(quadrantTypes: Array<QuadrantType>, quadrant: Int) = when(quadrantTypes[quadrant]) {
SMALL_RADIUS -> lidTopRoundSmall[quadrant]
LARGE_RADIUS -> lidTopRoundLarge[quadrant]
SQUARE -> lidTopSquare[quadrant]
INVISIBLE -> lidTopSquare[quadrant]
}
fun flatBottom(quadrantTypes: Array<QuadrantType>, quadrant: Int) = when(quadrantTypes[quadrant]) {
SMALL_RADIUS -> lidBottomRoundSmall[quadrant]
LARGE_RADIUS -> lidBottomRoundLarge[quadrant]
SQUARE -> lidBottomSquare[quadrant]
INVISIBLE -> lidBottomSquare[quadrant]
}
fun extendTop(quadrantTypes: Array<QuadrantType>, quadrant: Int) = when(quadrantTypes[quadrant]) {
SMALL_RADIUS -> sideExtendTopRoundSmall[quadrant]
LARGE_RADIUS -> sideExtendTopRoundLarge[quadrant]
SQUARE -> sideExtendTopSquare[quadrant]
INVISIBLE -> sideExtendTopSquare[quadrant]
}
fun extendBottom(quadrantTypes: Array<QuadrantType>, quadrant: Int) = when(quadrantTypes[quadrant]) {
SMALL_RADIUS -> sideExtendBottomRoundSmall[quadrant]
LARGE_RADIUS -> sideExtendBottomRoundLarge[quadrant]
SQUARE -> sideExtendBottomSquare[quadrant]
INVISIBLE -> sideExtendBottomSquare[quadrant]
}
}

View File

@@ -0,0 +1,108 @@
package mods.betterfoliage.render.column
import mods.betterfoliage.chunk.CachedBlockCtx
import mods.betterfoliage.chunk.ChunkOverlayManager
import mods.betterfoliage.render.column.ColumnLayerData.NormalRender
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.*
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.*
import mods.betterfoliage.resource.model.WrappedBakedModel
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext
import net.minecraft.block.BlockState
import net.minecraft.client.render.model.BakedModel
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction.Axis
import net.minecraft.world.ExtendedBlockView
import java.util.*
import java.util.function.Supplier
abstract class ColumnModelBase(wrapped: BakedModel) : WrappedBakedModel(wrapped) {
abstract val enabled: Boolean
abstract val overlayLayer: ColumnRenderLayer
abstract val connectPerpendicular: Boolean
abstract fun getMeshSet(axis: Axis, quadrant: Int): ColumnMeshSet
override fun emitBlockQuads(blockView: ExtendedBlockView, state: BlockState, pos: BlockPos, randomSupplier: Supplier<Random>, context: RenderContext) {
val ctx = CachedBlockCtx(blockView, pos)
val roundLog = ChunkOverlayManager.get(overlayLayer, ctx)
when(roundLog) {
ColumnLayerData.SkipRender -> return
NormalRender -> return super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
ColumnLayerData.ResolveError, null -> {
return super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
}
}
// if log axis is not defined and "Default to vertical" config option is not set, render normally
if ((roundLog as ColumnLayerData.SpecialRender).column.axis == null && !overlayLayer.defaultToY) {
return super.emitBlockQuads(blockView, state, pos, randomSupplier, context)
}
val axis = roundLog.column.axis ?: Axis.Y
val baseRotation = ColumnMeshSet.baseRotation(axis)
ColumnMeshSet.quadrantRotations.forEachIndexed { idx, quadrantRotation ->
// set rotation for the current quadrant
val rotation = baseRotation + quadrantRotation
val meshSet = getMeshSet(axis, idx)
// disallow sharp discontinuities in the chamfer radius, or tapering-in where inappropriate
if (roundLog.quadrants[idx] == LARGE_RADIUS &&
roundLog.upType == PARALLEL && roundLog.quadrantsTop[idx] != LARGE_RADIUS &&
roundLog.downType == PARALLEL && roundLog.quadrantsBottom[idx] != LARGE_RADIUS) {
roundLog.quadrants[idx] = SMALL_RADIUS
}
// select meshes for current quadrant based on connectivity rules
val sideMesh = when (roundLog.quadrants[idx]) {
SMALL_RADIUS -> meshSet.sideRoundSmall[idx]
LARGE_RADIUS -> if (roundLog.upType == PARALLEL && roundLog.quadrantsTop[idx] == SMALL_RADIUS) meshSet.transitionTop[idx]
else if (roundLog.downType == PARALLEL && roundLog.quadrantsBottom[idx] == SMALL_RADIUS) meshSet.transitionBottom[idx]
else meshSet.sideRoundLarge[idx]
SQUARE -> meshSet.sideSquare[idx]
else -> null
}
val upMesh = when(roundLog.upType) {
NONSOLID -> meshSet.flatTop(roundLog.quadrants, idx)
PERPENDICULAR -> {
if (!connectPerpendicular) {
meshSet.flatTop(roundLog.quadrants, idx)
} else {
meshSet.extendTop(roundLog.quadrants, idx)
}
}
PARALLEL -> {
if (roundLog.quadrants[idx] discontinuousWith roundLog.quadrantsTop[idx] &&
roundLog.quadrants[idx].let { it == SQUARE || it == INVISIBLE } )
meshSet.flatTop(roundLog.quadrants, idx)
else null
}
else -> null
}
val downMesh = when(roundLog.downType) {
NONSOLID -> meshSet.flatBottom(roundLog.quadrants, idx)
PERPENDICULAR -> {
if (!connectPerpendicular) {
meshSet.flatBottom(roundLog.quadrants, idx)
} else {
meshSet.extendBottom(roundLog.quadrants, idx)
}
}
PARALLEL -> {
if (roundLog.quadrants[idx] discontinuousWith roundLog.quadrantsBottom[idx] &&
roundLog.quadrants[idx].let { it == SQUARE || it == INVISIBLE } )
meshSet.flatBottom(roundLog.quadrants, idx)
else null
}
else -> null
}
// render
sideMesh?.let { context.meshConsumer().accept(it) }
upMesh?.let { context.meshConsumer().accept(it) }
downMesh?.let { context.meshConsumer().accept(it) }
}
}
}

View File

@@ -0,0 +1,186 @@
package mods.betterfoliage.render.column
import mods.betterfoliage.BetterFoliage
import mods.betterfoliage.chunk.ChunkOverlayLayer
import mods.betterfoliage.chunk.ChunkOverlayManager
import mods.betterfoliage.chunk.dimType
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.BlockType.*
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType
import mods.betterfoliage.render.column.ColumnLayerData.SpecialRender.QuadrantType.*
import mods.betterfoliage.chunk.BlockCtx
import mods.betterfoliage.util.*
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction.Axis
import net.minecraft.util.math.Direction.AxisDirection
import net.minecraft.world.ExtendedBlockView
/** Index of SOUTH-EAST quadrant. */
const val SE = 0
/** Index of NORTH-EAST quadrant. */
const val NE = 1
/** Index of NORTH-WEST quadrant. */
const val NW = 2
/** Index of SOUTH-WEST quadrant. */
const val SW = 3
interface ColumnBlockKey {
val axis: Axis?
}
/**
* Sealed class hierarchy for all possible render outcomes
*/
sealed class ColumnLayerData {
/**
* Data structure to cache texture and world neighborhood data relevant to column rendering
*/
@Suppress("ArrayInDataClass") // not used in comparisons anywhere
data class SpecialRender(
val column: ColumnBlockKey,
val upType: BlockType,
val downType: BlockType,
val quadrants: Array<QuadrantType>,
val quadrantsTop: Array<QuadrantType>,
val quadrantsBottom: Array<QuadrantType>
) : ColumnLayerData() {
enum class BlockType { SOLID, NONSOLID, PARALLEL, PERPENDICULAR }
enum class QuadrantType {
SMALL_RADIUS, LARGE_RADIUS, SQUARE, INVISIBLE;
infix fun continuousWith(other: QuadrantType) =
this == other || ((this == SQUARE || this == INVISIBLE) && (other == SQUARE || other == INVISIBLE))
infix fun discontinuousWith(other: QuadrantType) = !continuousWith(other)
}
}
/** Column block should not be rendered at all */
object SkipRender : ColumnLayerData()
/** Column block must be rendered normally */
object NormalRender : ColumnLayerData()
/** Error while resolving render data, column block must be rendered normally */
object ResolveError : ColumnLayerData()
}
abstract class ColumnRenderLayer : ChunkOverlayLayer<ColumnLayerData> {
abstract val connectSolids: Boolean
abstract val lenientConnect: Boolean
abstract val defaultToY: Boolean
abstract fun getColumnKey(state: BlockState): ColumnBlockKey?
val allNeighborOffsets = (-1..1).flatMap { offsetX -> (-1..1).flatMap { offsetY -> (-1..1).map { offsetZ -> Int3(offsetX, offsetY, offsetZ) }}}
override fun onBlockUpdate(world: ExtendedBlockView, pos: BlockPos) {
allNeighborOffsets.forEach { offset -> ChunkOverlayManager.clear(world.dimType, this, pos + offset) }
}
override fun calculate(ctx: BlockCtx): ColumnLayerData {
if (allDirections.all { ctx.offset(it).isNormalCube }) return ColumnLayerData.SkipRender
// val columnTextures = registry[ctx] ?: return ColumnLayerData.ResolveError
val columnTextures = getColumnKey(ctx.state) ?: return ColumnLayerData.ResolveError
// if log axis is not defined and "Default to vertical" config option is not set, render normally
val logAxis = columnTextures.axis ?: if (defaultToY) Axis.Y else return ColumnLayerData.NormalRender
// check log neighborhood
val baseRotation = Rotation.fromUp[(logAxis to AxisDirection.POSITIVE).face.ordinal]
val upType = ctx.blockType(baseRotation, logAxis, Int3(0, 1, 0))
val downType = ctx.blockType(baseRotation, logAxis, Int3(0, -1, 0))
val quadrants = Array(4) { SMALL_RADIUS }.checkNeighbors(ctx, baseRotation, logAxis, 0)
val quadrantsTop = Array(4) { SMALL_RADIUS }
if (upType == PARALLEL) quadrantsTop.checkNeighbors(ctx, baseRotation, logAxis, 1)
val quadrantsBottom = Array(4) { SMALL_RADIUS }
if (downType == PARALLEL) quadrantsBottom.checkNeighbors(ctx, baseRotation, logAxis, -1)
return ColumnLayerData.SpecialRender(columnTextures, upType, downType, quadrants, quadrantsTop, quadrantsBottom)
}
/** Sets the type of the given quadrant only if the new value is "stronger" (larger ordinal). */
inline fun Array<QuadrantType>.upgrade(idx: Int, value: QuadrantType) {
if (this[idx].ordinal < value.ordinal) this[idx] = value
}
/** Fill the array of [QuadrantType]s based on the blocks to the sides of this one. */
fun Array<QuadrantType>.checkNeighbors(ctx: BlockCtx, rotation: Rotation, logAxis: Axis, yOff: Int): Array<QuadrantType> {
val blkS = ctx.blockType(rotation, logAxis, Int3(0, yOff, 1))
val blkE = ctx.blockType(rotation, logAxis, Int3(1, yOff, 0))
val blkN = ctx.blockType(rotation, logAxis, Int3(0, yOff, -1))
val blkW = ctx.blockType(rotation, logAxis, Int3(-1, yOff, 0))
// a solid block on one side will make the 2 neighboring quadrants SQUARE
// if there are solid blocks to both sides of a quadrant, it is INVISIBLE
if (connectSolids) {
if (blkS == SOLID) {
upgrade(SW, SQUARE); upgrade(SE, SQUARE)
}
if (blkE == SOLID) {
upgrade(SE, SQUARE); upgrade(NE, SQUARE)
}
if (blkN == SOLID) {
upgrade(NE, SQUARE); upgrade(NW, SQUARE)
}
if (blkW == SOLID) {
upgrade(NW, SQUARE); upgrade(SW, SQUARE)
}
if (blkS == SOLID && blkE == SOLID) upgrade(SE, INVISIBLE)
if (blkN == SOLID && blkE == SOLID) upgrade(NE, INVISIBLE)
if (blkN == SOLID && blkW == SOLID) upgrade(NW, INVISIBLE)
if (blkS == SOLID && blkW == SOLID) upgrade(SW, INVISIBLE)
}
val blkSE = ctx.blockType(rotation, logAxis, Int3(1, yOff, 1))
val blkNE = ctx.blockType(rotation, logAxis, Int3(1, yOff, -1))
val blkNW = ctx.blockType(rotation, logAxis, Int3(-1, yOff, -1))
val blkSW = ctx.blockType(rotation, logAxis, Int3(-1, yOff, 1))
if (lenientConnect) {
// if the block forms the tip of an L-shape, connect to its neighbor with SQUARE quadrants
if (blkE == PARALLEL && (blkSE == PARALLEL || blkNE == PARALLEL)) {
upgrade(SE, SQUARE); upgrade(NE, SQUARE)
}
if (blkN == PARALLEL && (blkNE == PARALLEL || blkNW == PARALLEL)) {
upgrade(NE, SQUARE); upgrade(NW, SQUARE)
}
if (blkW == PARALLEL && (blkNW == PARALLEL || blkSW == PARALLEL)) {
upgrade(NW, SQUARE); upgrade(SW, SQUARE)
}
if (blkS == PARALLEL && (blkSE == PARALLEL || blkSW == PARALLEL)) {
upgrade(SW, SQUARE); upgrade(SE, SQUARE)
}
}
// if the block forms the middle of an L-shape, or is part of a 2x2 configuration,
// connect to its neighbors with SQUARE quadrants, INVISIBLE on the inner corner, and LARGE_RADIUS on the outer corner
if (blkN == PARALLEL && blkW == PARALLEL && (lenientConnect || blkNW == PARALLEL)) {
upgrade(SE, LARGE_RADIUS); upgrade(NE, SQUARE); upgrade(SW, SQUARE); upgrade(NW, INVISIBLE)
}
if (blkS == PARALLEL && blkW == PARALLEL && (lenientConnect || blkSW == PARALLEL)) {
upgrade(NE, LARGE_RADIUS); upgrade(SE, SQUARE); upgrade(NW, SQUARE); upgrade(SW, INVISIBLE)
}
if (blkS == PARALLEL && blkE == PARALLEL && (lenientConnect || blkSE == PARALLEL)) {
upgrade(NW, LARGE_RADIUS); upgrade(NE, SQUARE); upgrade(SW, SQUARE); upgrade(SE, INVISIBLE)
}
if (blkN == PARALLEL && blkE == PARALLEL && (lenientConnect || blkNE == PARALLEL)) {
upgrade(SW, LARGE_RADIUS); upgrade(SE, SQUARE); upgrade(NW, SQUARE); upgrade(NE, INVISIBLE)
}
return this
}
/**
* Get the type of the block at the given offset in a rotated reference frame.
*/
fun BlockCtx.blockType(rotation: Rotation, axis: Axis, offset: Int3): ColumnLayerData.SpecialRender.BlockType {
val offsetRot = offset.rotate(rotation)
val key = getColumnKey(state(offsetRot))
return if (key == null) {
if (offset(offsetRot).isNormalCube) SOLID else NONSOLID
} else {
(key.axis ?: if (BetterFoliage.config.roundLogs.defaultY) Axis.Y else null)?.let {
if (it == axis) PARALLEL else PERPENDICULAR
} ?: SOLID
}
}
}

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