1 # Copyright (C) 2010 Wildfire Games.
3 # Permission is hereby granted, free of charge, to any person obtaining
4 # a copy of this software and associated documentation files (the
5 # "Software"), to deal in the Software without restriction, including
6 # without limitation the rights to use, copy, modify, merge, publish,
7 # distribute, sublicense, and/or sell copies of the Software, and to
8 # permit persons to whom the Software is furnished to do so, subject to
9 # the following conditions:
11 # The above copyright notice and this permission notice shall be included
12 # in all copies or substantial portions of the Software.
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 # This is a fairly hacky converter from the .3do format used by Spring
23 # to the Collada + actor XML format used by 0 A.D.
32 use constant SCALE
=> 3.0 / 1048576;
34 my $ROOT = "../../../binaries/data/mods/public";
35 my $SOURCE = "../../../../../misc/spring/BA712";
43 read $fh, $b, 13*4 or die;
44 my ($sig, $num_verts, $num_prims, $sel_prim, $x, $y, $z,
45 $off_name, undef, $off_verts, $off_prims, $off_sib, $off_child) =
50 seek $fh, $off_name, 0;
53 read $fh, my $c, 1 or die;
58 seek $fh, $off_verts, 0;
59 read $fh, $b, 12*$num_verts or die;
60 my @verts = unpack 'l*', $b;
63 for my $p (0..$num_prims-1) {
64 seek $fh, $off_prims + 32*$p, 0;
65 read $fh, $b, 32 or die;
66 my ($palette, $num_idxs, undef, $off_idxs, $off_tex) = unpack 'l*', $b;
68 seek $fh, $off_idxs, 0;
69 read $fh, $b, 2*$num_idxs or die;
70 my @idxs = unpack 's*', $b;
75 seek $fh, $off_tex, 0;
77 read $fh, my $c, 1 or die;
94 offset
=> [$x, $y, $z],
98 seek $fh, $off_child, 0;
99 $ret->{children
} = [ parse_3do
($fh) ];
103 seek $fh, $off_sib, 0;
104 return ($ret, parse_3do
($fh));
112 my ($obj, $atlas, $name, $fh) = @_;
114 my $num_verts = @
{ $obj->{verts
} } / 3;
115 my $num_prims = @
{ $obj->{prims
} };
118 for (0..$num_verts-1) {
119 push @vertex_normals, [0,0,0];
123 for my $prim (@
{ $obj->{prims
} }) {
124 my @v0 = ($obj->{verts
}[$prim->{idxs
}[0]*3], $obj->{verts
}[$prim->{idxs
}[0]*3+1], $obj->{verts
}[$prim->{idxs
}[0]*3+2]);
125 my @v1 = ($obj->{verts
}[$prim->{idxs
}[1]*3], $obj->{verts
}[$prim->{idxs
}[1]*3+1], $obj->{verts
}[$prim->{idxs
}[1]*3+2]);
126 my @v2 = ($obj->{verts
}[$prim->{idxs
}[2]*3], $obj->{verts
}[$prim->{idxs
}[2]*3+1], $obj->{verts
}[$prim->{idxs
}[2]*3+2]);
127 my @d10 = ($v1[0] - $v0[0], $v1[1] - $v0[1], $v1[2] - $v0[2]);
128 my @d20 = ($v2[0] - $v0[0], $v2[1] - $v0[1], $v2[2] - $v0[2]);
129 my @x = ($d10[1]*$d20[2] - $d10[2]*$d20[1], $d10[2]*$d20[0] - $d10[0]*$d20[2], $d10[0]*$d20[1] - $d10[1]*$d20[0]);
130 my $d = sqrt($x[0]*$x[0] + $x[1]*$x[1] + $x[2]*$x[2]);
131 my @n = $d ?
($x[0]/$d, $x[1]/$d, $x[2]/$d) : (0,1,0);
132 push @prim_normals, \
@n;
133 for my $v (@
{ $prim->{idxs
} }) {
134 $vertex_normals[$v][0] += $n[0];
135 $vertex_normals[$v][1] += $n[1];
136 $vertex_normals[$v][2] += $n[2];
140 for (0..$num_verts-1) {
141 my @n = @
{ $vertex_normals[$_] };
142 my $d = sqrt($n[0]*$n[0] + $n[1]*$n[1] + $n[2]*$n[2]);
143 $vertex_normals[$_] = $d ?
[$n[0]/$d, $n[1]/$d, $n[2]/$d] : [0,0,0];
148 <geometry id="$name">
150 <source id="$name-Position">
151 <float_array id="$name-Position-array" count="@{[ $num_verts*3 ]}">
154 for my $n (0..$num_verts-1) {
155 printf $fh "%.6f %.6f %.6f\n", $obj->{verts
}[$n*3]*SCALE
, $obj->{verts
}[$n*3+1]*SCALE
, $obj->{verts
}[$n*3+2]*SCALE
;
161 <accessor source="#$name-Position-array" count="$num_verts" stride="3">
162 <param name="X" type="float"/>
163 <param name="Y" type="float"/>
164 <param name="Z" type="float"/>
168 <source id="$name-Normal0">
169 <float_array id="$name-Normal0-array" count="@{[ $num_verts*3 ]}">
172 for my $n (0..$num_verts-1) {
173 printf $fh "%.6f %.6f %.6f\n", @
{$vertex_normals[$n]};
177 for my $prim (@
{ $obj->{prims
} }) {
178 my ($u0,$v0, $u1,$v1) = $atlas->get_texcoords(texture_filename
($prim));
179 if (@
{ $prim->{idxs
} } == 3) {
180 push @uvs, $u0,$v0, $u0,$v1, $u1,$v1;
181 } elsif (@
{ $prim->{idxs
} } == 4) {
182 push @uvs, $u0,$v0, $u0,$v1, $u1,$v1, $u1,$v0;
184 push @uvs, ($u1,$v0) x @
{ $prim->{idxs
} };
187 my $num_uvs = @uvs/2;
192 <accessor source="#$name-Normal0-array" count="$num_verts" stride="3">
193 <param name="X" type="float"/>
194 <param name="Y" type="float"/>
195 <param name="Z" type="float"/>
199 <source id="$name-UV0">
200 <float_array id="$name-UV0-array" count="@{[ $num_uvs*2 ]}">
204 <accessor source="#$name-UV0-array" count="$num_uvs" stride="2">
205 <param name="S" type="float"/>
206 <param name="T" type="float"/>
210 <vertices id="$name-Vertex">
211 <input semantic="POSITION" source="#$name-Position"/>
213 <polygons count="$num_prims">
214 <input semantic="VERTEX" offset="0" source="#$name-Vertex"/>
215 <input semantic="NORMAL" offset="1" source="#$name-Normal0"/>
216 <input semantic="TEXCOORD" offset="2" set="0" source="#$name-UV0"/>
221 for my $prim (@
{ $obj->{prims
} }) {
223 for (@
{ $prim->{idxs
} }) {
224 print $fh "$_ $_ $j ";
235 </library_geometries>
241 my ($obj, $atlas, $name, $fh) = @_;
244 <?xml version="1.0" encoding="utf-8"?>
245 <COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.0">
247 <unit meter="0.025400"/>
248 <up_axis>Y_UP</up_axis>
252 write_geometries
($obj, $atlas, $name, $fh);
255 <library_visual_scenes>
256 <visual_scene id="RootNode" name="RootNode">
257 <node name="$name\_node">
258 <instance_geometry url="#$name"/>
261 </library_visual_scenes>
263 <instance_visual_scene url="#RootNode"/>
271 my ($joint, $fh, $indent) = @_;
273 print $fh qq[$indent<node id
="prop-$joint->{name}" name
="prop-$joint->{name}" type
="JOINT">\n];
274 print $fh qq[$indent <translate
>] . (sprintf "%.6f %.6f %.6f", map $_*SCALE
, $joint->{offset
}[0], $joint->{offset
}[1], -$joint->{offset
}[2]) . qq[</translate
>\n];
275 for my $c (@
{ $joint->{children
} }) {
276 write_joint
($c, $fh, "$indent ");
278 print $fh qq[$indent</node
>\n];
283 my ($skeleton, $rootobj, $atlas, $name, $fh) = @_;
286 <?xml version="1.0" encoding="utf-8"?>
287 <COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.0">
289 <unit meter="0.025400"/>
290 <up_axis>Y_UP</up_axis>
294 write_geometries
($rootobj, $atlas, $name, $fh);
297 <library_visual_scenes>
298 <visual_scene id="RootNode">
299 <node type="NODE" name="$name\_node">
300 <rotate>0 1.0 0 180</rotate>
301 <instance_geometry url="#$name"/>
304 write_joint
($_, $fh, " ") for @
$skeleton;
309 </library_visual_scenes>
311 <instance_visual_scene url="#RootNode"/>
317 sub extract_joint_names
320 return ($joint->{name
}, map { extract_joint_names
($_) } @
{ $joint->{children
} });
326 if ($prim->{texture
}) {
327 my $base = "$prim->{texture}";
328 my @files = ("${base}.tga", "${base}00.tga", "${base}00.bmp", "${base}00.BMP");
329 push @files, map { lc $_ } @files;
330 @files = grep { -e
"$SOURCE/unittextures/tatex/$_" } @files;
331 die "can't find $prim->{texture}" unless @files;
332 return "$SOURCE/unittextures/tatex/$files[0]";
334 return SpringPalette
::get_image
($prim->{palette
});
338 sub write_skeleton_actor
340 my ($skeleton, $name, $fh) = @_;
342 <?xml version="1.0" encoding="utf-8"?>
348 <variant frequency="100" name="Base">
349 <mesh>spring/$name.dae</mesh>
350 <texture>spring/$name.tga</texture>
352 <prop actor="props/units/weapons/spear_gold.xml" attachpoint="projectile"/>
355 my @joints = map { extract_joint_names
($_) } @
$skeleton;
356 for my $n (@joints[1..$#joints]) {
357 print $fh qq[ <prop actor
="spring/$name-$n.xml" attachpoint
="$n"/>\n];
365 <material>player_trans.xml</material>
373 my ($mesh, $prefix, $name, $fh) = @_;
376 <?xml version="1.0" encoding="utf-8"?>
382 <variant frequency="100" name="Base">
383 <mesh>spring/$name.dae</mesh>
384 <texture>spring/$prefix.tga</texture>
388 <material>player_trans.xml</material>
396 my ($prefix, $fh) = @_;
398 <?xml version="1.0" encoding="utf-8"?>
399 <Entity parent="template_unit_infantry_ranged">
402 <SpecificName>$prefix</SpecificName>
408 <WalkSpeed>9.0</WalkSpeed>
411 <Actor>spring/$prefix.xml</Actor>
415 if ($prefix =~ /^(armaas|armbats|corcrus)$/) {
418 <Floating>true</Floating>
430 my ($obj, $atlas) = @_;
433 my @prims = grep { @
{ $_->{idxs
} } >= 3 } @
{ $obj->{prims
} };
436 $atlas->add(texture_filename
($_));
439 extract_textures
($_, $atlas) for @
{ $obj->{children
} };
446 my @prims = grep { @
{ $_->{idxs
} } >= 3 } @
{ $obj->{prims
} };
449 push @meshes, { name
=> $obj->{name
}, verts
=> $obj->{verts
}, prims
=> \
@prims };
450 push @meshes, map { extract_meshes
($_) } @
{ $obj->{children
} };
457 return { name
=> $obj->{name
}, offset
=> $obj->{offset
}, children
=> [ map { extract_skeleton
($_) } @
{ $obj->{children
} } ] };
462 my ($prefix, $model) = @_;
464 my $atlas = new TextureAtlas
(256);
465 extract_textures
($model, $atlas);
466 $atlas->finish("$ROOT/art/textures/skins/spring/$prefix.tga");
468 my @meshes = extract_meshes
($model);
469 #print Dumper \@meshes;
473 my @skeleton = extract_skeleton
($model);
474 #print Dumper \@skeleton;
477 open my $fhm, '>', "$ROOT/art/meshes/spring/$prefix.dae" or die $!;
478 write_skeleton
(\
@skeleton, $meshes[0], $atlas, $prefix, $fhm);
480 open my $fha, '>', "$ROOT/art/actors/spring/$prefix.xml" or die $!;
481 write_skeleton_actor
(\
@skeleton, $prefix, $fha);
484 for my $mesh (@meshes[1..$#meshes]) {
485 my $name = $prefix.'-'.$mesh->{name
};
486 open my $fhm, '>', "$ROOT/art/meshes/spring/$name.dae" or die $!;
487 write_mesh
($mesh, $atlas, $name, $fhm);
489 open my $fha, '>', "$ROOT/art/actors/spring/$name.xml" or die $!;
490 write_mesh_actor
($mesh, $prefix, $name, $fha);
494 open my $fhe, '>', "$ROOT/simulation/templates/spring/$prefix.xml" or die $!;
495 write_entity
($prefix, $fhe);
499 #for my $n (qw(armpw armcom KrogTaar armlab corak corlab)) {
500 for my $n (map { (/.*\/(.*)\
.3do/)[0] } grep /\
/(cor|arm)[a-z]+\./, <$SOURCE/Objects3d/*.3do>) {
502 open my $t, "$SOURCE/Objects3d/$n.3do" or die "$n: $!";
504 my $model = parse_3do
($t);
505 #print Dumper $model;
506 write_model
($n, $model);