Last active
November 1, 2022 14:02
-
-
Save ljfa-ag/cd137f5c741a0cfb0ead to your computer and use it in GitHub Desktop.
Script for converting exported Techne models for Minecraft into other model formats
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
# Released into Public Domain | |
# A quick and dirty script to convert ModelBase Java code as generated | |
# by Techne (or possibly Tabula) into Minecraft's JSON model format. | |
# It is quite limited (it does not handle rotations, as the JSON model format | |
# is limited in that regard anyway), but it is at least a help. | |
# Usage: | |
# modelbase_to_json input.java > output.json | |
# Note that Minecraft requires the texure to be square, so you might need to | |
# resize your texture | |
# If you instead want to convert to Wavefront OBJ, check out modelbase_to_obj.perl | |
# in this Gist below. | |
use strict; | |
use warnings; | |
use List::Util qw[max]; | |
my %parts; | |
while(<>) { | |
if(/(\w+) = new ModelRenderer\(this, (\d+), (\d+)\);/) { | |
$parts{$1}{base_uv} = [$2, $3]; | |
$parts{$1}{tex_size} = [64, 32]; | |
} | |
elsif(/(\w+)\.addBox\((-?\d*\.?\d*)[Ff]?, (-?\d*\.?\d*)[Ff]?, (-?\d*\.?\d*)[Ff]?, (\d+), (\d+), (\d+)(,.*)?\);/) { | |
$parts{$1}{offset} = [$2, $3, $4]; | |
$parts{$1}{size} = [$5, $6, $7]; | |
} | |
elsif(/(\w+)\.setRotationPoint\((-?\d*\.?\d*)[Ff]?, (-?\d*\.?\d*)[Ff]?, (-?\d*\.?\d*)[Ff]?\);/) { | |
$parts{$1}{rot_point} = [$2, $3, $4]; | |
} | |
elsif(/(\w+)\.setTextureSize\((\d+), (\d+)\);/) { | |
$parts{$1}{tex_size} = [$2, $3]; | |
} | |
elsif(/(\w+)\.mirror = (true|false);/) { | |
#$parts{$1}{mirror} = ($2 eq "true"); #FIXME: This is being ignored | |
} | |
elsif(/setRotation\((\w+), (-?\d*\.?\d*)[Ff]?, (-?\d*\.?\d*)[Ff]?, (-?\d*\.?\d*)[Ff]?\)/) { | |
#$parts{$1}{rotation} = [$2, $3, $4]; | |
#We ignore the rotation | |
if($2 != 0 || $3 != 0 || $4 != 0) { | |
print STDERR "Warning: Line $.: Nonzero rotations are not supported\n"; | |
} | |
} | |
else { | |
#print STDERR "Ignoring line $.\n"; | |
} | |
} | |
print qq({ | |
"elements": [ | |
); | |
my $first = 1; | |
foreach my $part (keys %parts) { | |
my $offset = $parts{$part}{offset}; | |
my $size = $parts{$part}{size}; | |
my $rot_point = $parts{$part}{rot_point}; | |
#Textures need to be square | |
my $tex_size = max($parts{$part}{tex_size}->[0], $parts{$part}{tex_size}->[1]); | |
my $base_uv = $parts{$part}{base_uv}; | |
my $xmin = 8 - $offset->[0] - $size->[0] - $rot_point->[0]; | |
my $ymin = 24 - $offset->[1] - $size->[1] - $rot_point->[1]; | |
my $zmin = 8 + $offset->[2] + $rot_point->[2]; | |
my $xmax = 8 - $offset->[0] - $rot_point->[0]; | |
my $ymax = 24 - $offset->[1] - $rot_point->[1]; | |
my $zmax = 8 + $offset->[2] + $size->[2] + $rot_point->[2]; | |
my @uvmin = ( | |
[$base_uv->[0] + $size->[2] + 2*$size->[0], $base_uv->[1]], #down | |
[$base_uv->[0] + $size->[2] + $size->[0], $base_uv->[1] + $size->[2]], #up | |
[$base_uv->[0] + $size->[2], $base_uv->[1] + $size->[2]], #north | |
[$base_uv->[0] + 2*$size->[2] + $size->[0], $base_uv->[1] + $size->[2]], #south | |
[$base_uv->[0] + $size->[2] + $size->[0], $base_uv->[1] + $size->[2]], #west | |
[$base_uv->[0], $base_uv->[1] + $size->[2]]); #east | |
my @uvmax = ( | |
[$uvmin[0][0] - $size->[0], $uvmin[0][1] + $size->[2]], #down | |
[$uvmin[1][0] - $size->[0], $uvmin[1][1] - $size->[2]], #up | |
[$uvmin[2][0] + $size->[0], $uvmin[2][1] + $size->[1]], #north | |
[$uvmin[3][0] + $size->[0], $uvmin[3][1] + $size->[1]], #south | |
[$uvmin[4][0] + $size->[2], $uvmin[4][1] + $size->[1]], #west | |
[$uvmin[5][0] + $size->[2], $uvmin[5][1] + $size->[1]]); #east | |
#Scale UVs from 0 to 16 with respect to texture size | |
foreach my $f (@uvmin) { | |
foreach (@$f) { | |
$_ = $_ / $tex_size * 16; | |
} | |
} | |
foreach my $f (@uvmax) { | |
foreach (@$f) { | |
$_ = $_ / $tex_size * 16; | |
} | |
} | |
print ",\n" unless $first; | |
$first = 0; | |
qq( { | |
"__comment": "$part", | |
"from": [$xmin, $ymin, $zmin], | |
"to": [$xmax, $ymax, $zmax], | |
"faces": { | |
"down": {"uv": [$uvmin[0][0], $uvmin[0][1], $uvmax[0][0], $uvmax[0][1]], "texture": "#all"}, | |
"up": {"uv": [$uvmin[1][0], $uvmin[1][1], $uvmax[1][0], $uvmax[1][1]], "texture": "#all"}, | |
"north": {"uv": [$uvmin[2][0], $uvmin[2][1], $uvmax[2][0], $uvmax[2][1]], "texture": "#all"}, | |
"south": {"uv": [$uvmin[3][0], $uvmin[3][1], $uvmax[3][0], $uvmax[3][1]], "texture": "#all"}, | |
"west": {"uv": [$uvmin[4][0], $uvmin[4][1], $uvmax[4][0], $uvmax[4][1]], "texture": "#all"}, | |
"east": {"uv": [$uvmin[5][0], $uvmin[5][1], $uvmax[5][0], $uvmax[5][1]], "texture": "#all"} | |
} | |
}); | |
} | |
print qq( | |
], | |
"textures": { | |
"all": "...", | |
"particle": "..." | |
} | |
} | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
# Released into Public Domain | |
# A quick and dirty script to convert ModelBase Java code as generated | |
# by Techne (or maybe Tabula) into Wavefront OBJ. | |
# It is limited, but it is at least a help. | |
# Usage: | |
# modelbase_to_obj input.java > output.obj | |
# Note that you need to create an MTL file in order to see the textures. | |
# By default, the model refers to "model.mtl". You might need to adapt | |
# the first few lines of the generated OBJ file. | |
# This is how an MTL file might look like: | |
# | |
# newmtl model | |
# map_Ka modid:texture | |
# map_Kd modid:texture | |
# Note that Minecraft requires the texure to be square, so you might need to | |
# resize your texture. | |
use strict; | |
use warnings; | |
use experimental qw[signatures]; | |
use List::Util qw[max]; | |
use Math::Trig; | |
use Math::BLAS; | |
my %parts; | |
while(<>) { | |
if(/(\w+) = new ModelRenderer\(this, (\d+), (\d+)\);/) { | |
$parts{$1}{base_uv} = [$2, $3]; | |
$parts{$1}{tex_size} = [64, 32]; | |
} | |
elsif(/(\w+)\.addBox\((-?\d*\.?\d*)[Ff]?, (-?\d*\.?\d*)[Ff]?, (-?\d*\.?\d*)[Ff]?, (\d+), (\d+), (\d+)(,.*)?\);/) { | |
$parts{$1}{offset} = [$2, $3, $4]; | |
$parts{$1}{size} = [$5, $6, $7]; | |
$parts{$1}{rotation} = [0, 0, 0]; | |
} | |
elsif(/(\w+)\.setRotationPoint\((-?\d*\.?\d*)[Ff]?, (-?\d*\.?\d*)[Ff]?, (-?\d*\.?\d*)[Ff]?\);/) { | |
$parts{$1}{rot_point} = [$2, $3, $4]; | |
} | |
elsif(/(\w+)\.setTextureSize\((\d+), (\d+)\);/) { | |
$parts{$1}{tex_size} = [$2, $3]; | |
} | |
elsif(/(\w+)\.mirror = (true|false);/) { | |
#$parts{$1}{mirror} = ($2 eq "true"); #FIXME: This is being ignored | |
} | |
elsif(/setRotation\((\w+), (-?\d*\.?\d*)[Ff]?, (-?\d*\.?\d*)[Ff]?, (-?\d*\.?\d*)[Ff]?\)/) { | |
$parts{$1}{rotation} = [$2, $3, $4]; | |
} | |
else { | |
#print STDERR "Ignoring line $.\n"; | |
} | |
} | |
my $vi = 0; #Vertex index | |
my $ti = 0; #UV index | |
print "# Generated by modelbase_to_obj | |
mtllib model.mtl | |
usemtl model\n\n"; | |
foreach my $part (keys %parts) { | |
my $offset = $parts{$part}{offset}; | |
my $size = $parts{$part}{size}; | |
my $rot_point = $parts{$part}{rot_point}; | |
my $rot = $parts{$part}{rotation}; | |
#Textures need to be square | |
my $tex_size = max($parts{$part}{tex_size}->[0], $parts{$part}{tex_size}->[1]); | |
my $base_uv = $parts{$part}{base_uv}; | |
#Vertices relative to the offset | |
my @verts = ( | |
[0, 0, 0], | |
[$size->[0], 0, 0], | |
[$size->[0], $size->[1], 0], | |
[0, $size->[1], 0], | |
[0, 0, $size->[2]], | |
[$size->[0], 0, $size->[2]], | |
[$size->[0], $size->[1], $size->[2]], | |
[0, $size->[1], $size->[2]]); | |
#Transform vertices | |
my @rot_mat = rot_matrix(@$rot); | |
foreach (@verts) { | |
blas_axpby(3, $offset, $_); | |
my @tmp; | |
blas_gemv(3, 3, \@rot_mat, $_, \@tmp); | |
blas_waxpby(3, $rot_point, \@tmp, $_); | |
$_ = [(8 - $_->[0])/16, (24 - $_->[1])/16, (8 + $_->[2])/16]; #Rotate around the z-axis by 180°. | |
#Strange but that's how it works. | |
} | |
#The texture sheet looks like this: | |
# 1 2 3 | |
# U D | |
# 4 5 6 | |
# | |
# 7 4 5 8 9 | |
# E N W S | |
# 10 11 12 13 14 | |
my @uvs = ( | |
[ $size->[2], 0], | |
[ $size->[2] + $size->[0], 0], | |
[ $size->[2] + 2*$size->[0], 0], | |
[ $size->[2], $size->[2]], | |
[ $size->[2] + $size->[0], $size->[2]], | |
[ $size->[2] + 2*$size->[0], $size->[2]], | |
[0, $size->[2]], | |
[2*$size->[2] + $size->[0], $size->[2]], | |
[2*$size->[2] + 2*$size->[0], $size->[2]], | |
[0, $size->[2] + $size->[1]], | |
[ $size->[2], $size->[2] + $size->[1]], | |
[ $size->[2] + $size->[0], $size->[2] + $size->[1]], | |
[2*$size->[2] + $size->[0], $size->[2] + $size->[1]], | |
[2*$size->[2] + 2*$size->[0], $size->[2] + $size->[1]]); | |
foreach (@uvs) { | |
$_->[0] += $base_uv->[0]; | |
$_->[1] += $base_uv->[1]; | |
$_->[0] /= $tex_size; | |
$_->[1] /= $tex_size; | |
} | |
#What a mess of indices. Here we go. | |
my @faces = ( | |
[$vi+3, $ti+6, $vi+4, $ti+5, $vi+8, $ti+2, $vi+7, $ti+3], #down | |
[$vi+6, $ti+2, $vi+5, $ti+1, $vi+1, $ti+4, $vi+2, $ti+5], #up | |
[$vi+2, $ti+5, $vi+1, $ti+4, $vi+4, $ti+11, $vi+3, $ti+12], #north | |
[$vi+5, $ti+9, $vi+6, $ti+8, $vi+7, $ti+13, $vi+8, $ti+14], #south | |
[$vi+6, $ti+8, $vi+2, $ti+5, $vi+3, $ti+12, $vi+7, $ti+13], #west | |
[$vi+1, $ti+4, $vi+5, $ti+7, $vi+8, $ti+10, $vi+4, $ti+11]);#east | |
print "g $part\n"; | |
foreach (@verts) { | |
print "v $_->[0] $_->[1] $_->[2]\n"; | |
} | |
foreach (@uvs) { | |
print "vt $_->[0] $_->[1]\n"; | |
} | |
foreach (@faces) { | |
print "f $_->[0]/$_->[1] $_->[2]/$_->[3] $_->[4]/$_->[5] $_->[6]/$_->[7]\n"; | |
} | |
print "\n"; | |
$vi += 8; | |
$ti += 14; | |
} | |
#Creates a rotation matrix from the specified Euler angles | |
sub rot_matrix($x, $y, $z) { | |
my @xrot = ( | |
1, 0, 0, | |
0, cos($x), -sin($x), | |
0, sin($x), cos($x)); | |
my @yrot = ( | |
cos($y), 0, sin($y), | |
0, 1, 0, | |
-sin($y), 0, cos($y)); | |
my @zrot = ( | |
cos($z), -sin($z), 0, | |
sin($z), cos($z), 0, | |
0, 0, 1); | |
my @tmp; | |
blas_gemm(3, 3, 3, \@zrot, \@yrot, \@tmp); | |
my @matrix; | |
blas_gemm(3, 3, 3, \@tmp, \@xrot, \@matrix); | |
return @matrix; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment