Skip to content

Instantly share code, notes, and snippets.

@desht
Last active February 23, 2022 11:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save desht/f004324d5a6a5510e733ba8695c17e38 to your computer and use it in GitHub Desktop.
Save desht/f004324d5a6a5510e733ba8695c17e38 to your computer and use it in GitHub Desktop.
Convmodel.pl : rough & ready Perl script to convert MC 1.16 Java entity models to MC 1.17+ format
#!/usr/bin/perl
#
# Usage: convmodel.pl < YourOldModel.java
#
# Looks for declarations & instatiations of ModelRenderer, then any calls to
# .setPos(), .addBox() etc. and converts those to MC 1.17+ equivalents
#
# Spits out a new constructor for your model class, and a static createBodyLayer()
# method suitable for using when you register your layer definitions in your
# EntityRenderersEvent.RegisterLayerDefinitions event listener
# Other methods in your model class (render/setupAnim/etc.) should not need to change much, if at all
#
# You'll also need to make a ModelLayers class containing all your model layer
# definitions - see https://github.com/TeamPneumatic/pnc-repressurized/blob/1.18.1/src/main/java/me/desht/pneumaticcraft/client/model/PNCModelLayers.java for an example.
#
use strict;
use String::CamelCase;
my $texW = 0;
my $texH = 0;
my %parts;
my @parts;
my $cls = "??";
while (<>) {
next if /^\/\//;
if (/public class (\w+)/) {
$cls = $1;
} elsif (/texWidth = (\d+)/) {
$texW = $1;
} elsif (/texHeight = (\d+)/) {
$texH = $1;
} elsif (/private final Model(?:Part|Renderer)\s+(.+?);/) {
for my $part (split(/,\s*/, $1)) {
$parts{$part} = { varname => uc($1), boxes => [], texX => 0, texY => 0, rot => [ 0, 0, 0] };
push(@parts, $part);
}
} elsif (/(\w+) = new Model(?:Part|Renderer)\((\d+),\s*(\d+),\s*(\d+),\s*(\d+)\)/) {
$texW = $2;
$texH = $3;
$parts{$1}{texX} = $4;
$parts{$1}{texY} = $5;
} elsif (/(\w+)\.setPos\((.+?)\)/) {
$parts{$1}{offset} = [ split(/,\s*/, $2) ];
} elsif (/(\w+)\.addChild\((\w+)\)/) {
$parts{$2}{parent} = $1;
$parts{$1}{hasChildren} = "yes";
} elsif (/(\w+)\.render\(/) {
$parts{$1}{render} = "yes";
} elsif (/setRotation(?:Angle)?\((.+?)\)/) {
my @f = split(/,\s*/, $1);
$parts{$f[0]}{rot} = [ @f[1..3] ];
} elsif (/(\w+)\.xRot = (.+?);/) {
$parts{$1}{rot}[0] = $2
} elsif (/(\w+)\.yRot = (.+?);/) {
$parts{$1}{rot}[1] = $2
} elsif (/(\w+)\.zRot = (.+?);/) {
$parts{$1}{rot}[2] = $2
} elsif (/(\w+)\.texOffs\((\d+),\s*(\d+)\).addBox\((.+?)\)/) {
my $part = $1;
my $offX = $2;
my $offY = $3;
my @f = split(/,\s*/, $4);
$f[3] =~ s/\..*//;
$f[4] =~ s/\..*//;
$f[5] =~ s/\..*//;
my $n = scalar @{ $parts{$part}{boxes} };
my @a = ("\"${part}_$n\"", @f[0..5]);
if (uc($f[6]) ne "0.0F") {
push(@a, "new CubeDeformation($f[6])");
}
push(@a, $offX, $offY);
push(@{ $parts{$part}{boxes} }, join(", ", @a));
if ($f[7] eq "true") {
$parts{$part}{mirror} = "yes";
}
} elsif (/(\w+)\.addBox\((.+?)\)/) {
my $part = $1;
my @f = split(/,\s*/, $2);
$f[3] =~ s/\..*//;
$f[4] =~ s/\..*//;
$f[5] =~ s/\..*//;
#print STDERR "part = $part\n";
my $n = scalar @{ $parts{$part}{boxes} };
push(@{ $parts{$part}{boxes} }, join(", ", "\"${part}_$n\"", @f[0..5]));
}
}
for my $k (@parts) {
my $u = uc($k);
print "private static final String $u = \"$k\";\n";
}
print "\n";
my $layer_name = $cls;
$layer_name =~ s/^Render//;
$layer_name = String::CamelCase::decamelize($layer_name);
$layer_name = uc($layer_name);
# set up for using a BER. You can adjust this to take an EntityRendererProvider.Context for entity rendering...
print <<EOT;
public $cls(BlockEntityRendererProvider.Context ctx) {
super(ctx);
ModelPart root = ctx.bakeLayer(ModelLayers.$layer_name);
EOT
for my $k (@parts) {
if ($parts{$k}{render}) {
my $u = uc($k);
print " $k = root.getChild($u);\n";
}
}
print "}\n\n";
print <<EOT;
public static LayerDefinition createBodyLayer() {
MeshDefinition meshdefinition = new MeshDefinition();
PartDefinition partdefinition = meshdefinition.getRoot();
EOT
for my $k (@parts) {
my $part = $parts{$k};
my $box = "\"$k\", " . join(", ", @{ $part->{boxes} });
my $pose = "ZERO";
if ($part->{offset} and ($part->{rot}[0] != 0 || $part->{rot}[1] != 0 || $part->{rot}[2] != 0)) {
$pose = "offsetAndRotation(" . join(", ", @{ $part->{offset} }) . ", " . join(", ", @{ $part->{rot} }) . ")";
} elsif ($part->{rot}[0] != 0 || $part->{rot}[1] != 0 || $part->{rot}[2] != 0) {
$pose = "rotation(" . join(", ", @{ $part->{rot} }) . ")";
} elsif ($part->{offset}) {
$pose = "offset(" . join(", ", @{ $part->{offset} }) . ")";
}
my $var = $part->{hasChildren} ? "PartDefinition $k = " : "";
my $parent = $part->{parent} ? $part->{parent} : "partdefinition";
print " $var$parent.addOrReplaceChild($part->{varname}, CubeListBuilder.create().texOffs($part->{texX}, $part->{texY})";
if (@{ $part->{boxes} }) {
print "\n .";
print join("\n .", map { "addBox($_)" } @{ $part->{boxes} });
if ($part->{mirror}) {
print "\n .mirror()";
}
print ",\n";
} else {
print ",\n";
}
print " PartPose.$pose);\n";
}
print "\n return LayerDefinition.create(meshdefinition, $texW, $texH);\n}\n";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment