×
Menu

Cell to Nif file

 
Это старая версия скрипта, позволяющая выдирать игровые ячейки в ниф файлы.
При этом, со всем их содержимым, в т.ч. с мелкими объектами.
Выдираются, как интерьеры, так и экстерьеры.
Этого не умеет Цел2Ниф.
 
Создать текстовый файл, вставить текст, сохранить с расширением .PY
Выполнить посредством консоли и установленных в системе Pyffi утилит.
 
(С) Greatness7

#!python2
 
import os
from math import cos, sin
from subprocess import Popen, PIPE
from tempfile import NamedTemporaryFile
from pyffi.formats.nif import NifFormat
 
 
path = "C:\\Users\\Jad\\Games\\Morrowind\\Data Files\\"
 
 
perl = \
    """
    my $cell = "Balmora (-3, -2)";
 
    my %scan = map{$_, 1} (
        "ACTI",
        "ALCH",
        "APPA",
        "ARMO",
        "BODY",
        "BOOK",
        "CLOT",
        "CONT",
        "DOOR",
        "INGR",
        "LOCK",
        "MISC",
        "PROB",
        "REPA",
        "STAT",
        "WEAP",
        # "LIGH",
    );
 
    # extract x/y coords
    my($x, $y) = $cell =~ /\((-?\d+), (-?\d+)\)$/;
 
    # scan load order
    my(%refs, %mesh, %done);
    for my $plugin (reverse $T3->load_order) {
        my $input = open_for_input($plugin);
 
        # scan plugin records
        while (my $record = TES3::Record->new_from_input($input, undef, $plugin)) {
            my $type = $record->rectype;
 
            # make id/mesh dictionary
            if (exists $scan{$type}) {
                my $id = $record->decode->id;
 
                my $model = $record->get('MODL', 'model');
 
                $mesh{$id} = lc $model if defined $model;
            }
 
            # find specified cell
 
            next if $type ne 'CELL';
            next if $cell eq 'DONE';
 
            my $id = $record->decode->id;
 
            # filter unwanted cells
            if (defined $x and defined $y) {
                next if $record->is_interior;
                next if $id !~ /\($x, $y\)$/;
            } else {
                next if $id !~ lc $cell;
            }
 
            # loop through cell references
            for (map {@$_} $record->split_groups) {
                my %sub = map {substr(ref $_, 6), $_} @$_;
 
                next unless exists $sub{FRMR};
 
                my $key = $sub{FRMR}{objidx};
 
                # skip deleted refs
                if (exists $sub{DELE}) {
                    $refs{$key} = undef;
                }
 
                # skip removed indices
                next if exists $refs{$key};
 
                # center exterior coords
                unless ($record->is_interior) {
                    $sub{DATA}{x} -= ($x * 2 + 1) * 4096;
                    $sub{DATA}{y} -= ($y * 2 + 1) * 4096;
                }
 
                # save reference data
                $refs{$key} = [lc
                    $sub{NAME}{name},
                    $sub{XSCL}{scale} // 1,
                    $sub{DATA}{x},
                    $sub{DATA}{y},
                    $sub{DATA}{z},
                    $sub{DATA}{x_angle},
                    $sub{DATA}{y_angle},
                    $sub{DATA}{z_angle},
                ]
            }
 
            $cell = 'DONE';
        }
    }
 
    undef %done;
    local $" = ',';
 
    # scan references
    while (my($key, $data) = each %refs) {
        next unless $data;
 
        # skip no-model refs
        my $name = shift @$data;
        my $model = $mesh{$name};
        next unless defined $model;
 
        # replace backslashes
        $model =~ s/\\\/\\//g;
 
        # create model ref-array
        $done{$model} = [] unless exists $done{$model};
 
        # append ref data as python tuple
        push @{ $done{$model} },
            qq/("$name", @$data[0], [@$data[1..3]], [@$data[4..6]])/;
            # AKA name,  scale,     translation,    rotation
    }
 
 
    # send to python as dictionary
 
    print "{";
    while (my($mesh, $data) = each %done) {
        print qq/"$mesh":[@$data],/
    }
    print "}";
 
    exit
    """
 
 
try:
    os.chdir(path)
 
    f = NamedTemporaryFile(dir=path, suffix=".pl", delete=False)
    f.write(str.encode(perl))
    f.close()
 
    args = ["tes3cmd.exe", "modify", "-program", f.name]
    pipe = Popen(args, stdin=PIPE, stdout=PIPE)
 
    nifs = eval(pipe.communicate()[0])
 
except Exception as e:
    quit("ERROR! %s" % e)
 
finally:
    os.remove(f.name)
 
 
vec = NifFormat.Vector3()
mat = NifFormat.Matrix33()
 
parent = NifFormat.NiNode()
for file, refs in nifs.items():
 
    mesh = os.path.normpath(
        os.path.join(path, "meshes", file)
    )
 
    try: # read mesh data
        with open(mesh, 'rb') as stream:
            print "Reading Mesh:", file
 
            roots = []
            for i in range(len(refs)):
                data = NifFormat.Data()
                data.read(stream)
                stream.seek(0)
 
                root = data.roots[0]
                root.translation = vec
                root.rotation = mat
                root.scale = 1
 
                roots.append(root)
 
    except FileNotFoundError:
        print "Missing Mesh:", file
        continue
 
    for root, ref in zip(roots, refs):
 
        name, sca, loc, rot = ref
 
        node = NifFormat.NiNode()
        node.add_child(root)
 
        node.name = name
 
        node.scale = sca
 
        (node.translation.x,
         node.translation.y,
         node.translation.z) = loc
 
        for i, r in enumerate(rot):
            cr, sr = cos(r), sin(r)
 
            r = rot[i] = NifFormat.Matrix33()
 
            r.m_11 = ( 1,  cr,  cr ) [i]
            r.m_12 = ( 0,   0, -sr ) [i]
            r.m_13 = ( 0, -sr,   0 ) [i]
 
            r.m_21 = ( 0,   0, sr ) [i]
            r.m_22 = ( cr,  1, cr ) [i]
            r.m_23 = ( sr,  0,  0 ) [i]
 
            r.m_31 = ( 0, -sr, 0 ) [i]
            r.m_32 = ( sr,  0, 0 ) [i]
            r.m_33 = ( cr, cr, 1 ) [i]
 
        node.rotation = rot[0] * rot[1] * rot[2]
 
        parent.add_child(node)
 
 
if parent.children:
    out = os.path.join(path, "CELL.nif")
    print "Writing Mesh:", out
 
    with open(out, 'wb') as stream:
        data = NifFormat.Data()
        data.roots = [parent]
        data.write(stream)