Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

ELU/ANI Blender Importer

Status
Not open for further replies.
Experienced Elementalist
Joined
Aug 27, 2006
Messages
223
Reaction score
183
Blender Importer

Blender 2.63a+

Installation: (Intentionally Complex)

  • Download and install Blender 2.63a+ from
  • Create an io_mesh_elu directory in Blender add-ons directory
  • Copy __init__.py and import_elu.py into the newly created io_mesh_elu directory
How-to:

Usage:

  1. Click File > Import > Gunz: The Duel (.elu)
  2. Select *.elu
  3. Click Ok
License:

Source Code:

Copy and save as __init__.py

Code:
# Copyright (c) 2008-2012 AJ
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

# <pep8 compliant>

bl_info = {
    'name': 'GunZ: The Duel/The Second Duel, RaiderZ ELU/ANI/XML formats',
    'author': 'AJ',
    'blender': (2, 5, 7),
    'location': 'File > Import',
    'description': 'Import ELU/ANI data',
    'warning': '',
    'wiki_url': '',
    'tracker_url': '',
    'support': 'COMMUNITY',
    'category': 'Import-Export'
}

# To support reload properly, try to access a package var, if it's there, reload everything
if 'bpy' in locals():
    import imp

    if 'import_elu' in locals():
        imp.reload(import_elu)

#    if 'import_ani' in locals():
#        imp.reload(import_ani)


import os
import bpy
import bpy_extras


ELU_FILE_EXTENSION = '*.elu'

#
#ANI_FILE_EXTENSION = '*.ani'


