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 {
|
resources {
|
||||||
srcDirs = ["$rootDir/src/test/resources"]
|
srcDirs = [
|
||||||
|
"$rootDir/src/test/resources",
|
||||||
|
"$rootDir/src/generated_test/resources"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
userdev {
|
userdev {
|
||||||
|
@ -308,7 +311,8 @@ project(':forge') {
|
||||||
source sourceSets.main
|
source sourceSets.main
|
||||||
source sourceSets.userdev
|
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 {
|
forge_test_data {
|
||||||
|
@ -321,7 +325,8 @@ project(':forge') {
|
||||||
tests { sources sourceSets.test }
|
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.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@@ -21,8 +22,10 @@
|
@@ -21,8 +22,11 @@
|
||||||
OptionSpec<Void> optionspec6 = optionparser.accepts("all", "Include all generators");
|
OptionSpec<Void> optionspec6 = optionparser.accepts("all", "Include all generators");
|
||||||
OptionSpec<String> optionspec7 = optionparser.accepts("output", "Output folder").withRequiredArg().defaultsTo("generated");
|
OptionSpec<String> optionspec7 = optionparser.accepts("output", "Output folder").withRequiredArg().defaultsTo("generated");
|
||||||
OptionSpec<String> optionspec8 = optionparser.accepts("input", "Input folder").withRequiredArg();
|
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<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(",");
|
+ OptionSpec<String> mod = optionparser.accepts("mod", "A modid to dump").withRequiredArg().withValuesSeparatedBy(",");
|
||||||
OptionSet optionset = optionparser.parse(p_main_0_);
|
OptionSet optionset = optionparser.parse(p_main_0_);
|
||||||
|
@ -19,7 +20,7 @@
|
||||||
Path path = Paths.get(optionspec7.value(optionset));
|
Path path = Paths.get(optionspec7.value(optionset));
|
||||||
boolean flag = optionset.has(optionspec6);
|
boolean flag = optionset.has(optionspec6);
|
||||||
boolean flag1 = flag || optionset.has(optionspec2);
|
boolean flag1 = flag || optionset.has(optionspec2);
|
||||||
@@ -30,10 +33,11 @@
|
@@ -30,10 +34,12 @@
|
||||||
boolean flag3 = flag || optionset.has(optionspec3);
|
boolean flag3 = flag || optionset.has(optionspec3);
|
||||||
boolean flag4 = flag || optionset.has(optionspec4);
|
boolean flag4 = flag || optionset.has(optionspec4);
|
||||||
boolean flag5 = flag || optionset.has(optionspec5);
|
boolean flag5 = flag || optionset.has(optionspec5);
|
||||||
|
@ -28,8 +29,9 @@
|
||||||
- }).collect(Collectors.toList()), flag1, flag2, flag3, flag4, flag5);
|
- }).collect(Collectors.toList()), flag1, flag2, flag3, flag4, flag5);
|
||||||
- datagenerator.func_200392_c();
|
- datagenerator.func_200392_c();
|
||||||
+ Collection<Path> inputs = optionset.valuesOf(optionspec8).stream().map(Paths::get).collect(Collectors.toList());
|
+ 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));
|
+ 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())
|
+ if (mods.contains("minecraft") || mods.isEmpty())
|
||||||
+ func_200264_a(mods.isEmpty() ? path : path.resolve("minecraft"), inputs, flag1, flag2, flag3, flag4, flag5).func_200392_c();
|
+ func_200264_a(mods.isEmpty() ? path : path.resolve("minecraft"), inputs, flag1, flag2, flag3, flag4, flag5).func_200392_c();
|
||||||
} else {
|
} 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
|
4fbaf6f4a3ea05cc071076e27f44ac81f9cc50e3 data\data_gen_test\advancements\conditional.json
|
||||||
ed4cbf1a3a2f5d8969f6346fdc9acdbe81d0c919 data\data_gen_test\recipes\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 cpw.mods.modlauncher.TransformingClassLoader;
|
||||||
import net.minecraft.util.registry.Bootstrap;
|
import net.minecraft.util.registry.Bootstrap;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.client.model.generators.ExistingFileHelper;
|
||||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||||
import net.minecraftforge.eventbus.api.Event;
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
import net.minecraftforge.fml.config.ConfigTracker;
|
import net.minecraftforge.fml.config.ConfigTracker;
|
||||||
|
@ -103,6 +104,7 @@ public class ModLoader
|
||||||
private final List<ModLoadingException> loadingExceptions;
|
private final List<ModLoadingException> loadingExceptions;
|
||||||
private final List<ModLoadingWarning> loadingWarnings;
|
private final List<ModLoadingWarning> loadingWarnings;
|
||||||
private GatherDataEvent.DataGeneratorConfig dataGeneratorConfig;
|
private GatherDataEvent.DataGeneratorConfig dataGeneratorConfig;
|
||||||
|
private ExistingFileHelper existingFileHelper;
|
||||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
private final Optional<Consumer<String>> statusConsumer = StartupMessageManager.modLoaderConsumer();
|
private final Optional<Consumer<String>> statusConsumer = StartupMessageManager.modLoaderConsumer();
|
||||||
|
|
||||||
|
@ -265,17 +267,18 @@ public class ModLoader
|
||||||
this.loadingWarnings.add(warning);
|
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;
|
if (mods.contains("minecraft") && mods.size() == 1) return;
|
||||||
LOGGER.info("Initializing Data Gatherer for mods {}", mods);
|
LOGGER.info("Initializing Data Gatherer for mods {}", mods);
|
||||||
Bootstrap.register();
|
Bootstrap.register();
|
||||||
dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, serverGenerators, clientGenerators, devToolGenerators, reportsGenerator, structureValidator);
|
dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, serverGenerators, clientGenerators, devToolGenerators, reportsGenerator, structureValidator);
|
||||||
|
existingFileHelper = new ExistingFileHelper(existingPacks, structureValidator);
|
||||||
gatherAndInitializeMods(null);
|
gatherAndInitializeMods(null);
|
||||||
dispatchAndHandleError(LifecycleEventProvider.GATHERDATA, Runnable::run, null);
|
dispatchAndHandleError(LifecycleEventProvider.GATHERDATA, Runnable::run, null);
|
||||||
dataGeneratorConfig.runAll();
|
dataGeneratorConfig.runAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Function<ModContainer, ModLifecycleEvent> getDataGeneratorEvent() {
|
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 cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
||||||
import net.minecraft.data.DataGenerator;
|
import net.minecraft.data.DataGenerator;
|
||||||
|
import net.minecraftforge.client.model.generators.ExistingFileHelper;
|
||||||
import net.minecraftforge.fml.ModContainer;
|
import net.minecraftforge.fml.ModContainer;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
@ -34,14 +35,17 @@ public class GatherDataEvent extends ModLifecycleEvent
|
||||||
{
|
{
|
||||||
private final DataGenerator dataGenerator;
|
private final DataGenerator dataGenerator;
|
||||||
private final DataGeneratorConfig config;
|
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);
|
super(modContainer);
|
||||||
this.dataGenerator = dataGenerator;
|
this.dataGenerator = dataGenerator;
|
||||||
this.config = dataGeneratorConfig;
|
this.config = dataGeneratorConfig;
|
||||||
|
this.existingFileHelper = existingFileHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataGenerator getGenerator() { return this.dataGenerator; }
|
public DataGenerator getGenerator() { return this.dataGenerator; }
|
||||||
|
public ExistingFileHelper getExistingFileHelper() { return existingFileHelper; }
|
||||||
public boolean includeServer() { return this.config.server; }
|
public boolean includeServer() { return this.config.server; }
|
||||||
public boolean includeClient() { return this.config.client; }
|
public boolean includeClient() { return this.config.client; }
|
||||||
public boolean includeDev() { return this.config.dev; }
|
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_178318_c # textures
|
||||||
public net.minecraft.client.renderer.model.BlockModel field_178322_i # ambientOcclusion
|
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.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
|
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_177598_f # resourceManager
|
||||||
protected net.minecraft.client.renderer.model.ModelBakery field_177602_b # LOCATIONS_BUILTIN_TEXTURES
|
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
|
public net.minecraft.client.renderer.model.ModelBakery field_177604_a # MODEL_MISSING
|
||||||
|
|
|
@ -19,18 +19,72 @@
|
||||||
|
|
||||||
package net.minecraftforge.debug;
|
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.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.Advancement;
|
||||||
import net.minecraft.advancements.AdvancementRewards;
|
import net.minecraft.advancements.AdvancementRewards;
|
||||||
import net.minecraft.advancements.FrameType;
|
import net.minecraft.advancements.FrameType;
|
||||||
|
import net.minecraft.block.BarrelBlock;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.Blocks;
|
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.DataGenerator;
|
||||||
|
import net.minecraft.data.DirectoryCache;
|
||||||
import net.minecraft.data.IFinishedRecipe;
|
import net.minecraft.data.IFinishedRecipe;
|
||||||
import net.minecraft.data.RecipeProvider;
|
import net.minecraft.data.RecipeProvider;
|
||||||
import net.minecraft.data.ShapedRecipeBuilder;
|
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.ResourceLocation;
|
||||||
import net.minecraft.util.text.StringTextComponent;
|
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.ConditionalAdvancement;
|
||||||
import net.minecraftforge.common.crafting.ConditionalRecipe;
|
import net.minecraftforge.common.crafting.ConditionalRecipe;
|
||||||
import net.minecraftforge.common.crafting.conditions.IConditionBuilder;
|
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.common.Mod.EventBusSubscriber.Bus;
|
||||||
import net.minecraftforge.fml.event.lifecycle.GatherDataEvent;
|
import net.minecraftforge.fml.event.lifecycle.GatherDataEvent;
|
||||||
|
|
||||||
@Mod("data_gen_test")
|
@SuppressWarnings("deprecation")
|
||||||
|
@Mod(MODID)
|
||||||
@Mod.EventBusSubscriber(bus = Bus.MOD)
|
@Mod.EventBusSubscriber(bus = Bus.MOD)
|
||||||
public class DataGeneratorTest
|
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
|
@SubscribeEvent
|
||||||
public static void gatherData(GatherDataEvent event)
|
public static void gatherData(GatherDataEvent event)
|
||||||
{
|
{
|
||||||
DataGenerator gen = event.getGenerator();
|
DataGenerator gen = event.getGenerator();
|
||||||
|
|
||||||
|
if (event.includeClient())
|
||||||
|
{
|
||||||
|
gen.addProvider(new ItemModels(gen, event.getExistingFileHelper()));
|
||||||
|
gen.addProvider(new BlockStates(gen, event.getExistingFileHelper()));
|
||||||
|
}
|
||||||
if (event.includeServer())
|
if (event.includeServer())
|
||||||
{
|
{
|
||||||
gen.addProvider(new Recipes(gen));
|
gen.addProvider(new Recipes(gen));
|
||||||
|
@ -107,4 +175,438 @@ public class DataGeneratorTest
|
||||||
.build(consumer, ID);
|
.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