Exemple #1
0
class HeroFile:
    me = (2 ** 8) - 1
    ge = (2 ** 16) - 1
    H = math.pow(2, 16) - 1
    X = (math.pow(2, 16) - 2) / 2

    def __init__(self, path):
        self.reader = ByteIO(path=path)
        self.name = Path(path).name
        self.version = 0
        self.i32_count = 0
        self.i16_count = 0
        self.i8_count = 0
        self.i1_count = 0
        self.export_time = 0

        self.i32_offset = 0
        self.i16_offset = 0
        self.i8_offset = 0
        self.i1_offset = 0
        self.bit_cursor = 0

        self._i1_array = []

        self.options = {}
        self.geometry = HeroGeomerty()
        self.vertex_count = 0

    def read_float(self, offset=0):
        self.reader.seek(self.i32_offset + offset)
        ret = self.reader.read_float()
        self.i32_offset += 4
        return ret

    def read_uint32(self, offset=0):
        self.reader.seek(self.i32_offset + offset)
        val = self.read_float()
        ret = round(val)
        return ret

    def read_uint16(self, offset=0, increment=True):
        self.reader.seek(self.i16_offset + offset)
        ret = self.reader.read_uint16()
        if increment:
            self.i16_offset += 2
        return ret

    def read_int8(self, offset=0):
        self.reader.seek(self.i8_offset + offset)
        ret = self.reader.read_uint8()
        self.i8_offset += 1
        return ret

    def read_string(self, offset=0):
        self.reader.seek(self.i8_offset + offset)
        l = self.read_int8()
        ret = self.reader.read_ascii_string(l)
        self.i8_offset += len(ret)
        return ret

    def read_bit(self):
        bit = self._i1_array[self.bit_cursor]
        self.bit_cursor += 1
        return bit

    def get_quaternion_array(self, e):
        e *= 4
        r = np.zeros(e)
        for i in range(e):
            r[i] = self.read_uint16() / self.H * 2 - 1
        return r

    def get_position_array(self, e, t):
        n = np.zeros(e * 3)
        for a in range(e):
            for r in range(3):
                n[3 * a + r] = (self.read_uint16() - self.X) / self.X * t
        return n

    def get_scale_array(self, e, t):
        n = np.zeros(e * 3)
        for a in range(e):
            for r in range(3):
                n[3 * a + r] = self.read_uint16() / self.H * t
        return n

    def read(self):
        reader = self.reader
        self.version = round(reader.read_float(), 2)
        self.get_start_points()
        with reader.save_current_pos():
            reader.seek(self.i1_offset)
            for _ in range(math.ceil(self.i1_count / 8)):
                byte = reader.read_int8()
                for i in range(8):
                    self._i1_array.append(bool(byte & (1 << i)))
        self._init_settings()
        self._init_indices()
        self._init_points()
        self._init_normals()
        self._init_uvs()
        self._init_vertex_colors()
        self._init_blends()
        self._init_weights()
        self._init_parent()
        try:
            self._init_poses();
        except:
            pass

    def get_bit(self):
        self.bit_cursor += 1
        return self._i1_array[self.bit_cursor - 1]

    def get_start_points(self):
        reader = self.reader
        self.i32_count = reader.read_float_int32()
        self.i16_count = reader.read_float_int32()
        self.i8_count = reader.read_float_int32()
        self.i1_count = reader.read_float_int32()
        e = 20
        if self.version >= 1.4:
            e += 4
            self.export_time = reader.read_float()
        self.i32_offset = e
        self.i16_offset = self.i32_offset + 4 * self.i32_count
        self.i8_offset = self.i16_offset + 2 * self.i16_count
        self.i1_offset = self.i8_offset + self.i8_count

    def _init_settings(self):
        default_attributes = ["mesh", "normals", "uv1", "uv2", "blendTargets", "blendNormals", "weights", "animations",
                              "jointScales", "addon", "paintMapping", "singleParent", "frameMappings", "indices32bit",
                              "originalIndices", "vertexColors"]
        if self.version >= 1.2:
            default_attributes.append('posGroups')
        t = 32
        if self.version >= 1.25:
            default_attributes.append('uvSeams')
            default_attributes.append('rivets')
            t -= 2
        r = {}
        for attr in default_attributes:
            r[attr] = self.get_bit()
        if self.version >= 1.2:
            self.bit_cursor += t
            self.options = r
            self.geometry.main_skeleton = not self.options['addon'] and self.options['weights']

    def _init_indices(self):
        if self.options['mesh']:
            indices_count = self.read_uint32()
            if self.options['indices32bit']:
                self.geometry.index = [self.read_uint32() for _ in range(indices_count)]
            else:
                self.geometry.index = [self.read_uint16() for _ in range(indices_count)]
            if self.options['originalIndices']:
                if self.options['indices32bit']:
                    self.geometry.original_indices = [self.read_uint32() for _ in range(indices_count)]
                else:
                    self.geometry.original_indices = [self.read_uint16() for _ in range(indices_count)]

    def _init_points(self):
        if self.options['mesh']:
            vertex_count = self.read_uint32() if self.options['indices32bit'] else self.read_uint16()
            self.vertex_count = vertex_count
            self.geometry.has_geometry = True
            # Z Y X
            bbox = [self.read_float() for _ in range(6)]
            scale = [bbox[3] - bbox[0], bbox[4] - bbox[1], (bbox[5] - bbox[2])]
            self.geometry.offset = [bbox[0] * scale[0], bbox[1] * scale[1], bbox[2] * scale[2]]
            self.geometry.bounds = [bbox[0:3], bbox[3:6]]
            verts = []
            for _ in range(vertex_count):
                verts.append((self.read_uint16() / self.ge * scale[0] + bbox[0],
                              self.read_uint16() / self.ge * scale[1] + bbox[1],
                              self.read_uint16() / self.ge * scale[2] + bbox[2]
                              ))
                self.geometry.positions = verts

    def _init_normals(self):
        if self.options['normals']:
            if self.vertex_count != 0:
                normals = []
                r = 0
                for _ in range(self.vertex_count):
                    normals.append(self.read_int8() / self.me * 2 - 1)
                    normals.append(self.read_int8() / self.me * 2 - 1)
                    normals.append(
                        (2 * self.get_bit() - 1) * (1 - math.pow(normals[r], 2) - math.pow(normals[r + 1], 2)))
                    r += 3
                self.geometry.normals = split(normals, 3)

    def _init_uvs(self):
        if self.options['uv1']:
            uvs = ['uv', 'uv2'] if self.options['uv2'] else ['uv']
            for uv in uvs:
                n = [self.read_float() for _ in range(4)]
                s = [n[2] - n[0], n[3] - n[1]]
                u = []
                for i in range(self.vertex_count):
                    u.append((self.read_uint16() / self.ge * s[0] + n[0], self.read_uint16() / self.ge * s[1] + n[1]))
                setattr(self.geometry, uv, u)

    def _init_vertex_colors(self):
        if self.options['vertexColors']:
            layer_count = self.read_int8()
            for t in range(layer_count):
                layer_name = self.read_string()
                v_colors = []
                for _ in range(self.vertex_count):
                    col = self.read_int8()
                    v_colors.append(col / 255)
                    v_colors.append(col / 255)
                    v_colors.append(col / 255)
                    v_colors.append(1)
                self.geometry.vertex_colors[layer_name] = np.array(v_colors).reshape((-1, 4))

    def _init_blends(self):
        if self.options['blendTargets']:
            shape_key_count = self.read_int8()
            if shape_key_count:
                shape_key_data = {}
                for shape_key_id in range(shape_key_count):
                    shape_key_name = self.read_string()
                    o = [self.read_float() for _ in range(6)]
                    u = [o[3] - o[0], o[4] - o[1], o[5] - o[2]]
                    c = []
                    for d in range(self.vertex_count):
                        c.append(self.read_int8() / self.me * u[0] + o[0])
                        c.append(self.read_int8() / self.me * u[1] + o[1])
                        c.append(self.read_int8() / self.me * u[2] + o[2])
                    shape_key_data[shape_key_name] = split(c, 3)
                    if self.options['blendNormals']:
                        for _ in range(self.vertex_count):
                            self.read_int8()
                            self.read_int8()
                            self.get_bit()
                self.geometry.shape_key_data = shape_key_data

    def _init_weights(self):
        if self.options['weights']:
            self.geometry.skinned = True
            weight_per_vert = self.read_int8()
            additional_weights = max(0, weight_per_vert - 4)
            skin_indices = np.zeros(4 * self.vertex_count, dtype=np.int16)
            additional_skin_indices = np.zeros(additional_weights * self.vertex_count, dtype=np.int16)
            u = 4 if weight_per_vert < 4 else weight_per_vert
            for l in range(u):
                if weight_per_vert > l:
                    if l < 4:
                        for t in range(self.vertex_count):
                            skin_indices[4 * t + l] = self.read_uint16(2 * (t * weight_per_vert + l), False)
                    else:
                        for t in range(self.vertex_count):
                            additional_skin_indices[t * additional_weights + (l - 4)] = self.read_uint16(
                                2 * (t * additional_weights + l), False)
            self.geometry.skin_indices = skin_indices.reshape((-1, u,))
            self.geometry.additional_skin_indices = additional_skin_indices.reshape((-1, u,))
            self.i16_offset = self.i16_offset + weight_per_vert * self.vertex_count * 2;
            skin_weights = np.zeros(4 * self.vertex_count, dtype=np.float32)
            additional_skin_weights = np.zeros(additional_weights * self.vertex_count, dtype=np.float32)
            u = 4 if weight_per_vert < 4 else weight_per_vert
            for f in range(u):
                if weight_per_vert > f:
                    if f < 4:
                        for c in range(self.vertex_count):
                            skin_weights[4 * c + f] = self.read_uint16(2 * (c * weight_per_vert + f), False) / self.ge
                    else:
                        for c in range(self.vertex_count):
                            additional_skin_weights[c * additional_weights + (f - 4)] = self.read_uint16(
                                2 * (c * weight_per_vert + f), False) / self.ge
            self.geometry.skin_weights = skin_weights.reshape((-1, u))
            self.geometry.additional_skin_weights = additional_skin_weights.reshape((-1, weight_per_vert))
            self.i16_offset = self.i16_offset + weight_per_vert * self.vertex_count * 2

    def _init_parent(self):
        if self.options['singleParent']:
            name = self.read_string()
            e = self.read_uint16()
            r = np.zeros(4 * self.vertex_count)
            i = np.zeros(4 * self.vertex_count)
            a = 4 * self.vertex_count
            for n in range(a):
                r[n] = e if n % 4 == 0 else 0
                i[n] = 1 if n % 4 == 0 else 0

            self.geometry.skin_indices = r.reshape((-1, 4))
            self.geometry.skin_weights = i.reshape((-1, 4))

    def _init_poses(self):
        if self.options['animations']:
            bone_count = self.read_int8()
            if self.options['frameMappings']:
                n = self.read_uint16()
                a = [self.read_uint16() for _ in range(n)]
                if n:
                    i = {}
                    for s in range(n):
                        i[a[s]] = s
            p = self.read_float()
            m = self.options['jointScales']
            g = self.read_float() if m else 1
            poses = {}
            locators = {}
            bones = []
            for y in range(bone_count):
                o = self.read_string()
                l = self.read_uint16()
                u = self.read_uint16()
                # print(o, l, u)
                v = lambda: {
                    "pos": self.get_position_array(1, p) if self.get_bit() else self.get_position_array(u, p),
                    "rot": self.get_quaternion_array(1) if self.get_bit() else self.get_quaternion_array(u),
                    "scl": self.get_scale_array(1, g) if self.get_bit() else self.get_scale_array(u, g) if m else [1, 1,
                                                                                                                   1],
                    "frameMapping": i if self.options["frameMappings"] else None
                }
                if o == 'main':
                    for S in range(l):
                        b = self.read_uint16()
                        w = HeroBone()
                        w.bone_id = S
                        w.name = self.read_string()
                        # print(w.name)
                        if b == 5e3:
                            self.geometry.main_skeleton = True
                            w.parent_id = -1
                            print(w.name, b, S)
                        else:
                            w.parent_id = b
                        k = v()
                        w.pos = k['pos']
                        w.quat = k['rot']
                        w.scale = k['scl']
                        bones.append(w)
                elif o == 'locators':
                    for R in range(l):
                        bone = HeroBone()
                        bone.name = self.read_string()
                        C = v()
                        bone.pos = C['pos']
                        bone.scale = C['cls']
                        bone.quat = C['rot']
                        locators[bone.name] = bone
                else:
                    c = {}
                    for x in range(l):
                        c[self.read_string()] = v()
                    poses[o] = c
            # print(h)
            self.geometry.bones = bones
            self.geometry.poses = poses
            self.geometry.locations = locators