class BipMeshSettings(bpy.types.PropertyGroup):
    raw_world_matrix = bpy.props.FloatVectorProperty(
        name='raw_world_matrix',
        description='Raw World Matrix',
        default=(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
        options={'HIDDEN'},
        size=16,
        subtype='MATRIX'
    )

    raw_local_matrix = bpy.props.FloatVectorProperty(
        name='raw_local_matrix',
        description='Raw Local Matrix',
        default=(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
        options={'HIDDEN'},
        size=16,
        subtype='MATRIX'
    )

    parent_name = bpy.props.StringProperty(
        name="parent_name",
        description="Parent Name",
        default="",
        options={'HIDDEN'}
    )

    is_bip = bpy.props.BoolProperty()

#    is_parent_bip = bpy.props.BoolProperty()


class ImportELU(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
    '''Load a ELU file'''

    bl_idname = 'import_mesh.elu'

    bl_label = 'Import ELU'

    bl_options = {
        'UNDO'
    }

    files = bpy.props.CollectionProperty(
        name='File Path',
        description='File path used for importing the ELU file',
        type=bpy.types.OperatorFileListElement
    )

    directory = bpy.props.StringProperty()

    filter_glob = bpy.props.StringProperty(
        default=ELU_FILE_EXTENSION,
        options={
            'HIDDEN'
        }
    )

    def execute(self, context):
        paths = [os.path.join(self.directory, f.name) for f in self.files]

        if not paths:
            paths.append(self.filepath)

        from . import import_elu

        return import_elu.load_from_path(paths[0], context)

#
#class ImportANI(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
#    '''Load a ANI file'''
#
#    bl_idname = 'import_animation.ani'
#
#    bl_label = 'Import ANI'
#
#    bl_options = {
#        'UNDO'
#    }
#
#    files = bpy.props.CollectionProperty(
#        name='File Path',
#        description='File path used for importing the ANI file',
#        type=bpy.types.OperatorFileListElement
#    )
#
#    directory = bpy.props.StringProperty()
#
#    filter_glob = bpy.props.StringProperty(
#        default=ANI_FILE_EXTENSION,
#        options={
#            'HIDDEN'
#        }
#    )
#
#    def execute(self, context):
#        paths = [os.path.join(self.directory, f.name) for f in self.files]
#
#        if not paths:
#            paths.append(self.filepath)
#
#        from . import import_ani
#
#        return import_ani.load_from_path(paths[0], context)


def menu_func_import(self, context):
    self.layout.operator(ImportELU.bl_idname, text="GunZ: The Duel/The Second Duel, RaiderZ (.elu)")
#
#    self.layout.operator(ImportANI.bl_idname, text="Gunz: The Duel (.ani)")


def register():
    bpy.utils.register_class(BipMeshSettings)

    bpy.types.Mesh.bip_settings = bpy.props.PointerProperty(type=BipMeshSettings)

    bpy.utils.register_module(__name__, True)

    bpy.types.INFO_MT_file_import.append(menu_func_import)


def unregister():
    del bpy.types.Mesh.bip_settings

    bpy.utils.unregister_class(BipMeshSettings)

    bpy.utils.unregister_module(__name__)

    bpy.types.INFO_MT_file_import.remove(menu_func_import)


if __name__ == "__main__":
    register()

Copy and save as import_elu.py

Code:
# Copyright (c) 2008-2012 AJ
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

# <pep8 compliant>


import bpy
import bpy_extras
import io
import logging
import math
import mathutils
import os
import re
import struct


def enum(**attrs):
    return type('Enum', (frozenset,), attrs)(attrs.values())


ELU_MAGIC = 0x0107F060


ELU_VERSIONS = enum(
# GunZ: The Duel
    x11=0x11, 
    x5001=0x5001,
    x5002=0x5002,
    x5003=0x5003,
    x5004=0x5004,
    x5005=0x5005,
    x5006=0x5006,
    x5007=0x5007,
# GunZ: The Second Duel, RaiderZ
    x5008=0x5008,
    x500A=0x500A,
    x500B=0x500B,
    x500C=0x500C,
    x500E=0x500E,
    x500F=0x500F,
    x5010=0x5010,
    x5011=0x5011
)


NAME_LENGTH = 40


PATH_LENGTH = 256


FACE_VERTEX_COUNT = 3


BONE_INFLUENCE_COUNT = 4


SMOOTH_GROUP_COUNT = 32


ELU_BIP_REGEX = r'^Bip\d{2,}\s*(?P<side>L|R)?\s*(?P<name>[a-zA-Z]+)?(?P<index>\d+)?(?P<nub>[a-zA-Z]+)?$'


ELU_BIP_PROGRAM = re.compile(ELU_BIP_REGEX)


def elu_to_blender_name(name):
    def blend(match):
        if match.group('name') is not None:
            blend_format = "ZBip_{name}"
        else:
            blend_format = "ZBip_Root"

        if match.group('nub') is not None:
            blend_format += "_{nub}"

        if match.group('side') is not None:
            blend_format += ".{side}"

        if match.group('index') is not None:
            if len(match.group('index')) > 1 and \
               int(match.group('index')) < 10:
                blend_format += ".1{index:0>3}"
            elif int(match.group('index')) > 0:
                blend_format += ".{index:0>3}"

        return blend_format.format(**match.groupdict())

    return ELU_BIP_PROGRAM.subn(blend, name)


def load_elu_mesh_a(elu, version):
    '''GunZ: The Duel'''
    name = elu.read(NAME_LENGTH)

    parent_name = elu.read(NAME_LENGTH)

    mesh_name, is_bip = elu_to_blender_name(str(name, encoding='ascii').rstrip('\0'))

    parent_mesh_name, is_parent_bip = elu_to_blender_name(str(parent_name, encoding='ascii').rstrip('\0'))

    wm = struct.unpack('<16f', elu.read(64))

    world_matrix = mathutils.Matrix((
        (wm[0], wm[4], wm[8], wm[12]),
        (wm[2], wm[6], wm[10], wm[14]),
        (wm[1], wm[5], wm[9], wm[13]),
        (wm[3], wm[7], wm[11], wm[15])
    ))

    if version > ELU_VERSIONS.x11:
        sx, sy, sz = struct.unpack('<3f', elu.read(12))

        scale = mathutils.Vector((sx, sz, sy))
    else:
        scale = mathutils.Vector((1.0, 1.0, 1.0))

    scale_matrix = mathutils.Matrix.Scale(1.0, 4, scale)

    if version > ELU_VERSIONS.x5002:
        rx, ry, rz, ra = struct.unpack('<4f', elu.read(16))

        rotation_matrix = mathutils.Matrix.Rotation(ra, 4, mathutils.Vector((rx, rz, ry)))

        psx, psy, psz, psa = struct.unpack('<4f', elu.read(16))

        pivot_scale_matrix = mathutils.Matrix.Rotation(psa, 4,  mathutils.Vector((psx, psz, psy)))

        pm = struct.unpack('<16f', elu.read(64))

        pivot_matrix = mathutils.Matrix((
            (pm[0], pm[4], pm[8], pm[12]),
            (pm[2], pm[6], pm[10], pm[14]),
            (pm[1], pm[5], pm[9], pm[13]),
            (pm[3], pm[7], pm[11], pm[15])
        ))

    vertex_count = struct.unpack('<I', elu.read(4))[0]

    vertices = []

    for i in range(vertex_count):
        x, y, z = struct.unpack('<3f', elu.read(12))

        vertices.append((x, y, z))

    face_count = struct.unpack('<I', elu.read(4))[0]

    faces = []

    uv_faces = []

    smooth_groups = [[] for i in range(SMOOTH_GROUP_COUNT)]

    for i in range(face_count):
        v1, v2, v3 = struct.unpack('<3I', elu.read(12))

        if v3 == 0:
            faces.append((v3, v1, v2))
        else:
            faces.append((v1, v2, v3))

        face_uvs = []

        for j in range(FACE_VERTEX_COUNT):
            u, v, w = struct.unpack('<3f', elu.read(12))

            face_uvs.append((u, 1.0 - v)) # flip v, drop w

        if v3 == 0:
            uv_faces.append((face_uvs[2], face_uvs[0], face_uvs[1]))
        else:
            uv_faces.append(tuple(face_uvs))

        elu.seek(4, os.SEEK_CUR) # material index: 1 unsigned long integer

        if version > ELU_VERSIONS.x5001:
            smooth_group_index = struct.unpack('<I', elu.read(4))[0]

            smooth_groups[smooth_group_index].extend([v1, v2, v3])

    if version > ELU_VERSIONS.x5004:
        # face normals, skipped, it's not possible to set with blender api
        for i in range(face_count):
            elu.seek(12, os.SEEK_CUR) # face normal: 3 single precision float

            # vertex normals, skipped, recalculated using calc_normals method
            for j in range(FACE_VERTEX_COUNT):
                elu.seek(12, os.SEEK_CUR) # vertex normal: 3 single precision floats

    vertex_colors = []

    if version > ELU_VERSIONS.x5004:
        vertex_color_count = struct.unpack('<I', elu.read(4))[0]

        for i in range(vertex_color_count):
            r, g, b = struct.unpack('<3f', elu.read(12))

            vertex_colors.append((r, g, b))

    material_index, \
    bone_influence_count = struct.unpack('<2I', elu.read(8))

    weight_groups = {}

    for i in range(bone_influence_count):
        bone_names = []

        for j in range(BONE_INFLUENCE_COUNT):
            name = elu.read(NAME_LENGTH)

            bone_name = elu_to_blender_name(str(name, encoding='ascii').rstrip('\0'))[0]

            if len(bone_name) > 0:
                bone_names.append(bone_name)

        bone_weights = [w for w in struct.unpack('<4f', elu.read(16)) if w > 0.0]

        elu.seek(16, os.SEEK_CUR) # parent indices: 4 unsigned long integers

        elu.seek(4, os.SEEK_CUR) # weight count: 1 unsigned long integer

        for j in range(BONE_INFLUENCE_COUNT):
            elu.seek(12, os.SEEK_CUR) # offset vector: 3 single precision floats

        for j, bone_weight in enumerate(bone_weights):
            bone_name = bone_names[j]

            if bone_names[j] not in weight_groups:
                weight_groups[bone_name] = {}

            if bone_weight not in weight_groups[bone_name]:
                weight_groups[bone_name][bone_weight] = []

            weight_groups[bone_name][bone_weight].append(i)

    mesh_object = None

    if mesh_name not in bpy.data.meshes and \
       len(vertices) > 0 and \
       len(faces) > 0:
        mesh = bpy.data.meshes.new(mesh_name)

        mesh.bip_settings.is_bip = bool(is_bip)
        mesh.bip_settings.raw_world_matrix = [x for col in world_matrix.col[:] for x in col]

        local_matrix = mathutils.Matrix()
        local_matrix.identity()

        if parent_mesh_name in bpy.data.meshes:
            parent_mesh = bpy.data.meshes[parent_mesh_name]

            parent_world_matrix = parent_mesh.bip_settings.raw_world_matrix

            inverse_parent_world_matrix = parent_world_matrix.inverted()

            local_matrix = inverse_parent_world_matrix * world_matrix
        else:
            local_matrix = world_matrix

        mesh.bip_settings.raw_local_matrix = [x for col in local_matrix.col[:] for x in col]

        mesh.from_pydata(vertices, [], faces)

        image = None

        material_name = "z_material.{0:03}".format(material_index)

        if material_name in bpy.data.materials:
            material = bpy.data.materials[material_name]

            mesh.materials.append(material)

            texture = material.active_texture

            if texture is not None and \
               texture.type == 'IMAGE':
                image = texture.image

        if len(uv_faces) > 0:
            texture_face_layer = mesh.uv_textures.new('z_uv_texture')

            #texture_face_layer.active = True

            for i, texture_face in enumerate(texture_face_layer.data):
                if image is not None:
                    texture_face.image = image

            uv_layer = mesh.uv_layers.active.data[:]

            for fi, ply in enumerate(mesh.polygons):
                uv_layer[ply.loop_start].uv = uv_faces[fi][0]
                uv_layer[ply.loop_start + 1].uv = uv_faces[fi][1]
                uv_layer[ply.loop_start + 2].uv = uv_faces[fi][2]

        cloth = [[], [], {}]

        for i, (hold, collision, weight) in enumerate(vertex_colors):
            weight = 1.0 - weight

            if hold > 0.0:
                cloth[0].append(i)

            if collision > 0.0:
                cloth[1].append(i)

            if weight > 0.0:
                if weight not in cloth[2]:
                    cloth[2][weight] = []

                cloth[2][weight].append(i)

        mesh.show_double_sided = False
        mesh.show_normal_face = True
        mesh.show_normal_vertex = True

        mesh.calc_normals()

        mesh.validate()

        mesh.update()

        mesh_object = bpy.data.objects.new(mesh_name, mesh)

        if len(vertex_colors) > 0:
            cloth_modifier = mesh_object.modifiers.new('z_cloth', 'CLOTH')

            cloth_modifier.collision_settings.use_self_collision = True

            if len(cloth[0]) > 0:
                pinning_group = mesh_object.vertex_groups.new('z_cloth_pin')

                pinning_group.add(cloth[0], 1.0, 'ADD')

                cloth_modifier.settings.use_pin_cloth = True
                cloth_modifier.settings.vertex_group_mass = 'Zpin'

            stiff_group = mesh_object.vertex_groups.new('z_cloth_stiff')

            for w, v in cloth[2].items():
                stiff_group.add(v, w, 'ADD')

            cloth_modifier.settings.use_stiffness_scale = True
            cloth_modifier.settings.structural_stiffness = 0.5
            cloth_modifier.settings.structural_stiffness_max = 1.0
            cloth_modifier.settings.vertex_group_structural_stiffness = 'z_cloth_stiff'

        for i, vertices in enumerate(smooth_groups):
            if len(vertices) > 0:
                smooth_group_name = "z_smooth.{0:03}".format(i)

                smooth_group = mesh_object.vertex_groups.new(smooth_group_name)

                smooth_group.add(vertices, 1.0, 'ADD')

                smooth_modifier = mesh_object.modifiers.new(smooth_group_name, 'SMOOTH')

                smooth_modifier.factor = 0.0
                smooth_modifier.vertex_group = smooth_group_name

        for bone_name, bone_weights in weight_groups.items():
            weight_group = mesh_object.vertex_groups.new(bone_name)

            for bone_weight, vertices in bone_weights.items():
                weight_group.add(vertices, bone_weight, 'ADD')
    else:
        mesh_object = bpy.data.objects.new(mesh_name, None) # Empty

    if mesh_object is not None:
        #mesh_object.show_x_ray = bool(is_bip)

        if parent_mesh_name in bpy.data.objects:
            mesh_object.parent = bpy.data.objects[parent_mesh_name]

        mesh_object.matrix_world = world_matrix

        scene = bpy.context.scene

        scene.objects.link(mesh_object)

        scene.update()

    return mesh_object


def load_elu_mesh_b(elu, version):
    '''GunZ: The Second Duel and RaiderZ'''
    name_length = struct.unpack('<I', elu.read(4))[0]

    name = elu.read(name_length)

    parent_name_length = struct.unpack('<I', elu.read(4))[0]

    parent_name = elu.read(parent_name_length)

    mesh_name, is_bip = elu_to_blender_name(str(name, encoding='ascii').rstrip('\0'))

    parent_mesh_name, is_parent_bip = elu_to_blender_name(str(parent_name, encoding='ascii').rstrip('\0'))

    logging.info("Loading mesh: %s [%s], parent: %s [%s]", mesh_name, bool(is_bip), parent_mesh_name if parent_name_length > 0 else 'NIL', bool(is_parent_bip))

    parent_mesh_index = struct.unpack('<I', elu.read(4))[0]

    if version == ELU_VERSIONS.x5008:
        elu.seek(20, os.SEEK_CUR) # unknown
    else: 
        elu.seek(8, os.SEEK_CUR) # unknown

    lm = struct.unpack('<16f', elu.read(64))

    local_matrix = mathutils.Matrix((
        (lm[0], lm[4], lm[8], lm[12]),
        (lm[1], lm[5], lm[9], lm[13]),
        (lm[2], lm[6], lm[10], lm[14]),
        (lm[3], lm[7], lm[11], lm[15])
    ))

    if version >= ELU_VERSIONS.x500E and \
       version <= ELU_VERSIONS.x5010:
        elu.seek(12, os.SEEK_CUR) # unknown
    elif version > ELU_VERSIONS.x5008:
        elu.seek(4, os.SEEK_CUR) # unknown: 1 single precision floats

    vertex_position_count = struct.unpack('<I', elu.read(4))[0]

    vertex_positions = []

    logging.debug("loop %x", elu.tell())

    for i in range(vertex_position_count):
        px, py, pz = struct.unpack('<3f', elu.read(12))

        vertex_positions.append((px, py, pz))

    vertex_normal_count = struct.unpack('<I', elu.read(4))[0]

    vertex_normals = []

    logging.debug("looop %x", elu.tell())

    for i in range(vertex_normal_count):
        nx, ny, nz = struct.unpack('<3f', elu.read(12))

        vertex_normals.append((nx, ny, nz))

    unknown_count = struct.unpack('<I', elu.read(4))[0]

    skipped_length = 12

    if version > ELU_VERSIONS.x500E:
        skipped_length = 16

    logging.debug("loooop %x", elu.tell())

    for i in range(unknown_count):
        elu.seek(skipped_length, os.SEEK_CUR) # unknown: 3 single precision floats

    unknown_count = struct.unpack('<I', elu.read(4))[0]
    if version > ELU_VERSIONS.x500E and \
       unknown_count != 0:
        logging.warning("Oh no! %x", elu.tell())

        unknown_count = 0 # fix?

    logging.debug("looooop %x", elu.tell())

    for i in range(unknown_count):
        elu.seek(12, os.SEEK_CUR) # unknown: 3 single precision floats

    vertex_texcoord_count = struct.unpack('<I', elu.read(4))[0]

    vertex_texcoords = []

    logging.debug("loooooop %x", elu.tell())

    for i in range(vertex_texcoord_count):
        u, v, w = struct.unpack('<3f', elu.read(12))

        vertex_texcoords.append((u, 1.0 - v)) # flip v, drop w

    if version >= ELU_VERSIONS.x500E and \
       version != ELU_VERSIONS.x5010:
        unknown_count = struct.unpack('<I', elu.read(4))[0]

        logging.debug("looooooop+ %x", elu.tell())

        for i in range(unknown_count):
            elu.seek(12, os.SEEK_CUR) # unknown: 3 single precision floats

    unknown_count0 = struct.unpack('<I', elu.read(4))[0]
    face_count = unknown_count0 # version 500A

    logging.debug("loooooooop %x", elu.tell())

    if version > ELU_VERSIONS.x500A:
        if unknown_count0 > 0:
            elu.seek(8, os.SEEK_CUR) # unknown, 2 unsigned long integers

            for i in range(unknown_count0):
                unknown_count1 = struct.unpack('<I', elu.read(4))[0] # totaled = 1st unsigned long integer

                skipped_length = 12 # unknown: 1 unsigned short, 1 unsigned long, 3 unsigned short integers

                if version < ELU_VERSIONS.x500E:
                    skipped_length = 10 # unknown: 5 unsigned short integers

                for j in range(unknown_count1):
                    elu.seek(skipped_length, os.SEEK_CUR)
                elu.seek(2, os.SEEK_CUR) # unknown
    else:
        for i in range(unknown_count0):
            elu.seek(32, os.SEEK_CUR) # unknown

    unknown_count = struct.unpack('<I', elu.read(4))[0]

    logging.debug("looooooooop %x", elu.tell())

    for i in range(unknown_count):
        elu.seek(12, os.SEEK_CUR) # unknown
    elu.seek(4, os.SEEK_CUR) # unknown: 1 unsigned long integer

    blend_vertex_count = struct.unpack('<I', elu.read(4))[0]
    if version < ELU_VERSIONS.x500E and \
       blend_vertex_count != 0:
        logging.warning("Oh no! %x", elu.tell())

        blend_vertex_count = 0 # fix?

    bone_influences = {}
    logging.debug("loooooooooop %x", elu.tell())

    for i in range(blend_vertex_count):
        bone_influence_count = struct.unpack('<I', elu.read(4))[0]
        for j in range(bone_influence_count):
            elu.seek(2, os.SEEK_CUR) # unknown

            bone_index, bone_weight = struct.unpack('<Hf', elu.read(6))

            if bone_weight > 0.0:
                if bone_index not in bone_influences:
                    bone_influences[bone_index] = {}

                if bone_weight not in bone_influences[bone_index]:
                    bone_influences[bone_index][bone_weight] = []

                if i not in bone_influences[bone_index][bone_weight]:
                    bone_influences[bone_index][bone_weight].append(i)

    unknown_count = struct.unpack('<I', elu.read(4))[0]
    if version < ELU_VERSIONS.x500E and \
       unknown_count != 0:
        logging.warning("Oh no! %x", elu.tell())

        unknown_count = 0 # fix?

    logging.debug("looooooooooop %x", elu.tell())

    for i in range(unknown_count):
        elu.seek(64, os.SEEK_CUR) # unknown: 16 single precision floats = matrix

    for i in range(unknown_count):
        elu.seek(2, os.SEEK_CUR) # unknown: 1 unsigned short integer

    vertex_count = struct.unpack('<I', elu.read(4))[0]

    vertex_indices = []

    logging.debug("loooooooooooop %x", elu.tell())

    for i in range(vertex_count):
        vertex_position_index = vertex_normal_index = vertex_texcoord_index = vertex_unknown0_index = vertex_unknown1_index = 0

        if version < ELU_VERSIONS.x500E:
            vertex_position_index, \
            vertex_normal_index, \
            vertex_texcoord_index, \
            vertex_unknown0_index, \
            vertex_unknown1_index = struct.unpack('<5H', elu.read(10))
        elif version == ELU_VERSIONS.x500E:
            vertex_position_index, \
            vertex_normal_index, \
            vertex_unknown1_index, \
            vertex_unknown0_index, \
            vertex_texcoord_index = struct.unpack('<2HI2H', elu.read(12))
        else:
            vertex_position_index, \
            vertex_normal_index, \
            vertex_texcoord_index, \
            vertex_unknown0_index, \
            vertex_unknown1_index = struct.unpack('<2HI2H', elu.read(12))

        vertex_indices.append((vertex_position_index, vertex_normal_index, vertex_texcoord_index, vertex_unknown0_index, vertex_unknown1_index))

    if version > ELU_VERSIONS.x500A:
        elu.seek(4, os.SEEK_CUR) # unknown: 1 unsigned long integer

    face_index_count = 0

    if version > ELU_VERSIONS.x500A:
        face_index_count = struct.unpack('<I', elu.read(4))[0]

        face_count = int(face_index_count / 3)

    faces = []

    logging.debug("looooooooooooop %x", elu.tell())

    for i in range(face_count):
        face = [[], [], []]
    
        for y in struct.unpack('<3H', elu.read(6)):
            vertex_position_index = vertex_indices[y][0]
            vertex_normal_index = vertex_indices[y][1]
            vertex_texcoord_index = vertex_indices[y][2]

            face[0].append(vertex_position_index)
            face[1].append(vertex_normal_index)
            face[2].append(vertex_texcoord_index)
    
        faces.append(face)

    unknown_count = struct.unpack('<I', elu.read(4))[0]

    logging.debug("loooooooooooooop %x", elu.tell())

    for i in range(unknown_count):
        elu.seek(12, os.SEEK_CUR) # unknown

    if version >= ELU_VERSIONS.x500E:
        elu.seek(24, os.SEEK_CUR) # unknown

    mesh_object = None

    if mesh_name not in bpy.data.meshes:
        mesh = bpy.data.meshes.new(mesh_name)

        if mesh is not None:
            mesh.bip_settings.raw_local_matrix = [x for col in local_matrix.col[:] for x in col]

            if len(vertex_positions) > 0 and \
               len(faces) > 0:
                mesh.bip_settings.is_bip = False

                mesh.from_pydata(vertex_positions, [], [f[0] for f in faces])

                if version >= ELU_VERSIONS.x500B:
                    texture_face_layer = mesh.uv_textures.new('z_uv_texture')

                    for i, texture_face in enumerate(texture_face_layer.data):
                        pass # to-do: set uv image

                    uv_layer = mesh.uv_layers.active.data[:]

                    for fi, ply in enumerate(mesh.polygons):
                        uv_layer[ply.loop_start].uv = vertex_texcoords[faces[fi][2][0]]
                        uv_layer[ply.loop_start + 1].uv = vertex_texcoords[faces[fi][2][1]]
                        uv_layer[ply.loop_start + 2].uv = vertex_texcoords[faces[fi][2][2]]

                mesh.show_double_sided = False
                mesh.show_normal_face = True
                mesh.show_normal_vertex = True

                mesh.calc_normals()
            else:
                mesh.bip_settings.is_bip = True

                mesh.from_pydata(
                    [
                        (-0.5, 0.5, -0.5),
                        (-0.5, 0.5, 0.5),
                        (0.5, 0.5, 0.5),
                        (0.5, 0.5, -0.5),
                        (-0.5, -0.5, -0.5),
                        (-0.5, -0.5, 0.5),
                        (0.5, -0.5, 0.5),
                        (0.5, -0.5, -0.5)
                    ],
                    [
                        (0, 1),
                        (1, 2),
                        (2, 3),
                        (3, 0),
                        (4, 5),
                        (5, 6),
                        (6, 7),
                        (7, 4),
                        (0, 4),
                        (1, 5),
                        (2, 6),
                        (3, 7)
                    ],
                    []
                )

            mesh.validate()

            mesh.update()

            mesh_object = bpy.data.objects.new(mesh_name, mesh)

            if mesh_object is not None:
                if version > ELU_VERSIONS.x500B:
                    for bone_index, bone_weights in bone_influences.items():
                        weight_group = mesh_object.vertex_groups.new(str(bone_index))
    
                        for bone_weight, bone_vertices in bone_weights.items():
                            weight_group.add(bone_vertices, bone_weight, 'ADD')

                if parent_mesh_index != 0xFFFFFFFF and \
                   parent_mesh_name in bpy.data.objects:
                    mesh_object.parent = bpy.data.objects[parent_mesh_name]

                mesh_object.matrix_local = local_matrix

                scene = bpy.context.scene

                if scene is not None:
                    scene.objects.link(mesh_object)

                    scene.update()
    else:
        logging.error("WTF!")

        mesh_object = None

    return mesh_object


def load_elu_mesh(elu, version):
    mesh_object = None

    if version < ELU_VERSIONS.x5008:
        mesh_object = load_elu_mesh_a(elu, version) #
    else:
        mesh_object = load_elu_mesh_b(elu, version)

    return mesh_object


def load_elu_texture(elu, version):
    '''GunZ: The Duel'''
    name0 = None
    name1 = None

    if version < ELU_VERSIONS.x5006:
        name0 = elu.read(NAME_LENGTH)
        name1 = elu.read(NAME_LENGTH)
    else:
        name0 = elu.read(PATH_LENGTH)
        name1 = elu.read(PATH_LENGTH)

    image_name0 = str(name0, encoding='ascii').rstrip('\0')
    image_name1 = str(name1, encoding='ascii').rstrip('\0')

    texture_name0, texture_ext0 = os.path.splitext(image_name0)
    texture_name1, texture_ext1 = os.path.splitext(image_name1)

    texture = None

    if texture_name0 not in bpy.data.textures:
        texture = bpy.data.textures.new(texture_name0, 'IMAGE')

        texture_image = None

        if image_name0 not in bpy.data.images:
            fexts = [texture_ext0, '.dds', texture_ext0 + '.dds']

            fnames = [texture_name0]

            spaths = [os.path.dirname(elu.name), bpy.context.user_preferences.filepaths.texture_directory]

            fpaths = []

            for fname in fnames:
                for fext in fexts:
                    for spath in spaths:
                        fpath = os.path.join(os.path.normpath(spath), fname + fext)

                        if os.path.exists(fpath) is True:
                            fpaths.append(fpath)

            if len(fpaths) > 0:
                texture_image = bpy.data.images.load(fpaths[0])

            if texture_image is None:
                texture_image = bpy.data.images.new(fnames[0], 128, 128)
        else:
            texture_image = bpy.data.images[image_name0]

        if texture_image is not None:
            texture_image.mapping = 'UV'

            texture.image = texture_image
    else:
        texture = bpy.data.textures[texture_name0]

    return texture


def load_elu_material(elu, version):
    '''GunZ: The Duel'''
    index0, \
    index1 = struct.unpack('<2I', elu.read(8))

    ambient_color = struct.unpack('<4f', elu.read(16))
    diffuse_color = struct.unpack('<4f', elu.read(16))
    specular_color = struct.unpack('<4f', elu.read(16))
    specular_power = struct.unpack('<f', elu.read(4))[0]

    elu.seek(4, os.SEEK_CUR) # unknown: 1 unsigned long integer

    material_name = "z_material.{0:03}".format(index0)

    material = None
    
    if material_name not in bpy.data.materials:
        material = bpy.data.materials.new(material_name)

        material.diffuse_color = diffuse_color[:-1]
        material.alpha = diffuse_color[-1]
        material.specular_color = specular_color[:-1]
        material.specular_alpha = specular_color[-1]
        material.specular_intensity = specular_power

        material.use_shadeless = True
        material.use_mist = False
        material.use_raytrace = False
        material.use_face_texture = True
        material.use_face_texture_alpha = True
    else:
        material = bpy.data.materials[material_name]

    return material


def load_elu_textured_material(elu, version):
    '''GunZ: The Duel'''
    material = load_elu_material(elu, version)

    texture = load_elu_texture(elu, version)

    two_sided = 0

    if version > ELU_VERSIONS.x5001:
        two_sided = struct.unpack('<I', elu.read(4))[0]

    if version > ELU_VERSIONS.x5003:
        additive = struct.unpack('<I', elu.read(4))[0]

    alpha_percent = 0

    if version > ELU_VERSIONS.x5006:
        alpha_percent = struct.unpack('<I', elu.read(4))[0]

    if material is not None and \
       texture is not None:
        material_texture_slot = material.texture_slots.add()

        material_texture_slot.texture = texture

        material_texture_slot.texture_coords = 'UV'
        material_texture_slot.uv_layer = 'z_uv_texture'


def load_elu(elu, version):
    material_count, \
    mesh_count = struct.unpack('<2I', elu.read(8))

    if material_count > 0 and \
       version < ELU_VERSIONS.x5008:
        for i in range(material_count):
            load_elu_textured_material(elu, version)

    bip_mesh_objects = []

    z_mesh_objects = []

    names = [] # hack for GunZ: The Second Duel and RaiderZ

    for i in range(mesh_count):
        mesh_object = load_elu_mesh(elu, version)

        if mesh_object is not None:
            names.append(mesh_object.name)

            parent_mesh_object = mesh_object.parent

            if mesh_object.type == 'MESH':
                if mesh_object.data.bip_settings.is_bip is True:
                    bip_mesh_objects.append(mesh_object)
                else:
                    z_mesh_objects.append(mesh_object)

    scene = bpy.context.scene

    if len(bip_mesh_objects) > 0:
        armature = bpy.data.armatures.new('Armature')

        armature.use_mirror_x = False

        armature_object = bpy.data.objects.new('Armature', armature)

        armature_object.select = True

        scene.objects.link(armature_object)

        scene.update()

        scene.objects.active = armature_object

        bpy.ops.object.mode_set(mode='EDIT')

        for bip_mesh_object in bip_mesh_objects:
            bip_mesh = bip_mesh_object.data

            bone_name = bip_mesh_object.name if version < ELU_VERSIONS.x500E else str(names.index(bip_mesh_object.name))

            edit_bone = armature.edit_bones.new(bone_name)

            #edit_bone.use_connected = True
            #edit_bone.use_inherit_rotation = False
            #edit_bone.use_inherit_scale = False
            #edit_bone.use_local_location = False

            world_matrix = bip_mesh_object.matrix_world.copy()

            world_translation = world_matrix.to_translation()

            edit_bone.head.x = world_translation[0]
            edit_bone.head.y = world_translation[1]
            edit_bone.head.z = world_translation[2]

            edit_bone.tail = edit_bone.head + mathutils.Vector((0.0, 0.5, 0.0))

            bip_sibling_count = 0

            if bip_mesh_object.parent is not None and \
               bip_mesh_object.parent.data.bip_settings.is_bip is True:
                parent_bone_name = bip_mesh_object.parent.name if version < ELU_VERSIONS.x500E else str(names.index(bip_mesh_object.parent.name))

                edit_parent_bone = armature.edit_bones[parent_bone_name]

                edit_bone.parent = edit_parent_bone

                bip_sibling_count = sum(int(bip_sibling_mesh_object.data.bip_settings.is_bip) for bip_sibling_mesh_object in bip_mesh_object.parent.children)

                if bip_sibling_count == 1:
                    edit_parent_bone.tail = edit_bone.head

        bpy.ops.object.mode_set(mode='OBJECT')

        for z_mesh_object in z_mesh_objects:
            armature_modifier = z_mesh_object.modifiers.new('z_armature', 'ARMATURE')

            armature_modifier.object = armature_object

            if z_mesh_object.parent is None:
                z_mesh_object.parent = armature_object

        scene.update()


def load_from_path(path, context=None):
    logging.basicConfig(level=logging.DEBUG)

    logging.info("Opening file: %s", path)

    with io.open(path, mode='r+b') as elu:
        magic, \
        version = struct.unpack('<2I', elu.read(8))

        if magic != ELU_MAGIC or \
           version not in ELU_VERSIONS:
            logging.error('File not supported')

            return {
                'CANCELLED'
            }

        logging.debug("Version: %x", version)

        load_elu(elu, version)

    return {
        'FINISHED'
    }

Blender 2.49b

Installation:

  1. Download and install Blender 2.49b from
  2. Download and install Python 2.7.x from
  3. Copy gunz_import.py into your Blender scripts directory.

Usage:

  1. Click File > Import > Gunz (*.elu, *.ani)...
  2. Select *.elu/*.ani file
  3. Click Ok
License:

Source Code:

Copy and save as gunz_import.py

Code:
#!BPY
# Copyright (c) 2008-2012 AJ
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


"""
Name: 'GUNZ (*.elu, *.ani)...'
Blender: 246
Group: 'Import'
Tooltip: 'Import GUNZ *.elu and/or *.ani game files'
"""

__author__ = 'AJ'
__email__ = ''
__url__ = ('blender', 'elysiun', 'Project homepage, http://www.ragezone.com/')
__version__ = '11.11.05'
__bpydoc__ = """ \
This script imports GUNZ *.elu and/or *.ani game files.
"""


import Blender
import bpy
import math
import struct


ELU_VERSIONS = (0x5004, 0x5005, 0x5006, 0x5007)

ANI_VERSIONS = (0x12, 0x1001, 0x1002, 0x1003)

ANI_FORMAT = 2

DOT_DDS = '.dds'

SOFT_BODY_GROUP = 'SoftBody'

MAX_INFLUENCES = 4

FACE_VERTEX_COUNT = 3


class MagicError(ValueError):
    pass


class VersionError(ValueError):
    pass


class FormatError(ValueError):
    pass


def elu_to_blender_name(name):
    if name.startswith('Bip01 L ') is True:
        return "%s %s.L" % ('Bip01', name[8:])

    if name.startswith('Bip01 R ') is True:
        return "%s %s.R" % ('Bip01', name[8:])

    return name


def elu_read_material(file_object):
    high_index, \
    low_index = struct.unpack('<2I', file_object.read(8))

    print "material %d-%d start 0x%x" % (high_index, low_index, file_object.tell() - 8)

    ambient_color = struct.unpack('<4f', file_object.read(16))
    diffuse_color = struct.unpack('<4f', file_object.read(16))
    specular_color = struct.unpack('<4f', file_object.read(16))
    specular_power = struct.unpack('<f', file_object.read(4))[0]

    unknown_0 = struct.unpack('<I', file_object.read(4))[0]

    material_name = "Mat.%03d" % high_index

    material = None

    try:
        material = Blender.Material.Get(material_name)
    except:
        material = Blender.Material.New(material_name)

        if material is not None:
            material.setRGBCol(diffuse_color[0:3])
            material.setAlpha(diffuse_color[3])
            material.setSpecCol(specular_color[0:3])
            material.setSpec(specular_power)

    print "material %d-%d end 0x%x" % (high_index, low_index, file_object.tell())

    return material


def elu_search_for_path(search_names, search_extensions, search_paths):
    for search_path in search_paths:
        search_path_0 = Blender.sys.cleanpath(search_path)

        if Blender.sys.exists(search_path_0) == 2:
            for search_name in search_names:
                search_path_1 = Blender.sys.join(search_path_0, search_name)

                for search_extension in search_extensions:
                    search_path_2 = Blender.sys.makename(search_path_1, search_extension)

                    if Blender.sys.exists(search_path_2) == 1:
                        return search_path_2

    return None


def elu_load_image(image_path):
    image = None

    image_name = Blender.sys.makename(image_path, strip=1)

    try:
        image = Blender.Image.Get(image_name)
    except:
        try:
            image = Blender.Image.Load(image_path)
        except:
            Blender.Draw.PupMenu("Warning%%t|Could not load %s" % image_path)
        else:
            image.setName(image_name)

    return image


def elu_read_texture(file_object, version):
    texture_name_1 = ''
    texture_name_2 = ''

    if version == 0x5004 or version == 0x5005:
        texture_name_1 = file_object.read(40)
        texture_name_2 = file_object.read(40)
    elif version == 0x5006 or version == 0x5007:
        texture_name_1 = file_object.read(256)
        texture_name_2 = file_object.read(256)

    texture_name_1 = texture_name_1[0:texture_name_1.find('\0')]

    if version == 0x5004 or version == 0x5005:
        print "texture %s start 0x%x" % (texture_name_1, file_object.tell() - 40)
    elif version == 0x5006 or version == 0x5007:
        print "texture %s start 0x%x" % (texture_name_1, file_object.tell() - 256)

    texture_name_1, texture_extension_1 = Blender.sys.splitext(texture_name_1)

    texture = None

    try:
        texture = Blender.Texture.Get(texture_name_1)
    except:
        texture = Blender.Texture.New(texture_name_1)

        if texture is not None:
            search_names = [texture_name_1]

            search_extensions = [texture_extension_1, DOT_DDS, texture_extension_1 + DOT_DDS]

            search_paths = [Blender.sys.dirname(file_object.name), Blender.Get('texturesdir'), '//']

            found_path = elu_search_for_path(search_names, search_extensions, search_paths)

            if found_path is not None:
                image = elu_load_image(found_path)

                if image is not None:
                    texture.setType('Image')
                    texture.setImage(image)
                    texture.setImageFlags('UseAlpha')
                    texture.setExtend('Clip')
            else:
                Blender.Draw.PupMenu("Warning%%t|Could not locate %s" % Blender.sys.makename(texture_name_1, texture_extension_1))

    print "texture %s end 0x%x" % (texture_name_1, file_object.tell())

    return texture


def elu_read_material_texture(file_object, version):
    material = elu_read_material(file_object)

    texture = elu_read_texture(file_object, version)

    two_sided, \
    additive = struct.unpack('<2I', file_object.read(8))

    alpha_percentage = 0

    if version == 0x5007:
        alpha_percentage = struct.unpack('<I', file_object.read(4))[0]

    if material is not None:
        material.properties['isTwoSided'] = two_sided # Applied on faces

        material.setAlpha(1.0 - float(alpha_percentage / 100))

        material_mode = material.getMode()

        if texture is not None:
            material_textures = material.getTextures()

            material_texture_index = -1

            for i, material_texture in enumerate(material_textures):
                if material_texture is None:
                    material_texture_index = i
                    break

            if material_texture_index >= 0:
                material.setTexture(material_texture_index, texture, Blender.Texture.TexCo.UV)

                material_mode |= Blender.Material.Modes.TEXFACE
                material_mode |= Blender.Material.Modes.ZTRANSP

                if alpha_percentage == 0:
                    material_mode |= Blender.Material.Modes.TEXFACE_ALPHA

                if additive > 0:
                    material_textures = material.getTextures()

                    material_texture = material_textures[material_texture_index]

                    if material_texture is not None:
                        material_texture.blendmode |= Blender.Texture.BlendModes.ADD

        material.setMode(material_mode)

    return material


def elu_read_mesh(file_object, version, scene=None):
    if scene is None:
        scene = Blender.Scene.GetCurrent()

    mesh_name = file_object.read(40)
    parent_mesh_name = file_object.read(40)

    mesh_name = mesh_name[0:mesh_name.find('\0')]
    parent_mesh_name = parent_mesh_name[0:parent_mesh_name.find('\0')]

    mesh_name = elu_to_blender_name(mesh_name)
    parent_mesh_name = elu_to_blender_name(parent_mesh_name)

    print "mesh %s-%s start 0x%x" % (mesh_name, parent_mesh_name, file_object.tell() - 80)

    mesh = Blender.Mesh.New(mesh_name)

    mesh_object = scene.objects.new(mesh)

    wm = struct.unpack('<16f', file_object.read(64))

    world_matrix = Blender.Mathutils.Matrix([wm[0], wm[2], wm[1], wm[3]], \
                                            [wm[8], wm[10], wm[9], wm[11]], \
                                            [wm[4], wm[6], wm[5], wm[7]], \
                                            [wm[12], wm[14], wm[13], wm[15]])

    file_object.seek(44, 1) # Eleven single precision floats: scale vector, rotation vector, rotation angle, scale pivot vector, scale pivot angle

    lm = struct.unpack('<16f', file_object.read(64))

    local_matrix = Blender.Mathutils.Matrix([lm[0], lm[2], lm[1], lm[3]], \
                                            [lm[8], lm[10], lm[9], lm[11]], \
                                            [lm[4], lm[6], lm[5], lm[7]], \
                                            [lm[12], lm[14], lm[13], lm[15]])

    vertex_position_count = struct.unpack('<I', file_object.read(4))[0]

#    print "\tvertex position count %d" % vertex_position_count

    for x in xrange(vertex_position_count):
        px, py, pz = struct.unpack('<3f', file_object.read(12))

        position = Blender.Mathutils.Vector(px, pz, py)

        mesh.verts.extend(position)

    face_count = struct.unpack('<I', file_object.read(4))[0]

#    print "\tface count %d" % face_count

    face_diffs = []

    smooth_groups = {}

    for x in xrange(face_count):
        face_indices = [i for i in struct.unpack('<3I', file_object.read(12))] # Tri-face

        face_indices.reverse()

        face_vertices = [mesh.verts[i] for i in face_indices]

        face_index = mesh.faces.extend(*face_vertices, indexList=True)[0]

        # Check if duplicate
        if face_index is not None:
            face = mesh.faces[face_index]

            # This is a hack for when Blender decides to alter the face culling
            face_diff = [face_indices.index(v.index) for v in face.verts]

            face_diff.append(face_index)

            face_diffs.append(face_diff)

            face_uvs = []

            # Allocate because of hack above
            for x in xrange(FACE_VERTEX_COUNT):
                face_uvs.append(Blender.Mathutils.Vector(0.0, 0.0))

            for x in xrange(FACE_VERTEX_COUNT):
                u, v, w = struct.unpack('<3f', file_object.read(12))

                face_uvs[face_diff[x]] = Blender.Mathutils.Vector(u, float(1.0 - v))

            face_uvs.reverse()

            face.uv = tuple(face_uvs)
            face.transp = Blender.Mesh.FaceTranspModes.SOLID

            material_index, \
            smooth_group_index = struct.unpack('<2I', file_object.read(8))

            if smooth_group_index in smooth_groups:
                smooth_groups[smooth_group_index].extend([v.index for v in face.verts])
            else:
                smooth_groups[smooth_group_index] = [v.index for v in face.verts]
        else:
            face_diffs.append(None)

            file_object.seek(44, 1) # Skip duplicate

    for smooth_group_index, smooth_group_vertices in smooth_groups.iteritems():
        smooth_group_name = "Smooth.%03d" % smooth_group_index

        mesh.addVertGroup(smooth_group_name)

        mesh.assignVertsToGroup(smooth_group_name, \
                                smooth_group_vertices, \
                                1.0, \
                                Blender.Mesh.AssignModes.ADD)

        smooth_modifier = mesh_object.modifiers.append(Blender.Modifier.Types.SMOOTH)

        smooth_modifier[Blender.Modifier.Settings.FACTOR] = 0.0 # let the user modify
        smooth_modifier[Blender.Modifier.Settings.VERTGROUP] = smooth_group_name

    material_index = 0

    if version >= 0x5005 and version <= 0x5007:
        for face_diff in face_diffs:
            if face_diff is not None:
                face_index = face_diff.pop()

                face = mesh.faces[face_index]

                face_normal = Blender.Mathutils.Vector(struct.unpack('<3f', file_object.read(12))) # can't be assigned in blender, face.no read-only

                vertex_normals = []

                # Allocate because of hack above
                for x in xrange(FACE_VERTEX_COUNT):
                    vertex_normals.append(Blender.Mathutils.Vector(0.0, 0.0, 0.0))

                for x in xrange(FACE_VERTEX_COUNT):
                    nx, ny, nz = struct.unpack('<3f', file_object.read(12))

                    vertex_normals[face_diff[x]] = Blender.Mathutils.Vector(nx, nz, ny)

                vertex_normals.reverse()

                for j, vertex in enumerate(face):
                    vertex.no = vertex_normals[j]
            else:
                file_object.seek(48, 1) # Skip duplicate

        vertex_color_count = struct.unpack('<I', file_object.read(4))[0]

#        print "\tvertex color count %d" % vertex_color_count

        goal_weights = []

        vertex_colors = []

        if vertex_color_count > 0:
            mesh.addVertGroup(SOFT_BODY_GROUP) # blender soft-body vertex group, you'll need to apply as goal manualy

        for x in xrange(vertex_color_count):
            vertex_color = struct.unpack('<3f', file_object.read(12))

            goal_weights.append(math.fsum(vertex_color) / 3.0)

            mesh.assignVertsToGroup(SOFT_BODY_GROUP, \
                                    [x], \
                                    goal_weights[x], \
                                    Blender.Mesh.AssignModes.ADD)

            vertex_colors.append([int(y * 255.0) for y in vertex_color])

        if vertex_color_count > 0:
            mesh.vertexColors = True

            for face in mesh.faces:
                for i, vertex in enumerate(face):
                    vertex_color = vertex_colors[vertex.index]

                    face_color = face.col[i]
                    face_color.r = vertex_color[0]
                    face_color.g = vertex_color[1]
                    face_color.b = vertex_color[2]

        material_index = struct.unpack('<I', file_object.read(4))[0]

        bone_influence_count = struct.unpack('<I', file_object.read(4))[0]

#        print "\tbone influence count %d" % bone_influence_count

        for x in xrange(bone_influence_count):
            bone_influence_names = []

            for y in xrange(MAX_INFLUENCES):
                bone_influence_name = file_object.read(40)
                bone_influence_name = bone_influence_name[0:bone_influence_name.find('\0')]

                if len(bone_influence_name) > 0:
                    bone_influence_name = elu_to_blender_name(bone_influence_name)

                    bone_influence_names.append(bone_influence_name)

            bone_influence_weights = [y for y in struct.unpack('<4f', file_object.read(16)) if y > 0.0]

            file_object.seek(16, 1) # Three 32-bit unsigned integers: parent ids, always 0

            # doesn't seem to be reliable... what might be needed is verifying if
            # the sum of the weights is 1.0 just to be safe
            count = struct.unpack('<I', file_object.read(4))[0]

            for y in xrange(len(bone_influence_weights)):
                mesh.addVertGroup(bone_influence_names[y])

                mesh.assignVertsToGroup(bone_influence_names[y], \
                                        [x],
                                        bone_influence_weights[y],
                                        Blender.Mesh.AssignModes.ADD)

            for y in xrange(MAX_INFLUENCES):
                ox, oy, oz = struct.unpack('<3f', file_object.read(12))

                offset = Blender.Mathutils.Vector(ox, oz, oy)
    else:
        file_object.seek(4, 1) # Padding

        material_index = struct.unpack('<I', file_object.read(4))[0]

    try:
        material_name = "Mat.%03d" % material_index

        material = Blender.Material.Get(material_name)
    except:
        pass
    else:
        if material is not None:
            mesh.materials += [material]

            material_textures = material.getTextures()

            for i, material_texture in enumerate(material_textures):
                if material_texture is not None:
                    texture = material_texture.tex

                    if texture is not None:
                        image = texture.getImage()

                        if image is not None:
                            for face in mesh.faces:
                                face_mode = face.mode | Blender.Mesh.FaceModes.TEX

                                if material.properties['isTwoSided'] == 1:
                                    face_mode |= Blender.Mesh.FaceModes.TWOSIDE

                                face.mode = face_mode
                                face.image = image

                    break

    is_joint = int(mesh_name.startswith('Bip'))

    mesh.properties['isJoint'] = is_joint

    if is_joint == 1:
        mesh.hide = True

        mesh_object.setDrawMode(Blender.Object.DrawModes['TRANSP'])

    mesh.update()

    mesh_object.setMatrix(world_matrix)

    mesh_object.addProperty('isJoint', is_joint, 'INT')

    try:
        parent_mesh_object = Blender.Object.Get(parent_mesh_name)
    except:
        pass
    else:
        is_parent_joint = parent_mesh_object.getProperty('isJoint')

        if (is_parent_joint is not None and is_parent_joint.getData() == 1) and is_joint == 1:
            parent_mesh_object.makeParent([mesh_object])

    print "mesh %s-%s end 0x%x" % (mesh_name, parent_mesh_name, file_object.tell())

    return mesh_object


def ani_read_mesh_transformations(file_object, version, armature_object):
    key_frames = {}

    armature_pose = armature_object.getPose()

    armature_pose_bones = armature_pose.bones

    mesh_name = file_object.read(40)

    mesh_name = mesh_name[0:mesh_name.find('\0')]

    mesh_name = elu_to_blender_name(mesh_name)

    print "transform %s start 0x%x" % (mesh_name, file_object.tell() - 44)

    pm = struct.unpack('<16f', file_object.read(64))

    pose_matrix = Blender.Mathutils.Matrix([pm[0], pm[2], pm[1], pm[3]], \
                                           [pm[8], pm[10], pm[9], pm[11]], \
                                           [pm[4], pm[6], pm[5], pm[7]], \
                                           [pm[12], pm[14], pm[13], pm[15]])

    translation_count = struct.unpack('<I', file_object.read(4))[0]

#    print "translation count %d" % translation_count

    for x in xrange(translation_count):
        tx, ty, tz = struct.unpack('<3f', file_object.read(12))

        translation = Blender.Mathutils.Vector(tx, tz, ty)

        key_frame_second = int(struct.unpack('<I', file_object.read(4))[0] / 60)

        translation_matrix = Blender.Mathutils.TranslationMatrix(translation)

        key_frames[key_frame_second] = translation_matrix

    rotation_count = struct.unpack('<I', file_object.read(4))[0]

#    print "rotation count %d" % rotation_count

    for x in xrange(rotation_count):
        rotation_matrix = Blender.Mathutils.Matrix()

        rotation_matrix.identity()

        rx, ry, rz, rw = struct.unpack('<4f', file_object.read(16))

        if version >= 0x1003:
            rotation = Blender.Mathutils.Quaternion(-rw, rx, rz, ry)

            rotation_matrix *= rotation.toMatrix().resize4x4()
        else:
            rotation = Blender.Mathutils.Quaternion([rx, rz, ry], math.degrees(-rw))

            rotation_matrix *= rotation.toMatrix().resize4x4()

        key_frame_second = int(struct.unpack('<I', file_object.read(4))[0] / 60)

        if key_frame_second in key_frames:
            key_frames[key_frame_second] = rotation_matrix * key_frames[key_frame_second]
        else:
            parent_inverse_pose_matrix = Blender.Mathutils.Matrix()

            parent_inverse_pose_matrix.identity()

            if mesh_name in armature_pose_bones.keys():
                if armature_pose_bones[mesh_name].parent is not None:
                    parent_inverse_pose_matrix *= armature_pose_bones[mesh_name].parent.poseMatrix.copy()

            parent_inverse_pose_matrix.invert()

            key_frames[key_frame_second] = rotation_matrix * Blender.Mathutils.TranslationMatrix((pose_matrix * parent_inverse_pose_matrix).translationPart())

    if mesh_name in armature_pose_bones.keys():
        if len(key_frames) > 0:
            for key_frame_second, key_frame_transformation in key_frames.iteritems():
                if armature_pose_bones[mesh_name].parent is not None:
                    key_frame_transformation *= armature_pose_bones[mesh_name].parent.poseMatrix.copy()

                armature_pose_bones[mesh_name].poseMatrix = key_frame_transformation

                armature_pose.update()

                armature_pose_bones[mesh_name].insertKey(armature_object, key_frame_second + 1)

            armature_pose_bones[mesh_name].poseMatrix = pose_matrix

            armature_pose.update()
        else:
            armature_pose_bones[mesh_name].poseMatrix = pose_matrix

            armature_pose.update()

            armature_pose_bones[mesh_name].insertKey(armature_object, 1)

    if version >= 0x1001:
        unknown_0 = struct.unpack('<I', file_object.read(4))[0]

        for x in xrange(unknown_0):
            file_object.seek(8, 1) # 1 single precision float, 1 32-bit unsigned integer

    print "transform %s end 0x%x" % (mesh_name, file_object.tell())


def elu_import(file_path):
    file_object = None

    try:
        scene = Blender.Scene.GetCurrent()

        file_object = open(file_path, 'rb')

        magic, \
        version = struct.unpack('<2I', file_object.read(8))

        if magic != 0x0107F060:
            raise MagicError, "Bad magic number, %x" % magic

#        print "version %d" % version

        if version not in ELU_VERSIONS and version not in ANI_VERSIONS:
            raise VersionError, "Bad version number %d" % version

        if version in ELU_VERSIONS:
            material_count, \
            mesh_count = struct.unpack('<2I', file_object.read(8))

            for x in xrange(material_count):
                elu_read_material_texture(file_object, version)

            joint_mesh_objects = []

            skin_mesh_objects = []

            for x in xrange(mesh_count):
                mesh_object = elu_read_mesh(file_object, version, scene)

                is_joint = mesh_object.getProperty('isJoint')

                if (is_joint is not None and is_joint.getData() == 1) or mesh_object.name.startswith('Bip') is True:
                    joint_mesh_objects.append(mesh_object)
                else:
                    skin_mesh_objects.append(mesh_object)
        elif version in ANI_VERSIONS:
            maximum_frame, \
            mesh_count, \
            animation_format = struct.unpack('<3I', file_object.read(12))

            if animation_format != ANI_FORMAT:
                raise FormatError, "Unsupported animation format %d" % animation_format

            try:
                armature_object = Blender.Object.Get('Armature')
            except ValueError:
                Blender.Draw.PupMenu("Error%%t|The scene does not contain an armature")
            else:
                action_name = Blender.sys.basename(file_object.name)
                action_name = action_name[0:action_name.find('.')]

                action = Blender.Armature.NLA.NewAction(action_name)
                action.setActive(armature_object)

                for x in xrange(mesh_count):
                    ani_read_mesh_transformations(file_object, version, armature_object)
    except IOError, (errno, strerror):
        Blender.Draw.PupMenu("Error%%t|I/O error(%d): %s." % (errno, strerror))
#    except Exception, err:
#        Blender.Draw.PupMenu("Error%%t|.%s" % err)
    else:
        if version in ELU_VERSIONS:
            if len(joint_mesh_objects) > 0:
                armature = Blender.Armature.New('Armature')

                armature.drawType = Blender.Armature.STICK
                armature.envelopes = False
                armature.vertexGroups = True

                armature_object = scene.objects.new(armature)

                armature_object.drawMode = Blender.Object.DrawModes.XRAY

                armature.makeEditable()

                for joint_mesh_object in joint_mesh_objects:
                    edit_bone = Blender.Armature.Editbone()

                    edit_bone.name = joint_mesh_object.name

                    parent_joint_mesh_object = joint_mesh_object.getParent()

                    if parent_joint_mesh_object is not None:
                        edit_bone.parent = armature.bones[parent_joint_mesh_object.name]

                    edit_bone.matrix = joint_mesh_object.getMatrix()

                    armature.bones[edit_bone.name] = edit_bone

                armature.update()

                armature_pose = armature_object.getPose()

                armature_pose_bones = armature_pose.bones

                for armature_pose_bone in armature_pose_bones.values():
                    try:
                        joint_mesh_object = Blender.Object.Get(armature_pose_bone.name)
                    except:
                        pass
                    else:
                        armature_pose_bone.displayObject = joint_mesh_object

                        scene.objects.unlink(joint_mesh_object)

                armature_object.makeParent(skin_mesh_objects)

                armature_object.layers = [2]

                for skin_mesh_object in skin_mesh_objects:
                    skin_mesh_object.layers = [1, 2]

                    armature_modifier = skin_mesh_object.modifiers.append(Blender.Modifier.Types.ARMATURE)

                    armature_modifier[Blender.Modifier.Settings.OBJECT] = armature_object
    finally:
        scene.objects.selected = []

        scene.update(0)

        if file_object is not None:
            file_object.close()


def main():
    def elu_file_selector(file_path):
        if file_path and not Blender.sys.exists(file_path):
            Blender.Draw.PupMenu("Error%%t|The file %s does not exist." % file_path)
        else:
            elu_import(file_path)

    Blender.Window.FileSelector(elu_file_selector, 'Ok', Blender.sys.makename(ext='.elu'))


if __name__ == "__main__":
    main()
 
Last edited:
Reverse Engineer
Joined
Mar 19, 2007
Messages
879
Reaction score
37
Re: ELU Blender Importer

Interesting! Have you managed to save edited elu's/ani's and have them work?
 
Master Summoner
Joined
Oct 8, 2008
Messages
514
Reaction score
1
Re: ELU Blender Importer

Wow, that looks pretty cool.
 
Custom Title Activated
Loyal Member
Joined
Nov 5, 2006
Messages
1,358
Reaction score
15
Re: ELU Blender Importer

Really great work, my friend! And nice to see I'm not the only one running Ubuntu :p
 
Experienced Elementalist
Joined
Aug 27, 2006
Messages
223
Reaction score
183
Re: ELU Blender Importer

Uploaded what I have completed thus far with the plug-in script. If anyone is interested in learning read , , and

Interesting! Have you managed to save edited elu's/ani's and have them work?

No I have not written an exporter or begun with animations

...
BTW, GunZ models are REALLY low poly O.O

They have some quality to them

Really great work, my friend! And nice to see I'm not the only one running Ubuntu :p

It's Fedora with Clearlooks.
 
Last edited:
Custom Title Activated
Loyal Member
Joined
Nov 5, 2006
Messages
1,358
Reaction score
15
Re: ELU Blender Importer

Oh well, didn't know about it. It looks really similar to Ubuntu. Is it better?
 
Reverse Engineer
Joined
Mar 19, 2007
Messages
879
Reaction score
37
Re: ELU Blender Importer

Uploaded what I have completed thus far with the plug-in script. If anyone is interested in learning read and



No I have not written an exporter or begun with animations



They have some quality to them



It's Fedora with Clearlooks.

Well it's good that someone with experience decided to show up. What made you move to Gunz?
 
Experienced Elementalist
Joined
Aug 27, 2006
Messages
223
Reaction score
183
Re: ELU Blender Importer

Oh well, didn't know about it. It looks really similar to Ubuntu. Is it better?

Fedora is an RPM based distribution where some of the most active development occurs for the Linux desktop. They look identical because it's the same Gnome/Metacity with Clearlooks desktop.

Well it's good that someone with experience decided to show up. What made you move to Gunz?

I choose niche games that look ascetically appeasing from the screenshots with a semi active community. In other words, whatever is buzzing around ragezone :p

I jump games usually when I find myself looking for a new challenge. I enjoy the mental observation of these formats which provides me a glimpse into the the challenges the developer faced at the time of creation.

I always provide all source code and resources so someone might pickup when I'm trailing behind. It's usually a long shot but it gives me piece of mind.
 
Last edited:
Reverse Engineer
Joined
Mar 19, 2007
Messages
879
Reaction score
37
Re: ELU Blender Importer

Heh this community has through the ringer when it comes to understanding MAIET's train of thought while coding. Hopefully Gunz 2 coming in 09 keeps this format so we can put this to good use.
 
Custom Title Activated
Loyal Member
Joined
Nov 5, 2006
Messages
1,358
Reaction score
15
Re: ELU Blender Importer

I don't think so. I believe MAIET won't make the same mistake twice.

By the way, whats the blender script directory?
 
DRGunZ 2 Creator
Loyal Member
Joined
Jan 21, 2007
Messages
4,493
Reaction score
161
Re: ELU Blender Importer

xD tell him T6 :p
no offense Kochon xD
@Phantom this is nice, how did you get to the part in your pictures? im a noob in Blender lol
 
Last edited:
Newbie Spellweaver
Joined
Jan 4, 2008
Messages
7
Reaction score
0
Experienced Elementalist
Joined
Aug 27, 2006
Messages
223
Reaction score
183
Re: ELU Blender Importer

Sorry, but where is Blender's scripts directory? Can't seem to find it.

Alex.

If you're using Windows when you installed Blender it asked you where you want the directory. You either chose your application directory in your home directory or the blender installation directory. Once you know which one, open it, and you should see a .blender directory which contains the scripts directory. On linux (prefix dot directories are hidden, use ls -al) it's much the same, either in /home/<user name>/.blender/scripts or the installation directory.
 
Adrian - Gunz Addict
Joined
Oct 10, 2008
Messages
323
Reaction score
3
Re: ELU Blender Importer

do we put elu_import.py inside the python25.zip ? which is inside the blender directory or we just drop it in the blender folder?

and do we extract the python25.zip? into the blender folder?


i cant find this scripts folder anywhere, i made a folder called scripts put in blender folder put elu_import.py inside the scripts folder. looked at some tuts but nothing seems 2 be working... and i downloaded python 2.5.2 and i can run the program but when i go 2 file > import > there isn't a gunz or anything with (.elu)
 
Supreme Arcanarch
Loyal Member
Joined
Dec 21, 2006
Messages
957
Reaction score
0
Re: ELU Blender Importer

For me, the .blender folder wasn't present when I installed Blender using their installer. It was only present in the ZIP version of Blender. But once I got the ZIP version, your script worked fine.

Great job, it's one step closer to something people have been wanting for quite some time. Once the exporter is done it'll be perfect. Animation editing would also be pretty badass, heh. But regardless, great progress so far. I didn't think anything like this would be achieved with a simple Python script lol.
 
Status
Not open for further replies.
Back
Top