mirror of
https://github.com/wisplite/crystalline-sky.git
synced 2026-10-30 13:32:18 -05:00
Multiloader I
move all of src
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
// 1.21.1 2026-04-04T17:09:36.734955339 Crystalline Sky/Model Definitions
|
||||
fceb0c59b7f6d431200764ccf87596541bacb17f assets/crystalline_sky/models/block/sky_light_07.json
|
||||
dff33a68a694de0f3ef177886c002204ffe49a8c assets/crystalline_sky/models/item/sky_light_07.json
|
||||
51fa40ff02afcec303e7a18478cbae5375dfb140 assets/crystalline_sky/models/item/sky_light_04.json
|
||||
14508173fcca14cb42755d47374c94d3eace42ce assets/crystalline_sky/models/block/weeping_sky.json
|
||||
317f9881c20b1aaa3902b3999dd1c76d00afbde6 assets/crystalline_sky/models/block/sky_light_04.json
|
||||
9b3d01168a1cc29b99cf69fe225ad69b0923c9fe assets/crystalline_sky/blockstates/weeping_sky_light.json
|
||||
a87d8988452529a1f5af5662ad636223fdd98fec assets/crystalline_sky/models/block/sky_light_15.json
|
||||
0e8a96cfb88f4c58679709cc9397b5700878a9c4 assets/crystalline_sky/models/item/sky_light_01.json
|
||||
ecc125a24d3e2c86d922352d8449f26a4e06d122 assets/crystalline_sky/blockstates/sky_light.json
|
||||
b8795df31017499b35a5b37a4dfbe555a8242cb0 assets/crystalline_sky/models/item/sky_light_12.json
|
||||
d500e56057c0c2f3c948894694e2826f1726fd9e assets/crystalline_sky/models/item/sky_light_15.json
|
||||
0b4015acca6117ea633ae3cdaf3fe40fcab6b628 assets/crystalline_sky/models/block/sky_light_13.json
|
||||
7115b88da2cc0bfdc68646d034de8d14f181eb01 assets/crystalline_sky/models/item/sky_light_09.json
|
||||
910c51120a0931851db6b75b337bc21241165c2f assets/crystalline_sky/models/item/sky_light_06.json
|
||||
59463cef438e58b137e5ce27402acf42a3a0ae87 assets/crystalline_sky/models/block/sky_light_02.json
|
||||
9c6ba4d90f2c4f2a9beb910c1b32023e49445749 assets/crystalline_sky/models/block/sky_light_10.json
|
||||
ef5afdbcc8401a27029b6f0a29fa1117761c620d assets/crystalline_sky/models/item/sky_light.json
|
||||
9f0d9fe5dfb05bbe18177db5c40e600c5b256858 assets/crystalline_sky/models/item/sky_light_00.json
|
||||
b29c14f77a08da7a6994cff4c6801d6f04b12ec0 assets/crystalline_sky/models/item/sky_light_03.json
|
||||
19859c3124196f3788bfbae1ec2bcd4d1e46db2e assets/crystalline_sky/models/block/sky_light_06.json
|
||||
508ffb97ee711b53767b9d33659596197f69ee20 assets/crystalline_sky/models/block/weeping_sky_light.json
|
||||
ef9e5aca345b59cb4692a7bcfaac539aed6f6889 assets/crystalline_sky/models/block/sky_light_09.json
|
||||
04f1e4eba184b8e7b1669fc61006e3d00e09d164 assets/crystalline_sky/models/item/sky_light_11.json
|
||||
0ac5351c3a01e0f9e76517452918e12770b3b54b assets/crystalline_sky/models/block/sky_light_14.json
|
||||
58349dc5dedb8016fb0bc958fcd4a9ccde13a1cd assets/crystalline_sky/models/item/sky_light_14.json
|
||||
03e2eb768bc34fbeaabffbff8ed3a2a3044a6db1 assets/crystalline_sky/blockstates/weeping_sky.json
|
||||
be0be7e1d7ce1ccabed01ef09c6499a2a7f2b132 assets/crystalline_sky/models/item/weeping_sky_light.json
|
||||
e91808daecfc454b784f5fb27003c58bb0caedce assets/crystalline_sky/models/block/sky_light_11.json
|
||||
3183aeb9fff78d0c65f1ddd0dfb599420573e281 assets/crystalline_sky/models/block/sky_light_00.json
|
||||
ee9cb7604922cdb2ad71d021a593ca82f0baf493 assets/crystalline_sky/models/block/sky_light_03.json
|
||||
c0d693e4f78ce15c238909130382909abda4c922 assets/crystalline_sky/models/block/sky_light_08.json
|
||||
2ce612b8445da92c6cecd07bd5364a25bcfb127f assets/crystalline_sky/models/block/sky_light_05.json
|
||||
25f0831f8c64db0e5c08fc968445e329f1e3a91c assets/crystalline_sky/models/item/sky_light_13.json
|
||||
15c0a47d504ddd41bbecd4ac58c0c8ccd0f37943 assets/crystalline_sky/models/item/weeping_sky.json
|
||||
ed5894de7afa66f73c72093f2dd7d046cf647070 assets/crystalline_sky/models/item/sky_light_02.json
|
||||
42f8b696ba0f511edce656b9055764ed13013221 assets/crystalline_sky/models/item/sky_light_05.json
|
||||
9c6c61b7f805a614d2d15d8dde390ed068913182 assets/crystalline_sky/models/item/sky_light_10.json
|
||||
6d1e10716c05150e73b2b9e254091125264a7a3d assets/crystalline_sky/models/block/sky.json
|
||||
e2c7bebd9eca446bb06fff866ec4d4996917e7e4 assets/crystalline_sky/models/block/sky_light_12.json
|
||||
e832366c216102270b75085db7a7169881b853b3 assets/crystalline_sky/blockstates/sky.json
|
||||
7eaa4c3d4913e0cb117585204dd1dd9e58402b83 assets/crystalline_sky/models/block/sky_light_01.json
|
||||
89582890605d371d936384a83950af603d2fe495 assets/crystalline_sky/models/item/sky_light_08.json
|
||||
008a0d0a76a34d8f375fa355452a5d0f93f9af3b assets/crystalline_sky/models/item/sky.json
|
||||
@@ -0,0 +1,3 @@
|
||||
// 1.21.1 2026-04-04T17:09:36.733906405 Crystalline Sky/Tags for minecraft:block
|
||||
606477607a09045ff2830cbc036e744d6a728bbd data/minecraft/tags/block/crystal_sound_blocks.json
|
||||
1389c0b812f7a6e4d5dd00edac1dc07beb5f9552 data/crystalline_sky/tags/block/sky_light_emitters.json
|
||||
@@ -0,0 +1,3 @@
|
||||
// 1.21.1 2026-04-04T17:09:36.73618123 Crystalline Sky/Block Loot Tables
|
||||
337de0acfc1b9b86adde0705e342ed1d4b5c9c4e data/crystalline_sky/loot_table/blocks/weeping_sky.json
|
||||
6a9b7a08424cbd3d699dfd3252d432517254aa45 data/crystalline_sky/loot_table/blocks/sky.json
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "crystalline_sky:block/sky"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"variants": {
|
||||
"level=0": {
|
||||
"model": "crystalline_sky:block/sky_light_00"
|
||||
},
|
||||
"level=1": {
|
||||
"model": "crystalline_sky:block/sky_light_01"
|
||||
},
|
||||
"level=10": {
|
||||
"model": "crystalline_sky:block/sky_light_10"
|
||||
},
|
||||
"level=11": {
|
||||
"model": "crystalline_sky:block/sky_light_11"
|
||||
},
|
||||
"level=12": {
|
||||
"model": "crystalline_sky:block/sky_light_12"
|
||||
},
|
||||
"level=13": {
|
||||
"model": "crystalline_sky:block/sky_light_13"
|
||||
},
|
||||
"level=14": {
|
||||
"model": "crystalline_sky:block/sky_light_14"
|
||||
},
|
||||
"level=15": {
|
||||
"model": "crystalline_sky:block/sky_light_15"
|
||||
},
|
||||
"level=2": {
|
||||
"model": "crystalline_sky:block/sky_light_02"
|
||||
},
|
||||
"level=3": {
|
||||
"model": "crystalline_sky:block/sky_light_03"
|
||||
},
|
||||
"level=4": {
|
||||
"model": "crystalline_sky:block/sky_light_04"
|
||||
},
|
||||
"level=5": {
|
||||
"model": "crystalline_sky:block/sky_light_05"
|
||||
},
|
||||
"level=6": {
|
||||
"model": "crystalline_sky:block/sky_light_06"
|
||||
},
|
||||
"level=7": {
|
||||
"model": "crystalline_sky:block/sky_light_07"
|
||||
},
|
||||
"level=8": {
|
||||
"model": "crystalline_sky:block/sky_light_08"
|
||||
},
|
||||
"level=9": {
|
||||
"model": "crystalline_sky:block/sky_light_09"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "crystalline_sky:block/weeping_sky"
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "crystalline_sky:block/weeping_sky_light"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/cube_all",
|
||||
"textures": {
|
||||
"all": "crystalline_sky:block/sky"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_00"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_01"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_02"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_03"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_04"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_05"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_06"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_07"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_08"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_09"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_10"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_11"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_12"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_13"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_14"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/sky_light_15"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:block/cube_all",
|
||||
"textures": {
|
||||
"all": "crystalline_sky:block/weeping_sky"
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": {
|
||||
"particle": "crystalline_sky:item/weeping_sky_light"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:block/sky"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"overrides": [
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_00",
|
||||
"predicate": {
|
||||
"level": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_01",
|
||||
"predicate": {
|
||||
"level": 0.0625
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_02",
|
||||
"predicate": {
|
||||
"level": 0.125
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_03",
|
||||
"predicate": {
|
||||
"level": 0.1875
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_04",
|
||||
"predicate": {
|
||||
"level": 0.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_05",
|
||||
"predicate": {
|
||||
"level": 0.3125
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_06",
|
||||
"predicate": {
|
||||
"level": 0.375
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_07",
|
||||
"predicate": {
|
||||
"level": 0.4375
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_08",
|
||||
"predicate": {
|
||||
"level": 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_09",
|
||||
"predicate": {
|
||||
"level": 0.5625
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_10",
|
||||
"predicate": {
|
||||
"level": 0.625
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_11",
|
||||
"predicate": {
|
||||
"level": 0.6875
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_12",
|
||||
"predicate": {
|
||||
"level": 0.75
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_13",
|
||||
"predicate": {
|
||||
"level": 0.8125
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_14",
|
||||
"predicate": {
|
||||
"level": 0.875
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "crystalline_sky:item/sky_light_15",
|
||||
"predicate": {
|
||||
"level": 0.9375
|
||||
}
|
||||
}
|
||||
],
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_00"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_01"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_02"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_03"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_04"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_05"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_06"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_07"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_08"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_09"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_10"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_11"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_12"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_13"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_14"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/sky_light_15"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:block/weeping_sky"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "crystalline_sky:item/weeping_sky_light"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"type": "minecraft:block",
|
||||
"pools": [
|
||||
{
|
||||
"bonus_rolls": 0.0,
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:match_tool",
|
||||
"predicate": {
|
||||
"predicates": {
|
||||
"minecraft:enchantments": [
|
||||
{
|
||||
"enchantments": "minecraft:silk_touch",
|
||||
"levels": {
|
||||
"min": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "crystalline_sky:sky"
|
||||
}
|
||||
],
|
||||
"rolls": 1.0
|
||||
}
|
||||
]
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"type": "minecraft:block",
|
||||
"pools": [
|
||||
{
|
||||
"bonus_rolls": 0.0,
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:match_tool",
|
||||
"predicate": {
|
||||
"predicates": {
|
||||
"minecraft:enchantments": [
|
||||
{
|
||||
"enchantments": "minecraft:silk_touch",
|
||||
"levels": {
|
||||
"min": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "crystalline_sky:weeping_sky"
|
||||
}
|
||||
],
|
||||
"rolls": 1.0
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"values": [
|
||||
"crystalline_sky:sky",
|
||||
"crystalline_sky:sky_light",
|
||||
"crystalline_sky:weeping_sky",
|
||||
"crystalline_sky:weeping_sky_light"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"values": [
|
||||
"crystalline_sky:sky",
|
||||
"crystalline_sky:weeping_sky"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.github.slimeistdev.crystalline_sky;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class CrystallineSky implements ModInitializer {
|
||||
public static final String ID = "crystalline_sky";
|
||||
public static final Logger LOG = LoggerFactory.getLogger(ID);
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
ModSetup.init();
|
||||
}
|
||||
|
||||
public static ResourceLocation id(String path) {
|
||||
return ResourceLocation.fromNamespaceAndPath(ID, path);
|
||||
}
|
||||
|
||||
public static <T> ResourceKey<T> key(ResourceKey<? extends Registry<T>> registryKey, String path) {
|
||||
return ResourceKey.create(registryKey, id(path));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package io.github.slimeistdev.crystalline_sky;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.mixin_ducks.client.DebugRenderer_Duck;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineBlocks;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineItems;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.client.CrystallineRenderTypes;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
|
||||
import net.minecraft.world.level.block.LightBlock;
|
||||
import net.minecraft.client.renderer.item.ItemProperties;
|
||||
import net.minecraft.client.KeyMapping;
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.world.item.component.BlockItemStateProperties;
|
||||
import net.minecraft.network.chat.CommonComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
public class CrystallineSkyClient implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
BlockRenderLayerMap.INSTANCE.putBlock(CrystallineBlocks.SKY, CrystallineRenderTypes.SKY);
|
||||
BlockRenderLayerMap.INSTANCE.putBlock(CrystallineBlocks.WEEPING_SKY, CrystallineRenderTypes.SKY);
|
||||
|
||||
var weepingSkyKeybind = KeyBindingHelper.registerKeyBinding(new KeyMapping(
|
||||
"crystalline_sky.key.toggle_weeping_sky_debug",
|
||||
InputConstants.Type.KEYSYM,
|
||||
GLFW.GLFW_KEY_UNKNOWN,
|
||||
"crystalline_sky.category.crystalline_sky"
|
||||
));
|
||||
|
||||
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
||||
while (weepingSkyKeybind.consumeClick()) {
|
||||
int mode = ((DebugRenderer_Duck) client.debugRenderer).crystalline_sky$toggleWeepingSky();
|
||||
|
||||
Component message = getDebugMessage(ChatFormatting.YELLOW,
|
||||
Component.translatable("message.crystalline_sky.weeping_sky_debug." + mode)
|
||||
);
|
||||
|
||||
client.gui.getChat().addMessage(message);
|
||||
}
|
||||
});
|
||||
|
||||
ItemProperties.register(CrystallineItems.SKY_LIGHT, ResourceLocation.withDefaultNamespace("level"),
|
||||
(stack, world, entity, seed) -> {
|
||||
BlockItemStateProperties blockStateComponent = stack.getOrDefault(DataComponents.BLOCK_STATE, BlockItemStateProperties.EMPTY);
|
||||
Integer integer = blockStateComponent.get(LightBlock.LEVEL);
|
||||
return integer != null ? integer / 16.0F : 1.0F;
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static Component getDebugMessage(ChatFormatting formatting, Component message) {
|
||||
return Component.empty().append(Component.translatable("debug.prefix").withStyle(formatting, ChatFormatting.BOLD)).append(CommonComponents.SPACE).append(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.github.slimeistdev.crystalline_sky;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineBlocks;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineDataComponentTypes;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineItems;
|
||||
|
||||
public class ModSetup {
|
||||
public static void init() {
|
||||
CrystallineDataComponentTypes.init();
|
||||
CrystallineItems.init();
|
||||
CrystallineBlocks.init();
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023-Present Bawnorton
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package io.github.slimeistdev.crystalline_sky.annotation.mixin;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.compat.Mods;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ConditionalMixin {
|
||||
Mods[] mods();
|
||||
|
||||
boolean applyIfPresent() default true;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package io.github.slimeistdev.crystalline_sky.compat;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public enum Mods {
|
||||
SODIUM("sodium"),
|
||||
AXIOM("axiom");
|
||||
|
||||
public final String id;
|
||||
public final boolean isLoaded;
|
||||
|
||||
Mods(String id) {
|
||||
this.id = id;
|
||||
this.isLoaded = FabricLoader.getInstance().isModLoaded(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple hook to run code if a mod is installed
|
||||
*
|
||||
* @param toRun will be run only if the mod is loaded
|
||||
* @return Optional.empty() if the mod is not loaded, otherwise an Optional of the return value of the given supplier
|
||||
*/
|
||||
public <T> Optional<T> runIfInstalled(Supplier<Supplier<T>> toRun) {
|
||||
if (isLoaded)
|
||||
return Optional.of(toRun.get().get());
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple hook to execute code if a mod is installed
|
||||
*
|
||||
* @param toExecute will be executed only if the mod is loaded
|
||||
*/
|
||||
public void executeIfInstalled(Supplier<Runnable> toExecute) {
|
||||
if (isLoaded) {
|
||||
toExecute.get().run();
|
||||
}
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package io.github.slimeistdev.crystalline_sky.compat.sodium;
|
||||
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineItems;
|
||||
import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformFloat3v;
|
||||
import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformFloat4v;
|
||||
import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformInt;
|
||||
import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformMatrix4f;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.shader.ChunkShaderFogComponent;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.shader.ChunkShaderInterface;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.shader.ChunkShaderOptions;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.shader.ChunkShaderTextureSlot;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.shader.ShaderBindingContext;
|
||||
import net.caffeinemc.mods.sodium.client.util.TextureUtil;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.Mth;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.lwjgl.opengl.GL32C;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||
public class SkyShaderInterface implements ChunkShaderInterface {
|
||||
private final Map<ChunkShaderTextureSlot, GlUniformInt> uniformTextures;
|
||||
|
||||
private final GlUniformMatrix4f uniformModelViewMatrix;
|
||||
private final GlUniformMatrix4f uniformProjectionMatrix;
|
||||
private final GlUniformFloat3v uniformRegionOffset;
|
||||
private final GlUniformFloat4v uniformColorModulator;
|
||||
|
||||
// The fog shader component used by this program in order to set up the appropriate GL state
|
||||
private final ChunkShaderFogComponent fogShader;
|
||||
|
||||
private final float[] colorModulator = new float[] {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
|
||||
public SkyShaderInterface(ShaderBindingContext context, ChunkShaderOptions options) {
|
||||
this.uniformModelViewMatrix = context.bindUniform("u_ModelViewMatrix", GlUniformMatrix4f::new);
|
||||
this.uniformProjectionMatrix = context.bindUniform("u_ProjectionMatrix", GlUniformMatrix4f::new);
|
||||
this.uniformRegionOffset = context.bindUniform("u_RegionOffset", GlUniformFloat3v::new);
|
||||
this.uniformColorModulator = context.bindUniform("u_ColorModulator", GlUniformFloat4v::new);
|
||||
|
||||
this.uniformTextures = new EnumMap<>(ChunkShaderTextureSlot.class);
|
||||
this.uniformTextures.put(ChunkShaderTextureSlot.BLOCK, context.bindUniform("u_BlockTex", GlUniformInt::new));
|
||||
|
||||
this.fogShader = options.fog().getFactory().apply(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupState() {
|
||||
this.bindTexture(ChunkShaderTextureSlot.BLOCK, TextureUtil.getBlockTextureId());
|
||||
|
||||
Minecraft client = Minecraft.getInstance();
|
||||
if (client.level != null
|
||||
&& client.player != null
|
||||
&& (client.player.getMainHandItem().is(CrystallineItems.SKY)
|
||||
|| client.player.getOffhandItem().is(CrystallineItems.SKY)
|
||||
|| client.player.getMainHandItem().is(CrystallineItems.WEEPING_SKY)
|
||||
|| client.player.getOffhandItem().is(CrystallineItems.WEEPING_SKY))) {
|
||||
|
||||
float f = client.level.getGameTime() + client.getTimer().getGameTimeDeltaPartialTick(true);
|
||||
float alpha = (Mth.sin(f / 10.0f) + 1.0f) / 2.0f;
|
||||
// remap alpha from [0, 1] to [0, 0.75]
|
||||
float maxAlpha = 1.0f - 0.25f;
|
||||
alpha = alpha * maxAlpha;
|
||||
colorModulator[3] = alpha;
|
||||
} else {
|
||||
colorModulator[3] = 1.0f;
|
||||
}
|
||||
this.uniformColorModulator.set(colorModulator);
|
||||
|
||||
this.fogShader.setup();
|
||||
}
|
||||
|
||||
@Override // the shader interface should not modify pipeline state
|
||||
public void resetState() {
|
||||
// This is used by alternate implementations.
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
@Deprecated(
|
||||
forRemoval = true
|
||||
)
|
||||
private void bindTexture(ChunkShaderTextureSlot slot, int textureId) {
|
||||
GlStateManager._activeTexture(GL32C.GL_TEXTURE0 + slot.ordinal());
|
||||
GlStateManager._bindTexture(textureId);
|
||||
GlUniformInt uniform = this.uniformTextures.get(slot);
|
||||
uniform.setInt(slot.ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProjectionMatrix(Matrix4fc matrix) {
|
||||
this.uniformProjectionMatrix.set(matrix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModelViewMatrix(Matrix4fc matrix) {
|
||||
this.uniformModelViewMatrix.set(matrix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegionOffset(float x, float y, float z) {
|
||||
this.uniformRegionOffset.set(x, y, z);
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package io.github.slimeistdev.crystalline_sky.content.blocks;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineItems;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour.Properties;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.LightBlock;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import net.minecraft.world.phys.shapes.Shapes;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
|
||||
public class SkyLightBlock extends LightBlock {
|
||||
public SkyLightBlock(Properties settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
|
||||
return context.isHoldingItem(CrystallineItems.SKY_LIGHT) ? Shapes.block() : Shapes.empty();
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package io.github.slimeistdev.crystalline_sky.content.blocks;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineItems;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.RenderShape;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour.Properties;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import net.minecraft.world.phys.shapes.Shapes;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
|
||||
public class WeepingSkyLightBlock extends Block implements SimpleWaterloggedBlock {
|
||||
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
|
||||
|
||||
public WeepingSkyLightBlock(Properties settings) {
|
||||
super(settings);
|
||||
registerDefaultState(getStateDefinition().any().setValue(WATERLOGGED, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
builder.add(WATERLOGGED);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
|
||||
return context.isHoldingItem(CrystallineItems.WEEPING_SKY_LIGHT) ? Shapes.block() : Shapes.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean propagatesSkylightDown(BlockState state, BlockGetter world, BlockPos pos) {
|
||||
return state.getFluidState().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RenderShape getRenderShape(BlockState state) {
|
||||
return RenderShape.INVISIBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getShadeBrightness(BlockState state, BlockGetter world, BlockPos pos) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockState updateShape(
|
||||
BlockState state,
|
||||
Direction direction,
|
||||
BlockState neighborState,
|
||||
LevelAccessor world,
|
||||
BlockPos pos,
|
||||
BlockPos neighborPos
|
||||
) {
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
|
||||
}
|
||||
|
||||
return super.updateShape(state, direction, neighborState, world, pos, neighborPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FluidState getFluidState(BlockState state) {
|
||||
return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package io.github.slimeistdev.crystalline_sky.content.items;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineDataComponentTypes;
|
||||
import net.minecraft.world.item.Item.Properties;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ColumnPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
public class WeepingSkyBlockItem extends BlockItem {
|
||||
public WeepingSkyBlockItem(Block block, Properties settings) {
|
||||
super(block, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult useOn(UseOnContext context) {
|
||||
ItemStack stack = context.getItemInHand();
|
||||
if (stack.get(CrystallineDataComponentTypes.WEEPING_SKY_DEBUG_COLUMN_TOOL) != null) {
|
||||
if (context.getLevel().isClientSide) {
|
||||
BlockPos pos = context.getClickedPos();
|
||||
WeepingSkyBlockItemClient.setSelectedColumn(new ColumnPos(pos.getX(), pos.getZ()));
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
return super.useOn(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(Level world, Player user, InteractionHand hand) {
|
||||
ItemStack stack = user.getItemInHand(hand);
|
||||
if (stack.get(CrystallineDataComponentTypes.WEEPING_SKY_DEBUG_COLUMN_TOOL) != null) {
|
||||
if (world.isClientSide) {
|
||||
WeepingSkyBlockItemClient.setSelectedColumn(null);
|
||||
}
|
||||
return InteractionResultHolder.success(stack);
|
||||
}
|
||||
|
||||
return super.use(world, user, hand);
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package io.github.slimeistdev.crystalline_sky.content.items;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.infrastructure.client.WeepingSkyDebugRenderer;
|
||||
import io.github.slimeistdev.crystalline_sky.mixin_ducks.client.DebugRenderer_Duck;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ColumnPos;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class WeepingSkyBlockItemClient {
|
||||
// todone check if this is server-safe
|
||||
@Environment(EnvType.CLIENT)
|
||||
public static void setSelectedColumn(@Nullable ColumnPos column) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
WeepingSkyDebugRenderer weepingSkyDebugRenderer = ((DebugRenderer_Duck) mc.debugRenderer).crystalline_sky$getWeepingSkyDebugRenderer();
|
||||
weepingSkyDebugRenderer.setColumn(column);
|
||||
|
||||
Component text = column == null
|
||||
? Component.translatable("message.crystalline_sky.weeping_sky_debug.column.cleared")
|
||||
: Component.translatable("message.crystalline_sky.weeping_sky_debug.column.set", column.x(), column.z());
|
||||
mc.gui.setOverlayMessage(text, false);
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package io.github.slimeistdev.crystalline_sky.datagen;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.datagen.providers.CrystallineSkyBlockTagProvider;
|
||||
import io.github.slimeistdev.crystalline_sky.datagen.providers.CrystallineSkyLootProvider;
|
||||
import io.github.slimeistdev.crystalline_sky.datagen.providers.CrystallineSkyModelProvider;
|
||||
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
|
||||
|
||||
public class CrystallineSkyDataGenerator implements DataGeneratorEntrypoint {
|
||||
@Override
|
||||
public void onInitializeDataGenerator(FabricDataGenerator gen) {
|
||||
FabricDataGenerator.Pack pack = gen.createPack();
|
||||
|
||||
pack.addProvider(CrystallineSkyModelProvider::new);
|
||||
pack.addProvider(CrystallineSkyLootProvider::new);
|
||||
pack.addProvider(CrystallineSkyBlockTagProvider::new);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package io.github.slimeistdev.crystalline_sky.datagen.providers;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.CrystallineSky;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineBlocks;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class CrystallineSkyBlockTagProvider extends FabricTagProvider.BlockTagProvider {
|
||||
public CrystallineSkyBlockTagProvider(FabricDataOutput output, CompletableFuture<HolderLookup.Provider> registriesFuture) {
|
||||
super(output, registriesFuture);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addTags(HolderLookup.Provider wrapperLookup) {
|
||||
getOrCreateTagBuilder(TagKey.create(Registries.BLOCK, CrystallineSky.id("sky_light_emitters")))
|
||||
.add(CrystallineBlocks.SKY)
|
||||
.add(CrystallineBlocks.SKY_LIGHT)
|
||||
.add(CrystallineBlocks.WEEPING_SKY)
|
||||
.add(CrystallineBlocks.WEEPING_SKY_LIGHT);
|
||||
|
||||
getOrCreateTagBuilder(BlockTags.CRYSTAL_SOUND_BLOCKS)
|
||||
.add(CrystallineBlocks.SKY)
|
||||
.add(CrystallineBlocks.WEEPING_SKY);
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.github.slimeistdev.crystalline_sky.datagen.providers;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineBlocks;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class CrystallineSkyLootProvider extends FabricBlockLootTableProvider {
|
||||
public CrystallineSkyLootProvider(FabricDataOutput dataOutput, CompletableFuture<HolderLookup.Provider> registryLookup) {
|
||||
super(dataOutput, registryLookup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate() {
|
||||
dropWhenSilkTouch(CrystallineBlocks.SKY);
|
||||
dropWhenSilkTouch(CrystallineBlocks.WEEPING_SKY);
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package io.github.slimeistdev.crystalline_sky.datagen.providers;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineBlocks;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineItems;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.data.models.BlockModelGenerators;
|
||||
import net.minecraft.data.models.blockstates.Variant;
|
||||
import net.minecraft.data.models.blockstates.PropertyDispatch;
|
||||
import net.minecraft.data.models.ItemModelGenerators;
|
||||
import net.minecraft.data.models.model.ModelLocationUtils;
|
||||
import net.minecraft.data.models.model.ModelTemplates;
|
||||
import net.minecraft.data.models.model.TextureMapping;
|
||||
import net.minecraft.data.models.blockstates.VariantProperties;
|
||||
import net.minecraft.data.models.blockstates.MultiVariantGenerator;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
public class CrystallineSkyModelProvider extends FabricModelProvider {
|
||||
public CrystallineSkyModelProvider(FabricDataOutput output) {
|
||||
super(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateBlockStateModels(BlockModelGenerators gen) {
|
||||
registerSkyBlock(gen, CrystallineBlocks.SKY);
|
||||
registerSkyBlock(gen, CrystallineBlocks.WEEPING_SKY);
|
||||
registerSkyLightBlock(gen, CrystallineBlocks.SKY_LIGHT, CrystallineItems.SKY_LIGHT);
|
||||
|
||||
gen.createAirLikeBlock(CrystallineBlocks.WEEPING_SKY_LIGHT, CrystallineItems.WEEPING_SKY_LIGHT);
|
||||
gen.createSimpleFlatItemModel(CrystallineItems.WEEPING_SKY_LIGHT);
|
||||
}
|
||||
|
||||
private void registerSkyBlock(BlockModelGenerators gen, Block block) {
|
||||
gen.createTrivialCube(block);
|
||||
gen.createSimpleFlatItemModel(block);
|
||||
}
|
||||
|
||||
private void registerSkyLightBlock(BlockModelGenerators gen, Block block, Item item) {
|
||||
gen.skipAutoItemBlock(block);
|
||||
PropertyDispatch.C1<Integer> singleProperty = PropertyDispatch.property(BlockStateProperties.LEVEL);
|
||||
|
||||
for (int level = 0; level <= 15; level++) {
|
||||
String suffix = String.format(Locale.ROOT, "_%02d", level);
|
||||
ResourceLocation texture = TextureMapping.getItemTexture(item, suffix);
|
||||
|
||||
singleProperty.select(level, Variant.variant().with(
|
||||
VariantProperties.MODEL,
|
||||
ModelTemplates.PARTICLE_ONLY.createWithSuffix(block, suffix, TextureMapping.particle(texture), gen.modelOutput))
|
||||
);
|
||||
ModelTemplates.FLAT_ITEM.create(
|
||||
ModelLocationUtils.getModelLocation(item, suffix),
|
||||
TextureMapping.layer0(texture),
|
||||
gen.modelOutput
|
||||
);
|
||||
}
|
||||
|
||||
ModelTemplates.FLAT_ITEM.create(
|
||||
ModelLocationUtils.getModelLocation(item),
|
||||
TextureMapping.layer0(item),
|
||||
gen.modelOutput,
|
||||
(id, textures) -> {
|
||||
JsonObject base = ModelTemplates.FLAT_ITEM.createBaseTemplate(id, textures);
|
||||
var overrides = new JsonArray();
|
||||
for (int level = 0; level <= 15; level++) {
|
||||
JsonObject override = new JsonObject();
|
||||
|
||||
JsonObject predicate = new JsonObject();
|
||||
predicate.addProperty("level", level / 16f);
|
||||
override.add("predicate", predicate);
|
||||
|
||||
String suffix = String.format(Locale.ROOT, "_%02d", level);
|
||||
ResourceLocation model = TextureMapping.getItemTexture(item, suffix);
|
||||
override.addProperty("model", model.toString());
|
||||
|
||||
overrides.add(override);
|
||||
}
|
||||
base.add("overrides", overrides);
|
||||
return base;
|
||||
});
|
||||
gen.blockStateOutput.accept(MultiVariantGenerator.multiVariant(block).with(singleProperty));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateItemModels(ItemModelGenerators gen) {}
|
||||
}
|
||||
+753
@@ -0,0 +1,753 @@
|
||||
package io.github.slimeistdev.crystalline_sky.infrastructure;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.github.slimeistdev.crystalline_sky.util.ArrayUtils;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class IntWeepingStorage implements WeepingStorage {
|
||||
private final int[] sizes;
|
||||
|
||||
// each long is (y_sky << 32 | y_bottom)
|
||||
// y_sky is the position of the highest weeping sky block in a layer
|
||||
// y_bottom is the lowest illuminated position (so 1 above a solid block) (less than y_sky) in a layer, or -1 if the layer is unbounded
|
||||
// must be sorted top-to-bottom by y_sky
|
||||
private final int[][] columns;
|
||||
|
||||
private final int[] tempRunSplitSizes;
|
||||
private final int[][] tempRunSplits;
|
||||
|
||||
// only used for iterators
|
||||
private final int minY;
|
||||
|
||||
public IntWeepingStorage(int minY) {
|
||||
this.sizes = new int[16*16];
|
||||
this.columns = new int[16*16][0];
|
||||
|
||||
this.tempRunSplitSizes = new int[16*16*2];
|
||||
this.tempRunSplits = new int[16*16*2][0];
|
||||
|
||||
this.minY = minY;
|
||||
}
|
||||
|
||||
private static int getIndex(int localX, int localZ) {
|
||||
return localX + localZ * 16;
|
||||
}
|
||||
|
||||
private static int pack(short ySky, short yBottom) {
|
||||
return ((int)ySky << 16) | Short.toUnsignedInt(yBottom);
|
||||
}
|
||||
|
||||
private static short unpackSky(int packed) {
|
||||
return (short)(packed >>> 16);
|
||||
}
|
||||
|
||||
private static short unpackBottom(int packed) {
|
||||
return (short)(packed & 0xFFFF);
|
||||
}
|
||||
|
||||
private static int calculateIncreasedLength(int currentLength) {
|
||||
if (currentLength == 0)
|
||||
return 1;
|
||||
|
||||
// calculate next power of two
|
||||
return Integer.highestOneBit(currentLength) << 1;
|
||||
}
|
||||
|
||||
private void maybeShrinkColumn(int index) {
|
||||
int[] column = columns[index];
|
||||
int size = sizes[index];
|
||||
|
||||
int newLength = calculateIncreasedLength(size);
|
||||
if (column.length > newLength) {
|
||||
int[] newColumn = new int[newLength];
|
||||
System.arraycopy(column, 0, newColumn, 0, size);
|
||||
columns[index] = newColumn;
|
||||
}
|
||||
}
|
||||
|
||||
private void insertValue(int columnIndex, int i, int packed) {
|
||||
int[] column = columns[columnIndex];
|
||||
int size = sizes[columnIndex];
|
||||
|
||||
if (size + 1 > column.length) {
|
||||
int newLength = calculateIncreasedLength(column.length);
|
||||
int[] newColumn = new int[newLength];
|
||||
|
||||
System.arraycopy(column, 0, newColumn, 0, i);
|
||||
newColumn[i] = packed;
|
||||
System.arraycopy(column, i, newColumn, i + 1, size - i);
|
||||
|
||||
columns[columnIndex] = newColumn;
|
||||
} else {
|
||||
System.arraycopy(column, i, column, i + 1, size - i);
|
||||
column[i] = packed;
|
||||
}
|
||||
sizes[columnIndex] = ++size;
|
||||
}
|
||||
|
||||
/*
|
||||
unlit sections are iterated downward
|
||||
lit sections are iterated upward
|
||||
|
||||
a newly-unlit section should call insertTempRunSplit at its topY
|
||||
a newly-lit section should call insertTempRunSplit at its bottomY
|
||||
*/
|
||||
|
||||
/**
|
||||
* Will split a run in subsequent iterators
|
||||
* @param columnIndex column to split
|
||||
* @param splitY the new (isLit ? bottomY : topY) of a run that intersects this one
|
||||
* @param isLit whether this should be added to the splits for lit or unlit runs
|
||||
*/
|
||||
private void insertTempRunSplit(int columnIndex, int splitY, boolean isLit) {
|
||||
int index = columnIndex * 2 + (isLit ? 1 : 0);
|
||||
|
||||
int size = tempRunSplitSizes[index];
|
||||
int[] splits = tempRunSplits[index];
|
||||
|
||||
if (size == 0) {
|
||||
if (splits.length == 0) {
|
||||
tempRunSplits[index] = new int[] {splitY};
|
||||
} else {
|
||||
splits[0] = splitY;
|
||||
}
|
||||
tempRunSplitSizes[index] = ++size;
|
||||
} else {
|
||||
// binary search for insertion point
|
||||
int insertIndex = ArrayUtils.binarySearchSortedInsertionPoint(splits, splitY, size);
|
||||
if (insertIndex != -1) {
|
||||
if (size + 1 > splits.length) {
|
||||
int newLength = calculateIncreasedLength(splits.length);
|
||||
int[] newSplits = new int[newLength];
|
||||
|
||||
System.arraycopy(splits, 0, newSplits, 0, insertIndex);
|
||||
newSplits[insertIndex] = splitY;
|
||||
System.arraycopy(splits, insertIndex, newSplits, insertIndex + 1, splits.length - insertIndex);
|
||||
|
||||
tempRunSplits[index] = newSplits;
|
||||
} else {
|
||||
System.arraycopy(splits, insertIndex, splits, insertIndex + 1, size - insertIndex);
|
||||
splits[insertIndex] = splitY;
|
||||
}
|
||||
tempRunSplitSizes[index] = ++size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void litTempSplit(int columnIndex, int bottomY) {
|
||||
insertTempRunSplit(columnIndex, bottomY, true);
|
||||
}
|
||||
|
||||
private void unlitTempSplit(int columnIndex, int topY) {
|
||||
insertTempRunSplit(columnIndex, topY, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertSky(int localX, int y, int localZ, WeepingScanner scanner) {
|
||||
int index = getIndex(localX, localZ);
|
||||
int size = sizes[index];
|
||||
int[] column = columns[index];
|
||||
short yShort = (short) y;
|
||||
|
||||
if (size == 0) {
|
||||
int solidY = scanner.scan(localX, y, localZ);
|
||||
short shortLowestLitY = (short) (solidY == -1 ? -1 : solidY + 1);
|
||||
int packed = pack(yShort, shortLowestLitY);
|
||||
if (column.length == 0) {
|
||||
columns[index] = new int[] {packed};
|
||||
} else {
|
||||
column[0] = packed;
|
||||
}
|
||||
sizes[index] = ++size;
|
||||
litTempSplit(index, shortLowestLitY);
|
||||
} else {
|
||||
// possible cases:
|
||||
// 1. we are in a layer, and do nothing
|
||||
// 2. we are below the lowest layer, and create a new layer
|
||||
// 3. we are above a layer, and must scan to determine whether to join it or insert one above it
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
int packed = column[i];
|
||||
short sky = unpackSky(packed);
|
||||
short bottom = unpackBottom(packed);
|
||||
|
||||
if (yShort == sky)
|
||||
return; // no change
|
||||
|
||||
if (bottom != -1 && yShort < sky && (yShort == bottom || yShort == bottom - 1)) {
|
||||
// scan if layer can continue now, and extend it down if so. Else, fall through to in-or-below handling
|
||||
if (!scanner.faceBlocksLight(localX, bottom, localZ)) {
|
||||
int solidY = scanner.scan(localX, yShort, localZ);
|
||||
short shortLowestLitY = (short) (solidY == -1 ? -1 : solidY + 1);
|
||||
|
||||
if (i + 1 < size) { // maybe join layer below
|
||||
int packedBelow = column[i + 1];
|
||||
short skyBelow = unpackSky(packedBelow);
|
||||
short bottomBelow = unpackBottom(packedBelow);
|
||||
|
||||
if (solidY == -1 || solidY > skyBelow) { // join layer[i] and layer[i + 1]
|
||||
litTempSplit(index, skyBelow + 1); // the newly-lit region is [skyBelow + 1, bottom - 1]
|
||||
|
||||
column[i] = pack(sky, bottomBelow);
|
||||
System.arraycopy(column, i + 2, column, i + 1, size - (i + 2));
|
||||
sizes[index] = --size;
|
||||
|
||||
maybeShrinkColumn(index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// extend layer[i] down
|
||||
column[i] = pack(sky, shortLowestLitY);
|
||||
litTempSplit(index, shortLowestLitY);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (yShort > sky) { // above layer
|
||||
int solidY = scanner.scan(localX, y, localZ);
|
||||
if (solidY == -1 || solidY < sky) { // join layer
|
||||
column[i] = pack(yShort, bottom);
|
||||
litTempSplit(index, sky + 1); // the newly-lit region is [sky + 1, yShort]
|
||||
} else { // insert new layer above (at index i)
|
||||
insertValue(index, i, pack(yShort, (short) (solidY + 1)));
|
||||
litTempSplit(index, solidY + 1);
|
||||
}
|
||||
return;
|
||||
} else { // (yShort < sky) in or below
|
||||
if (bottom == -1 || yShort >= bottom) { // in layer, do nothing
|
||||
return;
|
||||
} else if (i == size - 1) { // below layer, add if last layer
|
||||
int solidY = scanner.scan(localX, y, localZ);
|
||||
short shortLowestLitY = (short) (solidY == -1 ? -1 : solidY + 1);
|
||||
|
||||
insertValue(index, size, pack(yShort, shortLowestLitY));
|
||||
litTempSplit(index, shortLowestLitY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertSolid(int localX, int y, int localZ, WeepingScanner scanner) {
|
||||
int index = getIndex(localX, localZ);
|
||||
int size = sizes[index];
|
||||
int[] column = columns[index];
|
||||
short yShort = (short) y;
|
||||
|
||||
// if we're inside a layer, limit it
|
||||
|
||||
boolean lightBlockedHere;
|
||||
boolean lightBlockedAbove;
|
||||
int lastLitY = -1;
|
||||
boolean blockingCalculated = false;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
int packed = column[i];
|
||||
short sky = unpackSky(packed);
|
||||
short bottom = unpackBottom(packed);
|
||||
|
||||
if (yShort > sky) { // we're above all remaining layers, so nothing to do
|
||||
return;
|
||||
} else if (yShort == sky) { // either remove layer or lower the start (scan for sky)
|
||||
int nextSky = scanner.scanForWeepingSky(localX, y, localZ);
|
||||
|
||||
if (nextSky == -1 || nextSky < bottom) { // remove layer
|
||||
System.arraycopy(column, i + 1, column, i, size - (i + 1));
|
||||
sizes[index] = --size;
|
||||
maybeShrinkColumn(index);
|
||||
} else { // lower start of layer
|
||||
column[i] = pack((short) nextSky, bottom);
|
||||
}
|
||||
|
||||
unlitTempSplit(index, sky);
|
||||
return;
|
||||
} else { // (yShort < sky) limit layer and potentially split layer
|
||||
if (!blockingCalculated) {
|
||||
lightBlockedHere = scanner.faceBlocksLight(localX, y, localZ);
|
||||
lightBlockedAbove = scanner.faceBlocksLight(localX, y + 1, localZ);
|
||||
|
||||
if (lightBlockedAbove) {
|
||||
lastLitY = y + 1;
|
||||
} else if (lightBlockedHere) {
|
||||
lastLitY = y;
|
||||
} else { // no effect
|
||||
return;
|
||||
}
|
||||
|
||||
blockingCalculated = true;
|
||||
}
|
||||
|
||||
if (lastLitY <= bottom) // no effect
|
||||
continue;
|
||||
|
||||
int nextSky = scanner.scanForWeepingSky(localX, y, localZ);
|
||||
|
||||
// limit
|
||||
column[i] = pack(sky, (short) lastLitY);
|
||||
|
||||
unlitTempSplit(index, lastLitY - 1);
|
||||
|
||||
if (nextSky != -1 && nextSky >= bottom) { // split
|
||||
insertValue(index, i + 1, pack((short) nextSky, bottom));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertAir(int localX, int y, int localZ, WeepingScanner scanner) {
|
||||
int index = getIndex(localX, localZ);
|
||||
int size = sizes[index];
|
||||
int[] column = columns[index];
|
||||
short yShort = (short) y;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
int packed = column[i];
|
||||
short sky = unpackSky(packed);
|
||||
short bottom = unpackBottom(packed);
|
||||
|
||||
if (yShort > sky) { // above all remaining layers, nothing to do
|
||||
return;
|
||||
} else if (yShort == sky) { // rescan for sky
|
||||
int nextSky = scanner.scanForWeepingSky(localX, y, localZ);
|
||||
|
||||
if (nextSky == -1 || nextSky < bottom) { // remove layer
|
||||
System.arraycopy(column, i + 1, column, i, size - (i + 1));
|
||||
sizes[index] = --size;
|
||||
maybeShrinkColumn(index);
|
||||
|
||||
unlitTempSplit(index, sky);
|
||||
} else { // lower start of layer
|
||||
column[i] = pack((short) nextSky, bottom);
|
||||
|
||||
unlitTempSplit(index, sky);
|
||||
}
|
||||
return;
|
||||
} else { // (yShort < sky) maybe extend layer down
|
||||
if (bottom != -1 && (yShort == bottom || yShort == bottom - 1)) { // we may have freed things up
|
||||
if (!scanner.faceBlocksLight(localX, bottom, localZ)) { // extend layer down or join with layer below
|
||||
int solidY = scanner.scan(localX, yShort, localZ);
|
||||
short shortLowestLitY = (short) (solidY == -1 ? -1 : solidY + 1);
|
||||
|
||||
if (i + 1 < size) { // maybe join layer below
|
||||
int packedBelow = column[i + 1];
|
||||
short skyBelow = unpackSky(packedBelow);
|
||||
short bottomBelow = unpackBottom(packedBelow);
|
||||
|
||||
if (solidY == -1 || solidY < skyBelow) { // join layer[i] and layer[i + 1]
|
||||
column[i] = pack(sky, bottomBelow);
|
||||
System.arraycopy(column, i + 2, column, i + 1, size - (i + 2));
|
||||
sizes[index] = --size;
|
||||
|
||||
litTempSplit(index, skyBelow + 1);
|
||||
|
||||
maybeShrinkColumn(index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// extend layer[i] down
|
||||
column[i] = pack(sky, shortLowestLitY);
|
||||
litTempSplit(index, shortLowestLitY);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
int[] empty = new int[0];
|
||||
for (int i = 0; i < 16*16; i++) {
|
||||
sizes[i] = 0;
|
||||
columns[i] = empty;
|
||||
|
||||
tempRunSplitSizes[i] = 0;
|
||||
tempRunSplits[i] = empty;
|
||||
}
|
||||
}
|
||||
|
||||
public void set(int localX, int localZ, ShortList layers) {
|
||||
Preconditions.checkArgument(layers.size() % 2 == 0, "Expected even number of shorts in layers");
|
||||
|
||||
int index = getIndex(localX, localZ);
|
||||
int size = layers.size() / 2;
|
||||
sizes[index] = size;
|
||||
int[] column = columns[index];
|
||||
|
||||
if (column.length < size) {
|
||||
column = new int[calculateIncreasedLength(size)];
|
||||
columns[index] = column;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
short sky = layers.getShort(i * 2);
|
||||
short bottom = layers.getShort(i * 2 + 1);
|
||||
column[i] = pack(sky, bottom);
|
||||
}
|
||||
|
||||
maybeShrinkColumn(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLit(int localX, int y, int localZ) {
|
||||
int index = getIndex(localX, localZ);
|
||||
int size = sizes[index];
|
||||
int[] column = columns[index];
|
||||
|
||||
if (size == 0)
|
||||
return false;
|
||||
|
||||
short yShort = (short) (y - minY);
|
||||
|
||||
// binary search
|
||||
int left = 0;
|
||||
int right = size - 1;
|
||||
|
||||
while (left <= right) {
|
||||
int mid = (left + right) / 2;
|
||||
int packed = column[mid];
|
||||
|
||||
short sky = unpackSky(packed);
|
||||
short bottom = unpackBottom(packed);
|
||||
|
||||
if (yShort > sky) { // we're higher
|
||||
right = mid - 1;
|
||||
} else if (yShort < sky) { // we're lower
|
||||
if (bottom == -1 || yShort >= bottom) {
|
||||
return true;
|
||||
} else {
|
||||
left = mid + 1;
|
||||
}
|
||||
} else { // yShort == sky
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: unlike all other methods in this class, this uses global y coordinates.
|
||||
*/
|
||||
@Override
|
||||
public Iterable<Run> iterateLitRuns(int localX, int localZ, int lowestSourceY) {
|
||||
int index = getIndex(localX, localZ);
|
||||
int size = sizes[index];
|
||||
int[] column = columns[index];
|
||||
|
||||
int splitsColumnIndex = index * 2 + 1;
|
||||
int splitsSize = tempRunSplitSizes[splitsColumnIndex];
|
||||
int[] splits = tempRunSplits[splitsColumnIndex];
|
||||
|
||||
List<Run> runs = new ArrayList<>(size);
|
||||
runs.add(new Run(lowestSourceY, Integer.MAX_VALUE));
|
||||
|
||||
if (lowestSourceY == Integer.MIN_VALUE) {
|
||||
return wrapUnlitRuns(splitsSize, splits, runs);
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
int packed = column[i];
|
||||
short sky = unpackSky(packed);
|
||||
short bottom = unpackBottom(packed);
|
||||
|
||||
int globalSky = sky + minY;
|
||||
int globalBottom = bottom == -1 ? Integer.MIN_VALUE : bottom + minY;
|
||||
|
||||
// if we're above lowestSourceY, don't bother
|
||||
|
||||
if (globalBottom >= lowestSourceY)
|
||||
continue;
|
||||
|
||||
// if we intersect with default sky, merge
|
||||
if (globalSky >= lowestSourceY - 1) {
|
||||
runs.set(0, new Run(globalBottom, Integer.MAX_VALUE));
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise, add a new run
|
||||
runs.add(new Run(globalBottom, globalSky));
|
||||
}
|
||||
|
||||
return wrapLitRuns(splitsSize, splits, runs);
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: unlike all other methods in this class, this uses global y coordinates.
|
||||
*/
|
||||
@Override
|
||||
public Iterable<Run> iterateUnlitRuns(int localX, int localZ, int lowestSourceY) {
|
||||
int index = getIndex(localX, localZ);
|
||||
int size = sizes[index];
|
||||
int[] column = columns[index];
|
||||
|
||||
int splitsColumnIndex = index * 2;
|
||||
int splitsSize = tempRunSplitSizes[splitsColumnIndex];
|
||||
int[] splits = tempRunSplits[splitsColumnIndex];
|
||||
|
||||
// fill with all gaps between layers, which are below lowestSourceY
|
||||
List<Run> runs = new ArrayList<>(size);
|
||||
|
||||
if (lowestSourceY == Integer.MIN_VALUE) {
|
||||
return wrapUnlitRuns(splitsSize, splits, runs);
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
runs.add(new Run(Integer.MIN_VALUE, lowestSourceY - 1));
|
||||
} else {
|
||||
int lastLowestLit = lowestSourceY;
|
||||
|
||||
// each iteration adds the gap above layer[i]
|
||||
for (int i = 0; i < size && lastLowestLit > Integer.MIN_VALUE; i++) {
|
||||
int packed = column[i];
|
||||
short sky = unpackSky(packed);
|
||||
short bottom = unpackBottom(packed);
|
||||
|
||||
int globalSky = sky + minY;
|
||||
int globalBottom = bottom == -1 ? Integer.MIN_VALUE : bottom + minY;
|
||||
|
||||
if (globalBottom >= lowestSourceY) // completely lit, what's the point?
|
||||
continue;
|
||||
|
||||
if (globalSky < lastLowestLit - 1) // gap
|
||||
runs.add(new Run(globalSky + 1, lastLowestLit - 1));
|
||||
|
||||
lastLowestLit = globalBottom;
|
||||
}
|
||||
|
||||
// add the gap below the lowest layer
|
||||
if (lastLowestLit > Integer.MIN_VALUE && lastLowestLit != lowestSourceY) {
|
||||
runs.add(new Run(Integer.MIN_VALUE, lastLowestLit - 1));
|
||||
}
|
||||
}
|
||||
|
||||
return wrapUnlitRuns(splitsSize, splits, runs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTempRunSplits(int localX, int localZ) {
|
||||
int[] empty = new int[0];
|
||||
|
||||
int baseIndex = getIndex(localX, localZ) * 2;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
int index = baseIndex + i;
|
||||
|
||||
tempRunSplitSizes[index] = 0;
|
||||
tempRunSplits[index] = empty;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isColumnEmpty(int localX, int localZ) {
|
||||
return sizes[getIndex(localX, localZ)] <= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNoUnlitSplits(int localX, int localZ) {
|
||||
return tempRunSplitSizes[getIndex(localX, localZ) * 2] <= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debugState(int localX, int localZ, Consumer<String> stringConsumer) {
|
||||
int index = getIndex(localX, localZ);
|
||||
int size = sizes[index];
|
||||
int[] column = columns[index];
|
||||
|
||||
if (size == 0) {
|
||||
stringConsumer.accept(" <empty>");
|
||||
} else {
|
||||
for (int i = 0; i < size; i++) {
|
||||
int packed = column[i];
|
||||
short sky = unpackSky(packed);
|
||||
short bottom = unpackBottom(packed);
|
||||
|
||||
stringConsumer.accept(" layer " + i + ": sky=" + (sky + minY) + ", bottom=" + (bottom == -1 ? "-1" : (bottom + minY)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** wrapped iterable must be sorted top-to-bottom, splits must be sorted bottom-to-top */
|
||||
public Iterable<Run> wrapLitRuns(int splitsSize, int[] splits, Iterable<Run> iterable) {
|
||||
if (splitsSize <= 0) return iterable;
|
||||
return () -> new LitSplittingIterator(splitsSize, splits, iterable.iterator());
|
||||
}
|
||||
|
||||
private class LitSplittingIterator implements Iterator<Run> {
|
||||
private final int[] splits;
|
||||
private final Iterator<Run> iterator;
|
||||
|
||||
private final Deque<Run> remainingSplits = new ArrayDeque<>();
|
||||
|
||||
private int currentSplitIndex;
|
||||
|
||||
private LitSplittingIterator(int splitsSize, int[] splits, Iterator<Run> iterator) {
|
||||
this.splits = splits;
|
||||
this.iterator = iterator;
|
||||
|
||||
this.currentSplitIndex = splitsSize - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !remainingSplits.isEmpty() || iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Run next() {
|
||||
if (!remainingSplits.isEmpty()) {
|
||||
return remainingSplits.removeFirst();
|
||||
}
|
||||
|
||||
Run run = iterator.next();
|
||||
while (currentSplitIndex >= 0) {
|
||||
int splitY = splits[currentSplitIndex] + minY; // becomes the new bottomY of a run
|
||||
if (splitY < run.bottomY()) { // split is below run, not relevant (yet)
|
||||
return run;
|
||||
} else if (splitY > run.topY()) { // split is above run, will never be relevant
|
||||
currentSplitIndex--;
|
||||
} else if (splitY == run.bottomY()) { // run already fulfills the split
|
||||
currentSplitIndex--;
|
||||
return run;
|
||||
} else { // split is inside run, split it
|
||||
Run upper = new Run(splitY, run.topY());
|
||||
|
||||
run = new Run(run.bottomY(), splitY - 1);
|
||||
remainingSplits.addFirst(upper);
|
||||
currentSplitIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
return run;
|
||||
}
|
||||
}
|
||||
|
||||
/** wrapped iterable must be sorted top-to-bottom, splits must be sorted bottom-to-top */
|
||||
private Iterable<Run> wrapUnlitRuns(int splitsSize, int[] splits, Iterable<Run> iterable) {
|
||||
if (splitsSize <= 0) return iterable;
|
||||
return () -> new UnlitSplittingIterator(splitsSize, splits, iterable.iterator());
|
||||
}
|
||||
|
||||
private class UnlitSplittingIterator implements Iterator<Run> {
|
||||
private final int[] splits;
|
||||
private final Iterator<Run> iterator;
|
||||
|
||||
private final Deque<Run> remainingSplits = new ArrayDeque<>();
|
||||
|
||||
private int currentSplitIndex;
|
||||
|
||||
private UnlitSplittingIterator(int splitsSize, int[] splits, Iterator<Run> iterator) {
|
||||
this.splits = splits;
|
||||
this.iterator = iterator;
|
||||
|
||||
this.currentSplitIndex = splitsSize - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !remainingSplits.isEmpty() || iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Run next() {
|
||||
if (!remainingSplits.isEmpty()) {
|
||||
return remainingSplits.removeFirst();
|
||||
}
|
||||
|
||||
Run run = iterator.next();
|
||||
while (currentSplitIndex >= 0) {
|
||||
int splitY = splits[currentSplitIndex] + minY; // becomes the new topY of a run
|
||||
if (splitY < run.bottomY()) { // split is below run, not relevant (yet)
|
||||
return run;
|
||||
} else if (splitY > run.topY()) { // split is above run, will never be relevant
|
||||
currentSplitIndex--;
|
||||
return run;
|
||||
} else if (splitY == run.topY()) { // run already fulfills the split
|
||||
currentSplitIndex--;
|
||||
return run;
|
||||
} else { // split is inside run, split it
|
||||
Run lower = new Run(run.bottomY(), splitY);
|
||||
|
||||
run = new Run(splitY + 1, run.topY());
|
||||
remainingSplits.addFirst(lower);
|
||||
currentSplitIndex--;
|
||||
}
|
||||
}
|
||||
return run;
|
||||
}
|
||||
}
|
||||
|
||||
/*private class DownwardIterator implements IntIterator {
|
||||
private final int[] column;
|
||||
private final int size;
|
||||
private int i;
|
||||
private int currentY;
|
||||
|
||||
public DownwardIterator(int localX, int localZ, int yMax) {
|
||||
int index = getIndex(localX, localZ);
|
||||
|
||||
int actualYMax = yMax - minY;
|
||||
|
||||
this.column = columns[index];
|
||||
this.size = sizes[index];
|
||||
|
||||
this.i = -1;
|
||||
|
||||
// advance to first layer
|
||||
for (int i = 0; i < size; i++) {
|
||||
int packed = column[i];
|
||||
short sky = unpackSky(packed);
|
||||
short bottom = unpackBottom(packed);
|
||||
|
||||
if (bottom != -1 && bottom > actualYMax)
|
||||
continue;
|
||||
|
||||
this.i = i;
|
||||
currentY = Math.min(currentY, sky);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt() {
|
||||
if (i == -1) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
int ret = currentY;
|
||||
|
||||
int packed = column[i];
|
||||
short bottom = unpackBottom(packed);
|
||||
|
||||
if (bottom != -1 && ret == bottom) {
|
||||
i++;
|
||||
if (i == size) {
|
||||
i = -1;
|
||||
} else {
|
||||
packed = column[i];
|
||||
currentY = unpackSky(packed);
|
||||
}
|
||||
} else {
|
||||
currentY--;
|
||||
}
|
||||
|
||||
return ret + minY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return i != -1;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
package io.github.slimeistdev.crystalline_sky.infrastructure;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineBlocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import net.minecraft.world.phys.shapes.Shapes;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.lighting.LightEngine;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class MutableWeepingScanner implements WeepingStorage.WeepingScanner {
|
||||
private @Nullable BlockGetter blockView;
|
||||
private final int minY;
|
||||
|
||||
private final BlockPos.MutableBlockPos reusablePos1 = new BlockPos.MutableBlockPos();
|
||||
private final BlockPos.MutableBlockPos reusablePos2 = new BlockPos.MutableBlockPos();
|
||||
|
||||
public MutableWeepingScanner(int minY) {
|
||||
this.minY = minY;
|
||||
}
|
||||
|
||||
public void setBlockView(@Nullable BlockGetter blockView) {
|
||||
this.blockView = blockView;
|
||||
}
|
||||
|
||||
private int transformInputY(int y) {
|
||||
return y + minY;
|
||||
}
|
||||
|
||||
private int transformOutputY(int y) {
|
||||
return y == Integer.MIN_VALUE ? -1 : y - minY;
|
||||
}
|
||||
|
||||
private @NotNull BlockGetter checkBlockView() {
|
||||
if (blockView == null) {
|
||||
throw new IllegalStateException("blockView is not set");
|
||||
} else {
|
||||
return blockView;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int scan(int localX, int yMax, int localZ) {
|
||||
BlockGetter blockView = checkBlockView();
|
||||
|
||||
int actualYMax = transformInputY(yMax);
|
||||
|
||||
BlockPos.MutableBlockPos topPos = reusablePos1.set(localX, actualYMax, localZ);
|
||||
BlockPos.MutableBlockPos bottomPos = reusablePos2.set(localX, actualYMax - 1, localZ);
|
||||
BlockState topState = blockView.getBlockState(topPos);
|
||||
|
||||
while (bottomPos.getY() >= minY) {
|
||||
BlockState bottomState = blockView.getBlockState(bottomPos);
|
||||
if (faceBlocksLight(blockView, topState, bottomState, topPos, bottomPos)) {
|
||||
return transformOutputY(bottomPos.getY());
|
||||
}
|
||||
|
||||
topState = bottomState;
|
||||
topPos.set(bottomPos);
|
||||
bottomPos.move(Direction.DOWN);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int scanForWeepingSky(int localX, int yMax, int localZ) {
|
||||
BlockGetter blockView = checkBlockView();
|
||||
|
||||
int actualYMax = transformInputY(yMax);
|
||||
|
||||
BlockPos.MutableBlockPos topPos = reusablePos1.set(localX, actualYMax, localZ);
|
||||
BlockPos.MutableBlockPos bottomPos = reusablePos2.set(localX, actualYMax - 1, localZ);
|
||||
BlockState topState = blockView.getBlockState(topPos);
|
||||
|
||||
if (CrystallineBlocks.isWeepingSky(topState))
|
||||
return yMax;
|
||||
|
||||
while (bottomPos.getY() >= minY) {
|
||||
BlockState bottomState = blockView.getBlockState(bottomPos);
|
||||
if (CrystallineBlocks.isWeepingSky(bottomState)) {
|
||||
return transformOutputY(bottomPos.getY());
|
||||
}
|
||||
|
||||
if (faceBlocksLight(blockView, topState, bottomState, topPos, bottomPos)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
topState = bottomState;
|
||||
topPos.set(bottomPos);
|
||||
bottomPos.move(Direction.DOWN);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean faceBlocksLight(int localX, int upperY, int localZ) {
|
||||
BlockGetter blockView = checkBlockView();
|
||||
|
||||
int actualY = transformInputY(upperY);
|
||||
reusablePos1.set(localX, actualY, localZ);
|
||||
reusablePos2.set(localX, actualY - 1, localZ);
|
||||
BlockState upper = blockView.getBlockState(reusablePos1);
|
||||
BlockState lower = blockView.getBlockState(reusablePos2);
|
||||
|
||||
return faceBlocksLight(blockView, upper, lower, reusablePos1, reusablePos2);
|
||||
}
|
||||
|
||||
private static boolean faceBlocksLight(BlockGetter world, BlockState upper, BlockState lower, BlockPos upperPos, BlockPos lowerPos) {
|
||||
if (lower.getLightBlock(world, lowerPos) != 0) {
|
||||
return true;
|
||||
} else {
|
||||
VoxelShape voxelShape = LightEngine.getOcclusionShape(world, upperPos, upper, Direction.DOWN);
|
||||
VoxelShape voxelShape2 = LightEngine.getOcclusionShape(world, lowerPos, lower, Direction.UP);
|
||||
return Shapes.faceShapeOccludes(voxelShape, voxelShape2);
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package io.github.slimeistdev.crystalline_sky.infrastructure;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntIterator;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class PeekableIntIterator implements IntIterator {
|
||||
private final IntIterator wrapped;
|
||||
|
||||
private boolean hasPeeked = false;
|
||||
private int peekedValue;
|
||||
|
||||
public PeekableIntIterator(IntIterator wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
public int peek() {
|
||||
if (!hasPeeked) {
|
||||
if (!wrapped.hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
peekedValue = wrapped.nextInt();
|
||||
hasPeeked = true;
|
||||
}
|
||||
return peekedValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt() {
|
||||
if (hasPeeked) {
|
||||
hasPeeked = false;
|
||||
return peekedValue;
|
||||
}
|
||||
|
||||
return wrapped.nextInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return hasPeeked || wrapped.hasNext();
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package io.github.slimeistdev.crystalline_sky.infrastructure;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Note: y coordinates are [0, ...)
|
||||
*/
|
||||
public interface WeepingStorage {
|
||||
void insertSky(int localX, int y, int localZ, WeepingScanner scanner);
|
||||
|
||||
void insertSolid(int localX, int y, int localZ, WeepingScanner scanner);
|
||||
|
||||
void insertAir(int localX, int y, int localZ, WeepingScanner scanner);
|
||||
|
||||
void clear();
|
||||
|
||||
boolean isLit(int localX, int y, int localZ);
|
||||
|
||||
Iterable<Run> iterateLitRuns(int localX, int localZ, int lowestSourceY);
|
||||
|
||||
Iterable<Run> iterateUnlitRuns(int localX, int localZ, int lowestSourceY);
|
||||
|
||||
void clearTempRunSplits(int localX, int localZ);
|
||||
|
||||
boolean isColumnEmpty(int localX, int localZ);
|
||||
|
||||
boolean hasNoUnlitSplits(int localX, int localZ);
|
||||
|
||||
void debugState(int localX, int localZ, Consumer<String> stringConsumer);
|
||||
|
||||
/** A continuous run of positions [bottomY, topY] */
|
||||
record Run(int bottomY, int topY) {
|
||||
public Run withBottomY(int newBottomY) {
|
||||
return new Run(newBottomY, this.topY);
|
||||
}
|
||||
|
||||
public Run withTopY(int newTopY) {
|
||||
return new Run(this.bottomY, newTopY);
|
||||
}
|
||||
}
|
||||
|
||||
interface WeepingScanner {
|
||||
/** Scans downwards from yMax (exclusive) to find the first solid block.
|
||||
* Returns the y coordinate of the first solid block, or -1 if none was found.
|
||||
* The returned value is guaranteed to be less than yMax.
|
||||
*/
|
||||
int scan(int localX, int yMax, int localZ);
|
||||
|
||||
int scanForWeepingSky(int localX, int yMax, int localZ);
|
||||
|
||||
boolean faceBlocksLight(int localX, int upperY, int localZ);
|
||||
}
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
package io.github.slimeistdev.crystalline_sky.infrastructure.client;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.infrastructure.WeepingStorage;
|
||||
import io.github.slimeistdev.crystalline_sky.mixin_ducks.ChunkSkyLight_Duck;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.debug.DebugRenderer;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.server.level.ColumnPos;
|
||||
import net.minecraft.world.level.lighting.ChunkSkyLightSources;
|
||||
import net.minecraft.world.level.chunk.LightChunk;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class WeepingSkyDebugRenderer implements DebugRenderer.SimpleDebugRenderer {
|
||||
private final Minecraft client;
|
||||
private @Nullable ColumnPos column = null;
|
||||
private boolean litMode = true;
|
||||
|
||||
private static final int[] COLORS = {
|
||||
0xFFFF0000,
|
||||
0xFFFF7F00,
|
||||
0xFFFFFF00,
|
||||
0xFF00FF00,
|
||||
0xFF00FFFF,
|
||||
0xFF007FFF,
|
||||
0xFF0000FF,
|
||||
0xFF7F00FF,
|
||||
0xFFFF00FF,
|
||||
0xFFFF007F,
|
||||
};
|
||||
|
||||
public WeepingSkyDebugRenderer(Minecraft client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public void setColumn(@Nullable ColumnPos column) {
|
||||
this.column = column;
|
||||
}
|
||||
|
||||
public void setLitMode(boolean lit) {
|
||||
this.litMode = lit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack matrices, MultiBufferSource vertexConsumers, double cameraX, double cameraY, double cameraZ) {
|
||||
if (column == null)
|
||||
return;
|
||||
|
||||
if (client.level == null)
|
||||
return;
|
||||
|
||||
ChunkPos chunkPos = column.toChunkPos();
|
||||
LightChunk chunk = client.level.getChunkSource().getChunkForLighting(chunkPos.x, chunkPos.z);
|
||||
if (chunk == null)
|
||||
return;
|
||||
|
||||
ChunkSkyLightSources chunkSkyLight = chunk.getSkyLightSources();
|
||||
WeepingStorage weepingStorage = ((ChunkSkyLight_Duck) chunkSkyLight).crystalline_sky$getWeepingStorage();
|
||||
if (weepingStorage == null)
|
||||
return;
|
||||
|
||||
int localX = column.x() & 15;
|
||||
int localZ = column.z() & 15;
|
||||
|
||||
int lowestSourceY = chunkSkyLight.getLowestSourceY(localX, localZ);
|
||||
|
||||
Matrix4f matrix4f = matrices.last().pose();
|
||||
|
||||
float x1 = (float) (column.x() - cameraX);
|
||||
float x2 = x1 + 1;
|
||||
float z1 = (float) (column.z() - cameraZ);
|
||||
float z2 = z1 + 1;
|
||||
|
||||
int colorIndex = 0;
|
||||
|
||||
Iterable<WeepingStorage.Run> runs = this.litMode
|
||||
? weepingStorage.iterateLitRuns(localX, localZ, lowestSourceY)
|
||||
: weepingStorage.iterateUnlitRuns(localX, localZ, lowestSourceY);
|
||||
for (WeepingStorage.Run run : runs) {
|
||||
int color = COLORS[colorIndex++ % COLORS.length];
|
||||
|
||||
// limit things to a renderable range
|
||||
int bottomY = Math.max(-0xFFFF, run.bottomY());
|
||||
int topY = Math.min(run.topY(), 0xFFFF) + 1;
|
||||
|
||||
float y1 = (float) (bottomY - cameraY);
|
||||
float y2 = (float) (topY - cameraY);
|
||||
|
||||
// draw outlined box
|
||||
renderOutlinedBox(
|
||||
vertexConsumers, matrix4f,
|
||||
x1, x2,
|
||||
y1, y2,
|
||||
z1, z2,
|
||||
color
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderOutlinedBox(MultiBufferSource vertexConsumers, Matrix4f matrix, float x1, float x2, float y1, float y2, float z1, float z2, int color) {
|
||||
VertexConsumer vertexConsumer = vertexConsumers.getBuffer(RenderType.debugLineStrip(2.0));
|
||||
|
||||
// render top face
|
||||
renderHorizontalSquare(matrix, x1, x2, y1, z1, z2, color, vertexConsumer);
|
||||
|
||||
// render bottom face
|
||||
renderHorizontalSquare(matrix, x1, x2, y2, z1, z2, color, vertexConsumer);
|
||||
|
||||
// the (x1, z1) vertical edge has been drawn, draw the other 3
|
||||
|
||||
// (x2, z1)
|
||||
vertexConsumer.addVertex(matrix, x2, y2, z1).setColor(color);
|
||||
vertexConsumer.addVertex(matrix, x2, y1, z1).setColor(color);
|
||||
|
||||
// (x2, z2)
|
||||
vertexConsumer.addVertex(matrix, x2, y1, z2).setColor(color);
|
||||
vertexConsumer.addVertex(matrix, x2, y2, z2).setColor(color);
|
||||
|
||||
// (x1, z2)
|
||||
vertexConsumer.addVertex(matrix, x1, y2, z2).setColor(color);
|
||||
vertexConsumer.addVertex(matrix, x1, y1, z2).setColor(color);
|
||||
}
|
||||
|
||||
private static void renderHorizontalSquare(Matrix4f matrix, float x1, float x2, float y, float z1, float z2, int color, VertexConsumer vertexConsumer) {
|
||||
vertexConsumer.addVertex(matrix, x1, y, z1).setColor(color);
|
||||
vertexConsumer.addVertex(matrix, x2, y, z1).setColor(color);
|
||||
vertexConsumer.addVertex(matrix, x2, y, z2).setColor(color);
|
||||
vertexConsumer.addVertex(matrix, x1, y, z2).setColor(color);
|
||||
vertexConsumer.addVertex(matrix, x1, y, z1).setColor(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.column = null;
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.CrystallineSky;
|
||||
import io.github.slimeistdev.crystalline_sky.infrastructure.IntWeepingStorage;
|
||||
import io.github.slimeistdev.crystalline_sky.infrastructure.MutableWeepingScanner;
|
||||
import io.github.slimeistdev.crystalline_sky.infrastructure.WeepingStorage;
|
||||
import io.github.slimeistdev.crystalline_sky.mixin_ducks.ChunkSkyLight_Duck;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineBlocks;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.lighting.ChunkSkyLightSources;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ChunkSkyLightSources.class)
|
||||
public abstract class ChunkSkyLightSourcesMixin implements ChunkSkyLight_Duck {
|
||||
@Shadow
|
||||
@Final
|
||||
private int minY;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private BlockPos.MutableBlockPos mutablePos1;
|
||||
|
||||
@Shadow
|
||||
private static boolean isEdgeOccluded(BlockGetter blockView, BlockPos upperPos, BlockState upperState, BlockPos lowerPos, BlockState lowerState) {
|
||||
throw new RuntimeException("Mixin failed to apply");
|
||||
}
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private BlockPos.MutableBlockPos mutablePos2;
|
||||
|
||||
@Unique
|
||||
@Final
|
||||
@Mutable
|
||||
private IntWeepingStorage crystalline_sky$weepingStorage;
|
||||
|
||||
@Unique
|
||||
@Final
|
||||
@Mutable
|
||||
private MutableWeepingScanner crystalline_sky$weepingScanner;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void init(LevelHeightAccessor heightLimitView, CallbackInfo ci) {
|
||||
crystalline_sky$weepingStorage = new IntWeepingStorage(minY + 1);
|
||||
crystalline_sky$weepingScanner = new MutableWeepingScanner(minY + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WeepingStorage crystalline_sky$getWeepingStorage() {
|
||||
return crystalline_sky$weepingStorage;
|
||||
}
|
||||
|
||||
@Inject(method = "fillFrom", at = @At("HEAD"))
|
||||
private void refreshWeepingSurfaceY(ChunkAccess chunk, CallbackInfo ci) {
|
||||
int highestNonEmptySectionIndex = chunk.getHighestFilledSectionIndex();
|
||||
if (highestNonEmptySectionIndex == -1) {
|
||||
crystalline_sky$weepingStorage.clear();
|
||||
} else {
|
||||
int topY = SectionPos.sectionToBlockCoord(chunk.getSectionYFromSectionIndex(highestNonEmptySectionIndex) + 1);
|
||||
int weepingMinY = minY + 1;
|
||||
|
||||
ShortList data = new ShortArrayList();
|
||||
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
data.clear();
|
||||
|
||||
BlockPos.MutableBlockPos topPos = mutablePos1.set(x, topY, z);
|
||||
BlockPos.MutableBlockPos bottomPos = mutablePos2.set(x, topY - 1, z);
|
||||
BlockState topState = Blocks.AIR.defaultBlockState();
|
||||
|
||||
short foundSky = -1;
|
||||
|
||||
for (int sectionIndex = highestNonEmptySectionIndex; sectionIndex >= 0; sectionIndex--) {
|
||||
LevelChunkSection section = chunk.getSection(sectionIndex);
|
||||
|
||||
if (section.hasOnlyAir()) {
|
||||
topState = Blocks.AIR.defaultBlockState();
|
||||
topPos.setY(SectionPos.sectionToBlockCoord(chunk.getSectionYFromSectionIndex(sectionIndex)));
|
||||
bottomPos.setY(topPos.getY() - 1);
|
||||
} else {
|
||||
for (int y = 15; y >= 0; y--) {
|
||||
BlockState bottomState = section.getBlockState(x, y, z);
|
||||
|
||||
if (CrystallineBlocks.isWeepingSky(bottomState)) {
|
||||
if (foundSky == -1) {
|
||||
foundSky = (short) (bottomPos.getY() - weepingMinY);
|
||||
}
|
||||
} else if (isEdgeOccluded(chunk, topPos, topState, bottomPos, bottomState)) {
|
||||
if (foundSky != -1) {
|
||||
short lastLitY = (short) (bottomPos.getY() - weepingMinY + 1);
|
||||
data.add(foundSky);
|
||||
data.add(lastLitY);
|
||||
foundSky = -1;
|
||||
}
|
||||
}
|
||||
|
||||
topState = bottomState;
|
||||
topPos.set(bottomPos);
|
||||
bottomPos.move(Direction.DOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundSky != -1) {
|
||||
data.add(foundSky);
|
||||
data.add((short) -1);
|
||||
}
|
||||
|
||||
crystalline_sky$weepingStorage.set(x, z, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "update(Lnet/minecraft/world/level/BlockGetter;III)Z", at = @At("HEAD"))
|
||||
private void updateWeepingSkyState(BlockGetter blockView, int localX, int y, int localZ, CallbackInfoReturnable<Boolean> cir) {
|
||||
BlockPos pos = mutablePos1.set(localX, y, localZ);
|
||||
BlockState state = blockView.getBlockState(pos);
|
||||
|
||||
crystalline_sky$weepingScanner.setBlockView(blockView);
|
||||
|
||||
int weepingMinY = minY + 1;
|
||||
|
||||
if (state.isAir()) {
|
||||
crystalline_sky$weepingStorage.insertAir(localX, y - weepingMinY, localZ, crystalline_sky$weepingScanner);
|
||||
} else if (CrystallineBlocks.isWeepingSky(state)) {
|
||||
crystalline_sky$weepingStorage.insertSky(localX, y - weepingMinY, localZ, crystalline_sky$weepingScanner);
|
||||
} else if (state.canOcclude()) {
|
||||
crystalline_sky$weepingStorage.insertSolid(localX, y - weepingMinY, localZ, crystalline_sky$weepingScanner);
|
||||
}
|
||||
|
||||
CrystallineSky.LOG.debug("Weeping sky ranges for local column [{}, {}] in {}:", localX, localZ, blockView);
|
||||
crystalline_sky$weepingStorage.debugState(localX, localZ, CrystallineSky.LOG::debug);
|
||||
|
||||
crystalline_sky$weepingScanner.setBlockView(null);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.util.ConditionalMixinManager;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class CrystallineSkyMixinPlugin implements IMixinConfigPlugin {
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger("CrystallineSky/MixinPlugin");
|
||||
|
||||
@Override
|
||||
public void onLoad(String mixinPackage) { } // NO-OP
|
||||
|
||||
@Override
|
||||
public String getRefMapperConfig() { return null; } // DEFAULT
|
||||
|
||||
@Override
|
||||
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
|
||||
return ConditionalMixinManager.shouldApply(mixinClassName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) { } // NO-OP
|
||||
|
||||
@Override
|
||||
public List<String> getMixins() { return null; } // DEFAULT
|
||||
|
||||
@Override
|
||||
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { } // NO-OP
|
||||
|
||||
@Override
|
||||
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { } // NO-OP
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin;
|
||||
|
||||
import net.minecraft.world.level.chunk.DataLayer;
|
||||
import net.minecraft.world.level.lighting.LayerLightSectionStorage;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(LayerLightSectionStorage.class)
|
||||
public interface LayerLightSectionStorageAccessor {
|
||||
@Invoker("lightOnInSection")
|
||||
boolean crystalline_sky$callLightOnInSection(long sectionPos);
|
||||
|
||||
@Invoker("storingLightForSection")
|
||||
boolean crystalline_sky$hasSection(long sectionPos);
|
||||
|
||||
@Invoker("getStoredLevel")
|
||||
int crystalline_sky$get(long blockPos);
|
||||
|
||||
@Invoker("setStoredLevel")
|
||||
void crystalline_sky$set(long blockPos, int lightLevel);
|
||||
|
||||
@Invoker("getDataLayerToWrite")
|
||||
@Nullable DataLayer crystalline_sky$method_51547(long sectionPos);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineBlocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.lighting.LightEngine;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
@Mixin(LightEngine.class)
|
||||
public class LightEngineMixin {
|
||||
@WrapMethod(method = "hasDifferentLightProperties")
|
||||
private static boolean updateSky(BlockGetter blockView, BlockPos pos, BlockState oldState, BlockState newState, Operation<Boolean> original) {
|
||||
return original.call(blockView, pos, oldState, newState)
|
||||
|| (CrystallineBlocks.getSkyLightLevel(oldState) != CrystallineBlocks.getSkyLightLevel(newState))
|
||||
|| (CrystallineBlocks.isWeepingSky(oldState) != CrystallineBlocks.isWeepingSky(newState));
|
||||
}
|
||||
}
|
||||
+371
@@ -0,0 +1,371 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.expression.Definition;
|
||||
import com.llamalad7.mixinextras.expression.Expression;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import io.github.slimeistdev.crystalline_sky.infrastructure.WeepingStorage;
|
||||
import io.github.slimeistdev.crystalline_sky.mixin_ducks.ChunkSkyLight_Duck;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineBlocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.DataLayer;
|
||||
import net.minecraft.world.level.chunk.LightChunkGetter;
|
||||
import net.minecraft.world.level.lighting.DataLayerStorageMap;
|
||||
import net.minecraft.world.level.lighting.LightEngine;
|
||||
import net.minecraft.world.level.lighting.ChunkSkyLightSources;
|
||||
import net.minecraft.world.level.lighting.SkyLightEngine;
|
||||
import net.minecraft.world.level.chunk.LightChunk;
|
||||
import net.minecraft.world.level.lighting.LayerLightSectionStorage;
|
||||
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;
|
||||
|
||||
// todone add ordinal to all name = "..." locals
|
||||
@Mixin(SkyLightEngine.class)
|
||||
public abstract class SkyLightEngineMixin<M extends DataLayerStorageMap<M>, S extends LayerLightSectionStorage<M>> extends LightEngine<M, S> {
|
||||
@Shadow
|
||||
@Final
|
||||
private BlockPos.MutableBlockPos mutablePos;
|
||||
|
||||
@Shadow
|
||||
private static boolean isSourceLevel(int lightLevel) {
|
||||
throw new RuntimeException("Mixin failed to apply");
|
||||
}
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private static long REMOVE_TOP_SKY_SOURCE_ENTRY;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private static long REMOVE_SKY_SOURCE_ENTRY;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private static long ADD_SKY_SOURCE_ENTRY;
|
||||
|
||||
protected SkyLightEngineMixin(LightChunkGetter chunkProvider, S lightStorage) {
|
||||
super(chunkProvider, lightStorage);
|
||||
}
|
||||
|
||||
@Inject(method = "checkNode", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/lighting/SkyLightSectionStorage;getStoredLevel(J)I"))
|
||||
private void emitFromCrystallineSky(long blockPos, CallbackInfo ci) {
|
||||
BlockState state = getState(mutablePos.set(blockPos));
|
||||
int level = CrystallineBlocks.isWeepingSky(state)
|
||||
? 15
|
||||
: CrystallineBlocks.getSkyLightLevel(state);
|
||||
if (level > 0 && ((LayerLightSectionStorageAccessor) storage).crystalline_sky$callLightOnInSection(SectionPos.blockToSection(blockPos))) {
|
||||
enqueueIncrease(blockPos, QueueEntry.increaseLightFromEmission(level, isEmptyShape(state)));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("LocalMayBeArgsOnly")
|
||||
@Definition(id = "y", local = @Local(type = int.class, name = "y", ordinal = 1))
|
||||
@Definition(id = "lowestSourceY", local = @Local(type = int.class, name = "lowestSourceY", ordinal = 3))
|
||||
@Expression("y >= lowestSourceY")
|
||||
@WrapOperation(method = "checkNode", at = @At("MIXINEXTRAS:EXPRESSION"))
|
||||
private boolean weepBelowTheLowest(int y, int lowestSourceY, Operation<Boolean> original, @Local(name = "x", ordinal = 0) int x, @Local(name = "z", ordinal = 2) int z) {
|
||||
if (original.call(y, lowestSourceY)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LightChunk chunk = chunkSource.getChunkForLighting(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z));
|
||||
if (chunk == null) return false;
|
||||
|
||||
ChunkSkyLightSources chunkSkyLight = chunk.getSkyLightSources();
|
||||
if (chunkSkyLight == null) return false;
|
||||
|
||||
int localX = SectionPos.sectionRelative(x);
|
||||
int localZ = SectionPos.sectionRelative(z);
|
||||
|
||||
WeepingStorage weepingStorage = ((ChunkSkyLight_Duck) chunkSkyLight).crystalline_sky$getWeepingStorage();
|
||||
if (weepingStorage.isColumnEmpty(localX, localZ)) return false;
|
||||
|
||||
return weepingStorage.isLit(localX, y, localZ);
|
||||
}
|
||||
|
||||
@WrapOperation(method = "propagateDecrease", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/lighting/SkyLightEngine;enqueueDecrease(JJ)V"))
|
||||
private void decreaseTakesLightIntoAccount1(SkyLightEngine instance, long blockPos, long flags,
|
||||
Operation<Void> original, @Local(name = "k", ordinal = 2) int k) {
|
||||
// k is the old light level at blockPos
|
||||
BlockState state = getState(mutablePos.set(blockPos));
|
||||
int luminance = CrystallineBlocks.getSkyLightLevel(state);
|
||||
|
||||
if (luminance < k) {
|
||||
original.call(instance, blockPos, flags);
|
||||
}
|
||||
|
||||
if (luminance > 0) {
|
||||
enqueueIncrease(blockPos, QueueEntry.increaseLightFromEmission(luminance, isEmptyShape(state)));
|
||||
}
|
||||
}
|
||||
|
||||
@WrapOperation(method = "propagateDecrease", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/lighting/SkyLightEngine;propagateFromEmptySections(JLnet/minecraft/core/Direction;IZI)V"))
|
||||
private void decreaseTakesLightIntoAccount2(SkyLightEngine instance, long blockPos, Direction direction,
|
||||
int lightLevel, boolean shouldIncrease, int emptySections,
|
||||
Operation<Void> original, @Local(name = "k", ordinal = 2) int k) {
|
||||
// k is the old light level at blockPos
|
||||
BlockState state = getState(mutablePos.set(blockPos));
|
||||
int luminance = CrystallineBlocks.getSkyLightLevel(state);
|
||||
|
||||
if (luminance < k) {
|
||||
original.call(instance, blockPos, direction, lightLevel, shouldIncrease, emptySections);
|
||||
}
|
||||
|
||||
if (luminance > 0) {
|
||||
original.call(instance, blockPos, direction, luminance, true, emptySections);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "propagateLightSources", at = @At("RETURN"))
|
||||
private void propagateCrystallineSkyLight(ChunkPos chunkPos, CallbackInfo ci) {
|
||||
LightChunk lightSourceView = this.chunkSource.getChunkForLighting(chunkPos.x, chunkPos.z);
|
||||
if (!(lightSourceView instanceof ChunkAccess chunk)) return;
|
||||
|
||||
chunk.findBlocks(CrystallineBlocks::isCrystallineSky, (pos, state) -> {
|
||||
enqueueIncrease(pos.asLong(), QueueEntry.increaseLightFromEmission(CrystallineBlocks.getSkyLightLevel(state), isEmptyShape(state)));
|
||||
});
|
||||
}
|
||||
|
||||
// NOTE: lowestSourceY becomes minY, and minY becomes bottomSectionY under parchment.
|
||||
// We're keeping the old names since it makes things more legible
|
||||
@Inject(method = "removeSourcesBelow", at = @At("HEAD"), cancellable = true)
|
||||
private void removeWeepingSourcesBelow(int x, int z, int lowestSourceY, int minY, CallbackInfo ci) {
|
||||
LightChunk chunk = chunkSource.getChunkForLighting(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z));
|
||||
if (chunk == null) return;
|
||||
|
||||
ChunkSkyLightSources chunkSkyLight = chunk.getSkyLightSources();
|
||||
if (chunkSkyLight == null) return;
|
||||
|
||||
int localX = SectionPos.sectionRelative(x);
|
||||
int localZ = SectionPos.sectionRelative(z);
|
||||
|
||||
WeepingStorage weepingStorage = ((ChunkSkyLight_Duck) chunkSkyLight).crystalline_sky$getWeepingStorage();
|
||||
if (weepingStorage.isColumnEmpty(localX, localZ) && weepingStorage.hasNoUnlitSplits(localX, localZ)) return;
|
||||
|
||||
// there's little chance of accidental mixin compatibility anyway, so just cancel
|
||||
ci.cancel();
|
||||
|
||||
int sectionX = SectionPos.blockToSectionCoord(x);
|
||||
int sectionZ = SectionPos.blockToSectionCoord(z);
|
||||
int effectiveLowestSourceY = lowestSourceY <= minY ? Integer.MIN_VALUE : lowestSourceY;
|
||||
|
||||
SkyLightSectionStorageAccessor lightStorageAccessor = (SkyLightSectionStorageAccessor) this.storage;
|
||||
|
||||
RunIter: for (WeepingStorage.Run run : weepingStorage.iterateUnlitRuns(localX, localZ, effectiveLowestSourceY)) {
|
||||
// check lighting state of run
|
||||
int topUnlit = run.topY();
|
||||
int bottomUnlit = run.bottomY();
|
||||
|
||||
for (int sectionY = SectionPos.blockToSectionCoord(topUnlit); lightStorageAccessor.crystalline_sky$isAboveMinHeight(sectionY); sectionY--) {
|
||||
if (lightStorageAccessor.crystalline_sky$hasSection(SectionPos.asLong(sectionX, sectionY, sectionZ))) {
|
||||
int minYInSection = SectionPos.sectionToBlockCoord(sectionY);
|
||||
int maxYInSection = minYInSection + 15;
|
||||
|
||||
if (maxYInSection < bottomUnlit) { // todone break outer early if we're below bottomUnlit
|
||||
break;
|
||||
}
|
||||
|
||||
for (int it_y = Math.min(maxYInSection, topUnlit); it_y >= Math.max(minYInSection, bottomUnlit); it_y--) {
|
||||
long it_pos = BlockPos.asLong(x, it_y, z);
|
||||
|
||||
if (CrystallineBlocks.isCrystallineSky(chunk.getBlockState(mutablePos.set(it_pos)))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isSourceLevel(lightStorageAccessor.crystalline_sky$get(it_pos))) {
|
||||
continue RunIter;
|
||||
}
|
||||
|
||||
lightStorageAccessor.crystalline_sky$set(it_pos, 0);
|
||||
// todone which of these is correct?
|
||||
enqueueDecrease(it_pos, it_y == topUnlit ? REMOVE_TOP_SKY_SOURCE_ENTRY : REMOVE_SKY_SOURCE_ENTRY);
|
||||
//enqueueDecrease(it_pos, REMOVE_SKY_SOURCE_ENTRY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: lowestSourceY becomes maxY, and minY becomes bottomSectionY under parchment.
|
||||
// We're keeping the old names since it makes things more legible
|
||||
@Inject(method = "addSourcesAbove", at = @At("RETURN"))
|
||||
private void addAdditionalWeepingSourcesAbove(int x, int z, int lowestSourceY, int minY, CallbackInfo ci,
|
||||
@Local(name = "maxAdjacentLowestSourceY", ordinal = 6) int maxAdjacentLowestSourceY) {
|
||||
LightChunk chunk = chunkSource.getChunkForLighting(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z));
|
||||
if (chunk == null) return;
|
||||
|
||||
ChunkSkyLightSources chunkSkyLight = chunk.getSkyLightSources();
|
||||
if (chunkSkyLight == null) return;
|
||||
|
||||
int localX = SectionPos.sectionRelative(x);
|
||||
int localZ = SectionPos.sectionRelative(z);
|
||||
|
||||
WeepingStorage weepingStorage = ((ChunkSkyLight_Duck) chunkSkyLight).crystalline_sky$getWeepingStorage();
|
||||
if (weepingStorage.isColumnEmpty(localX, localZ)) {
|
||||
weepingStorage.clearTempRunSplits(localX, localZ);
|
||||
return;
|
||||
}
|
||||
|
||||
int sectionX = SectionPos.blockToSectionCoord(x);
|
||||
int sectionZ = SectionPos.blockToSectionCoord(z);
|
||||
int effectiveLowestSourceY = lowestSourceY <= minY ? Integer.MIN_VALUE : lowestSourceY;
|
||||
|
||||
if (effectiveLowestSourceY == Integer.MIN_VALUE) {
|
||||
weepingStorage.clearTempRunSplits(localX, localZ);
|
||||
return;
|
||||
}
|
||||
|
||||
SkyLightSectionStorageAccessor lightStorageAccessor = (SkyLightSectionStorageAccessor) this.storage;
|
||||
|
||||
RunIter: for (WeepingStorage.Run run : weepingStorage.iterateLitRuns(localX, localZ, effectiveLowestSourceY)) {
|
||||
if (run.topY() == Integer.MAX_VALUE) {
|
||||
if (run.bottomY() == effectiveLowestSourceY) {
|
||||
continue;
|
||||
} else {
|
||||
run = run.withTopY(effectiveLowestSourceY - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int lowestRelevantY = Math.max(run.bottomY(), minY);
|
||||
for (long it_packedSection = SectionPos.asLong(sectionX, SectionPos.blockToSectionCoord(lowestRelevantY), sectionZ);
|
||||
!lightStorageAccessor.crystalline_sky$isAtOrAboveTopmostSection(it_packedSection);
|
||||
it_packedSection = SectionPos.offset(it_packedSection, Direction.UP)
|
||||
) {
|
||||
if (lightStorageAccessor.crystalline_sky$hasSection(it_packedSection)) {
|
||||
int minYInSection = SectionPos.sectionToBlockCoord(SectionPos.y(it_packedSection));
|
||||
int maxYInSection = minYInSection + 15;
|
||||
|
||||
if (minYInSection > run.topY()) { // todone break outer early if we're above run.topY()
|
||||
break;
|
||||
}
|
||||
|
||||
for (int it_y = Math.max(minYInSection, lowestRelevantY); it_y <= Math.min(maxYInSection, run.topY()); it_y++) {
|
||||
long it_pos = BlockPos.asLong(x, it_y, z);
|
||||
|
||||
if (CrystallineBlocks.isCrystallineSky(chunk.getBlockState(mutablePos.set(it_pos)))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isSourceLevel(lightStorageAccessor.crystalline_sky$get(it_pos))) {
|
||||
continue RunIter;
|
||||
}
|
||||
|
||||
lightStorageAccessor.crystalline_sky$set(it_pos, 15);
|
||||
if (it_y < maxAdjacentLowestSourceY || it_y == lowestRelevantY) {
|
||||
enqueueIncrease(it_pos, ADD_SKY_SOURCE_ENTRY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
weepingStorage.clearTempRunSplits(localX, localZ);
|
||||
}
|
||||
|
||||
@Inject(method = "propagateLightSources", at = @At("RETURN"))
|
||||
private void propagateWeepingLight(ChunkPos chunkPos, CallbackInfo ci,
|
||||
@Local(name = "sourcesO", ordinal = 0) ChunkSkyLightSources sourcesO,
|
||||
@Local(name = "sourcesZN", ordinal = 1) ChunkSkyLightSources sourcesZN,
|
||||
@Local(name = "sourcesZP", ordinal = 2) ChunkSkyLightSources sourcesZP,
|
||||
@Local(name = "sourcesXN", ordinal = 3) ChunkSkyLightSources sourcesXN,
|
||||
@Local(name = "sourcesXP", ordinal = 4) ChunkSkyLightSources sourcesXP
|
||||
) {
|
||||
WeepingStorage weepingStorage = ((ChunkSkyLight_Duck) sourcesO).crystalline_sky$getWeepingStorage();
|
||||
if (weepingStorage == null) return;
|
||||
|
||||
boolean hasAny = false;
|
||||
Outer: for (int localX = 0; localX < 16; localX++) {
|
||||
for (int localZ = 0; localZ < 16; localZ++) {
|
||||
if (!weepingStorage.isColumnEmpty(localX, localZ)) {
|
||||
hasAny = true;
|
||||
break Outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasAny)
|
||||
return;
|
||||
|
||||
long packedZeroPos = SectionPos.getZeroNode(chunkPos.x, chunkPos.z);
|
||||
SkyLightSectionStorageAccessor lightStorageAccessor = (SkyLightSectionStorageAccessor) this.storage;
|
||||
|
||||
int topSectionY = lightStorageAccessor.crystalline_sky$getTopSectionForColumn(packedZeroPos);
|
||||
int bottomSectionY = lightStorageAccessor.crystalline_sky$getMinSectionY();
|
||||
int baseX = SectionPos.sectionToBlockCoord(chunkPos.x);
|
||||
int baseZ = SectionPos.sectionToBlockCoord(chunkPos.z);
|
||||
|
||||
int minY = SectionPos.sectionToBlockCoord(bottomSectionY);
|
||||
|
||||
DataLayer[] lightArrays = new DataLayer[topSectionY - bottomSectionY];
|
||||
for (int sectionY = topSectionY - 1; sectionY >= bottomSectionY; sectionY--) {
|
||||
long packedSectionPos = SectionPos.asLong(chunkPos.x, sectionY, chunkPos.z);
|
||||
lightArrays[sectionY - bottomSectionY] = lightStorageAccessor.crystalline_sky$method_51547(packedSectionPos);
|
||||
}
|
||||
|
||||
for (int localZ = 0; localZ < 16; localZ++) {
|
||||
for (int localX = 0; localX < 16; localX++) {
|
||||
if (weepingStorage.isColumnEmpty(localX, localZ)) continue;
|
||||
|
||||
int lowestSourceY = sourcesO.getLowestSourceY(localX, localZ);
|
||||
int effectiveLowestSourceY = lowestSourceY <= minY ? Integer.MIN_VALUE : lowestSourceY;
|
||||
if (effectiveLowestSourceY == Integer.MIN_VALUE) continue;
|
||||
|
||||
int minSourceY_ZN = localZ == 0 ? sourcesZN.getLowestSourceY(localX, 15) : sourcesO.getLowestSourceY(localX, localZ - 1);
|
||||
int minSourceY_ZP = localZ == 15 ? sourcesZP.getLowestSourceY(localX, 0) : sourcesO.getLowestSourceY(localX, localZ + 1);
|
||||
int minSourceY_XN = localX == 0 ? sourcesXN.getLowestSourceY(15, localZ) : sourcesO.getLowestSourceY(localX - 1, localZ);
|
||||
int minSourceY_XP = localX == 15 ? sourcesXP.getLowestSourceY(0, localZ) : sourcesO.getLowestSourceY(localX + 1, localZ);
|
||||
int minSourceY_max = Math.max(Math.max(minSourceY_ZN, minSourceY_ZP), Math.max(minSourceY_XN, minSourceY_XP));
|
||||
|
||||
for (WeepingStorage.Run run : weepingStorage.iterateLitRuns(localX, localZ, effectiveLowestSourceY)) {
|
||||
if (run.topY() == Integer.MAX_VALUE) {
|
||||
if (run.bottomY() == effectiveLowestSourceY) {
|
||||
continue;
|
||||
} else {
|
||||
run = run.withTopY(effectiveLowestSourceY - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int lowestRelevantY = Math.max(run.bottomY(), minY);
|
||||
for (int sectionY = SectionPos.blockToSectionCoord(lowestRelevantY); sectionY <= topSectionY - 1; sectionY++) {
|
||||
DataLayer lightArray = lightArrays[sectionY - bottomSectionY];
|
||||
if (lightArray == null) continue;
|
||||
|
||||
int minYInSection = SectionPos.sectionToBlockCoord(sectionY);
|
||||
int maxYInSection = minYInSection + 15;
|
||||
|
||||
if (minYInSection > run.topY()) { // todone break outer early if we're above run.topY()
|
||||
break;
|
||||
}
|
||||
|
||||
for (int it_y = Math.max(minYInSection, lowestRelevantY); it_y <= Math.min(maxYInSection, run.topY()); it_y++) {
|
||||
lightArray.set(localX, SectionPos.sectionRelative(it_y), localZ, 15);
|
||||
|
||||
if (it_y == lowestRelevantY || it_y < minSourceY_max) {
|
||||
long it_pos = BlockPos.asLong(baseX + localX, it_y, baseZ + localZ);
|
||||
enqueueIncrease(
|
||||
it_pos,
|
||||
QueueEntry.increaseSkySourceInDirections(
|
||||
it_y == lowestRelevantY,
|
||||
it_y < minSourceY_ZN,
|
||||
it_y < minSourceY_ZP,
|
||||
it_y < minSourceY_XN,
|
||||
it_y < minSourceY_XP
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin;
|
||||
|
||||
import net.minecraft.world.level.lighting.SkyLightSectionStorage;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(SkyLightSectionStorage.class)
|
||||
public interface SkyLightSectionStorageAccessor extends LayerLightSectionStorageAccessor {
|
||||
@Invoker("hasLightDataAtOrBelow")
|
||||
boolean crystalline_sky$isAboveMinHeight(int sectionY);
|
||||
|
||||
@Invoker("isAboveData")
|
||||
boolean crystalline_sky$isAtOrAboveTopmostSection(long sectionPos);
|
||||
|
||||
@Invoker("getTopSectionY")
|
||||
int crystalline_sky$getTopSectionForColumn(long columnPos);
|
||||
|
||||
@Invoker("getBottomSectionY")
|
||||
int crystalline_sky$getMinSectionY();
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin.client;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineItems;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.world.item.Item;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
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 java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(ClientLevel.class)
|
||||
public class ClientLevelMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
@Mutable
|
||||
private static Set<Item> MARKER_PARTICLE_ITEMS;
|
||||
|
||||
@Inject(method = "<clinit>", at = @At("RETURN"))
|
||||
private static void modifyBlockMarkerItems(CallbackInfo ci) {
|
||||
MARKER_PARTICLE_ITEMS = new HashSet<>(MARKER_PARTICLE_ITEMS);
|
||||
MARKER_PARTICLE_ITEMS.add(CrystallineItems.SKY_LIGHT);
|
||||
MARKER_PARTICLE_ITEMS.add(CrystallineItems.WEEPING_SKY_LIGHT);
|
||||
MARKER_PARTICLE_ITEMS.add(CrystallineItems.SKY);
|
||||
MARKER_PARTICLE_ITEMS.add(CrystallineItems.WEEPING_SKY);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin.client;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.infrastructure.client.WeepingSkyDebugRenderer;
|
||||
import io.github.slimeistdev.crystalline_sky.mixin_ducks.client.DebugRenderer_Duck;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.debug.DebugRenderer;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(DebugRenderer.class)
|
||||
public class DebugRendererMixin implements DebugRenderer_Duck {
|
||||
@Unique
|
||||
@Mutable
|
||||
@Final
|
||||
private WeepingSkyDebugRenderer crystalline_sky$weepingSkyDebugRenderer;
|
||||
|
||||
@Unique
|
||||
private int crystalline_sky$weepingSkyMode = 0;
|
||||
|
||||
@Override
|
||||
public WeepingSkyDebugRenderer crystalline_sky$getWeepingSkyDebugRenderer() {
|
||||
return crystalline_sky$weepingSkyDebugRenderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int crystalline_sky$toggleWeepingSky() {
|
||||
crystalline_sky$weepingSkyMode = (crystalline_sky$weepingSkyMode + 1) % 3;
|
||||
return crystalline_sky$weepingSkyMode;
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void init(Minecraft client, CallbackInfo ci) {
|
||||
this.crystalline_sky$weepingSkyDebugRenderer = new WeepingSkyDebugRenderer(client);
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At("RETURN"))
|
||||
private void render(PoseStack matrices, MultiBufferSource.BufferSource vertexConsumers, double cameraX, double cameraY, double cameraZ, CallbackInfo ci) {
|
||||
if (crystalline_sky$weepingSkyMode != 0) {
|
||||
crystalline_sky$weepingSkyDebugRenderer.setLitMode(crystalline_sky$weepingSkyMode == 1);
|
||||
crystalline_sky$weepingSkyDebugRenderer.render(matrices, vertexConsumers, cameraX, cameraY, cameraZ);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "clear", at = @At("RETURN"))
|
||||
private void reset(CallbackInfo ci) {
|
||||
crystalline_sky$weepingSkyDebugRenderer.clear();
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin.client;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.client.CrystallineShaderInstances;
|
||||
import net.minecraft.client.renderer.ShaderInstance;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mixin(GameRenderer.class)
|
||||
public class GameRendererMixin {
|
||||
@WrapOperation(method = "reloadShaders", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z", ordinal = 0))
|
||||
private <E extends Pair<ShaderInstance, Consumer<ShaderInstance>>> boolean loadSkyProgram(
|
||||
List<E> instance,
|
||||
Object e,
|
||||
Operation<Boolean> original,
|
||||
ResourceProvider factory
|
||||
) throws IOException {
|
||||
original.call(instance, Pair.of(new ShaderInstance(
|
||||
factory,
|
||||
"crystalline_sky_rendertype_sky",
|
||||
DefaultVertexFormat.BLOCK
|
||||
), (Consumer<ShaderInstance>) program -> CrystallineShaderInstances.renderTypeSkyProgram = program));
|
||||
return original.call(instance, e);
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin.client;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import io.github.slimeistdev.crystalline_sky.mixin_ducks.client.LevelRenderer_Duck;
|
||||
import io.github.slimeistdev.crystalline_sky.mixin_ducks.client.RenderTarget_Duck;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.client.CrystallineRenderTypes;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
import com.mojang.blaze3d.pipeline.TextureTarget;
|
||||
import net.minecraft.client.CloudStatus;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.DeltaTracker;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix4f;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(value = LevelRenderer.class, remap = false)
|
||||
public abstract class LevelRendererMixin implements LevelRenderer_Duck {
|
||||
@Shadow
|
||||
@Final
|
||||
private Minecraft minecraft;
|
||||
|
||||
@Shadow
|
||||
protected abstract boolean doesMobEffectBlockSky(Camera camera);
|
||||
|
||||
@Shadow
|
||||
protected abstract void renderSectionLayer(RenderType renderLayer, double x, double y, double z, Matrix4f matrix4f, Matrix4f positionMatrix);
|
||||
|
||||
@Shadow
|
||||
private @Nullable RenderTarget cloudsTarget;
|
||||
|
||||
@Shadow
|
||||
public abstract void renderClouds(PoseStack matrices, Matrix4f matrix4f, Matrix4f matrix4f2, float tickDelta, double cameraX, double cameraY, double cameraZ);
|
||||
|
||||
@Unique
|
||||
@Nullable
|
||||
private RenderTarget crystalline_sky$skyBuffer;
|
||||
|
||||
@Override
|
||||
public RenderTarget crystalline_sky$getSkyFramebuffer() {
|
||||
if (crystalline_sky$skyBuffer == null) {
|
||||
var fb = minecraft.getMainRenderTarget();
|
||||
crystalline_sky$skyBuffer = new TextureTarget(fb.width, fb.height, true, Minecraft.ON_OSX);
|
||||
}
|
||||
return crystalline_sky$skyBuffer;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "renderLevel",
|
||||
at = @At(
|
||||
value = "INVOKE_STRING",
|
||||
target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V",
|
||||
args = "ldc=clear",
|
||||
shift = At.Shift.AFTER
|
||||
)
|
||||
)
|
||||
private void setupSkyBuffer(DeltaTracker tickCounter, boolean renderBlockOutline, Camera camera,
|
||||
GameRenderer gameRenderer, LightTexture lightmapTextureManager,
|
||||
Matrix4f matrix4f, Matrix4f matrix4f2, CallbackInfo ci) {
|
||||
crystalline_sky$getSkyFramebuffer();
|
||||
}
|
||||
|
||||
@Inject(method = "resize", at = @At("RETURN"))
|
||||
private void resizeSkyBuffer(int width, int height, CallbackInfo ci) {
|
||||
if (crystalline_sky$skyBuffer != null) {
|
||||
crystalline_sky$skyBuffer.resize(width, height, Minecraft.ON_OSX);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "close", at = @At("RETURN"))
|
||||
private void closeSkyBuffer(CallbackInfo ci) {
|
||||
if (crystalline_sky$skyBuffer != null) {
|
||||
crystalline_sky$skyBuffer.destroyBuffers();
|
||||
crystalline_sky$skyBuffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "renderLevel",
|
||||
at = @At(
|
||||
value = "INVOKE_STRING",
|
||||
target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V",
|
||||
args = "ldc=terrain_setup"
|
||||
)
|
||||
)
|
||||
private void copyAfterSky(DeltaTracker tickCounter, boolean renderBlockOutline, Camera camera,
|
||||
GameRenderer gameRenderer, LightTexture lightmapTextureManager,
|
||||
Matrix4f matrix4f, Matrix4f matrix4f2, CallbackInfo ci) {
|
||||
var skyBuffer = crystalline_sky$getSkyFramebuffer();
|
||||
skyBuffer.clear(Minecraft.ON_OSX);
|
||||
((RenderTarget_Duck) skyBuffer).crystalline_sky$copyColorFrom(minecraft.getMainRenderTarget());
|
||||
|
||||
// render clouds
|
||||
CloudStatus cloudRenderMode = this.minecraft.options.getCloudsType();
|
||||
if (cloudRenderMode != CloudStatus.OFF && !this.doesMobEffectBlockSky(camera)) {
|
||||
float f = tickCounter.getGameTimeDeltaPartialTick(false);
|
||||
Vec3 vec3d = camera.getPosition();
|
||||
double cx = vec3d.x();
|
||||
double cy = vec3d.y();
|
||||
double cz = vec3d.z();
|
||||
|
||||
var cloudsFramebuffer0 = this.cloudsTarget;
|
||||
this.cloudsTarget = skyBuffer;
|
||||
cloudsTarget.bindWrite(false);
|
||||
this.renderClouds(new PoseStack(), matrix4f, matrix4f2, f, cx, cy, cz);
|
||||
minecraft.getMainRenderTarget().bindWrite(false);
|
||||
this.cloudsTarget = cloudsFramebuffer0;
|
||||
}
|
||||
}
|
||||
|
||||
// after solid, cutoutMipped, and cutout
|
||||
@WrapOperation(
|
||||
method = "renderLevel",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;DDDLorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V",
|
||||
ordinal = 2
|
||||
)
|
||||
)
|
||||
private void renderCrystallineSky(LevelRenderer instance, RenderType renderLayer, double x, double y, double z,
|
||||
Matrix4f matrix4f, Matrix4f positionMatrix, Operation<Void> original) {
|
||||
original.call(instance, renderLayer, x, y, z, matrix4f, positionMatrix);
|
||||
renderSectionLayer(CrystallineRenderTypes.SKY, x, y, z, matrix4f, positionMatrix);
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin.client;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin;
|
||||
import io.github.slimeistdev.crystalline_sky.compat.Mods;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.CrystallineItems;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.client.CrystallineRenderTypes;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.ShaderInstance;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import com.mojang.blaze3d.platform.Window;
|
||||
import net.minecraft.util.Mth;
|
||||
import org.joml.Matrix4f;
|
||||
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;
|
||||
|
||||
@ConditionalMixin(mods = Mods.SODIUM, applyIfPresent = false)
|
||||
@Mixin(LevelRenderer.class)
|
||||
public class LevelRenderer_NoSodium_Mixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private Minecraft minecraft;
|
||||
|
||||
@Shadow
|
||||
private int ticks;
|
||||
|
||||
@WrapOperation(method = "renderSectionLayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ShaderInstance;setDefaultUniforms(Lcom/mojang/blaze3d/vertex/VertexFormat$Mode;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;Lcom/mojang/blaze3d/platform/Window;)V"))
|
||||
private void tintSkyBlocks(ShaderInstance instance, VertexFormat.Mode drawMode, Matrix4f viewMatrix,
|
||||
Matrix4f projectionMatrix, Window window, Operation<Void> original,
|
||||
RenderType renderLayer) {
|
||||
original.call(instance, drawMode, viewMatrix, projectionMatrix, window);
|
||||
|
||||
if (renderLayer == CrystallineRenderTypes.SKY
|
||||
&& instance.COLOR_MODULATOR != null
|
||||
&& minecraft.player != null
|
||||
&& (minecraft.player.getMainHandItem().is(CrystallineItems.SKY)
|
||||
|| minecraft.player.getOffhandItem().is(CrystallineItems.SKY)
|
||||
|| minecraft.player.getMainHandItem().is(CrystallineItems.WEEPING_SKY)
|
||||
|| minecraft.player.getOffhandItem().is(CrystallineItems.WEEPING_SKY))) {
|
||||
|
||||
float f = ticks + minecraft.getTimer().getGameTimeDeltaPartialTick(true);
|
||||
float alpha = (Mth.sin(f / 10.0f) + 1.0f) / 2.0f;
|
||||
// remap alpha from [0, 1] to [0, 0.75]
|
||||
float maxAlpha = 1.0f - 0.25f;
|
||||
alpha = alpha * maxAlpha;
|
||||
|
||||
float[] colorModulator = RenderSystem.getShaderColor().clone();
|
||||
colorModulator[3] *= alpha;
|
||||
instance.COLOR_MODULATOR.set(colorModulator);
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin.client;
|
||||
|
||||
import com.mojang.blaze3d.platform.GlConst;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import io.github.slimeistdev.crystalline_sky.mixin_ducks.client.RenderTarget_Duck;
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(RenderTarget.class)
|
||||
public class RenderTargetMixin implements RenderTarget_Duck {
|
||||
@Shadow
|
||||
protected int colorTextureId;
|
||||
|
||||
@Shadow
|
||||
public int width;
|
||||
|
||||
@Shadow
|
||||
public int height;
|
||||
|
||||
@Shadow
|
||||
public int frameBufferId;
|
||||
|
||||
@Override
|
||||
public void crystalline_sky$copyColorFrom(RenderTarget framebuffer) {
|
||||
RenderSystem.assertOnRenderThreadOrInit();
|
||||
if (this.colorTextureId == -1) {
|
||||
throw new IllegalStateException("Trying to copy color texture to a RenderTarget without a color texture");
|
||||
} else if (framebuffer.getColorTextureId() == -1) {
|
||||
throw new IllegalStateException("Trying to copy color texture from a RenderTarget without a color texture");
|
||||
} else {
|
||||
GlStateManager._glBindFramebuffer(GlConst.GL_READ_FRAMEBUFFER, framebuffer.frameBufferId);
|
||||
GlStateManager._glBindFramebuffer(GlConst.GL_DRAW_FRAMEBUFFER, this.frameBufferId);
|
||||
GlStateManager._glBlitFrameBuffer(
|
||||
0, 0, framebuffer.width, framebuffer.height,
|
||||
0, 0, this.width, this.height,
|
||||
GlConst.GL_COLOR_BUFFER_BIT, GlConst.GL_NEAREST
|
||||
);
|
||||
GlStateManager._glBindFramebuffer(GlConst.GL_FRAMEBUFFER, framebuffer.frameBufferId);
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin.client;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import io.github.slimeistdev.crystalline_sky.mixin_ducks.client.RenderType_Duck;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(RenderType.class)
|
||||
public class RenderTypeMixin implements RenderType_Duck {
|
||||
@Shadow
|
||||
@Final
|
||||
@Mutable
|
||||
private static ImmutableList<RenderType> CHUNK_BUFFER_LAYERS;
|
||||
|
||||
@Override
|
||||
public void crystalline_sky$markAsBlockLayer() {
|
||||
CHUNK_BUFFER_LAYERS = ImmutableList.<RenderType>builder()
|
||||
.addAll(CHUNK_BUFFER_LAYERS)
|
||||
.add((RenderType) (Object) this)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin.client.compat.axiom;
|
||||
|
||||
import com.moulberry.axiom.render.ChunkRenderOverrider;
|
||||
import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin;
|
||||
import io.github.slimeistdev.crystalline_sky.compat.Mods;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.client.CrystallineRenderTypes;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
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;
|
||||
|
||||
@ConditionalMixin(mods = Mods.AXIOM)
|
||||
@Mixin(ChunkRenderOverrider.AxiomChunkOverrideLayer.class)
|
||||
public class ChunkRenderOverrider_AxiomChunkOverrideLayerMixin {
|
||||
@Inject(method = "fromVanilla", at = @At("HEAD"), cancellable = true)
|
||||
private static void unexplodeSky(RenderType layer, CallbackInfoReturnable<ChunkRenderOverrider.AxiomChunkOverrideLayer> cir) {
|
||||
if (layer == CrystallineRenderTypes.SKY) {
|
||||
cir.setReturnValue(ChunkRenderOverrider.AxiomChunkOverrideLayer.CUTOUT_MIPPED);
|
||||
}
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin.client.compat.sodium;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin;
|
||||
import io.github.slimeistdev.crystalline_sky.compat.Mods;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.client.CrystallineRenderTypes;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.material.DefaultMaterials;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.material.Material;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.material.parameters.AlphaCutoffParameter;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@ConditionalMixin(mods = Mods.SODIUM)
|
||||
@Mixin(DefaultMaterials.class)
|
||||
public class DefaultMaterialsMixin {
|
||||
@Unique
|
||||
@SuppressWarnings("OptionalGetWithoutIsPresent")
|
||||
private static final Material crystalline_sky$SKY = new Material(
|
||||
Arrays.stream(DefaultTerrainRenderPasses.ALL)
|
||||
.filter(pass -> ((TerrainRenderPassAccessor) pass).crystalline_sky$getRenderLayer() == CrystallineRenderTypes.SKY)
|
||||
.findFirst()
|
||||
.get(),
|
||||
AlphaCutoffParameter.ZERO,
|
||||
false
|
||||
);
|
||||
|
||||
@Inject(method = "forRenderLayer", at = @At(value = "NEW", target = "(Ljava/lang/String;)Ljava/lang/IllegalArgumentException;"), cancellable = true)
|
||||
private static void addCrystallineSkyMaterial(RenderType layer, CallbackInfoReturnable<Material> cir) {
|
||||
if (layer == CrystallineRenderTypes.SKY) {
|
||||
cir.setReturnValue(crystalline_sky$SKY);
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin.client.compat.sodium;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin;
|
||||
import io.github.slimeistdev.crystalline_sky.compat.Mods;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.client.CrystallineRenderTypes;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
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;
|
||||
|
||||
@ConditionalMixin(mods = Mods.SODIUM)
|
||||
@Mixin(DefaultTerrainRenderPasses.class)
|
||||
public class DefaultTerrainRenderPassesMixin {
|
||||
@Mutable
|
||||
@Shadow
|
||||
@Final
|
||||
public static TerrainRenderPass[] ALL;
|
||||
|
||||
@Inject(method = "<clinit>", at = @At("RETURN"))
|
||||
private static void addCrystallineSkyPass(CallbackInfo ci) {
|
||||
var ALL$ = new TerrainRenderPass[ALL.length + 1];
|
||||
System.arraycopy(ALL, 0, ALL$, 0, ALL.length);
|
||||
ALL$[ALL.length] = new TerrainRenderPass(CrystallineRenderTypes.SKY, false, true);
|
||||
ALL = ALL$;
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
The following mixins in this package are from derived from https://github.com/wisp-forest/affinity, and as such are licensed under the MIT license:
|
||||
|
||||
- DefaultMaterialsMixin
|
||||
- DefaultTerrainRenderPassesMixin
|
||||
- SodiumWorldRendererMixin
|
||||
- TerrainRenderPassAccessor
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin.client.compat.sodium;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin;
|
||||
import io.github.slimeistdev.crystalline_sky.compat.Mods;
|
||||
import io.github.slimeistdev.crystalline_sky.compat.sodium.SkyShaderInterface;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.client.CrystallineRenderTypes;
|
||||
import net.caffeinemc.mods.sodium.client.gl.shader.GlProgram;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.ShaderChunkRenderer;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.shader.ChunkShaderInterface;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.shader.ChunkShaderOptions;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.shader.ShaderBindingContext;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.material.DefaultMaterials;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@ConditionalMixin(mods = Mods.SODIUM)
|
||||
@Mixin(ShaderChunkRenderer.class)
|
||||
public class ShaderChunkRendererMixin {
|
||||
@WrapOperation(method = "compileProgram", at = @At(value = "INVOKE", target = "Lnet/caffeinemc/mods/sodium/client/render/chunk/ShaderChunkRenderer;createShader(Ljava/lang/String;Lnet/caffeinemc/mods/sodium/client/render/chunk/shader/ChunkShaderOptions;)Lnet/caffeinemc/mods/sodium/client/gl/shader/GlProgram;"), remap = false)
|
||||
private GlProgram<ChunkShaderInterface> createSkyShader(ShaderChunkRenderer instance, String path, ChunkShaderOptions options, Operation<GlProgram<ChunkShaderInterface>> original) {
|
||||
if (options.pass() == DefaultMaterials.forRenderLayer(CrystallineRenderTypes.SKY).pass) {
|
||||
return original.call(instance, "sodium_compat/block_layer_sky", options);
|
||||
}
|
||||
|
||||
return original.call(instance, path, options);
|
||||
}
|
||||
|
||||
@WrapOperation(method = "createShader", at = @At(value = "INVOKE", target = "Lnet/minecraft/resources/ResourceLocation;fromNamespaceAndPath(Ljava/lang/String;Ljava/lang/String;)Lnet/minecraft/resources/ResourceLocation;"))
|
||||
private ResourceLocation redirectShaderNamespace(String namespace, String path, Operation<ResourceLocation> original, String pathArg) {
|
||||
if (pathArg.equals("sodium_compat/block_layer_sky")) {
|
||||
namespace = "crystalline_sky";
|
||||
}
|
||||
|
||||
return original.call(namespace, path);
|
||||
}
|
||||
|
||||
@WrapOperation(method = "createShader", at = @At(value = "INVOKE", target = "Lnet/caffeinemc/mods/sodium/client/gl/shader/GlProgram$Builder;link(Ljava/util/function/Function;)Lnet/caffeinemc/mods/sodium/client/gl/shader/GlProgram;"), remap = false)
|
||||
private static GlProgram<ChunkShaderInterface> useSkyShaderInterface(GlProgram.Builder instance, Function<ShaderBindingContext, ChunkShaderInterface> factory, Operation<GlProgram<ChunkShaderInterface>> original, String argPath, ChunkShaderOptions argOptions) {
|
||||
if (argPath.equals("sodium_compat/block_layer_sky")) {
|
||||
factory = (shader) -> new SkyShaderInterface(shader, argOptions);
|
||||
}
|
||||
|
||||
return original.call(instance, factory);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin.client.compat.sodium;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin;
|
||||
import io.github.slimeistdev.crystalline_sky.compat.Mods;
|
||||
import io.github.slimeistdev.crystalline_sky.registry.client.CrystallineRenderTypes;
|
||||
import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.ChunkRenderMatrices;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.material.DefaultMaterials;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
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;
|
||||
|
||||
@ConditionalMixin(mods = Mods.SODIUM)
|
||||
@Mixin(SodiumWorldRenderer.class)
|
||||
public class SodiumWorldRendererMixin {
|
||||
@Shadow
|
||||
private RenderSectionManager renderSectionManager;
|
||||
|
||||
@Inject(method = "drawChunkLayer", at = @At("RETURN"))
|
||||
private void drawCrystallineLayer(RenderType renderLayer, ChunkRenderMatrices matrices, double x, double y, double z, CallbackInfo ci) {
|
||||
if (renderLayer != CrystallineRenderTypes.SKY) return;
|
||||
|
||||
renderSectionManager.renderLayer(matrices, DefaultMaterials.forRenderLayer(CrystallineRenderTypes.SKY).pass, x, y, z);
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin.client.compat.sodium;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.annotation.mixin.ConditionalMixin;
|
||||
import io.github.slimeistdev.crystalline_sky.compat.Mods;
|
||||
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@ConditionalMixin(mods = Mods.SODIUM)
|
||||
@Mixin(TerrainRenderPass.class)
|
||||
public interface TerrainRenderPassAccessor {
|
||||
@Accessor("renderType")
|
||||
RenderType crystalline_sky$getRenderLayer();
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin_ducks;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.infrastructure.WeepingStorage;
|
||||
|
||||
public interface ChunkSkyLight_Duck {
|
||||
WeepingStorage crystalline_sky$getWeepingStorage();
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin_ducks.client;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.infrastructure.client.WeepingSkyDebugRenderer;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public interface DebugRenderer_Duck {
|
||||
WeepingSkyDebugRenderer crystalline_sky$getWeepingSkyDebugRenderer();
|
||||
|
||||
int crystalline_sky$toggleWeepingSky();
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin_ducks.client;
|
||||
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
|
||||
public interface LevelRenderer_Duck {
|
||||
RenderTarget crystalline_sky$getSkyFramebuffer();
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin_ducks.client;
|
||||
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
|
||||
public interface RenderTarget_Duck {
|
||||
void crystalline_sky$copyColorFrom(RenderTarget framebuffer);
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package io.github.slimeistdev.crystalline_sky.mixin_ducks.client;
|
||||
|
||||
public interface RenderType_Duck {
|
||||
void crystalline_sky$markAsBlockLayer();
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package io.github.slimeistdev.crystalline_sky.registry;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.CrystallineSky;
|
||||
import io.github.slimeistdev.crystalline_sky.content.blocks.SkyLightBlock;
|
||||
import io.github.slimeistdev.crystalline_sky.content.blocks.WeepingSkyLightBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.LightBlock;
|
||||
import net.minecraft.world.level.block.TransparentBlock;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour.Properties;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.level.block.SoundType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.material.MapColor;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
public class CrystallineBlocks {
|
||||
public static final Block SKY = register("sky", TransparentBlock::new, Properties.of()
|
||||
.strength(0.3f)
|
||||
.sound(SoundType.AMETHYST)
|
||||
.noOcclusion()
|
||||
.isValidSpawn(Blocks::never)
|
||||
.isRedstoneConductor(Blocks::never)
|
||||
.isSuffocating(Blocks::never)
|
||||
.isViewBlocking(Blocks::never)
|
||||
.emissiveRendering(Blocks::always));
|
||||
|
||||
public static final Block WEEPING_SKY = register("weeping_sky", TransparentBlock::new, Properties.ofFullCopy(SKY));
|
||||
|
||||
public static final Block SKY_LIGHT = register(
|
||||
"sky_light",
|
||||
SkyLightBlock::new,
|
||||
BlockBehaviour.Properties.of()
|
||||
.replaceable()
|
||||
.strength(-1.0F, 3600000.8F)
|
||||
.mapColor(state -> state.getValue(BlockStateProperties.WATERLOGGED) ? MapColor.WATER : MapColor.NONE)
|
||||
.noLootTable()
|
||||
.noOcclusion()
|
||||
);
|
||||
|
||||
public static final Block WEEPING_SKY_LIGHT = register(
|
||||
"weeping_sky_light",
|
||||
WeepingSkyLightBlock::new,
|
||||
BlockBehaviour.Properties.of()
|
||||
.replaceable()
|
||||
.strength(-1.0F, 3600000.8F)
|
||||
.mapColor(state -> state.getValue(BlockStateProperties.WATERLOGGED) ? MapColor.WATER : MapColor.NONE)
|
||||
.noLootTable()
|
||||
.noOcclusion()
|
||||
);
|
||||
|
||||
public static void init() {}
|
||||
|
||||
private static <T extends Block> T register(String id, Function<Properties, T> factory, Properties settings) {
|
||||
ResourceKey<Block> key = CrystallineSky.key(Registries.BLOCK, id);
|
||||
return Registry.register(BuiltInRegistries.BLOCK, key, factory.apply(settings));
|
||||
}
|
||||
|
||||
public static boolean isCrystallineSky(BlockState state) {
|
||||
return getSkyLightLevel(state) > 0;
|
||||
}
|
||||
|
||||
public static int getSkyLightLevel(BlockState state) {
|
||||
if (state.is(SKY_LIGHT)) {
|
||||
return state.getValue(LightBlock.LEVEL);
|
||||
} else if (state.is(SKY)) { // weeping sky explicitly excluded, that's handled differently
|
||||
return 15;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static boolean isWeepingSky(BlockState state) {
|
||||
return state.is(WEEPING_SKY) || state.is(WEEPING_SKY_LIGHT);
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package io.github.slimeistdev.crystalline_sky.registry;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.CrystallineSky;
|
||||
import net.minecraft.core.component.DataComponentType;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.util.Unit;
|
||||
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public class CrystallineDataComponentTypes {
|
||||
public static final DataComponentType<Unit> WEEPING_SKY_DEBUG_COLUMN_TOOL = register(
|
||||
"weeping_sky_debug_column_tool",
|
||||
b -> b
|
||||
.networkSynchronized(StreamCodec.unit(Unit.INSTANCE))
|
||||
.persistent(Unit.CODEC)
|
||||
);
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static <T> DataComponentType<T> register(String id, UnaryOperator<DataComponentType.Builder<T>> builderOperator) {
|
||||
return Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, CrystallineSky.id(id), builderOperator.apply(DataComponentType.builder()).build());
|
||||
}
|
||||
|
||||
public static void init() {}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package io.github.slimeistdev.crystalline_sky.registry;
|
||||
|
||||
import io.github.slimeistdev.crystalline_sky.content.blocks.SkyLightBlock;
|
||||
import io.github.slimeistdev.crystalline_sky.content.items.WeepingSkyBlockItem;
|
||||
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.LightBlock;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.world.item.component.BlockItemStateProperties;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.CreativeModeTabs;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.Rarity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class CrystallineItems {
|
||||
public static final Item SKY = Items.registerBlock(
|
||||
CrystallineBlocks.SKY,
|
||||
settings -> settings.rarity(Rarity.EPIC)
|
||||
);
|
||||
|
||||
public static final Item WEEPING_SKY = register(
|
||||
CrystallineBlocks.WEEPING_SKY,
|
||||
(block, settings) -> new WeepingSkyBlockItem(block, settings.rarity(Rarity.EPIC))
|
||||
);
|
||||
|
||||
public static final Item SKY_LIGHT = Items.registerBlock(
|
||||
CrystallineBlocks.SKY_LIGHT,
|
||||
settings -> settings.rarity(Rarity.EPIC)
|
||||
.component(DataComponents.BLOCK_STATE, BlockItemStateProperties.EMPTY.with(LightBlock.LEVEL, 15))
|
||||
);
|
||||
|
||||
public static final Item WEEPING_SKY_LIGHT = Items.registerBlock(
|
||||
CrystallineBlocks.WEEPING_SKY_LIGHT,
|
||||
settings -> settings.rarity(Rarity.EPIC)
|
||||
);
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static Item register(Block block, BiFunction<Block, Item.Properties, BlockItem> factory) {
|
||||
return Items.registerBlock(factory.apply(block, new Item.Properties()));
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
ItemGroupEvents.modifyEntriesEvent(CreativeModeTabs.OP_BLOCKS).register(entries -> {
|
||||
if (!entries.getContext().hasPermissions()) return;
|
||||
|
||||
List<ItemStack> stacks = new ArrayList<>();
|
||||
|
||||
stacks.add(new ItemStack(SKY));
|
||||
stacks.add(new ItemStack(WEEPING_SKY));
|
||||
|
||||
for (int i = 15; i >= 0; i--) {
|
||||
stacks.add(SkyLightBlock.setLightOnStack(new ItemStack(SKY_LIGHT), i));
|
||||
}
|
||||
|
||||
stacks.add(new ItemStack(WEEPING_SKY_LIGHT));
|
||||
|
||||
entries.addAfter(Items.LIGHT, stacks);
|
||||
});
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package io.github.slimeistdev.crystalline_sky.registry.client;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import io.github.slimeistdev.crystalline_sky.mixin_ducks.client.RenderType_Duck;
|
||||
import io.github.slimeistdev.crystalline_sky.mixin_ducks.client.LevelRenderer_Duck;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.RenderStateShard;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
|
||||
import static net.minecraft.client.renderer.RenderType.create;
|
||||
import static net.minecraft.client.renderer.RenderStateShard.LIGHTMAP;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class CrystallineRenderTypes {
|
||||
private static final RenderStateShard.EmptyTextureStateShard SKY_TEXTURE = new SkyBufferTexture();
|
||||
private static final RenderStateShard.ShaderStateShard SKY_PROGRAM = new RenderStateShard.ShaderStateShard(CrystallineShaderInstances::getRenderTypeSkyProgram);
|
||||
|
||||
public static final RenderType SKY = create(
|
||||
"crystalline_sky_sky",
|
||||
DefaultVertexFormat.BLOCK,
|
||||
VertexFormat.Mode.QUADS,
|
||||
4194304,
|
||||
true,
|
||||
false,
|
||||
RenderType.CompositeState.builder()
|
||||
.setLightmapState(LIGHTMAP)
|
||||
.setShaderState(SKY_PROGRAM)
|
||||
.setTextureState(SKY_TEXTURE)
|
||||
.createCompositeState(true)
|
||||
);
|
||||
|
||||
static {
|
||||
//noinspection DataFlowIssue
|
||||
((RenderType_Duck) SKY).crystalline_sky$markAsBlockLayer();
|
||||
}
|
||||
|
||||
private static class SkyBufferTexture extends RenderStateShard.EmptyTextureStateShard {
|
||||
public SkyBufferTexture() {
|
||||
super(() -> {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
var fb = ((LevelRenderer_Duck) mc.levelRenderer).crystalline_sky$getSkyFramebuffer();
|
||||
RenderSystem.setShaderTexture(0, fb.getColorTextureId());
|
||||
}, () -> {});
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package io.github.slimeistdev.crystalline_sky.registry.client;
|
||||
|
||||
import net.minecraft.client.renderer.ShaderInstance;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class CrystallineShaderInstances {
|
||||
@ApiStatus.Internal
|
||||
public static @Nullable ShaderInstance renderTypeSkyProgram;
|
||||
|
||||
public static @Nullable ShaderInstance getRenderTypeSkyProgram() {
|
||||
return renderTypeSkyProgram;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package io.github.slimeistdev.crystalline_sky.util;
|
||||
|
||||
public class ArrayUtils {
|
||||
/**
|
||||
* Binary search for the first index i in the sorted array where array[i] < value and array[i + 1] > value.
|
||||
* @param array must be sorted in ascending order
|
||||
* @param value the value to find a place for
|
||||
* @return the index to insert at, or -1 if no such index exists
|
||||
*/
|
||||
public static int binarySearchSortedInsertionPoint(int[] array, int value) {
|
||||
return binarySearchSortedInsertionPoint(array, value, array.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binary search for the first index i in the sorted array where array[i] < value and array[i + 1] > value.
|
||||
* @param array must be sorted in ascending order
|
||||
* @param value the value to find a place for
|
||||
* @param length limits the search to indexes [0, length)
|
||||
* @return the index to insert at, or -1 if no such index exists
|
||||
*/
|
||||
public static int binarySearchSortedInsertionPoint(int[] array, int value, int length) {
|
||||
if (length == 0) return 0;
|
||||
if (value < array[0]) return 0;
|
||||
if (value > array[length - 1]) return length;
|
||||
|
||||
int left = 0;
|
||||
int right = length - 1;
|
||||
|
||||
while (left < right) {
|
||||
int mid = (left + right) / 2;
|
||||
int midValue = array[mid];
|
||||
|
||||
if (midValue == value)
|
||||
return -1;
|
||||
|
||||
if (midValue < value) {
|
||||
left = mid + 1;
|
||||
} else { // midValue > value
|
||||
right = mid;
|
||||
}
|
||||
}
|
||||
|
||||
if (array[left] == value) return -1;
|
||||
if (array[right] == value) return -1;
|
||||
return left;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user