Exemple #2
0
    def read(self, reader: ByteIO):
        header_chunks = reader.get_items()
        for item in header_chunks:
            reader.seek(item.offset)

            if item.type == 20:
                self.chunk_name = reader.read_ascii_string(reader.read_int32())
            if item.type == 21:
                self.name = reader.read_ascii_string(reader.read_int32())
            if item.type == 1:
                items = reader.get_items()
                for item in items:
                    reader.seek(item.offset)

                    if item.type == 10:
                        items2 = reader.get_items()
                        for item2 in items2:
                            reader.seek(item2.offset)
                            if item2.type == 21:
                                self.indices_count = reader.read_int32()
                            if item2.type == 22:
                                self.indices_offset = reader.tell()
                        if self.indices_count is not None:
                            self.mode = 1
                            reader.seek(self.indices_offset)
                            self.indices = [
                                reader.read_uint16()
                                for _ in range(self.indices_count)
                            ]

                    if item.type == 21:
                        items2 = reader.get_items()
                        for item2 in items2:
                            reader.seek(item2.offset)
                            if item2.type == 21:
                                self.indices_count = reader.read_int32()
                            if item2.type == 22:
                                self.indices_offset = reader.tell()
                        if self.indices_count is not None:
                            self.mode = 2
                            reader.seek(self.indices_offset)
                            self.indices = [
                                reader.read_uint16()
                                for _ in range(self.indices_count)
                            ]

                    if item.type == 11:
                        items2 = reader.get_items()
                        for item2 in items2:
                            reader.seek(item2.offset)
                            if item2.type == 20:
                                items3 = reader.get_items()
                                for item3 in items3:
                                    reader.seek(item3.offset)

                                    if item3.type == 21:
                                        self.vert_stride = reader.read_int32()
                                    if item3.type == 22:
                                        self.vert_item_count = reader.read_int32(
                                        )
                                    if item3.type == 23:
                                        self.vert_offset = reader.tell()
                                    reader.seek(self.vert_offset)
                                off = 0
                                for k in range(self.vert_item_count):
                                    a, b, c, d = reader.read_fmt('BBBB')
                                    # print k,a,b,c,d,vertStrideSize
                                    if c == 1: self.pos_offset = off
                                    if c == 5 and a == 0:
                                        self.uv_offset = off
                                    # print k,a,b,c,d,vertStrideSize
                                    if c == 11: self.skin_ind_offset = off
                                    if c == 10: self.skin_weight_offset = off
                                    if d == 2: off += 12
                                    if d == 1: off += 8
                                    if d == 3: off += 16
                                    if d == 4: off += 1
                                    if d == 7: off += 1
                                    if d == 15: off += 4
                            if item2.type == 21:
                                self.vert_count = reader.read_int32()
                            if item2.type == 22:
                                self.stream_offset = reader.tell()
        reader.seek(self.stream_offset)
        for k in range(self.vert_count):
            tk = reader.tell()
            if self.pos_offset is not None:
                reader.seek(self.pos_offset + tk)
                self.vertices.append(reader.read_fmt('fff'))
            if self.uv_offset is not None:
                reader.seek(self.uv_offset + tk)
                self.uv.append([reader.read_float(), 1 - reader.read_float()])
            if self.skin_ind_offset:
                # reader.seek(self.skin_ind_offset + tk)
                i1, i2, i3 = reader.read_fmt('BBB')
                self.weight_inds.append([i1, i2])
            if self.skin_weight_offset:
                # reader.seek(self.skin_weight_offset + tk)
                w1, w2 = reader.read_fmt('BB')
                w3 = 255 - (w1 + w2)
                self.weight_weight.append([w1, w2])
            reader.seek(tk + self.vert_stride)