Add Blockstate and Model data providers (#6241)
This commit is contained in:
parent
3bf6c17bb8
commit
acaa470dea
82 changed files with 4765 additions and 10 deletions
11
build.gradle
11
build.gradle
|
@ -159,7 +159,10 @@ project(':forge') {
|
|||
]
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["$rootDir/src/test/resources"]
|
||||
srcDirs = [
|
||||
"$rootDir/src/test/resources",
|
||||
"$rootDir/src/generated_test/resources"
|
||||
]
|
||||
}
|
||||
}
|
||||
userdev {
|
||||
|
@ -308,7 +311,8 @@ project(':forge') {
|
|||
source sourceSets.main
|
||||
source sourceSets.userdev
|
||||
|
||||
args '--mod', 'forge', '--all', '--output', rootProject.file('src/generated/resources/')
|
||||
args '--mod', 'forge', '--all', '--output', rootProject.file('src/generated/resources/'), '--validate',
|
||||
'--existing', sourceSets.main.resources.srcDirs[0]
|
||||
}
|
||||
|
||||
forge_test_data {
|
||||
|
@ -321,7 +325,8 @@ project(':forge') {
|
|||
tests { sources sourceSets.test }
|
||||
}
|
||||
|
||||
args '--mod', 'data_gen_test', '--all', '--output', rootProject.file('src/generated_test/resources/')
|
||||
args '--mod', 'data_gen_test', '--all', '--output', rootProject.file('src/generated_test/resources/'), '--validate',
|
||||
'--existing', sourceSets.main.resources.srcDirs[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@@ -21,8 +22,10 @@
|
||||
@@ -21,8 +22,11 @@
|
||||
OptionSpec<Void> optionspec6 = optionparser.accepts("all", "Include all generators");
|
||||
OptionSpec<String> optionspec7 = optionparser.accepts("output", "Output folder").withRequiredArg().defaultsTo("generated");
|
||||
OptionSpec<String> optionspec8 = optionparser.accepts("input", "Input folder").withRequiredArg();
|
||||
+ OptionSpec<String> existing = optionparser.accepts("existing", "Existing resource packs that generated resources can reference").withRequiredArg();
|
||||
+ OptionSpec<File> gameDir = optionparser.accepts("gameDir").withRequiredArg().ofType(File.class).defaultsTo(new File(".")).required();
|
||||
+ OptionSpec<String> mod = optionparser.accepts("mod", "A modid to dump").withRequiredArg().withValuesSeparatedBy(",");
|
||||
OptionSet optionset = optionparser.parse(p_main_0_);
|
||||
|
@ -19,7 +20,7 @@
|
|||
Path path = Paths.get(optionspec7.value(optionset));
|
||||
boolean flag = optionset.has(optionspec6);
|
||||
boolean flag1 = flag || optionset.has(optionspec2);
|
||||
@@ -30,10 +33,11 @@
|
||||
@@ -30,10 +34,12 @@
|
||||
boolean flag3 = flag || optionset.has(optionspec3);
|
||||
boolean flag4 = flag || optionset.has(optionspec4);
|
||||
boolean flag5 = flag || optionset.has(optionspec5);
|
||||
|
@ -28,8 +29,9 @@
|
|||
- }).collect(Collectors.toList()), flag1, flag2, flag3, flag4, flag5);
|
||||
- datagenerator.func_200392_c();
|
||||
+ Collection<Path> inputs = optionset.valuesOf(optionspec8).stream().map(Paths::get).collect(Collectors.toList());
|
||||
+ Collection<Path> existingPacks = optionset.valuesOf(existing).stream().map(Paths::get).collect(Collectors.toList());
|
||||
+ java.util.Set<String> mods = new java.util.HashSet<>(optionset.valuesOf(mod));
|
||||
+ net.minecraftforge.fml.ModLoader.get().runDataGenerator(mods, path, inputs, flag2, flag1, flag3, flag4, flag5);
|
||||
+ net.minecraftforge.fml.ModLoader.get().runDataGenerator(mods, path, inputs, existingPacks, flag2, flag1, flag3, flag4, flag5);
|
||||
+ if (mods.contains("minecraft") || mods.isEmpty())
|
||||
+ func_200264_a(mods.isEmpty() ? path : path.resolve("minecraft"), inputs, flag1, flag2, flag3, flag4, flag5).func_200392_c();
|
||||
} else {
|
||||
|
|
|
@ -1,2 +1,64 @@
|
|||
dc2deb0c2da07695855bbb88882455a80787ccac assets\data_gen_test\models\block\acacia_door_bottom.json
|
||||
e987df4921fc71322984556e9617915d4df2bdff assets\data_gen_test\models\block\acacia_door_bottom_hinge.json
|
||||
5103910559b21ce74f315358935a3e582b0c45cd assets\data_gen_test\models\block\acacia_door_top.json
|
||||
590e2cc0ba335ffc618441d6fcc582401ee646cc assets\data_gen_test\models\block\acacia_door_top_hinge.json
|
||||
9c4e371aace89c681b4ea367975f9b4569acaada assets\data_gen_test\models\block\acacia_fence_gate.json
|
||||
1e2e4ace5adef04a3cee3d528c33382a18f40513 assets\data_gen_test\models\block\acacia_fence_gate_open.json
|
||||
557312487766c0801958d72d13b0ac16d8d5d9fd assets\data_gen_test\models\block\acacia_fence_gate_wall.json
|
||||
dd8d2e76831b2a445737f378458db5c505a9b03d assets\data_gen_test\models\block\acacia_fence_gate_wall_open.json
|
||||
4d5b8be3e004e34e75c0f7d415595e14f8985d6c assets\data_gen_test\models\block\acacia_fence_post.json
|
||||
90491fcfa42ee4d8ec7958655866f5a3b55811ad assets\data_gen_test\models\block\acacia_fence_side.json
|
||||
9bf079dec64b632446a867744149d0fccff6fc70 assets\data_gen_test\models\block\acacia_log.json
|
||||
462367a50d9ab2306ca642a72b5fd20c217dc451 assets\data_gen_test\models\block\acacia_slab.json
|
||||
3032bec1f17684ceef914e3763b9be773d17b02f assets\data_gen_test\models\block\acacia_slab_top.json
|
||||
3611756373823dced8f5db5237a6ee0a5f3cbb7a assets\data_gen_test\models\block\acacia_stairs.json
|
||||
72a7c67a73c6217ad117d5583a329e89fd34f246 assets\data_gen_test\models\block\acacia_stairs_inner.json
|
||||
34f85d11d44a198433f530bd54c2c172d7699872 assets\data_gen_test\models\block\acacia_stairs_outer.json
|
||||
31c3b7793cb61870bd74e8e85af7597be57827c6 assets\data_gen_test\models\block\acacia_trapdoor_bottom.json
|
||||
8be9644fe5227cb2c3af52af01b02e9c2c4f9a38 assets\data_gen_test\models\block\acacia_trapdoor_open.json
|
||||
8978e4a1d2902029738eac82749ffc2c30088fe9 assets\data_gen_test\models\block\acacia_trapdoor_top.json
|
||||
88c7bca952fda50e5287ddb5c2e74ff179d08eb2 assets\data_gen_test\models\block\barrel.json
|
||||
ce5d10ee7ab7afed216f3159bdcd5b1deb114104 assets\data_gen_test\models\block\barrel_open.json
|
||||
c3ecb601615c300cafbdd9dc28f9fc42b6835dcd assets\data_gen_test\models\block\birch_fence_gate.json
|
||||
3bcea12fba8714c7ad7c00f440f3e7546a8958ce assets\data_gen_test\models\block\birch_fence_gate_open.json
|
||||
13b47687fb80334b3e51781e691f53f8073843a7 assets\data_gen_test\models\block\birch_fence_gate_wall.json
|
||||
dd0a89461e5f72c08ccf6f3d07bce9c290019022 assets\data_gen_test\models\block\birch_fence_gate_wall_open.json
|
||||
1d0f4dceda264528ae1b1f5f971ae84202405e51 assets\data_gen_test\models\block\block.json
|
||||
42fcd2c027f204708f423808f22f6e4c0d5f586b assets\data_gen_test\models\block\cobblestone_wall_post.json
|
||||
21d8ec1410152c67c4b7a06184be545ef1e1d0cf assets\data_gen_test\models\block\cobblestone_wall_side.json
|
||||
26c80ee9a99336b434c9984445bfcf01b25f2cd0 assets\data_gen_test\models\block\cube.json
|
||||
428f68585535789c809eb40d8151a5baa2b0045b assets\data_gen_test\models\block\furnace.json
|
||||
51a161348cf0c51252dbdbad035b7945e02a8763 assets\data_gen_test\models\block\furnace_on.json
|
||||
eb911d39c848d09bc5910aef1a86af63b4fbb6c6 assets\data_gen_test\models\block\glass_pane_noside.json
|
||||
e5df8cc0b7dd9f79fe930711bcf8e60385b8b7ab assets\data_gen_test\models\block\glass_pane_noside_alt.json
|
||||
b178beb42e1db7d177ceb28f0703876943c22a90 assets\data_gen_test\models\block\glass_pane_post.json
|
||||
17cd1168c6c32e1fbd870a9e61716aed6ba7c8a2 assets\data_gen_test\models\block\glass_pane_side.json
|
||||
85326250de254ad1bba0ad183c78e876afa3da51 assets\data_gen_test\models\block\glass_pane_side_alt.json
|
||||
cebd38234491e0882901230538b4205bcf816597 assets\data_gen_test\models\block\oak_trapdoor_bottom.json
|
||||
d8842f8f1a63ee4ac0bcc52e83a375de983b8bb0 assets\data_gen_test\models\block\oak_trapdoor_open.json
|
||||
091f1cf54c828d8c8315cedeb28a3f864a72456e assets\data_gen_test\models\block\oak_trapdoor_top.json
|
||||
a5ace137c931b71acf2599184832187a241acc9c assets\data_gen_test\models\block\stone.json
|
||||
78f9176f15f3a728ee20b5a734e89a32c8108395 assets\data_gen_test\models\block\torch.json
|
||||
e1820400e7b8e4011121eae3f7c873d73f301cd7 assets\data_gen_test\models\block\wall_torch.json
|
||||
d96ff395b92ed2419c8c4aa6e61a610ef78b3216 assets\data_gen_test\models\item\fishing_rod.json
|
||||
abe69efd05a6349acca7e39f3353d7c928a55a72 assets\data_gen_test\models\item\fishing_rod_cast.json
|
||||
e1a3a09af48181e868c26741053e36eafe8bead6 assets\data_gen_test\models\item\test_block_model.json
|
||||
9a27e0d9a3f3d2a834eff92661e090eeda1c59fe assets\data_gen_test\models\item\test_generated_model.json
|
||||
1767c874758bd7c3235db446b6219e6e1edd25b4 assets\minecraft\blockstates\acacia_door.json
|
||||
e41383b4c13ec922c0f5cfa2cc313d88fd294685 assets\minecraft\blockstates\acacia_fence.json
|
||||
967d268bfcf32474b61578bef16e853b6360dd73 assets\minecraft\blockstates\acacia_fence_gate.json
|
||||
e9af65f877e6d012bce27ae35eef3822dda98675 assets\minecraft\blockstates\acacia_log.json
|
||||
2fc1c2cff82aaebdb4ecf298e6de278c62cce3b3 assets\minecraft\blockstates\acacia_slab.json
|
||||
075938fd82340c895eff226d11898abae9a5e178 assets\minecraft\blockstates\acacia_stairs.json
|
||||
9384d458574395650a976b3ad1fe5905b100ddbf assets\minecraft\blockstates\acacia_trapdoor.json
|
||||
42a10a132337264c8821426e17d0c09b92ad42f9 assets\minecraft\blockstates\barrel.json
|
||||
9bc8ba4b8e4ad3cc7074838cf1bf8dad89e2b339 assets\minecraft\blockstates\birch_fence_gate.json
|
||||
acbf9f921b05785d49d8526870ba13aee1a322f7 assets\minecraft\blockstates\cobblestone_wall.json
|
||||
6a3c8e2691b6b7f39f1236a17bb6aa145a9db53f assets\minecraft\blockstates\furnace.json
|
||||
1b2a1344020237ab877e27c43934b042d259bf36 assets\minecraft\blockstates\glass_pane.json
|
||||
bf2e445b48b024354a69138b20a4a4a8aa5d15d1 assets\minecraft\blockstates\oak_trapdoor.json
|
||||
9c4cc92efb78811e8d70a383a1a89eb75b69db04 assets\minecraft\blockstates\stone.json
|
||||
570fdd86046df75a073b759ff7aaf4c4caf43115 assets\minecraft\blockstates\torch.json
|
||||
16f12628d0c23a4ca66961b0a1c837c3b1e4457d assets\minecraft\blockstates\wall_torch.json
|
||||
4fbaf6f4a3ea05cc071076e27f44ac81f9cc50e3 data\data_gen_test\advancements\conditional.json
|
||||
ed4cbf1a3a2f5d8969f6346fdc9acdbe81d0c919 data\data_gen_test\recipes\conditional.json
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"parent": "block/door_bottom",
|
||||
"textures": {
|
||||
"bottom": "block/acacia_door_bottom",
|
||||
"top": "block/acacia_door_top"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"parent": "block/door_bottom_rh",
|
||||
"textures": {
|
||||
"bottom": "block/acacia_door_bottom",
|
||||
"top": "block/acacia_door_top"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"parent": "block/door_top",
|
||||
"textures": {
|
||||
"bottom": "block/acacia_door_bottom",
|
||||
"top": "block/acacia_door_top"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"parent": "block/door_top_rh",
|
||||
"textures": {
|
||||
"bottom": "block/acacia_door_bottom",
|
||||
"top": "block/acacia_door_top"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_fence_gate",
|
||||
"textures": {
|
||||
"texture": "block/acacia_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_fence_gate_open",
|
||||
"textures": {
|
||||
"texture": "block/acacia_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_fence_gate_wall",
|
||||
"textures": {
|
||||
"texture": "block/acacia_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_fence_gate_wall_open",
|
||||
"textures": {
|
||||
"texture": "block/acacia_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/fence_post",
|
||||
"textures": {
|
||||
"texture": "block/acacia_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/fence_side",
|
||||
"textures": {
|
||||
"texture": "block/acacia_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"parent": "block/cube_column",
|
||||
"textures": {
|
||||
"side": "block/acacia_log",
|
||||
"end": "block/acacia_log_top"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"parent": "block/slab",
|
||||
"textures": {
|
||||
"side": "block/acacia_planks",
|
||||
"bottom": "block/acacia_planks",
|
||||
"top": "block/acacia_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"parent": "block/slab_top",
|
||||
"textures": {
|
||||
"side": "block/acacia_planks",
|
||||
"bottom": "block/acacia_planks",
|
||||
"top": "block/acacia_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"parent": "block/stairs",
|
||||
"textures": {
|
||||
"side": "block/acacia_planks",
|
||||
"bottom": "block/acacia_planks",
|
||||
"top": "block/acacia_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"parent": "block/inner_stairs",
|
||||
"textures": {
|
||||
"side": "block/acacia_planks",
|
||||
"bottom": "block/acacia_planks",
|
||||
"top": "block/acacia_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"parent": "block/outer_stairs",
|
||||
"textures": {
|
||||
"side": "block/acacia_planks",
|
||||
"bottom": "block/acacia_planks",
|
||||
"top": "block/acacia_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_orientable_trapdoor_bottom",
|
||||
"textures": {
|
||||
"texture": "block/acacia_trapdoor"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_orientable_trapdoor_open",
|
||||
"textures": {
|
||||
"texture": "block/acacia_trapdoor"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_orientable_trapdoor_top",
|
||||
"textures": {
|
||||
"texture": "block/acacia_trapdoor"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"parent": "block/cube_bottom_top",
|
||||
"textures": {
|
||||
"side": "block/barrel_side",
|
||||
"bottom": "block/barrel_bottom",
|
||||
"top": "block/barrel_top"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"parent": "block/cube_bottom_top",
|
||||
"textures": {
|
||||
"side": "block/barrel_side",
|
||||
"bottom": "block/barrel_bottom",
|
||||
"top": "block/barrel_top_open"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_fence_gate",
|
||||
"textures": {
|
||||
"texture": "block/birch_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_fence_gate_open",
|
||||
"textures": {
|
||||
"texture": "block/birch_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_fence_gate_wall",
|
||||
"textures": {
|
||||
"texture": "block/birch_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_fence_gate_wall_open",
|
||||
"textures": {
|
||||
"texture": "block/birch_planks"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
{
|
||||
"display": {
|
||||
"gui": {
|
||||
"rotation": [
|
||||
30,
|
||||
225,
|
||||
0
|
||||
],
|
||||
"scale": [
|
||||
0.625,
|
||||
0.625,
|
||||
0.625
|
||||
]
|
||||
},
|
||||
"ground": {
|
||||
"translation": [
|
||||
0,
|
||||
3,
|
||||
0
|
||||
],
|
||||
"scale": [
|
||||
0.25,
|
||||
0.25,
|
||||
0.25
|
||||
]
|
||||
},
|
||||
"fixed": {
|
||||
"scale": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5
|
||||
]
|
||||
},
|
||||
"thirdperson_righthand": {
|
||||
"rotation": [
|
||||
75,
|
||||
45,
|
||||
0
|
||||
],
|
||||
"translation": [
|
||||
0,
|
||||
2.5,
|
||||
0
|
||||
],
|
||||
"scale": [
|
||||
0.375,
|
||||
0.375,
|
||||
0.375
|
||||
]
|
||||
},
|
||||
"firstperson_righthand": {
|
||||
"rotation": [
|
||||
0,
|
||||
45,
|
||||
0
|
||||
],
|
||||
"scale": [
|
||||
0.4,
|
||||
0.4,
|
||||
0.4
|
||||
]
|
||||
},
|
||||
"firstperson_lefthand": {
|
||||
"rotation": [
|
||||
0,
|
||||
225,
|
||||
0
|
||||
],
|
||||
"scale": [
|
||||
0.4,
|
||||
0.4,
|
||||
0.4
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_wall_post",
|
||||
"textures": {
|
||||
"wall": "block/cobblestone"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_wall_side",
|
||||
"textures": {
|
||||
"wall": "block/cobblestone"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"parent": "data_gen_test:block/block",
|
||||
"elements": [
|
||||
{
|
||||
"from": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"to": [
|
||||
16,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"faces": {
|
||||
"down": {
|
||||
"texture": "#down",
|
||||
"cullface": "down"
|
||||
},
|
||||
"up": {
|
||||
"texture": "#up",
|
||||
"cullface": "up"
|
||||
},
|
||||
"north": {
|
||||
"texture": "#north",
|
||||
"cullface": "north"
|
||||
},
|
||||
"south": {
|
||||
"texture": "#south",
|
||||
"cullface": "south"
|
||||
},
|
||||
"west": {
|
||||
"texture": "#west",
|
||||
"cullface": "west"
|
||||
},
|
||||
"east": {
|
||||
"texture": "#east",
|
||||
"cullface": "east"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"parent": "block/orientable",
|
||||
"textures": {
|
||||
"side": "block/furnace_side",
|
||||
"front": "block/furnace_front",
|
||||
"top": "block/furnace_top"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"parent": "block/orientable",
|
||||
"textures": {
|
||||
"side": "block/furnace_side",
|
||||
"front": "block/furnace_front_on",
|
||||
"top": "block/furnace_top"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_glass_pane_noside",
|
||||
"textures": {
|
||||
"pane": "block/glass"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_glass_pane_noside_alt",
|
||||
"textures": {
|
||||
"pane": "block/glass"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"parent": "block/template_glass_pane_post",
|
||||
"textures": {
|
||||
"pane": "block/glass",
|
||||
"edge": "block/glass_pane_top"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"parent": "block/template_glass_pane_side",
|
||||
"textures": {
|
||||
"pane": "block/glass",
|
||||
"edge": "block/glass_pane_top"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"parent": "block/template_glass_pane_side_alt",
|
||||
"textures": {
|
||||
"pane": "block/glass",
|
||||
"edge": "block/glass_pane_top"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_trapdoor_bottom",
|
||||
"textures": {
|
||||
"texture": "block/oak_trapdoor"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_trapdoor_open",
|
||||
"textures": {
|
||||
"texture": "block/oak_trapdoor"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_trapdoor_top",
|
||||
"textures": {
|
||||
"texture": "block/oak_trapdoor"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/cube_all",
|
||||
"textures": {
|
||||
"all": "block/stone"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/template_torch",
|
||||
"textures": {
|
||||
"torch": "block/torch"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "block/torch_wall",
|
||||
"textures": {
|
||||
"torch": "block/torch"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"parent": "item/handheld_rod",
|
||||
"textures": {
|
||||
"layer0": "item/fishing_rod"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"predicate": {
|
||||
"cast": 1.0
|
||||
},
|
||||
"model": "item/fishing_rod_cast"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "data_gen_test:item/fishing_rod",
|
||||
"textures": {
|
||||
"layer0": "item/fishing_rod_cast"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"parent": "block/block",
|
||||
"textures": {
|
||||
"all": "block/dirt",
|
||||
"top": "block/stone"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"to": [
|
||||
16,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"faces": {
|
||||
"down": {
|
||||
"texture": "#all",
|
||||
"cullface": "down"
|
||||
},
|
||||
"up": {
|
||||
"texture": "#top",
|
||||
"cullface": "up",
|
||||
"tintindex": 0
|
||||
},
|
||||
"north": {
|
||||
"texture": "#all",
|
||||
"cullface": "north"
|
||||
},
|
||||
"south": {
|
||||
"texture": "#all",
|
||||
"cullface": "south"
|
||||
},
|
||||
"west": {
|
||||
"texture": "#all",
|
||||
"cullface": "west"
|
||||
},
|
||||
"east": {
|
||||
"texture": "#all",
|
||||
"cullface": "east"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "item/generated",
|
||||
"textures": {
|
||||
"layer0": "block/stone"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
{
|
||||
"variants": {
|
||||
"facing=north,half=upper,hinge=left,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_top",
|
||||
"y": 270
|
||||
},
|
||||
"facing=south,half=upper,hinge=left,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_top",
|
||||
"y": 90
|
||||
},
|
||||
"facing=west,half=upper,hinge=left,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_top",
|
||||
"y": 180
|
||||
},
|
||||
"facing=east,half=upper,hinge=left,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_top"
|
||||
},
|
||||
"facing=north,half=lower,hinge=left,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom",
|
||||
"y": 270
|
||||
},
|
||||
"facing=south,half=lower,hinge=left,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom",
|
||||
"y": 90
|
||||
},
|
||||
"facing=west,half=lower,hinge=left,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom",
|
||||
"y": 180
|
||||
},
|
||||
"facing=east,half=lower,hinge=left,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom"
|
||||
},
|
||||
"facing=north,half=upper,hinge=right,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_top_hinge",
|
||||
"y": 270
|
||||
},
|
||||
"facing=south,half=upper,hinge=right,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_top_hinge",
|
||||
"y": 90
|
||||
},
|
||||
"facing=west,half=upper,hinge=right,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_top_hinge",
|
||||
"y": 180
|
||||
},
|
||||
"facing=east,half=upper,hinge=right,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_top_hinge"
|
||||
},
|
||||
"facing=north,half=lower,hinge=right,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom_hinge",
|
||||
"y": 270
|
||||
},
|
||||
"facing=south,half=lower,hinge=right,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom_hinge",
|
||||
"y": 90
|
||||
},
|
||||
"facing=west,half=lower,hinge=right,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom_hinge",
|
||||
"y": 180
|
||||
},
|
||||
"facing=east,half=lower,hinge=right,open=false": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom_hinge"
|
||||
},
|
||||
"facing=north,half=upper,hinge=left,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_top_hinge"
|
||||
},
|
||||
"facing=south,half=upper,hinge=left,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_top_hinge",
|
||||
"y": 180
|
||||
},
|
||||
"facing=west,half=upper,hinge=left,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_top_hinge",
|
||||
"y": 270
|
||||
},
|
||||
"facing=east,half=upper,hinge=left,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_top_hinge",
|
||||
"y": 90
|
||||
},
|
||||
"facing=north,half=lower,hinge=left,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom_hinge"
|
||||
},
|
||||
"facing=south,half=lower,hinge=left,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom_hinge",
|
||||
"y": 180
|
||||
},
|
||||
"facing=west,half=lower,hinge=left,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom_hinge",
|
||||
"y": 270
|
||||
},
|
||||
"facing=east,half=lower,hinge=left,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom_hinge",
|
||||
"y": 90
|
||||
},
|
||||
"facing=north,half=upper,hinge=right,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_top",
|
||||
"y": 180
|
||||
},
|
||||
"facing=south,half=upper,hinge=right,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_top"
|
||||
},
|
||||
"facing=west,half=upper,hinge=right,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_top",
|
||||
"y": 90
|
||||
},
|
||||
"facing=east,half=upper,hinge=right,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_top",
|
||||
"y": 270
|
||||
},
|
||||
"facing=north,half=lower,hinge=right,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom",
|
||||
"y": 180
|
||||
},
|
||||
"facing=south,half=lower,hinge=right,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom"
|
||||
},
|
||||
"facing=west,half=lower,hinge=right,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom",
|
||||
"y": 90
|
||||
},
|
||||
"facing=east,half=lower,hinge=right,open=true": {
|
||||
"model": "data_gen_test:block/acacia_door_bottom",
|
||||
"y": 270
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"multipart": [
|
||||
{
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/acacia_fence_post"
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"north": "true"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/acacia_fence_side",
|
||||
"uvlock": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"south": "true"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/acacia_fence_side",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"west": "true"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/acacia_fence_side",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"east": "true"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/acacia_fence_side",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"variants": {
|
||||
"facing=north,in_wall=false,open=false": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,in_wall=false,open=false": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate",
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,in_wall=false,open=false": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,in_wall=false,open=false": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=north,in_wall=true,open=false": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate_wall",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,in_wall=true,open=false": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate_wall",
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,in_wall=true,open=false": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate_wall",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,in_wall=true,open=false": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate_wall",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=north,in_wall=false,open=true": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate_open",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,in_wall=false,open=true": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate_open",
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,in_wall=false,open=true": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate_open",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,in_wall=false,open=true": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate_open",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=north,in_wall=true,open=true": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate_wall_open",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,in_wall=true,open=true": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate_wall_open",
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,in_wall=true,open=true": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate_wall_open",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,in_wall=true,open=true": {
|
||||
"model": "data_gen_test:block/acacia_fence_gate_wall_open",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"variants": {
|
||||
"axis=x": {
|
||||
"model": "data_gen_test:block/acacia_log",
|
||||
"x": 90,
|
||||
"y": 90
|
||||
},
|
||||
"axis=y": {
|
||||
"model": "data_gen_test:block/acacia_log"
|
||||
},
|
||||
"axis=z": {
|
||||
"model": "data_gen_test:block/acacia_log",
|
||||
"x": 90
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"variants": {
|
||||
"type=top": {
|
||||
"model": "data_gen_test:block/acacia_slab_top"
|
||||
},
|
||||
"type=bottom": {
|
||||
"model": "data_gen_test:block/acacia_slab"
|
||||
},
|
||||
"type=double": {
|
||||
"model": "minecraft:block/acacia_planks"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
{
|
||||
"variants": {
|
||||
"facing=north,half=top,shape=straight": {
|
||||
"model": "data_gen_test:block/acacia_stairs",
|
||||
"x": 180,
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,half=top,shape=straight": {
|
||||
"model": "data_gen_test:block/acacia_stairs",
|
||||
"x": 180,
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,half=top,shape=straight": {
|
||||
"model": "data_gen_test:block/acacia_stairs",
|
||||
"x": 180,
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,half=top,shape=straight": {
|
||||
"model": "data_gen_test:block/acacia_stairs",
|
||||
"x": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=north,half=bottom,shape=straight": {
|
||||
"model": "data_gen_test:block/acacia_stairs",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,half=bottom,shape=straight": {
|
||||
"model": "data_gen_test:block/acacia_stairs",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,half=bottom,shape=straight": {
|
||||
"model": "data_gen_test:block/acacia_stairs",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,half=bottom,shape=straight": {
|
||||
"model": "data_gen_test:block/acacia_stairs"
|
||||
},
|
||||
"facing=north,half=top,shape=inner_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"x": 180,
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,half=top,shape=inner_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"x": 180,
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,half=top,shape=inner_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"x": 180,
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,half=top,shape=inner_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"x": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=north,half=bottom,shape=inner_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,half=bottom,shape=inner_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner"
|
||||
},
|
||||
"facing=west,half=bottom,shape=inner_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,half=bottom,shape=inner_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=north,half=top,shape=inner_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"x": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,half=top,shape=inner_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"x": 180,
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,half=top,shape=inner_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"x": 180,
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,half=top,shape=inner_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"x": 180,
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=north,half=bottom,shape=inner_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,half=bottom,shape=inner_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,half=bottom,shape=inner_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,half=bottom,shape=inner_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_inner"
|
||||
},
|
||||
"facing=north,half=top,shape=outer_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"x": 180,
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,half=top,shape=outer_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"x": 180,
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,half=top,shape=outer_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"x": 180,
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,half=top,shape=outer_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"x": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=north,half=bottom,shape=outer_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,half=bottom,shape=outer_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer"
|
||||
},
|
||||
"facing=west,half=bottom,shape=outer_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,half=bottom,shape=outer_left": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=north,half=top,shape=outer_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"x": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,half=top,shape=outer_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"x": 180,
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,half=top,shape=outer_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"x": 180,
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,half=top,shape=outer_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"x": 180,
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=north,half=bottom,shape=outer_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,half=bottom,shape=outer_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,half=bottom,shape=outer_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,half=bottom,shape=outer_right": {
|
||||
"model": "data_gen_test:block/acacia_stairs_outer"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"variants": {
|
||||
"facing=north,half=top,open=false": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_top"
|
||||
},
|
||||
"facing=south,half=top,open=false": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_top",
|
||||
"y": 180
|
||||
},
|
||||
"facing=west,half=top,open=false": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_top",
|
||||
"y": 270
|
||||
},
|
||||
"facing=east,half=top,open=false": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_top",
|
||||
"y": 90
|
||||
},
|
||||
"facing=north,half=bottom,open=false": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_bottom"
|
||||
},
|
||||
"facing=south,half=bottom,open=false": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_bottom",
|
||||
"y": 180
|
||||
},
|
||||
"facing=west,half=bottom,open=false": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_bottom",
|
||||
"y": 270
|
||||
},
|
||||
"facing=east,half=bottom,open=false": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_bottom",
|
||||
"y": 90
|
||||
},
|
||||
"facing=north,half=top,open=true": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_open",
|
||||
"x": 180,
|
||||
"y": 180
|
||||
},
|
||||
"facing=south,half=top,open=true": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_open",
|
||||
"x": 180
|
||||
},
|
||||
"facing=west,half=top,open=true": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_open",
|
||||
"x": 180,
|
||||
"y": 90
|
||||
},
|
||||
"facing=east,half=top,open=true": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_open",
|
||||
"x": 180,
|
||||
"y": 270
|
||||
},
|
||||
"facing=north,half=bottom,open=true": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_open"
|
||||
},
|
||||
"facing=south,half=bottom,open=true": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_open",
|
||||
"y": 180
|
||||
},
|
||||
"facing=west,half=bottom,open=true": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_open",
|
||||
"y": 270
|
||||
},
|
||||
"facing=east,half=bottom,open=true": {
|
||||
"model": "data_gen_test:block/acacia_trapdoor_open",
|
||||
"y": 90
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"variants": {
|
||||
"facing=down,open=false": {
|
||||
"model": "data_gen_test:block/barrel",
|
||||
"x": 180
|
||||
},
|
||||
"facing=up,open=false": {
|
||||
"model": "data_gen_test:block/barrel"
|
||||
},
|
||||
"facing=north,open=false": {
|
||||
"model": "data_gen_test:block/barrel",
|
||||
"x": 90
|
||||
},
|
||||
"facing=south,open=false": {
|
||||
"model": "data_gen_test:block/barrel",
|
||||
"x": 90,
|
||||
"y": 180
|
||||
},
|
||||
"facing=west,open=false": {
|
||||
"model": "data_gen_test:block/barrel",
|
||||
"x": 90,
|
||||
"y": 270
|
||||
},
|
||||
"facing=east,open=false": {
|
||||
"model": "data_gen_test:block/barrel",
|
||||
"x": 90,
|
||||
"y": 90
|
||||
},
|
||||
"facing=down,open=true": {
|
||||
"model": "data_gen_test:block/barrel_open",
|
||||
"x": 180
|
||||
},
|
||||
"facing=up,open=true": {
|
||||
"model": "data_gen_test:block/barrel_open"
|
||||
},
|
||||
"facing=north,open=true": {
|
||||
"model": "data_gen_test:block/barrel_open",
|
||||
"x": 90
|
||||
},
|
||||
"facing=south,open=true": {
|
||||
"model": "data_gen_test:block/barrel_open",
|
||||
"x": 90,
|
||||
"y": 180
|
||||
},
|
||||
"facing=west,open=true": {
|
||||
"model": "data_gen_test:block/barrel_open",
|
||||
"x": 90,
|
||||
"y": 270
|
||||
},
|
||||
"facing=east,open=true": {
|
||||
"model": "data_gen_test:block/barrel_open",
|
||||
"x": 90,
|
||||
"y": 90
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
{
|
||||
"variants": {
|
||||
"facing=north,in_wall=false,open=false": [
|
||||
{
|
||||
"model": "minecraft:builtin/generated"
|
||||
},
|
||||
{
|
||||
"model": "data_gen_test:block/birch_fence_gate",
|
||||
"y": 180,
|
||||
"uvlock": true,
|
||||
"weight": 100
|
||||
}
|
||||
],
|
||||
"facing=south,in_wall=false,open=false": [
|
||||
{
|
||||
"model": "minecraft:builtin/generated"
|
||||
},
|
||||
{
|
||||
"model": "data_gen_test:block/birch_fence_gate",
|
||||
"uvlock": true,
|
||||
"weight": 100
|
||||
}
|
||||
],
|
||||
"facing=west,in_wall=false,open=false": [
|
||||
{
|
||||
"model": "minecraft:builtin/generated"
|
||||
},
|
||||
{
|
||||
"model": "data_gen_test:block/birch_fence_gate",
|
||||
"y": 90,
|
||||
"uvlock": true,
|
||||
"weight": 100
|
||||
}
|
||||
],
|
||||
"facing=east,in_wall=false,open=false": [
|
||||
{
|
||||
"model": "minecraft:builtin/generated"
|
||||
},
|
||||
{
|
||||
"model": "data_gen_test:block/birch_fence_gate",
|
||||
"y": 270,
|
||||
"uvlock": true,
|
||||
"weight": 100
|
||||
}
|
||||
],
|
||||
"facing=north,in_wall=true,open=false": {
|
||||
"model": "data_gen_test:block/birch_fence_gate_wall",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,in_wall=true,open=false": {
|
||||
"model": "data_gen_test:block/birch_fence_gate_wall",
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,in_wall=true,open=false": {
|
||||
"model": "data_gen_test:block/birch_fence_gate_wall",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,in_wall=true,open=false": {
|
||||
"model": "data_gen_test:block/birch_fence_gate_wall",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=north,in_wall=false,open=true": {
|
||||
"model": "data_gen_test:block/birch_fence_gate_open",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,in_wall=false,open=true": {
|
||||
"model": "data_gen_test:block/birch_fence_gate_open",
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,in_wall=false,open=true": {
|
||||
"model": "data_gen_test:block/birch_fence_gate_open",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,in_wall=false,open=true": {
|
||||
"model": "data_gen_test:block/birch_fence_gate_open",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=north,in_wall=true,open=true": {
|
||||
"model": "data_gen_test:block/birch_fence_gate_wall_open",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=south,in_wall=true,open=true": {
|
||||
"model": "data_gen_test:block/birch_fence_gate_wall_open",
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=west,in_wall=true,open=true": {
|
||||
"model": "data_gen_test:block/birch_fence_gate_wall_open",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
},
|
||||
"facing=east,in_wall=true,open=true": {
|
||||
"model": "data_gen_test:block/birch_fence_gate_wall_open",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"multipart": [
|
||||
{
|
||||
"when": {
|
||||
"up": "true"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/cobblestone_wall_post"
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"north": "true"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/cobblestone_wall_side",
|
||||
"uvlock": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"south": "true"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/cobblestone_wall_side",
|
||||
"y": 180,
|
||||
"uvlock": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"west": "true"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/cobblestone_wall_side",
|
||||
"y": 270,
|
||||
"uvlock": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"east": "true"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/cobblestone_wall_side",
|
||||
"y": 90,
|
||||
"uvlock": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"variants": {
|
||||
"facing=north,lit=false": {
|
||||
"model": "data_gen_test:block/furnace"
|
||||
},
|
||||
"facing=south,lit=false": {
|
||||
"model": "data_gen_test:block/furnace",
|
||||
"y": 180
|
||||
},
|
||||
"facing=west,lit=false": {
|
||||
"model": "data_gen_test:block/furnace",
|
||||
"y": 270
|
||||
},
|
||||
"facing=east,lit=false": {
|
||||
"model": "data_gen_test:block/furnace",
|
||||
"y": 90
|
||||
},
|
||||
"facing=north,lit=true": {
|
||||
"model": "data_gen_test:block/furnace_on"
|
||||
},
|
||||
"facing=south,lit=true": {
|
||||
"model": "data_gen_test:block/furnace_on",
|
||||
"y": 180
|
||||
},
|
||||
"facing=west,lit=true": {
|
||||
"model": "data_gen_test:block/furnace_on",
|
||||
"y": 270
|
||||
},
|
||||
"facing=east,lit=true": {
|
||||
"model": "data_gen_test:block/furnace_on",
|
||||
"y": 90
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"multipart": [
|
||||
{
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/glass_pane_post"
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"north": "true"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/glass_pane_side"
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"north": "false"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/glass_pane_noside"
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"south": "true"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/glass_pane_side_alt"
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"south": "false"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/glass_pane_noside_alt",
|
||||
"y": 90
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"west": "true"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/glass_pane_side_alt",
|
||||
"y": 90
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"west": "false"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/glass_pane_noside",
|
||||
"y": 270
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"east": "true"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/glass_pane_side",
|
||||
"y": 90
|
||||
}
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"east": "false"
|
||||
},
|
||||
"apply": {
|
||||
"model": "data_gen_test:block/glass_pane_noside_alt"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"variants": {
|
||||
"facing=north,half=top,open=false": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_top"
|
||||
},
|
||||
"facing=south,half=top,open=false": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_top"
|
||||
},
|
||||
"facing=west,half=top,open=false": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_top"
|
||||
},
|
||||
"facing=east,half=top,open=false": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_top"
|
||||
},
|
||||
"facing=north,half=bottom,open=false": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_bottom"
|
||||
},
|
||||
"facing=south,half=bottom,open=false": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_bottom"
|
||||
},
|
||||
"facing=west,half=bottom,open=false": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_bottom"
|
||||
},
|
||||
"facing=east,half=bottom,open=false": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_bottom"
|
||||
},
|
||||
"facing=north,half=top,open=true": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_open"
|
||||
},
|
||||
"facing=south,half=top,open=true": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_open",
|
||||
"y": 180
|
||||
},
|
||||
"facing=west,half=top,open=true": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_open",
|
||||
"y": 270
|
||||
},
|
||||
"facing=east,half=top,open=true": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_open",
|
||||
"y": 90
|
||||
},
|
||||
"facing=north,half=bottom,open=true": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_open"
|
||||
},
|
||||
"facing=south,half=bottom,open=true": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_open",
|
||||
"y": 180
|
||||
},
|
||||
"facing=west,half=bottom,open=true": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_open",
|
||||
"y": 270
|
||||
},
|
||||
"facing=east,half=bottom,open=true": {
|
||||
"model": "data_gen_test:block/oak_trapdoor_open",
|
||||
"y": 90
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"variants": {
|
||||
"": [
|
||||
{
|
||||
"model": "data_gen_test:block/stone"
|
||||
},
|
||||
{
|
||||
"model": "data_gen_test:block/stone",
|
||||
"y": 90
|
||||
},
|
||||
{
|
||||
"model": "data_gen_test:block/stone",
|
||||
"y": 180
|
||||
},
|
||||
{
|
||||
"model": "data_gen_test:block/stone",
|
||||
"y": 270
|
||||
},
|
||||
{
|
||||
"model": "data_gen_test:block/stone",
|
||||
"x": 180
|
||||
},
|
||||
{
|
||||
"model": "data_gen_test:block/stone",
|
||||
"x": 180,
|
||||
"y": 90
|
||||
},
|
||||
{
|
||||
"model": "data_gen_test:block/stone",
|
||||
"x": 180,
|
||||
"y": 180
|
||||
},
|
||||
{
|
||||
"model": "data_gen_test:block/stone",
|
||||
"x": 180,
|
||||
"y": 270
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "data_gen_test:block/torch"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"variants": {
|
||||
"facing=north": {
|
||||
"model": "data_gen_test:block/wall_torch",
|
||||
"y": 270
|
||||
},
|
||||
"facing=south": {
|
||||
"model": "data_gen_test:block/wall_torch",
|
||||
"y": 90
|
||||
},
|
||||
"facing=west": {
|
||||
"model": "data_gen_test:block/wall_torch",
|
||||
"y": 180
|
||||
},
|
||||
"facing=east": {
|
||||
"model": "data_gen_test:block/wall_torch",
|
||||
"y": 360
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Builder for block models, does not currently provide any additional
|
||||
* functionality over {@link ModelBuilder}, purely a stub class with a concrete
|
||||
* generic.
|
||||
*
|
||||
* @see ModelProvider
|
||||
* @see ModelBuilder
|
||||
*/
|
||||
public class BlockModelBuilder extends ModelBuilder<BlockModelBuilder> {
|
||||
|
||||
public BlockModelBuilder(ResourceLocation outputLocation, ExistingFileHelper existingFileHelper) {
|
||||
super(outputLocation, existingFileHelper);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import net.minecraft.data.DataGenerator;
|
||||
|
||||
/**
|
||||
* Stub class to extend for block model data providers, eliminates some
|
||||
* boilerplate constructor parameters.
|
||||
*/
|
||||
public abstract class BlockModelProvider extends ModelProvider<BlockModelBuilder> {
|
||||
|
||||
public BlockModelProvider(DataGenerator generator, String modid, ExistingFileHelper existingFileHelper) {
|
||||
super(generator, modid, BLOCK_FOLDER, BlockModelBuilder::new, existingFileHelper);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,546 @@
|
|||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.DoorBlock;
|
||||
import net.minecraft.block.FenceBlock;
|
||||
import net.minecraft.block.FenceGateBlock;
|
||||
import net.minecraft.block.FourWayBlock;
|
||||
import net.minecraft.block.LogBlock;
|
||||
import net.minecraft.block.PaneBlock;
|
||||
import net.minecraft.block.RotatedPillarBlock;
|
||||
import net.minecraft.block.SixWayBlock;
|
||||
import net.minecraft.block.SlabBlock;
|
||||
import net.minecraft.block.StairsBlock;
|
||||
import net.minecraft.block.TrapDoorBlock;
|
||||
import net.minecraft.block.WallBlock;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.IDataProvider;
|
||||
import net.minecraft.state.properties.AttachFace;
|
||||
import net.minecraft.state.properties.BlockStateProperties;
|
||||
import net.minecraft.state.properties.DoorHingeSide;
|
||||
import net.minecraft.state.properties.DoubleBlockHalf;
|
||||
import net.minecraft.state.properties.Half;
|
||||
import net.minecraft.state.properties.SlabType;
|
||||
import net.minecraft.state.properties.StairsShape;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.Direction.Axis;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Data provider for blockstate files. Extends {@link BlockModelProvider} so that
|
||||
* blockstates and their referenced models can be provided in tandem.
|
||||
*/
|
||||
public abstract class BlockStateProvider extends BlockModelProvider {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final Gson GSON = (new GsonBuilder()).setPrettyPrinting().disableHtmlEscaping().create();
|
||||
|
||||
@VisibleForTesting
|
||||
protected final Map<Block, IGeneratedBlockstate> registeredBlocks = new LinkedHashMap<>();
|
||||
|
||||
public BlockStateProvider(DataGenerator gen, String modid, ExistingFileHelper exFileHelper) {
|
||||
super(gen, modid, exFileHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void registerModels() {
|
||||
registeredBlocks.clear();
|
||||
registerStatesAndModels();
|
||||
for (Map.Entry<Block, IGeneratedBlockstate> entry : registeredBlocks.entrySet()) {
|
||||
saveBlockState(entry.getValue().toJson(), entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void registerStatesAndModels();
|
||||
|
||||
protected VariantBlockStateBuilder getVariantBuilder(Block b) {
|
||||
if (registeredBlocks.containsKey(b)) {
|
||||
IGeneratedBlockstate old = registeredBlocks.get(b);
|
||||
Preconditions.checkState(old instanceof VariantBlockStateBuilder);
|
||||
return (VariantBlockStateBuilder) old;
|
||||
} else {
|
||||
VariantBlockStateBuilder ret = new VariantBlockStateBuilder(b);
|
||||
registeredBlocks.put(b, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
protected MultiPartBlockStateBuilder getMultipartBuilder(Block b) {
|
||||
if (registeredBlocks.containsKey(b)) {
|
||||
IGeneratedBlockstate old = registeredBlocks.get(b);
|
||||
Preconditions.checkState(old instanceof MultiPartBlockStateBuilder);
|
||||
return (MultiPartBlockStateBuilder) old;
|
||||
} else {
|
||||
MultiPartBlockStateBuilder ret = new MultiPartBlockStateBuilder(b);
|
||||
registeredBlocks.put(b, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
private String name(Block block) {
|
||||
return block.getRegistryName().getPath();
|
||||
}
|
||||
|
||||
protected ResourceLocation blockTexture(Block block) {
|
||||
ResourceLocation name = block.getRegistryName();
|
||||
return new ResourceLocation(name.getNamespace(), BLOCK_FOLDER + "/" + name.getPath());
|
||||
}
|
||||
|
||||
private ResourceLocation extend(ResourceLocation rl, String suffix) {
|
||||
return new ResourceLocation(rl.getNamespace(), rl.getPath() + suffix);
|
||||
}
|
||||
|
||||
protected ModelFile cubeAll(Block block) {
|
||||
return cubeAll(name(block), blockTexture(block));
|
||||
}
|
||||
|
||||
protected void simpleBlock(Block block) {
|
||||
simpleBlock(block, cubeAll(block));
|
||||
}
|
||||
|
||||
protected void simpleBlock(Block block, Function<ModelFile, ConfiguredModel[]> expander) {
|
||||
simpleBlock(block, expander.apply(cubeAll(block)));
|
||||
}
|
||||
|
||||
protected void simpleBlock(Block block, ModelFile model) {
|
||||
simpleBlock(block, new ConfiguredModel(model));
|
||||
}
|
||||
|
||||
protected void simpleBlock(Block block, ConfiguredModel... models) {
|
||||
getVariantBuilder(block)
|
||||
.partialState().setModels(models);
|
||||
}
|
||||
|
||||
protected void axisBlock(RotatedPillarBlock block) {
|
||||
axisBlock(block, blockTexture(block));
|
||||
}
|
||||
|
||||
protected void logBlock(LogBlock block) {
|
||||
axisBlock(block, blockTexture(block), extend(blockTexture(block), "_top"));
|
||||
}
|
||||
|
||||
protected void axisBlock(RotatedPillarBlock block, ResourceLocation baseName) {
|
||||
axisBlock(block, extend(baseName, "_side"), extend(baseName, "_end"));
|
||||
}
|
||||
|
||||
protected void axisBlock(RotatedPillarBlock block, ResourceLocation side, ResourceLocation end) {
|
||||
axisBlock(block, cubeColumn(name(block), side, end));
|
||||
}
|
||||
|
||||
protected void axisBlock(RotatedPillarBlock block, ModelFile model) {
|
||||
getVariantBuilder(block)
|
||||
.partialState().with(RotatedPillarBlock.AXIS, Axis.Y)
|
||||
.modelForState().modelFile(model).addModel()
|
||||
.partialState().with(RotatedPillarBlock.AXIS, Axis.Z)
|
||||
.modelForState().modelFile(model).rotationX(90).addModel()
|
||||
.partialState().with(RotatedPillarBlock.AXIS, Axis.X)
|
||||
.modelForState().modelFile(model).rotationX(90).rotationY(90).addModel();
|
||||
}
|
||||
|
||||
private static final int DEFAULT_ANGLE_OFFSET = 180;
|
||||
|
||||
protected void horizontalBlock(Block block, ResourceLocation side, ResourceLocation front, ResourceLocation top) {
|
||||
horizontalBlock(block, orientable(name(block), side, front, top));
|
||||
}
|
||||
|
||||
protected void horizontalBlock(Block block, ModelFile model) {
|
||||
horizontalBlock(block, model, DEFAULT_ANGLE_OFFSET);
|
||||
}
|
||||
|
||||
protected void horizontalBlock(Block block, ModelFile model, int angleOffset) {
|
||||
horizontalBlock(block, $ -> model, angleOffset);
|
||||
}
|
||||
|
||||
protected void horizontalBlock(Block block, Function<BlockState, ModelFile> modelFunc) {
|
||||
horizontalBlock(block, modelFunc, DEFAULT_ANGLE_OFFSET);
|
||||
}
|
||||
|
||||
protected void horizontalBlock(Block block, Function<BlockState, ModelFile> modelFunc, int angleOffset) {
|
||||
getVariantBuilder(block)
|
||||
.forAllStates(state -> ConfiguredModel.builder()
|
||||
.modelFile(modelFunc.apply(state))
|
||||
.rotationY((int) state.get(BlockStateProperties.HORIZONTAL_FACING).getHorizontalAngle() + angleOffset)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
protected void horizontalFaceBlock(Block block, ModelFile model) {
|
||||
horizontalFaceBlock(block, model, DEFAULT_ANGLE_OFFSET);
|
||||
}
|
||||
|
||||
protected void horizontalFaceBlock(Block block, ModelFile model, int angleOffset) {
|
||||
horizontalFaceBlock(block, $ -> model, angleOffset);
|
||||
}
|
||||
|
||||
protected void horizontalFaceBlock(Block block, Function<BlockState, ModelFile> modelFunc) {
|
||||
horizontalBlock(block, modelFunc, DEFAULT_ANGLE_OFFSET);
|
||||
}
|
||||
|
||||
protected void horizontalFaceBlock(Block block, Function<BlockState, ModelFile> modelFunc, int angleOffset) {
|
||||
getVariantBuilder(block)
|
||||
.forAllStates(state -> ConfiguredModel.builder()
|
||||
.modelFile(modelFunc.apply(state))
|
||||
.rotationX(state.get(BlockStateProperties.FACE).ordinal() * 90)
|
||||
.rotationY((((int) state.get(BlockStateProperties.HORIZONTAL_FACING).getHorizontalAngle() + angleOffset) + (state.get(BlockStateProperties.FACE) == AttachFace.CEILING ? 180 : 0)) % 360)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
protected void directionalBlock(Block block, ModelFile model) {
|
||||
directionalBlock(block, model, DEFAULT_ANGLE_OFFSET);
|
||||
}
|
||||
|
||||
protected void directionalBlock(Block block, ModelFile model, int angleOffset) {
|
||||
directionalBlock(block, $ -> model, angleOffset);
|
||||
}
|
||||
|
||||
protected void directionalBlock(Block block, Function<BlockState, ModelFile> modelFunc) {
|
||||
directionalBlock(block, modelFunc, DEFAULT_ANGLE_OFFSET);
|
||||
}
|
||||
|
||||
protected void directionalBlock(Block block, Function<BlockState, ModelFile> modelFunc, int angleOffset) {
|
||||
getVariantBuilder(block)
|
||||
.forAllStates(state -> {
|
||||
Direction dir = state.get(BlockStateProperties.FACING);
|
||||
return ConfiguredModel.builder()
|
||||
.modelFile(modelFunc.apply(state))
|
||||
.rotationX(dir == Direction.DOWN ? 180 : dir.getAxis().isHorizontal() ? 90 : 0)
|
||||
.rotationY(dir.getAxis().isVertical() ? 0 : (((int) dir.getHorizontalAngle()) + angleOffset) % 360)
|
||||
.build();
|
||||
});
|
||||
}
|
||||
|
||||
protected void stairsBlock(StairsBlock block, ResourceLocation texture) {
|
||||
stairsBlock(block, texture, texture, texture);
|
||||
}
|
||||
|
||||
protected void stairsBlock(StairsBlock block, String name, ResourceLocation texture) {
|
||||
stairsBlock(block, name, texture, texture, texture);
|
||||
}
|
||||
|
||||
protected void stairsBlock(StairsBlock block, ResourceLocation side, ResourceLocation bottom, ResourceLocation top) {
|
||||
stairsBlockInternal(block, block.getRegistryName().toString(), side, bottom, top);
|
||||
}
|
||||
|
||||
protected void stairsBlock(StairsBlock block, String name, ResourceLocation side, ResourceLocation bottom, ResourceLocation top) {
|
||||
stairsBlockInternal(block, name + "_stairs", side, bottom, top);
|
||||
}
|
||||
|
||||
private void stairsBlockInternal(StairsBlock block, String baseName, ResourceLocation side, ResourceLocation bottom, ResourceLocation top) {
|
||||
ModelFile stairs = stairs(baseName, side, bottom, top);
|
||||
ModelFile stairsInner = stairsInner(baseName + "_inner", side, bottom, top);
|
||||
ModelFile stairsOuter = stairsOuter(baseName + "_outer", side, bottom, top);
|
||||
stairsBlock(block, stairs, stairsInner, stairsOuter);
|
||||
}
|
||||
|
||||
protected void stairsBlock(StairsBlock block, ModelFile stairs, ModelFile stairsInner, ModelFile stairsOuter) {
|
||||
getVariantBuilder(block)
|
||||
.forAllStatesExcept(state -> {
|
||||
Direction facing = state.get(StairsBlock.FACING);
|
||||
Half half = state.get(StairsBlock.HALF);
|
||||
StairsShape shape = state.get(StairsBlock.SHAPE);
|
||||
int yRot = (int) facing.rotateY().getHorizontalAngle(); // Stairs model is rotated 90 degrees clockwise for some reason
|
||||
if (shape == StairsShape.INNER_LEFT || shape == StairsShape.OUTER_LEFT) {
|
||||
yRot += 270; // Left facing stairs are rotated 90 degrees clockwise
|
||||
}
|
||||
if (shape != StairsShape.STRAIGHT && half == Half.TOP) {
|
||||
yRot += 90; // Top stairs are rotated 90 degrees clockwise
|
||||
}
|
||||
yRot %= 360;
|
||||
boolean uvlock = yRot != 0 || half == Half.TOP; // Don't set uvlock for states that have no rotation
|
||||
return ConfiguredModel.builder()
|
||||
.modelFile(shape == StairsShape.STRAIGHT ? stairs : shape == StairsShape.INNER_LEFT || shape == StairsShape.INNER_RIGHT ? stairsInner : stairsOuter)
|
||||
.rotationX(half == Half.BOTTOM ? 0 : 180)
|
||||
.rotationY(yRot)
|
||||
.uvLock(uvlock)
|
||||
.build();
|
||||
}, StairsBlock.WATERLOGGED);
|
||||
}
|
||||
|
||||
protected void slabBlock(SlabBlock block, ResourceLocation doubleslab, ResourceLocation texture) {
|
||||
slabBlock(block, doubleslab, texture, texture, texture);
|
||||
}
|
||||
|
||||
protected void slabBlock(SlabBlock block, ResourceLocation doubleslab, ResourceLocation side, ResourceLocation bottom, ResourceLocation top) {
|
||||
slabBlock(block, slab(name(block), side, bottom, top), slabTop(name(block) + "_top", side, bottom, top), getExistingFile(doubleslab));
|
||||
}
|
||||
|
||||
protected void slabBlock(SlabBlock block, ModelFile bottom, ModelFile top, ModelFile doubleslab) {
|
||||
getVariantBuilder(block)
|
||||
.partialState().with(SlabBlock.TYPE, SlabType.BOTTOM).addModels(new ConfiguredModel(bottom))
|
||||
.partialState().with(SlabBlock.TYPE, SlabType.TOP).addModels(new ConfiguredModel(top))
|
||||
.partialState().with(SlabBlock.TYPE, SlabType.DOUBLE).addModels(new ConfiguredModel(doubleslab));
|
||||
}
|
||||
|
||||
protected void fourWayBlock(FourWayBlock block, ModelFile post, ModelFile side) {
|
||||
MultiPartBlockStateBuilder builder = getMultipartBuilder(block)
|
||||
.part().modelFile(post).addModel().end();
|
||||
fourWayMultipart(builder, side);
|
||||
}
|
||||
|
||||
protected void fourWayMultipart(MultiPartBlockStateBuilder builder, ModelFile side) {
|
||||
SixWayBlock.FACING_TO_PROPERTY_MAP.entrySet().forEach(e -> {
|
||||
Direction dir = e.getKey();
|
||||
if (dir.getAxis().isHorizontal()) {
|
||||
builder.part().modelFile(side).rotationY((((int) dir.getHorizontalAngle()) + 180) % 360).uvLock(true).addModel()
|
||||
.condition(e.getValue(), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void fenceBlock(FenceBlock block, ResourceLocation texture) {
|
||||
String baseName = block.getRegistryName().toString();
|
||||
fourWayBlock(block, fencePost(baseName + "_post", texture), fenceSide(baseName + "_side", texture));
|
||||
}
|
||||
|
||||
protected void fenceBlock(FenceBlock block, String name, ResourceLocation texture) {
|
||||
fourWayBlock(block, fencePost(name + "_fence_post", texture), fenceSide(name + "_fence_side", texture));
|
||||
}
|
||||
|
||||
protected void fenceGateBlock(FenceGateBlock block, ResourceLocation texture) {
|
||||
fenceGateBlockInternal(block, block.getRegistryName().toString(), texture);
|
||||
}
|
||||
|
||||
protected void fenceGateBlock(FenceGateBlock block, String name, ResourceLocation texture) {
|
||||
fenceGateBlockInternal(block, name + "_fence_gate", texture);
|
||||
}
|
||||
|
||||
private void fenceGateBlockInternal(FenceGateBlock block, String baseName, ResourceLocation texture) {
|
||||
ModelFile gate = fenceGate(baseName, texture);
|
||||
ModelFile gateOpen = fenceGateOpen(baseName + "_open", texture);
|
||||
ModelFile gateWall = fenceGateWall(baseName + "_wall", texture);
|
||||
ModelFile gateWallOpen = fenceGateWallOpen(baseName + "_wall_open", texture);
|
||||
fenceGateBlock(block, gate, gateOpen, gateWall, gateWallOpen);
|
||||
}
|
||||
|
||||
protected void fenceGateBlock(FenceGateBlock block, ModelFile gate, ModelFile gateOpen, ModelFile gateWall, ModelFile gateWallOpen) {
|
||||
getVariantBuilder(block).forAllStatesExcept(state -> {
|
||||
ModelFile model = gate;
|
||||
if (state.get(FenceGateBlock.IN_WALL)) {
|
||||
model = gateWall;
|
||||
}
|
||||
if (state.get(FenceGateBlock.OPEN)) {
|
||||
model = model == gateWall ? gateWallOpen : gateOpen;
|
||||
}
|
||||
return ConfiguredModel.builder()
|
||||
.modelFile(model)
|
||||
.rotationY((int) state.get(FenceGateBlock.HORIZONTAL_FACING).getHorizontalAngle())
|
||||
.uvLock(true)
|
||||
.build();
|
||||
}, FenceGateBlock.POWERED);
|
||||
}
|
||||
|
||||
protected void wallBlock(WallBlock block, ResourceLocation texture) {
|
||||
wallBlockInternal(block, block.getRegistryName().toString(), texture);
|
||||
}
|
||||
|
||||
protected void wallBlock(WallBlock block, String name, ResourceLocation texture) {
|
||||
wallBlockInternal(block, name + "_wall", texture);
|
||||
}
|
||||
|
||||
private void wallBlockInternal(WallBlock block, String baseName, ResourceLocation texture) {
|
||||
wallBlock(block, wallPost(baseName + "_post", texture), wallSide(baseName + "_side", texture));
|
||||
}
|
||||
|
||||
protected void wallBlock(WallBlock block, ModelFile post, ModelFile side) {
|
||||
MultiPartBlockStateBuilder builder = getMultipartBuilder(block)
|
||||
.part().modelFile(post).addModel()
|
||||
.condition(WallBlock.UP, true).end();
|
||||
fourWayMultipart(builder, side);
|
||||
}
|
||||
|
||||
protected void paneBlock(PaneBlock block, ResourceLocation pane, ResourceLocation edge) {
|
||||
paneBlockInternal(block, block.getRegistryName().toString(), pane, edge);
|
||||
}
|
||||
|
||||
protected void paneBlock(PaneBlock block, String name, ResourceLocation pane, ResourceLocation edge) {
|
||||
paneBlockInternal(block, name + "_pane", pane, edge);
|
||||
}
|
||||
|
||||
private void paneBlockInternal(PaneBlock block, String baseName, ResourceLocation pane, ResourceLocation edge) {
|
||||
ModelFile post = panePost(baseName + "_post", pane, edge);
|
||||
ModelFile side = paneSide(baseName + "_side", pane, edge);
|
||||
ModelFile sideAlt = paneSideAlt(baseName + "_side_alt", pane, edge);
|
||||
ModelFile noSide = paneNoSide(baseName + "_noside", pane);
|
||||
ModelFile noSideAlt = paneNoSideAlt(baseName + "_noside_alt", pane);
|
||||
paneBlock(block, post, side, sideAlt, noSide, noSideAlt);
|
||||
}
|
||||
|
||||
protected void paneBlock(PaneBlock block, ModelFile post, ModelFile side, ModelFile sideAlt, ModelFile noSide, ModelFile noSideAlt) {
|
||||
MultiPartBlockStateBuilder builder = getMultipartBuilder(block)
|
||||
.part().modelFile(post).addModel().end();
|
||||
SixWayBlock.FACING_TO_PROPERTY_MAP.entrySet().forEach(e -> {
|
||||
Direction dir = e.getKey();
|
||||
if (dir.getAxis().isHorizontal()) {
|
||||
boolean alt = dir == Direction.SOUTH;
|
||||
builder.part().modelFile(alt || dir == Direction.WEST ? sideAlt : side).rotationY(dir.getAxis() == Axis.X ? 90 : 0).addModel()
|
||||
.condition(e.getValue(), true).end()
|
||||
.part().modelFile(alt || dir == Direction.EAST ? noSideAlt : noSide).rotationY(dir == Direction.WEST ? 270 : dir == Direction.SOUTH ? 90 : 0).addModel()
|
||||
.condition(e.getValue(), false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void doorBlock(DoorBlock block, ResourceLocation bottom, ResourceLocation top) {
|
||||
doorBlockInternal(block, block.getRegistryName().toString(), bottom, top);
|
||||
}
|
||||
|
||||
protected void doorBlock(DoorBlock block, String name, ResourceLocation bottom, ResourceLocation top) {
|
||||
doorBlockInternal(block, name + "_door", bottom, top);
|
||||
}
|
||||
|
||||
private void doorBlockInternal(DoorBlock block, String baseName, ResourceLocation bottom, ResourceLocation top) {
|
||||
ModelFile bottomLeft = doorBottomLeft(baseName + "_bottom", bottom, top);
|
||||
ModelFile bottomRight = doorBottomRight(baseName + "_bottom_hinge", bottom, top);
|
||||
ModelFile topLeft = doorTopLeft(baseName + "_top", bottom, top);
|
||||
ModelFile topRight = doorTopRight(baseName + "_top_hinge", bottom, top);
|
||||
doorBlock(block, bottomLeft, bottomRight, topLeft, topRight);
|
||||
}
|
||||
|
||||
protected void doorBlock(DoorBlock block, ModelFile bottomLeft, ModelFile bottomRight, ModelFile topLeft, ModelFile topRight) {
|
||||
getVariantBuilder(block).forAllStatesExcept(state -> {
|
||||
int yRot = ((int) state.get(DoorBlock.FACING).getHorizontalAngle()) + 90;
|
||||
boolean rh = state.get(DoorBlock.HINGE) == DoorHingeSide.RIGHT;
|
||||
boolean open = state.get(DoorBlock.OPEN);
|
||||
boolean right = rh ^ open;
|
||||
if (open) {
|
||||
yRot += 90;
|
||||
}
|
||||
if (rh && open) {
|
||||
yRot += 180;
|
||||
}
|
||||
yRot %= 360;
|
||||
return ConfiguredModel.builder().modelFile(state.get(DoorBlock.HALF) == DoubleBlockHalf.LOWER ? (right ? bottomRight : bottomLeft) : (right ? topRight : topLeft))
|
||||
.rotationY(yRot)
|
||||
.build();
|
||||
}, DoorBlock.POWERED);
|
||||
}
|
||||
|
||||
protected void trapdoorBlock(TrapDoorBlock block, ResourceLocation texture, boolean orientable) {
|
||||
trapdoorBlockInternal(block, block.getRegistryName().toString(), texture, orientable);
|
||||
}
|
||||
|
||||
protected void trapdoorBlock(TrapDoorBlock block, String name, ResourceLocation texture, boolean orientable) {
|
||||
trapdoorBlockInternal(block, name + "_trapdoor", texture, orientable);
|
||||
}
|
||||
|
||||
private void trapdoorBlockInternal(TrapDoorBlock block, String baseName, ResourceLocation texture, boolean orientable) {
|
||||
ModelFile bottom = orientable ? trapdoorOrientableBottom(baseName + "_bottom", texture) : trapdoorBottom(baseName + "_bottom", texture);
|
||||
ModelFile top = orientable ? trapdoorOrientableTop(baseName + "_top", texture) : trapdoorTop(baseName + "_top", texture);
|
||||
ModelFile open = orientable ? trapdoorOrientableOpen(baseName + "_open", texture) : trapdoorOpen(baseName + "_open", texture);
|
||||
trapdoorBlock(block, bottom, top, open, orientable);
|
||||
}
|
||||
|
||||
protected void trapdoorBlock(TrapDoorBlock block, ModelFile bottom, ModelFile top, ModelFile open, boolean orientable) {
|
||||
getVariantBuilder(block).forAllStatesExcept(state -> {
|
||||
int xRot = 0;
|
||||
int yRot = ((int) state.get(TrapDoorBlock.HORIZONTAL_FACING).getHorizontalAngle()) + 180;
|
||||
boolean isOpen = state.get(TrapDoorBlock.OPEN);
|
||||
if (orientable && isOpen && state.get(TrapDoorBlock.HALF) == Half.TOP) {
|
||||
xRot += 180;
|
||||
yRot += 180;
|
||||
}
|
||||
if (!orientable && !isOpen) {
|
||||
yRot = 0;
|
||||
}
|
||||
yRot %= 360;
|
||||
return ConfiguredModel.builder().modelFile(isOpen ? open : state.get(TrapDoorBlock.HALF) == Half.TOP ? top : bottom)
|
||||
.rotationX(xRot)
|
||||
.rotationY(yRot)
|
||||
.build();
|
||||
}, TrapDoorBlock.POWERED, TrapDoorBlock.WATERLOGGED);
|
||||
}
|
||||
|
||||
private void saveBlockState(JsonObject stateJson, Block owner) {
|
||||
ResourceLocation blockName = Preconditions.checkNotNull(owner.getRegistryName());
|
||||
Path mainOutput = generator.getOutputFolder();
|
||||
String pathSuffix = "assets/" + blockName.getNamespace() + "/blockstates/" + blockName.getPath() + ".json";
|
||||
Path outputPath = mainOutput.resolve(pathSuffix);
|
||||
try {
|
||||
IDataProvider.save(GSON, cache, stateJson, outputPath);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Couldn't save blockstate to {}", outputPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Block States";
|
||||
}
|
||||
|
||||
public static class ConfiguredModelList {
|
||||
private final List<ConfiguredModel> models;
|
||||
|
||||
private ConfiguredModelList(List<ConfiguredModel> models) {
|
||||
Preconditions.checkArgument(!models.isEmpty());
|
||||
this.models = models;
|
||||
}
|
||||
|
||||
public ConfiguredModelList(ConfiguredModel model) {
|
||||
this(ImmutableList.of(model));
|
||||
}
|
||||
|
||||
public ConfiguredModelList(ConfiguredModel... models) {
|
||||
this(Arrays.asList(models));
|
||||
}
|
||||
|
||||
public JsonElement toJSON() {
|
||||
if (models.size()==1) {
|
||||
return models.get(0).toJSON(false);
|
||||
} else {
|
||||
JsonArray ret = new JsonArray();
|
||||
for (ConfiguredModel m:models) {
|
||||
ret.add(m.toJSON(true));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public ConfiguredModelList append(ConfiguredModel... models) {
|
||||
return new ConfiguredModelList(ImmutableList.<ConfiguredModel>builder().addAll(this.models).add(models).build());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,301 @@
|
|||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ObjectArrays;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.minecraft.client.renderer.model.ModelRotation;
|
||||
import net.minecraftforge.client.model.generators.MultiPartBlockStateBuilder.PartBuilder;
|
||||
import net.minecraftforge.client.model.generators.VariantBlockStateBuilder.PartialBlockstate;
|
||||
|
||||
/**
|
||||
* Represents a model with blockstate configurations, e.g. rotation, uvlock, and
|
||||
* random weight.
|
||||
* <p>
|
||||
* Can be manually constructed, created by static factory such as
|
||||
* {@link #allYRotations(ModelFile, int, boolean)}, or created by builder via
|
||||
* {@link #builder()}.
|
||||
*/
|
||||
public final class ConfiguredModel {
|
||||
|
||||
/**
|
||||
* The default random weight of configured models, used by convenience
|
||||
* overloads.
|
||||
*/
|
||||
public static final int DEFAULT_WEIGHT = 1;
|
||||
|
||||
public final ModelFile model;
|
||||
public final int rotationX;
|
||||
public final int rotationY;
|
||||
public final boolean uvLock;
|
||||
public final int weight;
|
||||
|
||||
private static IntStream validRotations() {
|
||||
return IntStream.range(0, 4).map(i -> i * 90);
|
||||
}
|
||||
|
||||
public static ConfiguredModel[] allYRotations(ModelFile model, int x, boolean uvlock) {
|
||||
return allYRotations(model, x, uvlock, DEFAULT_WEIGHT);
|
||||
}
|
||||
|
||||
public static ConfiguredModel[] allYRotations(ModelFile model, int x, boolean uvlock, int weight) {
|
||||
return validRotations()
|
||||
.mapToObj(y -> new ConfiguredModel(model, x, y, uvlock, weight))
|
||||
.toArray(ConfiguredModel[]::new);
|
||||
}
|
||||
|
||||
public static ConfiguredModel[] allRotations(ModelFile model, boolean uvlock) {
|
||||
return allRotations(model, uvlock, DEFAULT_WEIGHT);
|
||||
}
|
||||
|
||||
public static ConfiguredModel[] allRotations(ModelFile model, boolean uvlock, int weight) {
|
||||
return validRotations()
|
||||
.mapToObj(x -> allYRotations(model, x, uvlock, weight))
|
||||
.flatMap(Arrays::stream)
|
||||
.toArray(ConfiguredModel[]::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link ConfiguredModel}.
|
||||
*
|
||||
* @param model the underlying model
|
||||
* @param rotationX x-rotation to apply to the model
|
||||
* @param rotationY y-rotation to apply to the model
|
||||
* @param uvLock if uvlock should be enabled
|
||||
* @param weight the random weight of the model
|
||||
*
|
||||
* @throws NullPointerException if {@code model} is {@code null}
|
||||
* @throws IllegalArgumentException if x and/or y rotation are not valid (see
|
||||
* {@link ModelRotation})
|
||||
* @throws IllegalArgumentException if weight is less than or equal to zero
|
||||
*/
|
||||
public ConfiguredModel(ModelFile model, int rotationX, int rotationY, boolean uvLock, int weight) {
|
||||
Preconditions.checkNotNull(model);
|
||||
this.model = model;
|
||||
checkRotation(rotationX, rotationY);
|
||||
this.rotationX = rotationX;
|
||||
this.rotationY = rotationY;
|
||||
this.uvLock = uvLock;
|
||||
checkWeight(weight);
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link ConfiguredModel} with the {@link #DEFAULT_WEIGHT
|
||||
* default random weight}.
|
||||
*
|
||||
* @param model the underlying model
|
||||
* @param rotationX x-rotation to apply to the model
|
||||
* @param rotationY y-rotation to apply to the model
|
||||
* @param uvLock if uvlock should be enabled
|
||||
*
|
||||
* @throws NullPointerException if {@code model} is {@code null}
|
||||
* @throws IllegalArgumentException if x and/or y rotation are not valid (see
|
||||
* {@link ModelRotation})
|
||||
*/
|
||||
public ConfiguredModel(ModelFile model, int rotationX, int rotationY, boolean uvLock) {
|
||||
this(model, rotationX, rotationY, uvLock, DEFAULT_WEIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link ConfiguredModel} with the default rotation (0, 0),
|
||||
* uvlock (false), and {@link #DEFAULT_WEIGHT default random weight}.
|
||||
*
|
||||
* @throws NullPointerException if {@code model} is {@code null}
|
||||
*/
|
||||
public ConfiguredModel(ModelFile model) {
|
||||
this(model, 0, 0, false);
|
||||
}
|
||||
|
||||
static void checkRotation(int rotationX, int rotationY) {
|
||||
Preconditions.checkArgument(ModelRotation.getModelRotation(rotationX, rotationY) != null, "Invalid model rotation x=%d, y=%d", rotationX, rotationY);
|
||||
}
|
||||
|
||||
static void checkWeight(int weight) {
|
||||
Preconditions.checkArgument(weight >= 1, "Model weight must be greater than or equal to 1. Found: %d", weight);
|
||||
}
|
||||
|
||||
JsonObject toJSON(boolean includeWeight) {
|
||||
JsonObject modelJson = new JsonObject();
|
||||
modelJson.addProperty("model", model.getLocation().toString());
|
||||
if (rotationX != 0)
|
||||
modelJson.addProperty("x", rotationX);
|
||||
if (rotationY != 0)
|
||||
modelJson.addProperty("y", rotationY);
|
||||
if (uvLock)
|
||||
modelJson.addProperty("uvlock", uvLock);
|
||||
if (includeWeight && weight != DEFAULT_WEIGHT)
|
||||
modelJson.addProperty("weight", weight);
|
||||
return modelJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new unowned {@link Builder}.
|
||||
*
|
||||
* @return the builder
|
||||
* @see Builder
|
||||
*/
|
||||
public static Builder<?> builder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
static Builder<VariantBlockStateBuilder> builder(VariantBlockStateBuilder outer, VariantBlockStateBuilder.PartialBlockstate state) {
|
||||
return new Builder<>(models -> outer.setModels(state, models), ImmutableList.of());
|
||||
}
|
||||
|
||||
static Builder<PartBuilder> builder(MultiPartBlockStateBuilder outer) {
|
||||
return new Builder<PartBuilder>(models -> {
|
||||
PartBuilder ret = outer.new PartBuilder(new BlockStateProvider.ConfiguredModelList(models));
|
||||
outer.addPart(ret);
|
||||
return ret;
|
||||
}, ImmutableList.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for {@link ConfiguredModel}s, which can contain a callback for
|
||||
* processing the finished result. If no callback is available (e.g. in the case
|
||||
* of {@link ConfiguredModel#builder()}), some methods will not be available.
|
||||
* <p>
|
||||
* Multiple models can be configured at once through the use of
|
||||
* {@link #nextModel()}.
|
||||
*
|
||||
* @param <T> the type of the owning builder, which supplied the callback, and
|
||||
* will be returned upon completion.
|
||||
*/
|
||||
public static class Builder<T> {
|
||||
|
||||
private ModelFile model;
|
||||
@Nullable
|
||||
private final Function<ConfiguredModel[], T> callback;
|
||||
private final List<ConfiguredModel> otherModels;
|
||||
private int rotationX;
|
||||
private int rotationY;
|
||||
private boolean uvLock;
|
||||
private int weight = DEFAULT_WEIGHT;
|
||||
|
||||
Builder() {
|
||||
this(null, ImmutableList.of());
|
||||
}
|
||||
|
||||
Builder(@Nullable Function<ConfiguredModel[], T> callback, List<ConfiguredModel> otherModels) {
|
||||
this.callback = callback;
|
||||
this.otherModels = otherModels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the underlying model object for this configured model.
|
||||
*
|
||||
* @param model the model
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code model} is {@code null}
|
||||
*/
|
||||
public Builder<T> modelFile(ModelFile model) {
|
||||
Preconditions.checkNotNull(model, "Model must not be null");
|
||||
this.model = model;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the x-rotation for this model.
|
||||
*
|
||||
* @param value the x-rotation value
|
||||
* @return this builder
|
||||
* @throws IllegalArgumentException if {@code value} is not a valid x-rotation
|
||||
* (see {@link ModelRotation})
|
||||
*/
|
||||
public Builder<T> rotationX(int value) {
|
||||
checkRotation(value, rotationY);
|
||||
rotationX = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the y-rotation for this model.
|
||||
*
|
||||
* @param value the y-rotation value
|
||||
* @return this builder
|
||||
* @throws IllegalArgumentException if {@code value} is not a valid y-rotation
|
||||
* (see {@link ModelRotation})
|
||||
*/
|
||||
public Builder<T> rotationY(int value) {
|
||||
checkRotation(rotationX, value);
|
||||
rotationY = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> uvLock(boolean value) {
|
||||
uvLock = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the random weight for this model.
|
||||
*
|
||||
* @param value the weight value
|
||||
* @return this builder
|
||||
* @throws IllegalArgumentException if {@code value} is less than or equal to
|
||||
* zero
|
||||
*/
|
||||
public Builder<T> weight(int value) {
|
||||
checkWeight(value);
|
||||
weight = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the most recent model, as if {@link #nextModel()} was never called.
|
||||
* Useful for single-model builders.
|
||||
*
|
||||
* @return the most recently configured model
|
||||
*/
|
||||
public ConfiguredModel buildLast() {
|
||||
return new ConfiguredModel(model, rotationX, rotationY, uvLock, weight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build all configured models and return them as an array.
|
||||
*
|
||||
* @return the array of built models.
|
||||
*/
|
||||
public ConfiguredModel[] build() {
|
||||
return ObjectArrays.concat(otherModels.toArray(new ConfiguredModel[0]), buildLast());
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the contained callback and return the owning builder object. What the
|
||||
* callback does is not defined by this class, but most likely it adds the built
|
||||
* models to the current variant being configured.
|
||||
* <p>
|
||||
* Known callbacks include:
|
||||
* <ul>
|
||||
* <li>{@link PartialBlockstate#modelForState()}</li>
|
||||
* <li>{@link MultiPartBlockStateBuilder#part()}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the owning builder object
|
||||
* @throws NullPointerException if there is no owning builder (and thus no callback)
|
||||
*/
|
||||
public T addModel() {
|
||||
Preconditions.checkNotNull(callback, "Cannot use addModel() without an owning builder present");
|
||||
return callback.apply(build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the current model and return a new builder instance with the same
|
||||
* callback, and storing all previously built models.
|
||||
*
|
||||
* @return a new builder for configuring the next model
|
||||
*/
|
||||
public Builder<T> nextModel() {
|
||||
return new Builder<>(callback, Arrays.asList(build()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import net.minecraft.resources.FilePack;
|
||||
import net.minecraft.resources.FolderPack;
|
||||
import net.minecraft.resources.IResource;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
import net.minecraft.resources.IResourcePack;
|
||||
import net.minecraft.resources.ResourcePackType;
|
||||
import net.minecraft.resources.SimpleReloadableResourceManager;
|
||||
import net.minecraft.resources.VanillaPack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.event.lifecycle.GatherDataEvent;
|
||||
|
||||
/**
|
||||
* Enables data providers to check if other data files currently exist. The
|
||||
* instance provided in the {@link GatherDataEvent} utilizes the standard
|
||||
* resources (via {@link VanillaPack}), as well as any extra resource packs
|
||||
* passed in via the {@code --existing} argument.
|
||||
*/
|
||||
public class ExistingFileHelper {
|
||||
|
||||
private final IResourceManager clientResources, serverData;
|
||||
private final boolean enable;
|
||||
|
||||
public ExistingFileHelper(Collection<Path> existingPacks, boolean enable) {
|
||||
this.clientResources = new SimpleReloadableResourceManager(ResourcePackType.CLIENT_RESOURCES, Thread.currentThread());
|
||||
this.serverData = new SimpleReloadableResourceManager(ResourcePackType.SERVER_DATA, Thread.currentThread());
|
||||
this.clientResources.addResourcePack(new VanillaPack("minecraft", "realms"));
|
||||
this.serverData.addResourcePack(new VanillaPack("minecraft"));
|
||||
for (Path existing : existingPacks) {
|
||||
File file = existing.toFile();
|
||||
IResourcePack pack = file.isDirectory() ? new FolderPack(file) : new FilePack(file);
|
||||
this.clientResources.addResourcePack(pack);
|
||||
this.serverData.addResourcePack(pack);
|
||||
};
|
||||
this.enable = enable;
|
||||
}
|
||||
|
||||
private IResourceManager getManager(ResourcePackType type) {
|
||||
return type == ResourcePackType.CLIENT_RESOURCES ? clientResources : serverData;
|
||||
}
|
||||
|
||||
private ResourceLocation getLocation(ResourceLocation base, String suffix, String prefix) {
|
||||
return new ResourceLocation(base.getNamespace(), prefix + "/" + base.getPath() + suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given resource exists in the known resource packs.
|
||||
*
|
||||
* @param loc the base location of the resource, e.g.
|
||||
* {@code "minecraft:block/stone"}
|
||||
* @param type the type of resources to check
|
||||
* @param pathSuffix a string to append after the path, e.g. {@code ".json"}
|
||||
* @param pathPrefix a string to append before the path, before a slash, e.g.
|
||||
* {@code "models"}
|
||||
* @return {@code true} if the resource exists in any pack, {@code false}
|
||||
* otherwise
|
||||
*/
|
||||
public boolean exists(ResourceLocation loc, ResourcePackType type, String pathSuffix, String pathPrefix) {
|
||||
if (!enable) {
|
||||
return true;
|
||||
}
|
||||
return getManager(type).hasResource(getLocation(loc, pathSuffix, pathPrefix));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public IResource getResource(ResourceLocation loc, ResourcePackType type, String pathSuffix, String pathPrefix) throws IOException {
|
||||
return getManager(type).getResource(getLocation(loc, pathSuffix, pathPrefix));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if validation is enabled, {@code false} otherwise
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enable;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
@VisibleForTesting
|
||||
public interface IGeneratedBlockstate {
|
||||
|
||||
JsonObject toJson();
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Builder for item models, adds the ability to build overrides via
|
||||
* {@link #override()}.
|
||||
*/
|
||||
public class ItemModelBuilder extends ModelBuilder<ItemModelBuilder> {
|
||||
|
||||
protected List<OverrideBuilder> overrides = new ArrayList<>();
|
||||
|
||||
public ItemModelBuilder(ResourceLocation outputLocation, ExistingFileHelper existingFileHelper) {
|
||||
super(outputLocation, existingFileHelper);
|
||||
}
|
||||
|
||||
public OverrideBuilder override() {
|
||||
OverrideBuilder ret = new OverrideBuilder();
|
||||
overrides.add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an existing override builder
|
||||
*
|
||||
* @param index the index of the existing override builder
|
||||
* @return the override builder
|
||||
* @throws IndexOutOfBoundsException if {@code} index is out of bounds
|
||||
*/
|
||||
public OverrideBuilder override(int index) {
|
||||
Preconditions.checkElementIndex(index, overrides.size(), "override");
|
||||
return overrides.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject toJson() {
|
||||
JsonObject root = super.toJson();
|
||||
if (!overrides.isEmpty()) {
|
||||
JsonArray overridesJson = new JsonArray();
|
||||
overrides.stream().map(OverrideBuilder::toJson).forEach(overridesJson::add);
|
||||
root.add("overrides", overridesJson);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
public class OverrideBuilder {
|
||||
|
||||
private ModelFile model;
|
||||
private final Map<ResourceLocation, Float> predicates = new LinkedHashMap<>();
|
||||
|
||||
public OverrideBuilder model(ModelFile model) {
|
||||
this.model = model;
|
||||
model.assertExistence();
|
||||
return this;
|
||||
}
|
||||
|
||||
public OverrideBuilder predicate(ResourceLocation key, float value) {
|
||||
this.predicates.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemModelBuilder end() { return ItemModelBuilder.this; }
|
||||
|
||||
JsonObject toJson() {
|
||||
JsonObject ret = new JsonObject();
|
||||
JsonObject predicatesJson = new JsonObject();
|
||||
predicates.forEach((key, val) -> predicatesJson.addProperty(serializeLoc(key), val));
|
||||
ret.add("predicate", predicatesJson);
|
||||
ret.addProperty("model", serializeLoc(model.getLocation()));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import net.minecraft.data.DataGenerator;
|
||||
|
||||
/**
|
||||
* Stub class to extend for item model data providers, eliminates some
|
||||
* boilerplate constructor parameters.
|
||||
*/
|
||||
public abstract class ItemModelProvider extends ModelProvider<ItemModelBuilder> {
|
||||
|
||||
public ItemModelProvider(DataGenerator generator, String modid, ExistingFileHelper existingFileHelper) {
|
||||
super(generator, modid, ITEM_FOLDER, ItemModelBuilder::new, existingFileHelper);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,666 @@
|
|||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.minecraft.client.renderer.Vector3f;
|
||||
import net.minecraft.client.renderer.model.BlockFaceUV;
|
||||
import net.minecraft.client.renderer.model.BlockPart;
|
||||
import net.minecraft.client.renderer.model.BlockPartFace;
|
||||
import net.minecraft.client.renderer.model.BlockPartRotation;
|
||||
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
|
||||
import net.minecraft.client.renderer.model.ItemTransformVec3f;
|
||||
import net.minecraft.client.renderer.texture.MissingTextureSprite;
|
||||
import net.minecraft.resources.ResourcePackType;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
/**
|
||||
* General purpose model builder, contains all the commonalities between item
|
||||
* and block models.
|
||||
*
|
||||
* @see ModelProvider
|
||||
* @see BlockModelBuilder
|
||||
* @see ItemModelBuilder
|
||||
*
|
||||
* @param <T> Self type, for simpler chaining of methods.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ModelBuilder<T extends ModelBuilder<T>> extends ModelFile {
|
||||
|
||||
@Nullable
|
||||
protected ModelFile parent;
|
||||
protected final Map<String, String> textures = new LinkedHashMap<>();
|
||||
protected final TransformsBuilder transforms = new TransformsBuilder();
|
||||
protected final ExistingFileHelper existingFileHelper;
|
||||
|
||||
protected boolean ambientOcclusion = true;
|
||||
protected boolean gui3d = false;
|
||||
|
||||
protected final List<ElementBuilder> elements = new ArrayList<>();
|
||||
|
||||
protected ModelBuilder(ResourceLocation outputLocation, ExistingFileHelper existingFileHelper) {
|
||||
super(outputLocation);
|
||||
this.existingFileHelper = existingFileHelper;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T self() { return (T) this; }
|
||||
|
||||
@Override
|
||||
protected boolean exists() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parent model for the current model.
|
||||
*
|
||||
* @param parent the parent model
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code parent} is {@code null}
|
||||
* @throws IllegalStateException if {@code parent} does not {@link ModelFile#assertExistence() exist}
|
||||
*/
|
||||
public T parent(ModelFile parent) {
|
||||
Preconditions.checkNotNull(parent, "Parent must not be null");
|
||||
parent.assertExistence();
|
||||
this.parent = parent;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the texture for a given dictionary key.
|
||||
*
|
||||
* @param key the texture key
|
||||
* @param texture the texture, can be another key e.g. {@code "#all"}
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code key} is {@code null}
|
||||
* @throws NullPointerException if {@code texture} is {@code null}
|
||||
* @throws IllegalStateException if {@code texture} is not a key (does not start
|
||||
* with {@code '#'}) and does not exist in any
|
||||
* known resource pack
|
||||
*/
|
||||
public T texture(String key, String texture) {
|
||||
Preconditions.checkNotNull(key, "Key must not be null");
|
||||
Preconditions.checkNotNull(texture, "Texture must not be null");
|
||||
if (texture.charAt(0) == '#') {
|
||||
this.textures.put(key, texture);
|
||||
return self();
|
||||
} else {
|
||||
ResourceLocation asLoc;
|
||||
if (texture.contains(":")) {
|
||||
asLoc = new ResourceLocation(texture);
|
||||
} else {
|
||||
asLoc = new ResourceLocation(getLocation().getNamespace(), texture);
|
||||
}
|
||||
return texture(key, asLoc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the texture for a given dictionary key.
|
||||
*
|
||||
* @param key the texture key
|
||||
* @param texture the texture
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code key} is {@code null}
|
||||
* @throws NullPointerException if {@code texture} is {@code null}
|
||||
* @throws IllegalStateException if {@code texture} is not a key (does not start
|
||||
* with {@code '#'}) and does not exist in any
|
||||
* known resource pack
|
||||
*/
|
||||
public T texture(String key, ResourceLocation texture) {
|
||||
Preconditions.checkNotNull(key, "Key must not be null");
|
||||
Preconditions.checkNotNull(texture, "Texture must not be null");
|
||||
Preconditions.checkArgument(existingFileHelper.exists(texture, ResourcePackType.CLIENT_RESOURCES, ".png", "textures"),
|
||||
"Texture %s does not exist in any known resource pack", texture);
|
||||
this.textures.put(key, texture.toString());
|
||||
return self();
|
||||
}
|
||||
|
||||
public TransformsBuilder transforms() {
|
||||
return transforms;
|
||||
}
|
||||
|
||||
public T ao(boolean ao) {
|
||||
this.ambientOcclusion = ao;
|
||||
return self();
|
||||
}
|
||||
|
||||
public T gui3d(boolean gui3d) {
|
||||
this.gui3d = gui3d;
|
||||
return self();
|
||||
}
|
||||
|
||||
public ElementBuilder element() {
|
||||
ElementBuilder ret = new ElementBuilder();
|
||||
elements.add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an existing element builder
|
||||
*
|
||||
* @param index the index of the existing element builder
|
||||
* @return the element builder
|
||||
* @throws IndexOutOfBoundsException if {@code} index is out of bounds
|
||||
*/
|
||||
public ElementBuilder element(int index) {
|
||||
Preconditions.checkElementIndex(index, elements.size(), "Element index");
|
||||
return elements.get(index);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public JsonObject toJson() {
|
||||
JsonObject root = new JsonObject();
|
||||
if (this.parent != null) {
|
||||
root.addProperty("parent", serializeLoc(this.parent.getLocation()));
|
||||
}
|
||||
|
||||
if (!this.ambientOcclusion) {
|
||||
root.addProperty("ambientocclusion", this.ambientOcclusion);
|
||||
}
|
||||
|
||||
Map<Perspective, ItemTransformVec3f> transforms = this.transforms.build();
|
||||
if (!transforms.isEmpty()) {
|
||||
JsonObject display = new JsonObject();
|
||||
for (Entry<Perspective, ItemTransformVec3f> e : transforms.entrySet()) {
|
||||
JsonObject transform = new JsonObject();
|
||||
ItemTransformVec3f vec = e.getValue();
|
||||
if (vec.equals(ItemTransformVec3f.DEFAULT)) continue;
|
||||
if (!vec.rotation.equals(ItemTransformVec3f.Deserializer.ROTATION_DEFAULT)) {
|
||||
transform.add("rotation", serializeVector3f(vec.rotation));
|
||||
}
|
||||
if (!vec.translation.equals(ItemTransformVec3f.Deserializer.TRANSLATION_DEFAULT)) {
|
||||
transform.add("translation", serializeVector3f(e.getValue().translation));
|
||||
}
|
||||
if (!vec.scale.equals(ItemTransformVec3f.Deserializer.SCALE_DEFAULT)) {
|
||||
transform.add("scale", serializeVector3f(e.getValue().scale));
|
||||
}
|
||||
display.add(e.getKey().name, transform);
|
||||
}
|
||||
root.add("display", display);
|
||||
}
|
||||
|
||||
if (!this.textures.isEmpty()) {
|
||||
JsonObject textures = new JsonObject();
|
||||
for (Entry<String, String> e : this.textures.entrySet()) {
|
||||
textures.addProperty(e.getKey(), serializeLocOrKey(e.getValue()));
|
||||
}
|
||||
root.add("textures", textures);
|
||||
}
|
||||
|
||||
if (!this.elements.isEmpty()) {
|
||||
JsonArray elements = new JsonArray();
|
||||
this.elements.stream().map(ElementBuilder::build).forEach(part -> {
|
||||
JsonObject partObj = new JsonObject();
|
||||
partObj.add("from", serializeVector3f(part.positionFrom));
|
||||
partObj.add("to", serializeVector3f(part.positionTo));
|
||||
|
||||
if (part.partRotation != null) {
|
||||
JsonObject rotation = new JsonObject();
|
||||
rotation.add("origin", serializeVector3f(part.partRotation.origin));
|
||||
rotation.addProperty("axis", part.partRotation.axis.getName());
|
||||
rotation.addProperty("angle", part.partRotation.angle);
|
||||
if (part.partRotation.rescale) {
|
||||
rotation.addProperty("rescale", part.partRotation.rescale);
|
||||
}
|
||||
partObj.add("rotation", rotation);
|
||||
}
|
||||
|
||||
if (!part.shade) {
|
||||
partObj.addProperty("shade", part.shade);
|
||||
}
|
||||
|
||||
JsonObject faces = new JsonObject();
|
||||
for (Direction dir : Direction.values()) {
|
||||
BlockPartFace face = part.mapFaces.get(dir);
|
||||
if (face == null) continue;
|
||||
|
||||
JsonObject faceObj = new JsonObject();
|
||||
faceObj.addProperty("texture", serializeLocOrKey(face.texture));
|
||||
if (!Arrays.equals(face.blockFaceUV.uvs, part.getFaceUvs(dir))) {
|
||||
faceObj.add("uv", new Gson().toJsonTree(face.blockFaceUV.uvs));
|
||||
}
|
||||
if (face.cullFace != null) {
|
||||
faceObj.addProperty("cullface", face.cullFace.getName());
|
||||
}
|
||||
if (face.blockFaceUV.rotation != 0) {
|
||||
faceObj.addProperty("rotation", face.blockFaceUV.rotation);
|
||||
}
|
||||
if (face.tintIndex != -1) {
|
||||
faceObj.addProperty("tintindex", face.tintIndex);
|
||||
}
|
||||
faces.add(dir.getName(), faceObj);
|
||||
}
|
||||
if (!part.mapFaces.isEmpty()) {
|
||||
partObj.add("faces", faces);
|
||||
}
|
||||
elements.add(partObj);
|
||||
});
|
||||
root.add("elements", elements);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private String serializeLocOrKey(String tex) {
|
||||
if (tex.charAt(0) == '#') {
|
||||
return tex;
|
||||
}
|
||||
return serializeLoc(new ResourceLocation(tex));
|
||||
}
|
||||
|
||||
String serializeLoc(ResourceLocation loc) {
|
||||
if (loc.getNamespace().equals("minecraft")) {
|
||||
return loc.getPath();
|
||||
}
|
||||
return loc.toString();
|
||||
}
|
||||
|
||||
private JsonArray serializeVector3f(Vector3f vec) {
|
||||
JsonArray ret = new JsonArray();
|
||||
ret.add(serializeFloat(vec.getX()));
|
||||
ret.add(serializeFloat(vec.getY()));
|
||||
ret.add(serializeFloat(vec.getZ()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
private Number serializeFloat(float f) {
|
||||
if ((int) f == f) {
|
||||
return (int) f;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
public class ElementBuilder {
|
||||
|
||||
private Vector3f from = new Vector3f();
|
||||
private Vector3f to = new Vector3f(16, 16, 16);
|
||||
private final Map<Direction, FaceBuilder> faces = new LinkedHashMap<>();
|
||||
private RotationBuilder rotation;
|
||||
private boolean shade = true;
|
||||
|
||||
private void validateCoordinate(float coord, char name) {
|
||||
Preconditions.checkArgument(!(coord < -16.0F) && !(coord > 32.0F), "Position " + name + " out of range, must be within [-16, 32]. Found: %d", coord);
|
||||
}
|
||||
|
||||
private void validatePosition(Vector3f pos) {
|
||||
validateCoordinate(pos.getX(), 'x');
|
||||
validateCoordinate(pos.getY(), 'y');
|
||||
validateCoordinate(pos.getZ(), 'z');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "from" position for this element.
|
||||
*
|
||||
* @param x x-position for this vector
|
||||
* @param y y-position for this vector
|
||||
* @param z z-position for this vector
|
||||
* @return this builder
|
||||
* @throws IllegalArgumentException if the vector is out of bounds (any
|
||||
* coordinate not between -16 and 32,
|
||||
* inclusive)
|
||||
*/
|
||||
public ElementBuilder from(float x, float y, float z) {
|
||||
this.from = new Vector3f(x, y, z);
|
||||
validatePosition(this.from);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "to" position for this element.
|
||||
*
|
||||
* @param x x-position for this vector
|
||||
* @param y y-position for this vector
|
||||
* @param z z-position for this vector
|
||||
* @return this builder
|
||||
* @throws IllegalArgumentException if the vector is out of bounds (any
|
||||
* coordinate not between -16 and 32,
|
||||
* inclusive)
|
||||
*/
|
||||
public ElementBuilder to(float x, float y, float z) {
|
||||
this.to = new Vector3f(x, y, z);
|
||||
validatePosition(this.to);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return or create the face builder for the given direction.
|
||||
*
|
||||
* @param dir the direction
|
||||
* @return the face builder for the given direction
|
||||
* @throws NullPointerException if {@code dir} is {@code null}
|
||||
*/
|
||||
public FaceBuilder face(Direction dir) {
|
||||
Preconditions.checkNotNull(dir, "Direction must not be null");
|
||||
return faces.computeIfAbsent(dir, FaceBuilder::new);
|
||||
}
|
||||
|
||||
public RotationBuilder rotation(BlockPartRotation rotation) {
|
||||
if (this.rotation == null) {
|
||||
this.rotation = new RotationBuilder();
|
||||
}
|
||||
return this.rotation;
|
||||
}
|
||||
|
||||
public ElementBuilder shade(boolean shade) {
|
||||
this.shade = shade;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify all <em>possible</em> faces dynamically using a function, creating new
|
||||
* faces as necessary.
|
||||
*
|
||||
* @param action the function to apply to each direction
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code action} is {@code null}
|
||||
*/
|
||||
public ElementBuilder allFaces(BiConsumer<Direction, FaceBuilder> action) {
|
||||
Arrays.stream(Direction.values())
|
||||
.forEach(d -> action.accept(d, face(d)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify all <em>existing</em> faces dynamically using a function.
|
||||
*
|
||||
* @param action the function to apply to each direction
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code action} is {@code null}
|
||||
*/
|
||||
public ElementBuilder faces(BiConsumer<Direction, FaceBuilder> action) {
|
||||
faces.entrySet().stream()
|
||||
.forEach(e -> action.accept(e.getKey(), e.getValue()));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Texture all <em>possible</em> faces in the current element with the given
|
||||
* texture, creating new faces where necessary.
|
||||
*
|
||||
* @param texture the texture
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code texture} is {@code null}
|
||||
*/
|
||||
public ElementBuilder textureAll(String texture) {
|
||||
return allFaces(addTexture(texture));
|
||||
}
|
||||
|
||||
/**
|
||||
* Texture all <em>existing</em> faces in the current element with the given
|
||||
* texture.
|
||||
*
|
||||
* @param texture the texture
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code texture} is {@code null}
|
||||
*/
|
||||
public ElementBuilder texture(String texture) {
|
||||
return faces(addTexture(texture));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a typical cube element, creating new faces as needed, applying the
|
||||
* given texture, and setting the cullface.
|
||||
*
|
||||
* @param texture the texture
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code texture} is {@code null}
|
||||
*/
|
||||
public ElementBuilder cube(String texture) {
|
||||
return allFaces(addTexture(texture).andThen((dir, f) -> f.cullface(dir)));
|
||||
}
|
||||
|
||||
private BiConsumer<Direction, FaceBuilder> addTexture(String texture) {
|
||||
return ($, f) -> f.texture(texture);
|
||||
}
|
||||
|
||||
BlockPart build() {
|
||||
Map<Direction, BlockPartFace> faces = this.faces.entrySet().stream()
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().build(), (k1, k2) -> { throw new IllegalArgumentException(); }, LinkedHashMap::new));
|
||||
return new BlockPart(from, to, faces, rotation == null ? null : rotation.build(), shade);
|
||||
}
|
||||
|
||||
public T end() { return self(); }
|
||||
|
||||
public class FaceBuilder {
|
||||
|
||||
private Direction cullface;
|
||||
private int tintindex = -1;
|
||||
private String texture = MissingTextureSprite.getLocation().toString();
|
||||
private float[] uvs;
|
||||
private FaceRotation rotation = FaceRotation.ZERO;
|
||||
|
||||
FaceBuilder(Direction dir) {
|
||||
// param unused for functional match
|
||||
}
|
||||
|
||||
public FaceBuilder cullface(@Nullable Direction dir) {
|
||||
this.cullface = dir;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FaceBuilder tintindex(int index) {
|
||||
this.tintindex = index;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the texture for the current face.
|
||||
*
|
||||
* @param texture the texture
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code texture} is {@code null}
|
||||
*/
|
||||
public FaceBuilder texture(String texture) {
|
||||
Preconditions.checkNotNull(texture, "Texture must not be null");
|
||||
this.texture = texture;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FaceBuilder uvs(float u1, float v1, float u2, float v2) {
|
||||
this.uvs = new float[] { u1, v1, u2, v2 };
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the texture rotation for the current face.
|
||||
*
|
||||
* @param rot the rotation
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code rot} is {@code null}
|
||||
*/
|
||||
public FaceBuilder rotation(FaceRotation rot) {
|
||||
Preconditions.checkNotNull(rot, "Rotation must not be null");
|
||||
this.rotation = rot;
|
||||
return this;
|
||||
}
|
||||
|
||||
BlockPartFace build() {
|
||||
if (this.texture == null) {
|
||||
throw new IllegalStateException("A model face must have a texture");
|
||||
}
|
||||
return new BlockPartFace(cullface, tintindex, texture, new BlockFaceUV(uvs, rotation.rotation));
|
||||
}
|
||||
|
||||
public ElementBuilder end() { return ElementBuilder.this; }
|
||||
}
|
||||
|
||||
public class RotationBuilder {
|
||||
|
||||
private Vector3f origin;
|
||||
private Direction.Axis axis;
|
||||
private float angle;
|
||||
private boolean rescale;
|
||||
|
||||
public RotationBuilder origin(float x, float y, float z) {
|
||||
this.origin = new Vector3f(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param axis the axis of rotation
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code axis} is {@code null}
|
||||
*/
|
||||
public RotationBuilder axis(Direction.Axis axis) {
|
||||
Preconditions.checkNotNull(axis, "Axis must not be null");
|
||||
this.axis = axis;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param angle the rotation angle
|
||||
* @return this builder
|
||||
* @throws IllegalArgumentException if {@code angle} is invalid (not one of 0, +/-22.5, +/-45)
|
||||
*/
|
||||
public RotationBuilder angle(float angle) {
|
||||
// Same logic from BlockPart.Deserializer#parseAngle
|
||||
Preconditions.checkArgument(angle != 0.0F && MathHelper.abs(angle) != 22.5F && MathHelper.abs(angle) != 45.0F, "Invalid rotation %f found, only -45/-22.5/0/22.5/45 allowed", angle);
|
||||
this.angle = angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RotationBuilder rescale(boolean rescale) {
|
||||
this.rescale = rescale;
|
||||
return this;
|
||||
}
|
||||
|
||||
BlockPartRotation build() {
|
||||
return new BlockPartRotation(origin, axis, angle, rescale);
|
||||
}
|
||||
|
||||
public ElementBuilder end() { return ElementBuilder.this; }
|
||||
}
|
||||
}
|
||||
|
||||
public enum FaceRotation {
|
||||
ZERO(0),
|
||||
CLOCKWISE_90(90),
|
||||
UPSIDE_DOWN(180),
|
||||
COUNTERCLOCKWISE_90(270),
|
||||
;
|
||||
|
||||
final int rotation;
|
||||
|
||||
private FaceRotation(int rotation) {
|
||||
this.rotation = rotation;
|
||||
}
|
||||
}
|
||||
|
||||
// Since vanilla doesn't keep the name in TransformType...
|
||||
public enum Perspective {
|
||||
|
||||
THIRDPERSON_RIGHT(TransformType.THIRD_PERSON_RIGHT_HAND, "thirdperson_righthand"),
|
||||
THIRDPERSON_LEFT(TransformType.THIRD_PERSON_LEFT_HAND, "thirdperson_lefthand"),
|
||||
FIRSTPERSON_RIGHT(TransformType.FIRST_PERSON_RIGHT_HAND, "firstperson_righthand"),
|
||||
FIRSTPERSON_LEFT(TransformType.FIRST_PERSON_LEFT_HAND, "firstperson_lefthand"),
|
||||
HEAD(TransformType.HEAD, "head"),
|
||||
GUI(TransformType.GUI, "gui"),
|
||||
GROUND(TransformType.GROUND, "ground"),
|
||||
FIXED(TransformType.FIXED, "fixed"),
|
||||
;
|
||||
|
||||
public final TransformType vanillaType;
|
||||
final String name;
|
||||
|
||||
private Perspective(TransformType vanillaType, String name) {
|
||||
this.vanillaType = vanillaType;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public class TransformsBuilder {
|
||||
|
||||
private final Map<Perspective, TransformVecBuilder> transforms = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Begin building a new transform for the given perspective.
|
||||
*
|
||||
* @param type the perspective to create or return the builder for
|
||||
* @return the builder for the given perspective
|
||||
* @throws NullPointerException if {@code type} is {@code null}
|
||||
*/
|
||||
public TransformVecBuilder transform(Perspective type) {
|
||||
Preconditions.checkNotNull(type, "Perspective cannot be null");
|
||||
return transforms.computeIfAbsent(type, TransformVecBuilder::new);
|
||||
}
|
||||
|
||||
Map<Perspective, ItemTransformVec3f> build() {
|
||||
return this.transforms.entrySet().stream()
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().build(), (k1, k2) -> { throw new IllegalArgumentException(); }, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
public T end() { return self(); }
|
||||
|
||||
public class TransformVecBuilder {
|
||||
|
||||
private Vector3f rotation = new Vector3f();
|
||||
private Vector3f translation = new Vector3f();
|
||||
private Vector3f scale = new Vector3f();
|
||||
|
||||
TransformVecBuilder(Perspective type) {
|
||||
// param unused for functional match
|
||||
}
|
||||
|
||||
public TransformVecBuilder rotation(float x, float y, float z) {
|
||||
this.rotation = new Vector3f(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransformVecBuilder translation(float x, float y, float z) {
|
||||
this.translation = new Vector3f(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransformVecBuilder scale(float sc) {
|
||||
return scale(sc, sc, sc);
|
||||
}
|
||||
|
||||
public TransformVecBuilder scale(float x, float y, float z) {
|
||||
this.scale = new Vector3f(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
ItemTransformVec3f build() {
|
||||
return new ItemTransformVec3f(rotation, translation, scale);
|
||||
}
|
||||
|
||||
public TransformsBuilder end() { return TransformsBuilder.this; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import net.minecraft.resources.ResourcePackType;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public abstract class ModelFile {
|
||||
|
||||
protected ResourceLocation location;
|
||||
|
||||
protected ModelFile(ResourceLocation location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
protected abstract boolean exists();
|
||||
|
||||
public ResourceLocation getLocation() {
|
||||
assertExistence();
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that this model exists.
|
||||
* @throws IllegalStateException if this model does not exist
|
||||
*/
|
||||
public void assertExistence() {
|
||||
Preconditions.checkState(exists(), "Model at %s does not exist", location);
|
||||
}
|
||||
|
||||
public ResourceLocation getUncheckedLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public static class UncheckedModelFile extends ModelFile {
|
||||
|
||||
public UncheckedModelFile(String location) {
|
||||
this(new ResourceLocation(location));
|
||||
}
|
||||
public UncheckedModelFile(ResourceLocation location) {
|
||||
super(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean exists() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExistingModelFile extends ModelFile {
|
||||
private final ExistingFileHelper existingHelper;
|
||||
|
||||
@Deprecated
|
||||
public ExistingModelFile(String location, ExistingFileHelper existingHelper) {
|
||||
this(new ResourceLocation(location), existingHelper);
|
||||
}
|
||||
|
||||
public ExistingModelFile(ResourceLocation location, ExistingFileHelper existingHelper) {
|
||||
super(location);
|
||||
this.existingHelper = existingHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean exists() {
|
||||
if (getUncheckedLocation().getPath().contains("."))
|
||||
return existingHelper.exists(getUncheckedLocation(), ResourcePackType.CLIENT_RESOURCES, "", "models");
|
||||
else
|
||||
return existingHelper.exists(getUncheckedLocation(), ResourcePackType.CLIENT_RESOURCES, ".json", "models");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.DirectoryCache;
|
||||
import net.minecraft.data.IDataProvider;
|
||||
import net.minecraft.resources.ResourcePackType;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public abstract class ModelProvider<T extends ModelBuilder<T>> implements IDataProvider {
|
||||
|
||||
private class ExistingFileHelperIncludingGenerated extends ExistingFileHelper {
|
||||
|
||||
private final ExistingFileHelper delegate;
|
||||
|
||||
public ExistingFileHelperIncludingGenerated(ExistingFileHelper delegate) {
|
||||
super(Collections.emptyList(), true);
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(ResourceLocation loc, ResourcePackType type, String pathSuffix, String pathPrefix) {
|
||||
if (generatedModels.containsKey(loc)) {
|
||||
return true;
|
||||
}
|
||||
return delegate.exists(loc, type, pathSuffix, pathPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String BLOCK_FOLDER = "block";
|
||||
public static final String ITEM_FOLDER = "item";
|
||||
|
||||
private static final Gson GSON = (new GsonBuilder()).setPrettyPrinting().create();
|
||||
protected final DataGenerator generator;
|
||||
protected final String modid;
|
||||
protected final String folder;
|
||||
protected final Function<ResourceLocation, T> factory;
|
||||
@VisibleForTesting
|
||||
protected final Map<ResourceLocation, T> generatedModels = new HashMap<>();
|
||||
protected final ExistingFileHelper existingFileHelper;
|
||||
|
||||
protected DirectoryCache cache;
|
||||
|
||||
protected abstract void registerModels();
|
||||
|
||||
public ModelProvider(DataGenerator generator, String modid, String folder, Function<ResourceLocation, T> factory, ExistingFileHelper existingFileHelper) {
|
||||
Preconditions.checkNotNull(generator);
|
||||
this.generator = generator;
|
||||
Preconditions.checkNotNull(modid);
|
||||
this.modid = modid;
|
||||
Preconditions.checkNotNull(folder);
|
||||
this.folder = folder;
|
||||
Preconditions.checkNotNull(factory);
|
||||
this.factory = factory;
|
||||
Preconditions.checkNotNull(existingFileHelper);
|
||||
this.existingFileHelper = new ExistingFileHelperIncludingGenerated(existingFileHelper);
|
||||
}
|
||||
|
||||
public ModelProvider(DataGenerator generator, String modid, String folder, BiFunction<ResourceLocation, ExistingFileHelper, T> builderFromModId, ExistingFileHelper existingFileHelper) {
|
||||
this(generator, modid, folder, loc->builderFromModId.apply(loc, existingFileHelper), existingFileHelper);
|
||||
}
|
||||
|
||||
protected T getBuilder(String path) {
|
||||
Preconditions.checkNotNull(path, "Path must not be null");
|
||||
ResourceLocation outputLoc = extendWithFolder(path.contains(":") ? new ResourceLocation(path) : new ResourceLocation(modid, path));
|
||||
return generatedModels.computeIfAbsent(outputLoc, factory);
|
||||
}
|
||||
|
||||
private ResourceLocation extendWithFolder(ResourceLocation rl) {
|
||||
if (rl.getPath().contains("/")) {
|
||||
return rl;
|
||||
}
|
||||
return new ResourceLocation(rl.getNamespace(), folder + "/" + rl.getPath());
|
||||
}
|
||||
|
||||
protected ResourceLocation modLoc(String name) {
|
||||
return new ResourceLocation(modid, name);
|
||||
}
|
||||
|
||||
protected ResourceLocation mcLoc(String name) {
|
||||
return new ResourceLocation(name);
|
||||
}
|
||||
|
||||
protected T withExistingParent(String name, String parent) {
|
||||
return withExistingParent(name, mcLoc(parent));
|
||||
}
|
||||
|
||||
protected T withExistingParent(String name, ResourceLocation parent) {
|
||||
return getBuilder(name).parent(getExistingFile(parent));
|
||||
}
|
||||
|
||||
protected T cube(String name, ResourceLocation down, ResourceLocation up, ResourceLocation north, ResourceLocation south, ResourceLocation east, ResourceLocation west) {
|
||||
return withExistingParent(name, "cube")
|
||||
.texture("down", down)
|
||||
.texture("up", up)
|
||||
.texture("north", north)
|
||||
.texture("south", south)
|
||||
.texture("east", east)
|
||||
.texture("west", west);
|
||||
}
|
||||
|
||||
private T singleTexture(String name, String parent, ResourceLocation texture) {
|
||||
return singleTexture(name, mcLoc(parent), texture);
|
||||
}
|
||||
|
||||
protected T singleTexture(String name, ResourceLocation parent, ResourceLocation texture) {
|
||||
return singleTexture(name, parent, "texture", texture);
|
||||
}
|
||||
|
||||
private T singleTexture(String name, String parent, String textureKey, ResourceLocation texture) {
|
||||
return singleTexture(name, mcLoc(parent), textureKey, texture);
|
||||
}
|
||||
|
||||
protected T singleTexture(String name, ResourceLocation parent, String textureKey, ResourceLocation texture) {
|
||||
return withExistingParent(name, parent)
|
||||
.texture(textureKey, texture);
|
||||
}
|
||||
|
||||
protected T cubeAll(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/cube_all", "all", texture);
|
||||
}
|
||||
|
||||
protected T cubeTop(String name, ResourceLocation side, ResourceLocation top) {
|
||||
return withExistingParent(name, BLOCK_FOLDER + "/cube_top")
|
||||
.texture("side", side)
|
||||
.texture("top", top);
|
||||
}
|
||||
|
||||
private T sideBottomTop(String name, String parent, ResourceLocation side, ResourceLocation bottom, ResourceLocation top) {
|
||||
return withExistingParent(name, parent)
|
||||
.texture("side", side)
|
||||
.texture("bottom", bottom)
|
||||
.texture("top", top);
|
||||
}
|
||||
|
||||
protected T cubeBottomTop(String name, ResourceLocation side, ResourceLocation bottom, ResourceLocation top) {
|
||||
return sideBottomTop(name, BLOCK_FOLDER + "/cube_bottom_top", side, bottom, top);
|
||||
}
|
||||
|
||||
protected T cubeColumn(String name, ResourceLocation side, ResourceLocation end) {
|
||||
return withExistingParent(name, BLOCK_FOLDER + "/cube_column")
|
||||
.texture("side", side)
|
||||
.texture("end", end);
|
||||
}
|
||||
|
||||
protected T orientableVertical(String name, ResourceLocation side, ResourceLocation front) {
|
||||
return withExistingParent(name, BLOCK_FOLDER + "/orientable_vertical")
|
||||
.texture("side", side)
|
||||
.texture("front", front);
|
||||
}
|
||||
|
||||
protected T orientableWithBottom(String name, ResourceLocation side, ResourceLocation front, ResourceLocation bottom, ResourceLocation top) {
|
||||
return withExistingParent(name, BLOCK_FOLDER + "/orientable_with_bottom")
|
||||
.texture("side", side)
|
||||
.texture("front", front)
|
||||
.texture("bottom", bottom)
|
||||
.texture("top", top);
|
||||
}
|
||||
|
||||
protected T orientable(String name, ResourceLocation side, ResourceLocation front, ResourceLocation top) {
|
||||
return withExistingParent(name, BLOCK_FOLDER + "/orientable")
|
||||
.texture("side", side)
|
||||
.texture("front", front)
|
||||
.texture("top", top);
|
||||
}
|
||||
|
||||
protected T crop(String name, ResourceLocation crop) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/crop", "crop", crop);
|
||||
}
|
||||
|
||||
protected T cross(String name, ResourceLocation cross) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/cross", "cross", cross);
|
||||
}
|
||||
|
||||
protected T stairs(String name, ResourceLocation side, ResourceLocation bottom, ResourceLocation top) {
|
||||
return sideBottomTop(name, BLOCK_FOLDER + "/stairs", side, bottom, top);
|
||||
}
|
||||
|
||||
protected T stairsOuter(String name, ResourceLocation side, ResourceLocation bottom, ResourceLocation top) {
|
||||
return sideBottomTop(name, BLOCK_FOLDER + "/outer_stairs", side, bottom, top);
|
||||
}
|
||||
|
||||
protected T stairsInner(String name, ResourceLocation side, ResourceLocation bottom, ResourceLocation top) {
|
||||
return sideBottomTop(name, BLOCK_FOLDER + "/inner_stairs", side, bottom, top);
|
||||
}
|
||||
|
||||
protected T slab(String name, ResourceLocation side, ResourceLocation bottom, ResourceLocation top) {
|
||||
return sideBottomTop(name, BLOCK_FOLDER + "/slab", side, bottom, top);
|
||||
}
|
||||
|
||||
protected T slabTop(String name, ResourceLocation side, ResourceLocation bottom, ResourceLocation top) {
|
||||
return sideBottomTop(name, BLOCK_FOLDER + "/slab_top", side, bottom, top);
|
||||
}
|
||||
|
||||
protected T fencePost(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/fence_post", texture);
|
||||
}
|
||||
|
||||
protected T fenceSide(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/fence_side", texture);
|
||||
}
|
||||
|
||||
protected T fenceInventory(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/fence_inventory", texture);
|
||||
}
|
||||
|
||||
protected T fenceGate(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_fence_gate", texture);
|
||||
}
|
||||
|
||||
protected T fenceGateOpen(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_fence_gate_open", texture);
|
||||
}
|
||||
|
||||
protected T fenceGateWall(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_fence_gate_wall", texture);
|
||||
}
|
||||
|
||||
protected T fenceGateWallOpen(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_fence_gate_wall_open", texture);
|
||||
}
|
||||
|
||||
protected T wallPost(String name, ResourceLocation wall) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_wall_post", "wall", wall);
|
||||
}
|
||||
|
||||
protected T wallSide(String name, ResourceLocation wall) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_wall_side", "wall", wall);
|
||||
}
|
||||
|
||||
protected T wallInventory(String name, ResourceLocation wall) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/wall_inventory", "wall", wall);
|
||||
}
|
||||
|
||||
private T pane(String name, String parent, ResourceLocation pane, ResourceLocation edge) {
|
||||
return withExistingParent(name, BLOCK_FOLDER + "/" + parent)
|
||||
.texture("pane", pane)
|
||||
.texture("edge", edge);
|
||||
}
|
||||
|
||||
protected T panePost(String name, ResourceLocation pane, ResourceLocation edge) {
|
||||
return pane(name, "template_glass_pane_post", pane, edge);
|
||||
}
|
||||
|
||||
protected T paneSide(String name, ResourceLocation pane, ResourceLocation edge) {
|
||||
return pane(name, "template_glass_pane_side", pane, edge);
|
||||
}
|
||||
|
||||
protected T paneSideAlt(String name, ResourceLocation pane, ResourceLocation edge) {
|
||||
return pane(name, "template_glass_pane_side_alt", pane, edge);
|
||||
}
|
||||
|
||||
protected T paneNoSide(String name, ResourceLocation pane) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_glass_pane_noside", "pane", pane);
|
||||
}
|
||||
|
||||
protected T paneNoSideAlt(String name, ResourceLocation pane) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_glass_pane_noside_alt", "pane", pane);
|
||||
}
|
||||
|
||||
private T door(String name, String model, ResourceLocation bottom, ResourceLocation top) {
|
||||
return withExistingParent(name, BLOCK_FOLDER + "/" + model)
|
||||
.texture("bottom", bottom)
|
||||
.texture("top", top);
|
||||
}
|
||||
|
||||
protected T doorBottomLeft(String name, ResourceLocation bottom, ResourceLocation top) {
|
||||
return door(name, "door_bottom", bottom, top);
|
||||
}
|
||||
|
||||
protected T doorBottomRight(String name, ResourceLocation bottom, ResourceLocation top) {
|
||||
return door(name, "door_bottom_rh", bottom, top);
|
||||
}
|
||||
|
||||
protected T doorTopLeft(String name, ResourceLocation bottom, ResourceLocation top) {
|
||||
return door(name, "door_top", bottom, top);
|
||||
}
|
||||
|
||||
protected T doorTopRight(String name, ResourceLocation bottom, ResourceLocation top) {
|
||||
return door(name, "door_top_rh", bottom, top);
|
||||
}
|
||||
|
||||
protected T trapdoorBottom(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_trapdoor_bottom", texture);
|
||||
}
|
||||
|
||||
protected T trapdoorTop(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_trapdoor_top", texture);
|
||||
}
|
||||
|
||||
protected T trapdoorOpen(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_trapdoor_open", texture);
|
||||
}
|
||||
|
||||
protected T trapdoorOrientableBottom(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_orientable_trapdoor_bottom", texture);
|
||||
}
|
||||
|
||||
protected T trapdoorOrientableTop(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_orientable_trapdoor_top", texture);
|
||||
}
|
||||
|
||||
protected T trapdoorOrientableOpen(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_orientable_trapdoor_open", texture);
|
||||
}
|
||||
|
||||
protected T torch(String name, ResourceLocation torch) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/template_torch", "torch", torch);
|
||||
}
|
||||
|
||||
protected T torchWall(String name, ResourceLocation torch) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/torch_wall", "torch", torch);
|
||||
}
|
||||
|
||||
protected T carpet(String name, ResourceLocation wool) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/carpet", "wool", wool);
|
||||
}
|
||||
|
||||
protected ModelFile.ExistingModelFile getExistingFile(ResourceLocation path) {
|
||||
ModelFile.ExistingModelFile ret = new ModelFile.ExistingModelFile(extendWithFolder(path), existingFileHelper);
|
||||
ret.assertExistence();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void act(DirectoryCache cache) throws IOException {
|
||||
this.cache = cache;
|
||||
generatedModels.clear();
|
||||
registerModels();
|
||||
generateAll();
|
||||
this.cache = null;
|
||||
}
|
||||
|
||||
private void generateAll() {
|
||||
for (T model : generatedModels.values()) {
|
||||
Path target = getPath(model);
|
||||
try {
|
||||
IDataProvider.save(GSON, cache, model.toJson(), target);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Path getPath(T model) {
|
||||
ResourceLocation loc = model.getLocation();
|
||||
return generator.getOutputFolder().resolve("assets/" + loc.getNamespace() + "/models/" + loc.getPath() + ".json");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.state.IProperty;
|
||||
|
||||
public final class MultiPartBlockStateBuilder implements IGeneratedBlockstate {
|
||||
|
||||
private final List<PartBuilder> parts = new ArrayList<>();
|
||||
private final Block owner;
|
||||
|
||||
public MultiPartBlockStateBuilder(Block owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a builder for models to assign to a {@link PartBuilder}, which when
|
||||
* completed via {@link ConfiguredModel.Builder#addModel()} will assign the
|
||||
* resultant set of models to the part and return it for further processing.
|
||||
*
|
||||
* @return the model builder
|
||||
* @see ConfiguredModel.Builder
|
||||
*/
|
||||
public ConfiguredModel.Builder<PartBuilder> part() {
|
||||
return ConfiguredModel.builder(this);
|
||||
}
|
||||
|
||||
MultiPartBlockStateBuilder addPart(PartBuilder part) {
|
||||
this.parts.add(part);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject toJson() {
|
||||
JsonArray variants = new JsonArray();
|
||||
for (PartBuilder part : parts) {
|
||||
variants.add(part.toJson());
|
||||
}
|
||||
JsonObject main = new JsonObject();
|
||||
main.add("multipart", variants);
|
||||
return main;
|
||||
}
|
||||
|
||||
public class PartBuilder {
|
||||
public BlockStateProvider.ConfiguredModelList models;
|
||||
public boolean useOr;
|
||||
public final Multimap<IProperty<?>, Comparable<?>> conditions = HashMultimap.create();
|
||||
|
||||
PartBuilder(BlockStateProvider.ConfiguredModelList models) {
|
||||
this.models = models;
|
||||
}
|
||||
|
||||
public PartBuilder useOr() {
|
||||
this.useOr = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a condition for this part, which consists of a property and a set of
|
||||
* valid values. Can be called multiple times for multiple different properties.
|
||||
*
|
||||
* @param <T> the type of the property value
|
||||
* @param prop the property
|
||||
* @param values a set of valid values
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code prop} is {@code null}
|
||||
* @throws NullPointerException if {@code values} is {@code null}
|
||||
* @throws IllegalArgumentException if {@code values} is empty
|
||||
* @throws IllegalArgumentException if {@code prop} has already been configured
|
||||
* @throws IllegalArgumentException if {@code prop} is not applicable to the
|
||||
* current block's state
|
||||
*/
|
||||
@SafeVarargs
|
||||
public final <T extends Comparable<T>> PartBuilder condition(IProperty<T> prop, T... values) {
|
||||
Preconditions.checkNotNull(prop, "Property must not be null");
|
||||
Preconditions.checkNotNull(values, "Value list must not be null");
|
||||
Preconditions.checkArgument(values.length > 0, "Value list must not be empty");
|
||||
Preconditions.checkArgument(!conditions.containsKey(prop), "Cannot set condition for property \"%s\" more than once", prop.getName());
|
||||
Preconditions.checkArgument(canApplyTo(owner), "IProperty %s is not valid for the block %s", prop, owner);
|
||||
this.conditions.putAll(prop, Arrays.asList(values));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MultiPartBlockStateBuilder end() { return MultiPartBlockStateBuilder.this; }
|
||||
|
||||
JsonObject toJson() {
|
||||
JsonObject out = new JsonObject();
|
||||
if (!conditions.isEmpty()) {
|
||||
JsonObject when = new JsonObject();
|
||||
for (Entry<IProperty<?>, Collection<Comparable<?>>> e : conditions.asMap().entrySet()) {
|
||||
StringBuilder activeString = new StringBuilder();
|
||||
for (Object val : e.getValue()) {
|
||||
if (activeString.length() > 0)
|
||||
activeString.append("|");
|
||||
activeString.append(val.toString());
|
||||
}
|
||||
when.addProperty(e.getKey().getName(), activeString.toString());
|
||||
}
|
||||
if (useOr) {
|
||||
JsonObject innerWhen = when;
|
||||
when = new JsonObject();
|
||||
when.add("OR", innerWhen);
|
||||
}
|
||||
out.add("when", when);
|
||||
}
|
||||
out.add("apply", models.toJSON());
|
||||
return out;
|
||||
}
|
||||
|
||||
public boolean canApplyTo(Block b) {
|
||||
return b.getStateContainer().getProperties().containsAll(conditions.keySet());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,340 @@
|
|||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.state.IProperty;
|
||||
import net.minecraftforge.client.model.generators.BlockStateProvider.ConfiguredModelList;
|
||||
|
||||
/**
|
||||
* Builder for variant-type blockstates, i.e. non-multipart blockstates. Should
|
||||
* not be manually instantiated, instead use
|
||||
* {@link BlockStateProvider#getVariantBuilder(Block)}.
|
||||
* <p>
|
||||
* Variants can either be set via
|
||||
* {@link #setModels(PartialBlockstate, ConfiguredModel...)} or
|
||||
* {@link #addModels(PartialBlockstate, ConfiguredModel...)}, where model(s) can
|
||||
* be assigned directly to {@link PartialBlockstate partial states}, or builder
|
||||
* style via {@link #partialState()} and its subsequent methods.
|
||||
* <p>
|
||||
* This class also provides the convenience methods
|
||||
* {@link #forAllStates(Function)} and
|
||||
* {@link #forAllStatesExcept(Function, IProperty...)} for cases where the model
|
||||
* for each variant can be decided dynamically based on the state's property
|
||||
* values.
|
||||
*
|
||||
* @see BlockStateProvider
|
||||
*/
|
||||
public class VariantBlockStateBuilder implements IGeneratedBlockstate {
|
||||
|
||||
private final Block owner;
|
||||
private final Map<PartialBlockstate, ConfiguredModelList> models = new LinkedHashMap<>();
|
||||
private final Set<BlockState> coveredStates = new HashSet<>();
|
||||
|
||||
VariantBlockStateBuilder(Block owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public Map<PartialBlockstate, ConfiguredModelList> getModels() {
|
||||
return models;
|
||||
}
|
||||
|
||||
public Block getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject toJson() {
|
||||
List<BlockState> missingStates = Lists.newArrayList(owner.getStateContainer().getValidStates());
|
||||
missingStates.removeAll(coveredStates);
|
||||
Preconditions.checkState(missingStates.isEmpty(), "Blockstate for block %s does not cover all states. Missing: %s", owner, missingStates);
|
||||
JsonObject variants = new JsonObject();
|
||||
getModels().entrySet().stream()
|
||||
.sorted(Entry.comparingByKey(PartialBlockstate.comparingByProperties()))
|
||||
.forEach(entry -> variants.add(entry.getKey().toString(), entry.getValue().toJSON()));
|
||||
JsonObject main = new JsonObject();
|
||||
main.add("variants", variants);
|
||||
return main;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign some models to a given {@link PartialBlockstate partial state}.
|
||||
*
|
||||
* @param state The {@link PartialBlockstate partial state} for which to add
|
||||
* the models
|
||||
* @param models A set of models to add to this state
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code state} is {@code null}
|
||||
* @throws IllegalArgumentException if {@code models} is empty
|
||||
* @throws IllegalArgumentException if {@code state}'s owning block differs from
|
||||
* the builder's
|
||||
* @throws IllegalArgumentException if {@code state} partially matches another
|
||||
* state which has already been configured
|
||||
*/
|
||||
public VariantBlockStateBuilder addModels(PartialBlockstate state, ConfiguredModel... models) {
|
||||
Preconditions.checkNotNull(state, "state must not be null");
|
||||
Preconditions.checkArgument(models.length > 0, "Cannot set models to empty array");
|
||||
Preconditions.checkArgument(state.getOwner() == owner, "Cannot set models for a different block. Found: %s, Current: %s", state.getOwner(), owner);
|
||||
if (!this.models.containsKey(state)) {
|
||||
Preconditions.checkArgument(disjointToAll(state), "Cannot set models for a state for which a partial match has already been configured");
|
||||
this.models.put(state, new ConfiguredModelList(models));
|
||||
for (BlockState fullState : owner.getStateContainer().getValidStates()) {
|
||||
if (state.test(fullState)) {
|
||||
coveredStates.add(fullState);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.models.compute(state, ($, cml) -> cml.append(models));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign some models to a given {@link PartialBlockstate partial state},
|
||||
* throwing an exception if the state has already been configured. Otherwise,
|
||||
* simply calls {@link #addModels(PartialBlockstate, ConfiguredModel...)}.
|
||||
*
|
||||
* @param state The {@link PartialBlockstate partial state} for which to set
|
||||
* the models
|
||||
* @param models A set of models to assign to this state
|
||||
* @return this builder
|
||||
* @throws IllegalArgumentException if {@code state} has already been configured
|
||||
* @see #addModels(PartialBlockstate, ConfiguredModel...)
|
||||
*/
|
||||
public VariantBlockStateBuilder setModels(PartialBlockstate state, ConfiguredModel... model) {
|
||||
Preconditions.checkArgument(!models.containsKey(state), "Cannot set models for a state that has already been configured: %s", state);
|
||||
addModels(state, model);
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean disjointToAll(PartialBlockstate newState) {
|
||||
return coveredStates.stream().noneMatch(newState);
|
||||
}
|
||||
|
||||
public PartialBlockstate partialState() {
|
||||
return new PartialBlockstate(owner, this);
|
||||
}
|
||||
|
||||
public VariantBlockStateBuilder forAllStates(Function<BlockState, ConfiguredModel[]> mapper) {
|
||||
return forAllStatesExcept(mapper);
|
||||
}
|
||||
|
||||
public VariantBlockStateBuilder forAllStatesExcept(Function<BlockState, ConfiguredModel[]> mapper, IProperty<?>... ignored) {
|
||||
Set<PartialBlockstate> seen = new HashSet<>();
|
||||
for (BlockState fullState : owner.getStateContainer().getValidStates()) {
|
||||
Map<IProperty<?>, Comparable<?>> propertyValues = Maps.newLinkedHashMap(fullState.getValues());
|
||||
for (IProperty<?> p : ignored) {
|
||||
propertyValues.remove(p);
|
||||
}
|
||||
PartialBlockstate partialState = new PartialBlockstate(owner, propertyValues, this);
|
||||
if (seen.add(partialState)) {
|
||||
setModels(partialState, mapper.apply(fullState));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class PartialBlockstate implements Predicate<BlockState> {
|
||||
private final Block owner;
|
||||
private final SortedMap<IProperty<?>, Comparable<?>> setStates;
|
||||
@Nullable
|
||||
private final VariantBlockStateBuilder outerBuilder;
|
||||
|
||||
PartialBlockstate(Block owner, @Nullable VariantBlockStateBuilder outerBuilder) {
|
||||
this(owner, ImmutableMap.of(), outerBuilder);
|
||||
}
|
||||
|
||||
PartialBlockstate(Block owner, Map<IProperty<?>, Comparable<?>> setStates, @Nullable VariantBlockStateBuilder outerBuilder) {
|
||||
this.owner = owner;
|
||||
this.outerBuilder = outerBuilder;
|
||||
for (Map.Entry<IProperty<?>, Comparable<?>> entry : setStates.entrySet()) {
|
||||
IProperty<?> prop = entry.getKey();
|
||||
Comparable<?> value = entry.getValue();
|
||||
Preconditions.checkArgument(owner.getStateContainer().getProperties().contains(prop), "Property %s not found on block %s", entry, this.owner);
|
||||
Preconditions.checkArgument(prop.getAllowedValues().contains(value), "%s is not a valid value for %s", value, prop);
|
||||
}
|
||||
this.setStates = Maps.newTreeMap(Comparator.comparing(IProperty::getName));
|
||||
this.setStates.putAll(setStates);
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> PartialBlockstate with(IProperty<T> prop, T value) {
|
||||
Preconditions.checkArgument(!setStates.containsKey(prop), "Property %s has already been set", prop);
|
||||
Map<IProperty<?>, Comparable<?>> newState = new HashMap<>(setStates);
|
||||
newState.put(prop, value);
|
||||
return new PartialBlockstate(owner, newState, outerBuilder);
|
||||
}
|
||||
|
||||
private void checkValidOwner() {
|
||||
Preconditions.checkNotNull(outerBuilder, "Partial blockstate must have a valid owner to perform this action");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a builder for models to assign to this state, which when completed
|
||||
* via {@link ConfiguredModel.Builder#addModel()} will assign the resultant set
|
||||
* of models to this state.
|
||||
*
|
||||
* @return the model builder
|
||||
* @see ConfiguredModel.Builder
|
||||
*/
|
||||
public ConfiguredModel.Builder<VariantBlockStateBuilder> modelForState() {
|
||||
checkValidOwner();
|
||||
return ConfiguredModel.builder(outerBuilder, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add models to the current state's variant. For use when it is more convenient
|
||||
* to add multiple sets of models, as a replacement for
|
||||
* {@link #setModels(ConfiguredModel...)}.
|
||||
*
|
||||
* @param models The models to add.
|
||||
* @return {@code this}
|
||||
* @throws NullPointerException If the parent builder is {@code null}
|
||||
*/
|
||||
public PartialBlockstate addModels(ConfiguredModel... models) {
|
||||
checkValidOwner();
|
||||
outerBuilder.addModels(this, models);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this variant's models, and return the parent builder.
|
||||
*
|
||||
* @param models The models to set
|
||||
* @return The parent builder instance
|
||||
* @throws NullPointerException If the parent builder is {@code null}
|
||||
*/
|
||||
public VariantBlockStateBuilder setModels(ConfiguredModel... models) {
|
||||
checkValidOwner();
|
||||
return outerBuilder.setModels(this, models);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete this state without adding any new models, and return a new partial
|
||||
* state via the parent builder. For use after calling
|
||||
* {@link #addModels(ConfiguredModel...)}.
|
||||
*
|
||||
* @return A fresh partial state as specified by
|
||||
* {@link VariantBlockStateBuilder#partialState()}.
|
||||
* @throws NullPointerException If the parent builder is {@code null}
|
||||
*/
|
||||
public PartialBlockstate partialState() {
|
||||
checkValidOwner();
|
||||
return outerBuilder.partialState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PartialBlockstate that = (PartialBlockstate) o;
|
||||
return owner.equals(that.owner) &&
|
||||
setStates.equals(that.setStates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(owner, setStates);
|
||||
}
|
||||
|
||||
public Block getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public SortedMap<IProperty<?>, Comparable<?>> getSetStates() {
|
||||
return setStates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(BlockState blockState) {
|
||||
if (blockState.getBlock() != getOwner()) {
|
||||
return false;
|
||||
}
|
||||
for (Map.Entry<IProperty<?>, Comparable<?>> entry : setStates.entrySet()) {
|
||||
if (blockState.get(entry.getKey()) != entry.getValue()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder ret = new StringBuilder();
|
||||
for (Map.Entry<IProperty<?>, Comparable<?>> entry : setStates.entrySet()) {
|
||||
if (ret.length() > 0) {
|
||||
ret.append(',');
|
||||
}
|
||||
ret.append(entry.getKey().getName())
|
||||
.append('=')
|
||||
.append(entry.getValue());
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static Comparator<PartialBlockstate> comparingByProperties() {
|
||||
// Sort variants inversely by property values, to approximate vanilla style
|
||||
return (s1, s2) -> {
|
||||
SortedSet<IProperty<?>> propUniverse = new TreeSet<>(s1.getSetStates().comparator().reversed());
|
||||
propUniverse.addAll(s1.getSetStates().keySet());
|
||||
propUniverse.addAll(s2.getSetStates().keySet());
|
||||
for (IProperty<?> prop : propUniverse) {
|
||||
Comparable val1 = s1.getSetStates().get(prop);
|
||||
Comparable val2 = s2.getSetStates().get(prop);
|
||||
if (val1 == null && val2 != null) {
|
||||
return -1;
|
||||
} else if (val2 == null && val1 != null) {
|
||||
return 1;
|
||||
} else if (val1 != null && val2 != null){
|
||||
int cmp = val1.compareTo(val2);
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableList;
|
|||
import cpw.mods.modlauncher.TransformingClassLoader;
|
||||
import net.minecraft.util.registry.Bootstrap;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.model.generators.ExistingFileHelper;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.config.ConfigTracker;
|
||||
|
@ -103,6 +104,7 @@ public class ModLoader
|
|||
private final List<ModLoadingException> loadingExceptions;
|
||||
private final List<ModLoadingWarning> loadingWarnings;
|
||||
private GatherDataEvent.DataGeneratorConfig dataGeneratorConfig;
|
||||
private ExistingFileHelper existingFileHelper;
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private final Optional<Consumer<String>> statusConsumer = StartupMessageManager.modLoaderConsumer();
|
||||
|
||||
|
@ -265,17 +267,18 @@ public class ModLoader
|
|||
this.loadingWarnings.add(warning);
|
||||
}
|
||||
|
||||
public void runDataGenerator(final Set<String> mods, final Path path, final Collection<Path> inputs, final boolean serverGenerators, final boolean clientGenerators, final boolean devToolGenerators, final boolean reportsGenerator, final boolean structureValidator) {
|
||||
public void runDataGenerator(final Set<String> mods, final Path path, final Collection<Path> inputs, Collection<Path> existingPacks, final boolean serverGenerators, final boolean clientGenerators, final boolean devToolGenerators, final boolean reportsGenerator, final boolean structureValidator) {
|
||||
if (mods.contains("minecraft") && mods.size() == 1) return;
|
||||
LOGGER.info("Initializing Data Gatherer for mods {}", mods);
|
||||
Bootstrap.register();
|
||||
dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, serverGenerators, clientGenerators, devToolGenerators, reportsGenerator, structureValidator);
|
||||
existingFileHelper = new ExistingFileHelper(existingPacks, structureValidator);
|
||||
gatherAndInitializeMods(null);
|
||||
dispatchAndHandleError(LifecycleEventProvider.GATHERDATA, Runnable::run, null);
|
||||
dataGeneratorConfig.runAll();
|
||||
}
|
||||
|
||||
public Function<ModContainer, ModLifecycleEvent> getDataGeneratorEvent() {
|
||||
return mc -> new GatherDataEvent(mc, dataGeneratorConfig.makeGenerator(p->dataGeneratorConfig.getMods().size() == 1 ? p : p.resolve(mc.getModId()), dataGeneratorConfig.getMods().contains(mc.getModId())), dataGeneratorConfig);
|
||||
return mc -> new GatherDataEvent(mc, dataGeneratorConfig.makeGenerator(p->dataGeneratorConfig.getMods().size() == 1 ? p : p.resolve(mc.getModId()), dataGeneratorConfig.getMods().contains(mc.getModId())), dataGeneratorConfig, existingFileHelper);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ package net.minecraftforge.fml.event.lifecycle;
|
|||
|
||||
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraftforge.client.model.generators.ExistingFileHelper;
|
||||
import net.minecraftforge.fml.ModContainer;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
@ -34,14 +35,17 @@ public class GatherDataEvent extends ModLifecycleEvent
|
|||
{
|
||||
private final DataGenerator dataGenerator;
|
||||
private final DataGeneratorConfig config;
|
||||
public GatherDataEvent(final ModContainer modContainer, final DataGenerator dataGenerator, final DataGeneratorConfig dataGeneratorConfig)
|
||||
private final ExistingFileHelper existingFileHelper;
|
||||
public GatherDataEvent(final ModContainer modContainer, final DataGenerator dataGenerator, final DataGeneratorConfig dataGeneratorConfig, ExistingFileHelper existingFileHelper)
|
||||
{
|
||||
super(modContainer);
|
||||
this.dataGenerator = dataGenerator;
|
||||
this.config = dataGeneratorConfig;
|
||||
this.existingFileHelper = existingFileHelper;
|
||||
}
|
||||
|
||||
public DataGenerator getGenerator() { return this.dataGenerator; }
|
||||
public ExistingFileHelper getExistingFileHelper() { return existingFileHelper; }
|
||||
public boolean includeServer() { return this.config.server; }
|
||||
public boolean includeClient() { return this.config.client; }
|
||||
public boolean includeDev() { return this.config.dev; }
|
||||
|
|
|
@ -37,7 +37,11 @@ public net.minecraft.client.renderer.model.BlockModel field_178315_d # parent
|
|||
public net.minecraft.client.renderer.model.BlockModel field_178318_c # textures
|
||||
public net.minecraft.client.renderer.model.BlockModel field_178322_i # ambientOcclusion
|
||||
public net.minecraft.client.renderer.model.BlockModel func_187966_f()Ljava/util/List; # getOverrides
|
||||
public net.minecraft.client.renderer.model.BlockPart func_178236_a(Lnet/minecraft/util/Direction;)[F # getFaceUvs
|
||||
protected net.minecraft.client.renderer.model.ItemOverrideList <init>()V
|
||||
public net.minecraft.client.renderer.model.ItemTransformVec3f$Deserializer field_178362_a # ROTATION_DEFAULT
|
||||
public net.minecraft.client.renderer.model.ItemTransformVec3f$Deserializer field_178360_b # TRANSLATION_DEFAULT
|
||||
public net.minecraft.client.renderer.model.ItemTransformVec3f$Deserializer field_178361_c # SCALE_DEFAULT
|
||||
protected net.minecraft.client.renderer.model.ModelBakery field_177598_f # resourceManager
|
||||
protected net.minecraft.client.renderer.model.ModelBakery field_177602_b # LOCATIONS_BUILTIN_TEXTURES
|
||||
public net.minecraft.client.renderer.model.ModelBakery field_177604_a # MODEL_MISSING
|
||||
|
|
|
@ -19,18 +19,72 @@
|
|||
|
||||
package net.minecraftforge.debug;
|
||||
|
||||
import static net.minecraftforge.debug.DataGeneratorTest.MODID;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jline.utils.InputStreamReader;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.ObjectArrays;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.minecraft.advancements.Advancement;
|
||||
import net.minecraft.advancements.AdvancementRewards;
|
||||
import net.minecraft.advancements.FrameType;
|
||||
import net.minecraft.block.BarrelBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.DoorBlock;
|
||||
import net.minecraft.block.FenceBlock;
|
||||
import net.minecraft.block.FenceGateBlock;
|
||||
import net.minecraft.block.FurnaceBlock;
|
||||
import net.minecraft.block.LogBlock;
|
||||
import net.minecraft.block.PaneBlock;
|
||||
import net.minecraft.block.SlabBlock;
|
||||
import net.minecraft.block.StairsBlock;
|
||||
import net.minecraft.block.TrapDoorBlock;
|
||||
import net.minecraft.block.WallBlock;
|
||||
import net.minecraft.client.renderer.model.ItemCameraTransforms;
|
||||
import net.minecraft.client.renderer.model.ItemTransformVec3f;
|
||||
import net.minecraft.client.renderer.model.Variant;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.DirectoryCache;
|
||||
import net.minecraft.data.IFinishedRecipe;
|
||||
import net.minecraft.data.RecipeProvider;
|
||||
import net.minecraft.data.ShapedRecipeBuilder;
|
||||
import net.minecraft.resources.IResource;
|
||||
import net.minecraft.resources.ResourcePackType;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.StringTextComponent;
|
||||
import net.minecraftforge.client.model.generators.BlockStateProvider;
|
||||
import net.minecraftforge.client.model.generators.ConfiguredModel;
|
||||
import net.minecraftforge.client.model.generators.ExistingFileHelper;
|
||||
import net.minecraftforge.client.model.generators.ItemModelProvider;
|
||||
import net.minecraftforge.client.model.generators.ModelBuilder;
|
||||
import net.minecraftforge.client.model.generators.ModelBuilder.Perspective;
|
||||
import net.minecraftforge.client.model.generators.ModelFile;
|
||||
import net.minecraftforge.client.model.generators.ModelFile.UncheckedModelFile;
|
||||
import net.minecraftforge.client.model.generators.MultiPartBlockStateBuilder;
|
||||
import net.minecraftforge.client.model.generators.VariantBlockStateBuilder;
|
||||
import net.minecraftforge.common.crafting.ConditionalAdvancement;
|
||||
import net.minecraftforge.common.crafting.ConditionalRecipe;
|
||||
import net.minecraftforge.common.crafting.conditions.IConditionBuilder;
|
||||
|
@ -39,15 +93,29 @@ import net.minecraftforge.fml.common.Mod;
|
|||
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
|
||||
import net.minecraftforge.fml.event.lifecycle.GatherDataEvent;
|
||||
|
||||
@Mod("data_gen_test")
|
||||
@SuppressWarnings("deprecation")
|
||||
@Mod(MODID)
|
||||
@Mod.EventBusSubscriber(bus = Bus.MOD)
|
||||
public class DataGeneratorTest
|
||||
{
|
||||
static final String MODID = "data_gen_test";
|
||||
|
||||
private static final Gson GSON = new GsonBuilder()
|
||||
.registerTypeAdapter(Variant.class, new Variant.Deserializer())
|
||||
.registerTypeAdapter(ItemCameraTransforms.class, new ItemCameraTransforms.Deserializer())
|
||||
.registerTypeAdapter(ItemTransformVec3f.class, new ItemTransformVec3f.Deserializer())
|
||||
.create();
|
||||
|
||||
@SubscribeEvent
|
||||
public static void gatherData(GatherDataEvent event)
|
||||
{
|
||||
DataGenerator gen = event.getGenerator();
|
||||
|
||||
if (event.includeClient())
|
||||
{
|
||||
gen.addProvider(new ItemModels(gen, event.getExistingFileHelper()));
|
||||
gen.addProvider(new BlockStates(gen, event.getExistingFileHelper()));
|
||||
}
|
||||
if (event.includeServer())
|
||||
{
|
||||
gen.addProvider(new Recipes(gen));
|
||||
|
@ -107,4 +175,438 @@ public class DataGeneratorTest
|
|||
.build(consumer, ID);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ItemModels extends ItemModelProvider
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
public ItemModels(DataGenerator generator, ExistingFileHelper existingFileHelper)
|
||||
{
|
||||
super(generator, MODID, existingFileHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerModels()
|
||||
{
|
||||
getBuilder("test_generated_model")
|
||||
.parent(new UncheckedModelFile("item/generated"))
|
||||
.texture("layer0", mcLoc("block/stone"));
|
||||
|
||||
getBuilder("test_block_model")
|
||||
.parent(getExistingFile(mcLoc("block/block")))
|
||||
.texture("all", mcLoc("block/dirt"))
|
||||
.texture("top", mcLoc("block/stone"))
|
||||
.element()
|
||||
.cube("#all")
|
||||
.face(Direction.UP)
|
||||
.texture("#top")
|
||||
.tintindex(0)
|
||||
.end()
|
||||
.end();
|
||||
|
||||
// Testing consistency
|
||||
|
||||
// Test overrides
|
||||
ModelFile fishingRod = withExistingParent("fishing_rod", "handheld_rod")
|
||||
.texture("layer0", mcLoc("item/fishing_rod"))
|
||||
.override()
|
||||
.predicate(mcLoc("cast"), 1)
|
||||
.model(getExistingFile(mcLoc("item/fishing_rod_cast"))) // Use the vanilla model for validation
|
||||
.end();
|
||||
|
||||
withExistingParent("fishing_rod_cast", modLoc("fishing_rod"))
|
||||
.parent(fishingRod)
|
||||
.texture("layer0", mcLoc("item/fishing_rod_cast"));
|
||||
}
|
||||
|
||||
private static final Set<String> IGNORED_MODELS = ImmutableSet.of("test_generated_model", "test_block_model");
|
||||
|
||||
@Override
|
||||
public void act(DirectoryCache cache) throws IOException
|
||||
{
|
||||
super.act(cache);
|
||||
List<String> errors = testModelResults(this.generatedModels, existingFileHelper, IGNORED_MODELS.stream().map(s -> new ResourceLocation(MODID, folder + "/" + s)).collect(Collectors.toSet()));
|
||||
if (!errors.isEmpty()) {
|
||||
LOGGER.error("Found {} discrepancies between generated and vanilla item models: ", errors.size());
|
||||
for (String s : errors) {
|
||||
LOGGER.error(" {}", s);
|
||||
}
|
||||
throw new AssertionError("Generated item models differed from vanilla equivalents, check above errors.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "Forge Test Item Models";
|
||||
}
|
||||
}
|
||||
|
||||
public static class BlockStates extends BlockStateProvider
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
public BlockStates(DataGenerator gen, ExistingFileHelper exFileHelper)
|
||||
{
|
||||
super(gen, MODID, exFileHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerStatesAndModels()
|
||||
{
|
||||
// Unnecessarily complicated example to showcase how manual building works
|
||||
ModelFile birchFenceGate = fenceGate("birch_fence_gate", mcLoc("block/birch_planks"));
|
||||
ModelFile birchFenceGateOpen = fenceGateOpen("birch_fence_gate_open", mcLoc("block/birch_planks"));
|
||||
ModelFile birchFenceGateWall = fenceGateWall("birch_fence_gate_wall", mcLoc("block/birch_planks"));
|
||||
ModelFile birchFenceGateWallOpen = fenceGateWallOpen("birch_fence_gate_wall_open", mcLoc("block/birch_planks"));
|
||||
ModelFile invisbleModel = new UncheckedModelFile(new ResourceLocation("builtin/generated"));
|
||||
VariantBlockStateBuilder builder = getVariantBuilder(Blocks.BIRCH_FENCE_GATE);
|
||||
for (Direction dir : FenceGateBlock.HORIZONTAL_FACING.getAllowedValues()) {
|
||||
int angle = (int) dir.getHorizontalAngle();
|
||||
builder
|
||||
.partialState()
|
||||
.with(FenceGateBlock.HORIZONTAL_FACING, dir)
|
||||
.with(FenceGateBlock.IN_WALL, false)
|
||||
.with(FenceGateBlock.OPEN, false)
|
||||
.modelForState()
|
||||
.modelFile(invisbleModel)
|
||||
.nextModel()
|
||||
.modelFile(birchFenceGate)
|
||||
.rotationY(angle)
|
||||
.uvLock(true)
|
||||
.weight(100)
|
||||
.addModel()
|
||||
.partialState()
|
||||
.with(FenceGateBlock.HORIZONTAL_FACING, dir)
|
||||
.with(FenceGateBlock.IN_WALL, false)
|
||||
.with(FenceGateBlock.OPEN, true)
|
||||
.modelForState()
|
||||
.modelFile(birchFenceGateOpen)
|
||||
.rotationY(angle)
|
||||
.uvLock(true)
|
||||
.addModel()
|
||||
.partialState()
|
||||
.with(FenceGateBlock.HORIZONTAL_FACING, dir)
|
||||
.with(FenceGateBlock.IN_WALL, true)
|
||||
.with(FenceGateBlock.OPEN, false)
|
||||
.modelForState()
|
||||
.modelFile(birchFenceGateWall)
|
||||
.rotationY(angle)
|
||||
.uvLock(true)
|
||||
.addModel()
|
||||
.partialState()
|
||||
.with(FenceGateBlock.HORIZONTAL_FACING, dir)
|
||||
.with(FenceGateBlock.IN_WALL, true)
|
||||
.with(FenceGateBlock.OPEN, true)
|
||||
.modelForState()
|
||||
.modelFile(birchFenceGateWallOpen)
|
||||
.rotationY(angle)
|
||||
.uvLock(true)
|
||||
.addModel();
|
||||
}
|
||||
|
||||
// Realistic examples using helpers
|
||||
simpleBlock(Blocks.STONE, model -> ObjectArrays.concat(
|
||||
ConfiguredModel.allYRotations(model, 0, false),
|
||||
ConfiguredModel.allYRotations(model, 180, false),
|
||||
ConfiguredModel.class));
|
||||
|
||||
// From here on, models are 1-to-1 copies of vanilla (except for model locations) and will be tested as such below
|
||||
ModelFile block = getBuilder("block").transforms()
|
||||
.transform(Perspective.GUI)
|
||||
.rotation(30, 225, 0)
|
||||
.scale(0.625f)
|
||||
.end()
|
||||
.transform(Perspective.GROUND)
|
||||
.translation(0, 3, 0)
|
||||
.scale(0.25f)
|
||||
.end()
|
||||
.transform(Perspective.FIXED)
|
||||
.scale(0.5f)
|
||||
.end()
|
||||
.transform(Perspective.THIRDPERSON_RIGHT)
|
||||
.rotation(75, 45, 0)
|
||||
.translation(0, 2.5f, 0)
|
||||
.scale(0.375f)
|
||||
.end()
|
||||
.transform(Perspective.FIRSTPERSON_RIGHT)
|
||||
.rotation(0, 45, 0)
|
||||
.scale(0.4f)
|
||||
.end()
|
||||
.transform(Perspective.FIRSTPERSON_LEFT)
|
||||
.rotation(0, 225, 0)
|
||||
.scale(0.4f)
|
||||
.end()
|
||||
.end();
|
||||
|
||||
getBuilder("cube")
|
||||
.parent(block)
|
||||
.element()
|
||||
.allFaces((dir, face) -> face.texture("#" + dir.getName()).cullface(dir));
|
||||
|
||||
ModelFile furnace = orientable("furnace", mcLoc("block/furnace_side"), mcLoc("block/furnace_front"), mcLoc("block/furnace_top"));
|
||||
ModelFile furnaceLit = orientable("furnace_on", mcLoc("block/furnace_side"), mcLoc("block/furnace_front_on"), mcLoc("block/furnace_top"));
|
||||
|
||||
getVariantBuilder(Blocks.FURNACE)
|
||||
.forAllStates(state -> ConfiguredModel.builder()
|
||||
.modelFile(state.get(FurnaceBlock.LIT) ? furnaceLit : furnace)
|
||||
.rotationY((int) state.get(FurnaceBlock.FACING).getOpposite().getHorizontalAngle())
|
||||
.build()
|
||||
);
|
||||
|
||||
ModelFile barrel = cubeBottomTop("barrel", mcLoc("block/barrel_side"), mcLoc("block/barrel_bottom"), mcLoc("block/barrel_top"));
|
||||
ModelFile barrelOpen = cubeBottomTop("barrel_open", mcLoc("block/barrel_side"), mcLoc("block/barrel_bottom"), mcLoc("block/barrel_top_open"));
|
||||
directionalBlock(Blocks.BARREL, state -> state.get(BarrelBlock.PROPERTY_OPEN) ? barrelOpen : barrel); // Testing custom state interpreter
|
||||
|
||||
logBlock((LogBlock) Blocks.ACACIA_LOG);
|
||||
|
||||
stairsBlock((StairsBlock) Blocks.ACACIA_STAIRS, "acacia", mcLoc("block/acacia_planks"));
|
||||
slabBlock((SlabBlock) Blocks.ACACIA_SLAB, Blocks.ACACIA_PLANKS.getRegistryName(), mcLoc("block/acacia_planks"));
|
||||
|
||||
fenceBlock((FenceBlock) Blocks.ACACIA_FENCE, "acacia", mcLoc("block/acacia_planks"));
|
||||
fenceGateBlock((FenceGateBlock) Blocks.ACACIA_FENCE_GATE, "acacia", mcLoc("block/acacia_planks"));
|
||||
|
||||
wallBlock((WallBlock) Blocks.COBBLESTONE_WALL, "cobblestone", mcLoc("block/cobblestone"));
|
||||
|
||||
paneBlock((PaneBlock) Blocks.GLASS_PANE, "glass", mcLoc("block/glass"), mcLoc("block/glass_pane_top"));
|
||||
|
||||
doorBlock((DoorBlock) Blocks.ACACIA_DOOR, "acacia", mcLoc("block/acacia_door_bottom"), mcLoc("block/acacia_door_top"));
|
||||
trapdoorBlock((TrapDoorBlock) Blocks.ACACIA_TRAPDOOR, "acacia", mcLoc("block/acacia_trapdoor"), true);
|
||||
trapdoorBlock((TrapDoorBlock) Blocks.OAK_TRAPDOOR, "oak", mcLoc("block/oak_trapdoor"), false); // Test a non-orientable trapdoor
|
||||
|
||||
simpleBlock(Blocks.TORCH, torch("torch", mcLoc("block/torch")));
|
||||
horizontalBlock(Blocks.WALL_TORCH, torchWall("wall_torch", mcLoc("block/torch")), 90);
|
||||
}
|
||||
|
||||
// Testing the outputs
|
||||
|
||||
private static final Set<Block> IGNORED_BLOCKS = ImmutableSet.of(Blocks.BIRCH_FENCE_GATE, Blocks.STONE);
|
||||
private static final Set<ResourceLocation> IGNORED_MODELS = ImmutableSet.of();
|
||||
|
||||
private List<String> errors = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void act(DirectoryCache cache) throws IOException
|
||||
{
|
||||
super.act(cache);
|
||||
this.errors.addAll(testModelResults(this.generatedModels, existingFileHelper, IGNORED_MODELS));
|
||||
this.registeredBlocks.forEach((block, state) -> {
|
||||
if (IGNORED_BLOCKS.contains(block)) return;
|
||||
JsonObject generated = state.toJson();
|
||||
try {
|
||||
IResource vanillaResource = existingFileHelper.getResource(block.getRegistryName(), ResourcePackType.CLIENT_RESOURCES, ".json", "blockstates");
|
||||
JsonObject existing = GSON.fromJson(new InputStreamReader(vanillaResource.getInputStream()), JsonObject.class);
|
||||
if (state instanceof VariantBlockStateBuilder) {
|
||||
compareVariantBlockstates(block, generated, existing);
|
||||
} else if (state instanceof MultiPartBlockStateBuilder) {
|
||||
compareMultipartBlockstates(block, generated, existing);
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown blockstate type: " + state.getClass());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
LOGGER.error("Found {} discrepancies between generated and vanilla models/blockstates: ", errors.size());
|
||||
for (String s : errors) {
|
||||
LOGGER.error(" {}", s);
|
||||
}
|
||||
throw new AssertionError("Generated blockstates/models differed from vanilla equivalents, check above errors.");
|
||||
}
|
||||
}
|
||||
|
||||
private void compareVariantBlockstates(Block block, JsonObject generated, JsonObject vanilla) {
|
||||
JsonObject generatedVariants = generated.getAsJsonObject("variants");
|
||||
JsonObject vanillaVariants = vanilla.getAsJsonObject("variants");
|
||||
Stream.concat(generatedVariants.entrySet().stream(), vanillaVariants.entrySet().stream())
|
||||
.map(e -> e.getKey())
|
||||
.distinct()
|
||||
.forEach(key -> {
|
||||
JsonElement generatedVariant = generatedVariants.get(key);
|
||||
JsonElement vanillaVariant = vanillaVariants.get(key);
|
||||
if (generatedVariant.isJsonArray()) {
|
||||
compareArrays(block, "key " + key, "random variants", generatedVariant, vanillaVariant);
|
||||
for (int i = 0; i < generatedVariant.getAsJsonArray().size(); i++) {
|
||||
compareVariant(block, key + "[" + i + "]", generatedVariant.getAsJsonArray().get(i).getAsJsonObject(), vanillaVariant.getAsJsonArray().get(i).getAsJsonObject());
|
||||
}
|
||||
}
|
||||
if (generatedVariant.isJsonObject()) {
|
||||
if (!vanillaVariant.isJsonObject()) {
|
||||
blockstateError(block, "incorrectly does not have an array of variants for key %s", key);
|
||||
return;
|
||||
}
|
||||
compareVariant(block, key, generatedVariant.getAsJsonObject(), vanillaVariant.getAsJsonObject());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void compareVariant(Block block, String key, JsonObject generatedVariant, JsonObject vanillaVariant) {
|
||||
if (generatedVariant == null) {
|
||||
blockstateError(block, "missing variant for %s", key);
|
||||
return;
|
||||
}
|
||||
if (vanillaVariant == null) {
|
||||
blockstateError(block, "has extra variant %s", key);
|
||||
return;
|
||||
}
|
||||
String generatedModel = toVanillaModel(generatedVariant.get("model").getAsString());
|
||||
String vanillaModel = vanillaVariant.get("model").getAsString();
|
||||
if (!generatedModel.equals(vanillaModel)) {
|
||||
blockstateError(block, "has incorrect model \"%s\" for variant %s. Expecting: %s", generatedModel, key, vanillaModel);
|
||||
return;
|
||||
}
|
||||
generatedVariant.addProperty("model", generatedModel);
|
||||
// Parse variants to objects to handle default values in vanilla jsons
|
||||
Variant parsedGeneratedVariant = GSON.fromJson(generatedVariant, Variant.class);
|
||||
Variant parsedVanillaVariant = GSON.fromJson(vanillaVariant, Variant.class);
|
||||
if (!parsedGeneratedVariant.equals(parsedVanillaVariant)) {
|
||||
blockstateError(block, "has incorrect variant %s. Expecting: %s, Found: %s", key, vanillaVariant, generatedVariant);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void compareMultipartBlockstates(Block block, JsonObject generated, JsonObject vanilla) {
|
||||
JsonElement generatedPartsElement = generated.get("multipart");
|
||||
JsonElement vanillaPartsElement = vanilla.getAsJsonArray("multipart");
|
||||
compareArrays(block, "parts", "multipart", generatedPartsElement, vanillaPartsElement);
|
||||
// String instead of JSON types due to inconsistent hashing
|
||||
Multimap<String, String> generatedPartsByCondition = HashMultimap.create();
|
||||
Multimap<String, String> vanillaPartsByCondition = HashMultimap.create();
|
||||
|
||||
JsonArray generatedParts = generatedPartsElement.getAsJsonArray();
|
||||
JsonArray vanillaParts = vanillaPartsElement.getAsJsonArray();
|
||||
for (int i = 0; i < generatedParts.size(); i++) {
|
||||
JsonObject generatedPart = generatedParts.get(i).getAsJsonObject();
|
||||
String generatedCondition = toEquivalentString(generatedPart.get("when"));
|
||||
JsonElement generatedVariants = generatedPart.get("apply");
|
||||
if (generatedVariants.isJsonObject()) {
|
||||
correctVariant(generatedVariants.getAsJsonObject());
|
||||
} else if (generatedVariants.isJsonArray()) {
|
||||
for (int j = 0; j < generatedVariants.getAsJsonArray().size(); j++) {
|
||||
correctVariant(generatedVariants.getAsJsonArray().get(i).getAsJsonObject());
|
||||
}
|
||||
}
|
||||
generatedPartsByCondition.put(generatedCondition, toEquivalentString(generatedVariants));
|
||||
|
||||
JsonObject vanillaPart = vanillaParts.get(i).getAsJsonObject();
|
||||
String vanillaCondition = toEquivalentString(vanillaPart.get("when"));
|
||||
String vanillaVariants = toEquivalentString(vanillaPart.get("apply"));
|
||||
|
||||
vanillaPartsByCondition.put(vanillaCondition, vanillaVariants);
|
||||
}
|
||||
|
||||
Stream.concat(generatedPartsByCondition.keySet().stream(), vanillaPartsByCondition.keySet().stream())
|
||||
.distinct()
|
||||
.forEach(cond -> {
|
||||
Collection<String> generatedVariants = generatedPartsByCondition.get(cond);
|
||||
Collection<String> vanillaVariants = vanillaPartsByCondition.get(cond);
|
||||
if (generatedVariants.size() != vanillaVariants.size()) {
|
||||
if (vanillaVariants.isEmpty()) {
|
||||
blockstateError(block, " has extra condition %s", cond);
|
||||
} else if (generatedVariants.isEmpty()) {
|
||||
blockstateError(block, " is missing condition %s", cond);
|
||||
} else {
|
||||
blockstateError(block, " has differing amounts of variant lists matching condition %s. Expected: %d, Found: %d", cond, vanillaVariants.size(), generatedVariants.size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!vanillaVariants.containsAll(generatedVariants) || !generatedVariants.containsAll(vanillaVariants)) {
|
||||
List<String> extra = new ArrayList<>(generatedVariants);
|
||||
extra.removeAll(vanillaVariants);
|
||||
List<String> missing = new ArrayList<>(vanillaVariants);
|
||||
missing.removeAll(generatedVariants);
|
||||
if (!extra.isEmpty()) {
|
||||
blockstateError(block, " has extra variants for condition %s: %s", cond, extra);
|
||||
}
|
||||
if (!missing.isEmpty()) {
|
||||
blockstateError(block, " has missing variants for condition %s: %s", cond, missing);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Eliminate some formatting differences that are not meaningful
|
||||
private String toEquivalentString(JsonElement element) {
|
||||
return Objects.toString(element)
|
||||
.replaceAll("\"(true|false)\"", "$1") // Unwrap booleans in strings
|
||||
.replaceAll("\"(-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?)\"", "$1"); // Unwrap numbers in strings, regex from https://stackoverflow.com/questions/13340717/json-numbers-regular-expression
|
||||
}
|
||||
|
||||
private void correctVariant(JsonObject variant) {
|
||||
variant.addProperty("model", toVanillaModel(variant.get("model").getAsString()));
|
||||
}
|
||||
|
||||
private boolean compareArrays(Block block, String key, String name, JsonElement generated, JsonElement vanilla) {
|
||||
if (!vanilla.isJsonArray()) {
|
||||
blockstateError(block, "incorrectly has an array of %s for %s", name, key);
|
||||
return false;
|
||||
}
|
||||
JsonArray generatedArray = generated.getAsJsonArray();
|
||||
JsonArray vanillaArray = vanilla.getAsJsonArray();
|
||||
if (generatedArray.size() != vanillaArray.size()) {
|
||||
blockstateError(block, "has incorrect number of %s for %s. Expecting: %s, Found: %s", name, key, vanillaArray.size(), generatedArray.size());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void blockstateError(Block block, String fmt, Object... args) {
|
||||
errors.add("Generated blockstate for block " + block + " " + String.format(fmt, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Forge Test Blockstates";
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends ModelBuilder<T>> List<String> testModelResults(Map<ResourceLocation, T> models, ExistingFileHelper existingFileHelper, Set<ResourceLocation> toIgnore) {
|
||||
List<String> ret = new ArrayList<>();
|
||||
models.forEach((loc, model) -> {
|
||||
if (toIgnore.contains(loc)) return;
|
||||
JsonObject generated = model.toJson();
|
||||
if (generated.has("parent")) {
|
||||
generated.addProperty("parent", toVanillaModel(generated.get("parent").getAsString()));
|
||||
}
|
||||
try {
|
||||
IResource vanillaResource = existingFileHelper.getResource(new ResourceLocation(loc.getPath()), ResourcePackType.CLIENT_RESOURCES, ".json", "models");
|
||||
JsonObject existing = GSON.fromJson(new InputStreamReader(vanillaResource.getInputStream()), JsonObject.class);
|
||||
|
||||
JsonElement generatedDisplay = generated.remove("display");
|
||||
JsonElement vanillaDisplay = existing.remove("display");
|
||||
if (generatedDisplay == null && vanillaDisplay != null) {
|
||||
ret.add("Model " + loc + " is missing transforms");
|
||||
return;
|
||||
} else if (generatedDisplay != null && vanillaDisplay == null) {
|
||||
ret.add("Model " + loc + " has transforms when vanilla equivalent does not");
|
||||
return;
|
||||
} else if (generatedDisplay != null) { // Both must be non-null
|
||||
ItemCameraTransforms generatedTransforms = GSON.fromJson(generatedDisplay, ItemCameraTransforms.class);
|
||||
ItemCameraTransforms vanillaTransforms = GSON.fromJson(vanillaDisplay, ItemCameraTransforms.class);
|
||||
for (Perspective type : Perspective.values()) {
|
||||
if (!generatedTransforms.getTransform(type.vanillaType).equals(vanillaTransforms.getTransform(type.vanillaType))) {
|
||||
ret.add("Model " + loc + " has transforms that differ from vanilla equivalent for perspective " + type.name());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!existing.equals(generated)) {
|
||||
ret.add("Model " + loc + " does not match vanilla equivalent");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static String toVanillaModel(String model) {
|
||||
// We generate our own model jsons to test model building, but otherwise our blockstates should be identical
|
||||
// So remove modid to match
|
||||
return model.replaceAll("^\\w+:", "");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue