コード例 #1
0
def rgb_to_palette(img: Image, palette: bytes, name: str) -> bytes:
    """Convert an RGB image to palette entries.

    Pillow's quantize is pretty broken, so don't use that.

    :param img: The image to be converted. Must be RGB.
    :param palette: The colors of the palette. Must be RGB.
    :param name: The name of the image (for debug).
    :return: The corresponding indices in the palette for each pixel.

    :raises Mech3TextureError: if the image is not RGB
    :raises Mech3TextureError: if the palette contains duplicate colors
    :raises Mech3TextureError: if the palette does not contain a color in the image
    """
    assert_eq("image mode", "RGB", img.mode, name, Mech3TextureError)

    rgb_to_index = {}
    it = iter(palette)
    for i, rgb in enumerate(zip(it, it, it)):
        if rgb in rgb_to_index:
            # this occurs frequently; but the original images use the first color/index
            LOG.debug("Duplicate color found in palette of %s", name)
        else:
            rgb_to_index[rgb] = i

    size = img.width * img.height
    buf = bytearray(size)
    it = iter(img.tobytes())
    try:
        for i, rgb in enumerate(zip(it, it, it)):
            buf[i] = rgb_to_index[rgb]
    except KeyError:  # pragma: no cover
        raise Mech3TextureError(f"Color not found in palette of {name}")

    return bytes(buf)
コード例 #2
0
 def validate_length(cls, reader: BinReader, actual_length: int) -> None:
     assert_eq(
         f"{cls._NAME} size",
         cls._STRUCT.size,
         actual_length,
         reader.prev + 4,
     )
コード例 #3
0
def read_activation_prereq_anim(reader: BinReader) -> str:
    name_raw, zero32, zero36 = reader.read(ACTIV_PREREQ_ANIM)
    with assert_ascii("activ prereq name", name_raw, reader.prev + 0):
        name = ascii_zterm_padded(name_raw)
    # field offset from start of record
    assert_eq("activ prereq field 40", 0, zero32, reader.prev + 32)
    assert_eq("activ prereq field 44", 0, zero36, reader.prev + 36)
    return name
コード例 #4
0
    def read(cls, reader: BinReader,
             _anim_def: AnimDef) -> InvalidateAnimation:
        name_raw, sentinel = reader.read(cls._STRUCT)
        with assert_ascii("name", name_raw, reader.prev + 0):
            name = ascii_zterm_padded(name_raw)

        assert_eq("sentinel", 0, sentinel, reader.prev + 0)
        return cls(name=name)
コード例 #5
0
 def read(cls, reader: BinReader, anim_def: AnimDef) -> ObjectCycleTexture:
     one, zero, node_index, reset = reader.read(cls._STRUCT)
     # increment?
     assert_eq("field 0", 1, one, reader.prev + 0)
     # start index?
     assert_eq("field 4", 0, zero, reader.prev + 2)
     node = anim_def.get_node(node_index - 1, reader.prev + 4)
     assert_between("reset", 0, 5, reset, reader.prev + 6)
     return cls(node=node, reset=reset)
コード例 #6
0
def read_anim_def_zero(reader: BinReader) -> None:
    # the first entry is always zero
    data = bytearray(reader.read_bytes(ANIM_DEF.size))
    # except for this one byte?
    assert_eq("anim def header byte 153", 3, data[153], reader.prev + 153)
    data[153] = 0
    assert_all_zero("anim def header", data, reader.prev)
    bdata = reader.read_bytes(RESET_STATE.size)
    assert_all_zero("anim def reset", bdata, reader.prev)
コード例 #7
0
    def validate_and_read(cls, reader: BinReader, _anim_def: AnimDef,
                          actual_length: int) -> ObjectMotionSIScript:
        abs_end = reader.offset + actual_length
        (
            index,
            count,
            zero08,
            zero12,
            zero16,
            zero20,
        ) = reader.read(cls._STRUCT)

        assert_between("index", 0, 20, index, reader.prev + 0)  # 12
        assert_eq("field 08", 0.0, zero08, reader.prev + 8)  # 20
        assert_eq("field 12", 0.0, zero12, reader.prev + 12)  # 24
        assert_eq("field 16", 0, zero16, reader.prev + 16)  # 28
        assert_eq("field 20", 0, zero20, reader.prev + 20)  # 32

        frames = []
        for i in range(count):
            flag, start_time, end_time = reader.read(SI_SCRIPT_FRAME)
            assert_between(f"motion {i} flag", 1, 7, flag, reader.prev + 0)
            assert_ge(f"motion {i} start", 0.0, start_time, reader.prev + 4)
            if end_time != 0.0:
                assert_ge(f"motion {i} end", start_time, end_time,
                          reader.prev + 8)

            if (flag & 1) == 0:
                translate: Optional[Vector] = None
            else:
                values = cast(Values, reader.read(SI_SCRIPT_DATA))
                translate = parse_vec_data(values, start_time)

            if (flag & 2) == 0:
                rotate: Optional[Quaternion] = None
            else:
                values = cast(Values, reader.read(SI_SCRIPT_DATA))
                rotate = parse_quat_data(values, start_time)

            if (flag & 4) == 0:
                scale: Optional[Vector] = None
            else:
                values = cast(Values, reader.read(SI_SCRIPT_DATA))
                scale = parse_vec_data(values, start_time)

            frame = MotionFrame(
                start=start_time,
                end=end_time,
                translate=translate,
                rotate=rotate,
                scale=scale,
            )
            frames.append(frame)

        assert_eq("motion script end", abs_end, reader.offset, reader.offset)
        return cls(index=index, frames=frames)
コード例 #8
0
 def read(cls, reader: BinReader,
          anim_def: AnimDef) -> ObjectTranslateState:
     at_node_matrix, tx, ty, tz, node_index = reader.read(cls._STRUCT)
     assert_eq("field 00", 0, at_node_matrix, reader.prev + 0)
     if node_index < 1:
         assert_lt("node index", -13107190, node_index, reader.prev + 16)
         node = "INPUT_NODE"
     else:
         node = anim_def.get_node(node_index - 1, reader.prev + 16)
     return cls(node=node, state=(tx, ty, tz))
コード例 #9
0
def _read_static_sounds(reader: BinReader, count: int) -> List[NameRaw]:
    # the first entry is always zero
    name_raw, ptr = reader.read(STATIC_SOUND)
    assert_all_zero("name", name_raw, reader.prev + 0)
    assert_eq("field 32", 0, ptr, reader.prev + 32)

    sounds = []
    for _ in range(1, count):
        name_raw, ptr = reader.read(STATIC_SOUND)
        with assert_ascii("name", name_raw, reader.prev + 0):
            name, pad = ascii_zterm_partition(name_raw)
        assert_eq("field 32", 0, ptr, reader.prev + 32)
        sounds.append(NameRaw(name=name, pad=Base64(pad)))

    return sounds
コード例 #10
0
def _read_anim_header(reader: BinReader) -> List[AnimName]:
    (signature, version, count) = reader.read(ANIM_FILE_HEADER)
    LOG.debug(
        "Anim signature 0x%08x, version %d", signature, version,
    )
    assert_eq("signature", SIGNATURE, signature, reader.prev + 0)
    assert_eq("version", VERSION, version, reader.prev + 4)

    LOG.debug("Reading %d anim names at %d", count, reader.offset)
    names = []
    for _ in range(count):
        name_raw, unk = reader.read(ANIM_NAME)
        with assert_ascii("name", name_raw, reader.prev + 0):
            name, pad = ascii_zterm_partition(name_raw)
        names.append(AnimName(name=name, pad=Base64(pad), unk=unk))
    return names
コード例 #11
0
def _read_mesh_zero(reader: BinReader, mesh_count: int,
                    array_size: int) -> None:
    LOG.debug("Reading %d zeroed meshes at %d", array_size - mesh_count,
              reader.offset)
    for i in range(mesh_count, array_size):
        mesh = reader.read(MESH)
        for j, value in enumerate(mesh):
            offset = j * 4
            assert_eq(f"field {offset:02d}", 0, value, reader.prev + offset)

        expected = i + 1
        if expected == array_size:
            expected = -1

        (index, ) = reader.read(SINT32)
        assert_eq("index", expected, index, reader.prev)
コード例 #12
0
 def read(cls, reader: BinReader, anim_def: AnimDef) -> ObjectOpacityState:
     is_set_raw, state_raw, opacity, node_index = reader.read(cls._STRUCT)
     # this could be another value (e.g. -1), in which case is set is not changed
     assert_in("is set", (0, 1), is_set_raw, reader.prev + 0)
     # the state does not seem to depend on is_set?
     assert_in("state", (0, 1), state_raw, reader.prev + 2)
     state = state_raw == 1
     if state:
         assert_between("opacity", 0.0, 1.0, opacity, reader.prev + 4)
     else:
         assert_eq("opacity", 0.0, opacity, reader.prev + 4)
     node = anim_def.get_node(node_index - 1, reader.prev + 8)
     return cls(node=node,
                is_set=is_set_raw == 1,
                state=state,
                opacity=opacity)
コード例 #13
0
def _read_anim_refs(reader: BinReader, count: int) -> List[NameRaw]:
    # the first entry... is not zero! as this is not a node list
    # there's one anim ref per CALL_ANIMATION, and there may be duplicates to
    # the same anim since multiple calls might need to be ordered
    anim_refs = []
    for _ in range(count):
        name_raw, zero64, zero68 = reader.read(ANIM_REF)
        # a bunch of these values are properly zero-terminated at 32 and beyond,
        # but not all... i suspect a lack of memset
        with assert_ascii("name", name_raw, reader.prev + 0):
            name, pad = ascii_zterm_partition(name_raw)

        assert_eq("field 64", 0, zero64, reader.prev + 64)
        assert_eq("field 68", 0, zero68, reader.prev + 68)

        anim_refs.append(NameRaw(name=name, pad=Base64(pad.rstrip(b"\0"))))

    return anim_refs
コード例 #14
0
ファイル: node_data_read.py プロジェクト: tobywf/mech3ax-py
def _read_node_data_lod(reader: BinReader) -> LevelOfDetail:
    (
        level,  # 00
        range_near_sq,  # 04
        range_far,  # 08
        range_far_sq,  # 12
        zero16,  # 44 zero bytes
        unk60,  # 60
        unk64,  # 64
        one68,  # 68
        zero72,  # 72
        unk76,  # 76
    ) = reader.read(LEVEL_OF_DETAIL)

    assert_in("level", (0, 1), level, reader.prev + 0)

    assert_between(
        "range near sq",
        0.0,
        1000.0 * 1000.0,
        range_near_sq,
        reader.prev + 4,
    )
    range_near = sqrt(range_near_sq)

    assert_ge("range far", 0.0, range_far, reader.prev + 8)
    expected = force_single_prec(range_far * range_far)
    assert_eq("range far sq", expected, range_far_sq, reader.prev + 12)

    assert_all_zero("field 16", zero16, reader.prev + 16)

    # TODO:
    assert_ge("field 60", 0.0, unk60, reader.prev + 60)
    expected = force_single_prec(unk60 * unk60)
    assert_eq("field 64", expected, unk64, reader.prev + 64)

    assert_eq("field 68", 1, one68, reader.prev + 68)
    # TODO:
    assert_in("field 72", (0, 1), zero72, reader.prev + 72)
    if zero72 == 0:
        assert_eq("field 76", 0, unk76, reader.prev + 76)
    else:
        assert_ne("field 76", 0, unk76, reader.prev + 76)

    # object 3d nodes always have an action priority of 6

    return LevelOfDetail(
        type="LOD",
        level=level == 1,
        range=(range_near, range_far),
        unk60=unk60,
        unk76=unk76,
    )
コード例 #15
0
def read_anim(data: bytes) -> Tuple[AnimMetadata, List[AnimDef]]:
    reader = BinReader(data)
    LOG.debug("Reading animation data...")

    anim_names = _read_anim_header(reader)
    anim_count, anim_ptr, world_ptr = _read_anim_info(reader)
    anim_defs, anim_def_ptrs = _read_anim_defs(reader, anim_count)

    assert_eq("anim end", len(data), reader.offset, reader.offset)
    LOG.debug("Read animation data")

    anim_md = AnimMetadata(
        anim_names=anim_names,
        anim_ptr=anim_ptr,
        world_ptr=world_ptr,
        anim_def_ptrs=anim_def_ptrs,
    )

    return anim_md, anim_defs
コード例 #16
0
def _read_dynamic_sounds(reader: BinReader, count: int) -> List[NamePtrFlag]:
    # the first entry is always zero
    name_raw, flag, ptr, zero = reader.read(READER_LOOKUP)
    assert_all_zero("name", name_raw, reader.prev + 0)
    assert_eq("field 32", 0, flag, reader.prev + 32)
    assert_eq("field 36", 0, ptr, reader.prev + 36)
    assert_eq("field 40", 0, zero, reader.prev + 40)

    sounds = []
    for _ in range(1, count):
        name_raw, flag, ptr, zero = reader.read(READER_LOOKUP)
        with assert_ascii("name", name_raw, reader.prev + 0):
            name = ascii_zterm_node_name(name_raw)
        assert_eq("field 32", 0, flag, reader.prev + 32)
        assert_ne("field 36", 0, ptr, reader.prev + 36)
        assert_eq("field 40", 0, zero, reader.prev + 40)
        sounds.append(NamePtrFlag(name=name, ptr=ptr))

    return sounds
コード例 #17
0
def _read_objects(reader: BinReader, count: int) -> List[NameRaw]:
    # the first entry is always zero
    data = reader.read_bytes(OBJECT.size)
    assert_all_zero("object", data, reader.prev)

    objects = []
    for _ in range(1, count):
        (name_raw, zero32, bin_dump) = reader.read(OBJECT)

        with assert_ascii("name", name_raw, reader.prev + 0):
            name = ascii_zterm_node_name(name_raw)

        assert_eq("field 32", 0, zero32, reader.prev + 32)
        # TODO: this is cheating, but i have no idea how to interpret this data
        # sometimes it's sensible, e.g. floats. other times, it seems like random
        # garbage.
        objects.append(NameRaw(name=name, pad=Base64(bin_dump.rstrip(b"\0"))))

    return objects
コード例 #18
0
def _read_lights(reader: BinReader, count: int) -> List[NamePtrFlag]:
    # the first entry is always zero
    name_raw, flag, ptr, zero = reader.read(READER_LOOKUP)
    assert_all_zero("name", name_raw, reader.prev + 0)
    assert_eq("field 32", 0, flag, reader.prev + 32)
    assert_eq("field 36", 0, ptr, reader.prev + 36)
    assert_eq("field 40", 0, zero, reader.prev + 40)

    lights = []
    for _ in range(1, count):
        name_raw, flag, ptr, zero = reader.read(READER_LOOKUP)
        with assert_ascii("name", name_raw, reader.prev + 0):
            name = ascii_zterm_node_name(name_raw)
        assert_eq("field 32", 0, flag, reader.prev + 32)
        assert_ne("field 36", 0, ptr, reader.prev + 36)
        # if this were non-zero, it would cause the light to be removed instead
        # of added (???)
        assert_eq("field 40", 0, zero, reader.prev + 40)
        lights.append(NamePtrFlag(name=name, ptr=ptr))

    return lights
コード例 #19
0
ファイル: script.py プロジェクト: tobywf/mech3ax-py
def _parse_script(reader: BinReader, anim_def: AnimDef,
                  rel_end: int) -> List[ScriptItem]:
    LOG.debug("Reading script with length %d at %d", rel_end, reader.offset)

    abs_end = rel_end + reader.offset
    script = []

    while reader.offset < abs_end:
        stype, start_offset, pad, size, start_time = reader.read(SCRIPT_HEADER)
        assert_in("type", OBJECT_REGISTRY_NUM.keys(), stype, reader.prev + 0)
        assert_in("start offset", (1, 2, 3), start_offset, reader.prev + 1)
        assert_eq("field 02", 0, pad, reader.prev + 2)

        start_offset = StartOffset(start_offset)
        if start_time == 0.0:
            assert_eq("start offset", StartOffset.Animation, start_offset,
                      reader.prev + 1)
            start_offset = StartOffset.Unset

        actual_length = size - SCRIPT_HEADER.size
        base_model = OBJECT_REGISTRY_NUM[stype]

        obj = base_model.validate_and_read(reader, anim_def, actual_length)
        item = ScriptItem(
            name=base_model._NAME,  # pylint: disable=protected-access
            item=obj,
            start_offset=start_offset,
            start_time=start_time,
        )
        script.append(item)

    assert_eq("script end", abs_end, reader.offset, reader.offset)
    LOG.debug("Read script")
    return script
コード例 #20
0
ファイル: textures.py プロジェクト: tobywf/mech3ax-py
def read_textures(reader: BinReader, count: int) -> List[Texture]:
    textures = []
    for _ in range(count):
        zero00, zero04, texture_raw, used, index, mone36 = reader.read(
            TEXTURE_INFO)
        # not sure. a pointer to the previous texture in the global array? or a
        # pointer to the texture?
        assert_eq("field 00", 0, zero00, reader.prev + 0)
        # a non-zero value here causes additional dynamic code to be called
        assert_eq("field 04", 0, zero04, reader.prev + 4)
        with assert_ascii("texture", texture_raw, reader.prev + 8):
            texture, suffix = _ascii_zterm_suffix(texture_raw)
        # 2 if the texture is used, 0 if the texture is unused
        # 1 or 3 if the texture is being processed (deallocated?)
        assert_eq("used", 2, used, reader.prev + 28)
        # stores the texture's index in the global texture array
        assert_eq("index", 0, index, reader.prev + 32)
        # not sure. a pointer to the next texture in the global array? or
        # something to do with mipmaps?
        assert_eq("field 36", -1, mone36, reader.prev + 36)
        textures.append(Texture(name=texture, suffix=suffix))

    return textures
コード例 #21
0
def _read_puffers(reader: BinReader, count: int) -> List[NamePtrFlag]:
    # the first entry is always zero
    name_raw, flag, ptr, zero = reader.read(READER_LOOKUP)
    assert_all_zero("name", name_raw, reader.prev + 0)
    assert_eq("field 32", 0, flag, reader.prev + 32)
    assert_eq("field 36", 0, ptr, reader.prev + 36)
    assert_eq("field 40", 0, zero, reader.prev + 40)

    puffers = []
    for _ in range(1, count):
        name_raw, flag, ptr, zero = reader.read(READER_LOOKUP)
        with assert_ascii("name", name_raw, reader.prev + 0):
            name = ascii_zterm_padded(name_raw)
        assert_eq("field 32", 0, (flag & 0x00FFFFFF), reader.prev + 32)
        # TODO: what does this flag mean?
        # this is something the code does, but i'm not sure why
        # some of these values make decent floating point numbers
        flag = flag >> 24
        assert_ne("field 36", 0, ptr, reader.prev + 36)
        assert_eq("field 40", 0, zero, reader.prev + 40)
        puffers.append(NamePtrFlag(name=name, ptr=ptr, flag=flag))

    return puffers
コード例 #22
0
ファイル: materials.py プロジェクト: tobywf/mech3ax-py
def read_materials(reader: BinReader, texture_count: int) -> Tuple[int, List[Material]]:
    (array_size, mat_count, index_max, mat_unknown) = reader.read(MATERIAL_HEADER)
    assert_eq("index max", mat_count, index_max, reader.prev + 8)
    assert_eq("field 12", mat_count - 1, mat_unknown, reader.prev + 12)

    materials_and_cycle = _read_materials(reader, mat_count, texture_count)
    _read_materials_zero(reader, mat_count, array_size)

    materials = []
    for material, cycle_info_ptr in materials_and_cycle:
        if cycle_info_ptr:
            (
                unk00,
                unk04,
                zero08,
                unk12,
                cycle_count1,
                cycle_count2,
                data_ptr,
            ) = reader.read(CYCLE_HEADER)

            assert_in("field 00", (0, 1), unk00, reader.prev + 0)
            # field 04
            assert_eq("field 08", 0, zero08, reader.prev + 8)
            assert_between("field 12", 2.0, 16.0, unk12, reader.prev + 12)
            assert_eq("cycle count", cycle_count1, cycle_count2, reader.prev + 20)
            assert_ne("field 24", 0, data_ptr, reader.prev + 24)

            cycle_textures = reader.read(Struct(f"<{cycle_count1}I"))

            for i, cycle_texture in enumerate(cycle_textures):
                # the texture should be in range
                assert_between(
                    "texture", 0, texture_count - 1, cycle_texture, reader.prev + i * 4
                )

            material.cycle = Cycle(
                textures=list(cycle_textures),
                unk00=unk00 == 1,
                unk04=unk04,
                unk12=unk12,
                info_ptr=cycle_info_ptr,
                data_ptr=data_ptr,
            )
        materials.append(material)

    return array_size, materials
コード例 #23
0
def read_activation_prereq_obj(
        reader: BinReader, required: bool, prereq_type: int,
        prev: OptPrereqObj) -> Tuple[OptPrereqObj, OptPrereqObj]:
    active, name_raw, ptr = reader.read(ACTIV_PREREQ_OBJ)
    with assert_ascii("activ prereq name", name_raw, reader.prev + 4):
        name = ascii_zterm_padded(name_raw)
    assert_ne("activ prereq ptr", 0, ptr, reader.prev + 36)

    if prereq_type == 3:
        assert_eq("activ prereq active", 0, active, reader.prev + 0)
        # remember the current node as the previous one
        prev = PrereqObject(required=required,
                            active=False,
                            name=name,
                            ptr=ptr)
        return prev, None

    assert_in("activ prereq active", (0, 1), active, reader.prev + 0)
    if prev:
        assert_eq("activ prereq required", prev.required, required,
                  reader.prev + 0)
        parent_name = prev.name
        parent_ptr = prev.ptr
    else:
        parent_name = ""
        parent_ptr = 0

    obj = PrereqObject(
        required=required,
        active=active == 1,
        name=name,
        ptr=ptr,
        parent_name=parent_name,
        parent_ptr=parent_ptr,
    )
    # set the previous node to NULL
    return None, obj
コード例 #24
0
    def read(cls, reader: BinReader, anim_def: AnimDef) -> SoundNode:
        (
            name_raw,
            one32,
            inherit_trans,
            active_state,
            node_index,
            tx,
            ty,
            tz,
        ) = reader.read(cls._STRUCT)

        with assert_ascii("name", name_raw, reader.prev + 0):
            name = ascii_zterm_padded(name_raw)

        # this would cause the sound node to be dynamically allocated
        assert_eq("field 32", 1, one32, reader.prev + 32)  # 44
        assert_in("active state", (0, 1), active_state, reader.prev + 40)  # 52

        # this is actually a bit field.
        # if it's 1, then the translation would be applied directly to the node
        # if it's 2 and AT_NODE is given, then the translation is applied relative
        # to the node
        assert_in("field 36", (0, 2), inherit_trans, reader.prev + 36)  # 48

        if inherit_trans == 0:
            at_node = None
            assert_eq("at node", 0, node_index, reader.prev + 44)
            assert_eq("at tx", 0.0, tx, reader.prev + 48)
            assert_eq("at ty", 0.0, ty, reader.prev + 52)
            assert_eq("at tz", 0.0, tz, reader.prev + 56)
        else:
            node = anim_def.get_node(node_index - 1, reader.prev + 44)
            at_node = AtNodeShort(node=node, tx=tx, ty=ty, tz=tz)

        return cls(name=name, active_state=active_state == 1, at_node=at_node)
コード例 #25
0
def _read_reset_state(
    reader: BinReader,
    anim_def: AnimDef,
    length: int,
    ptr: int,
    offset: int,
) -> Optional[SeqDef]:
    reset_raw, reset_ptr, reset_len = reader.read(RESET_STATE)
    with assert_ascii("reset end", reset_raw, reader.prev + 0):
        reset_end = ascii_zterm_padded(reset_raw)

    # this is always "RESET_SEQUENCE"
    assert_eq("reset end", "RESET_SEQUENCE", reset_end, reader.prev + 0)
    assert_eq("reset ptr", ptr, reset_ptr, reader.prev + 56)
    assert_eq("reset len", length, reset_len, reader.prev + 60)

    if not length:
        assert_eq("reset ptr", 0, ptr, offset)
        return None

    assert_ne("reset ptr", 0, ptr, offset)
    script = _parse_script(reader, anim_def, length)
    return SeqDef(name="RESET_SEQUENCE", ptr=ptr, script=script)
コード例 #26
0
def _read_nodes(reader: BinReader, count: int) -> List[NamePtrFlag]:
    # the first entry is always zero
    name_raw, zero, ptr = reader.read(NODE)
    assert_all_zero("name", name_raw, reader.prev + 0)
    assert_eq("field 32", 0, zero, reader.prev + 32)
    assert_eq("field 36", 0, ptr, reader.prev + 36)

    nodes = []
    for _ in range(1, count):
        name_raw, zero, ptr = reader.read(NODE)
        with assert_ascii("name", name_raw, reader.prev + 0):
            name = ascii_zterm_node_name(name_raw)
        assert_eq("field 32", 0, zero, reader.prev + 32)
        assert_ne("field 36", 0, ptr, reader.prev + 36)
        nodes.append(NamePtrFlag(name=name, ptr=ptr))

    return nodes
コード例 #27
0
ファイル: fog.py プロジェクト: tobywf/mech3ax-py
    def read(cls, reader: BinReader, _anim_def: AnimDef) -> FogState:
        (
            name_raw,
            flag_raw,
            fog_type,
            color_r,
            color_g,
            color_b,
            altitude_min,
            altitude_max,
            range_min,
            range_max,
        ) = reader.read(cls._STRUCT)
        assert_eq("name", DEFAULT_FOG_NAME, name_raw, reader.prev + 0)
        # see LIGHT_STATE - in this case, all FOG_STATEs use the same flags
        # 1 << 0 set fog state
        # 1 << 1 set fog color
        # 1 << 2 set fog altitude
        # 1 << 3 set fog range
        assert_eq("flag", 14, flag_raw, reader.prev + 32)
        # OFF = 0, LINEAR = 1, EXPONENTIAL = 2
        assert_eq("fog type", 1, fog_type, reader.prev + 36)

        assert_between("red", 0.0, 1.0, color_r, reader.prev + 40)
        assert_between("green", 0.0, 1.0, color_g, reader.prev + 44)
        assert_between("blue", 0.0, 1.0, color_b, reader.prev + 48)

        # altitude is always ordered this way even negative (unlike range)
        assert_ge("altitude max", altitude_min, altitude_max, reader.prev + 56)

        assert_ge("range min", 0.0, range_min, reader.prev + 60)
        assert_ge("range max", range_min, range_max, reader.prev + 64)

        return cls(
            fog_type="LINEAR",
            color=(color_r, color_g, color_b),
            altitude=(altitude_min, altitude_max),
            range=(range_min, range_max),
        )
コード例 #28
0
ファイル: node_data_read.py プロジェクト: tobywf/mech3ax-py
def _read_node_data_light(  # pylint: disable=too-many-locals
        reader: BinReader, ) -> Light:
    (
        direction_x,  # 000
        direction_y,  # 004
        direction_z,  # 008
        trans_x,  # 012
        trans_y,  # 016
        trans_z,  # 020
        zero024,  # 112 zero bytes
        one136,
        zero140,
        zero144,
        zero148,
        zero152,
        diffuse,  # 156
        ambient,  # 160
        color_r,  # 164
        color_g,  # 168
        color_b,  # 172
        flag_raw,  # 176
        range_min,  # 180
        range_max,  # 184
        range_min_sq,  # 188
        range_max_sq,  # 192
        range_inv,  # 196
        parent_count,  # 200
        parent_ptr,  # 204
        zero208,
    ) = reader.read(LIGHT)

    # translation is never set
    assert_eq("trans x", 0.0, trans_x, reader.prev + 12)
    assert_eq("trans y", 0.0, trans_y, reader.prev + 16)
    assert_eq("trans z", 0.0, trans_z, reader.prev + 20)

    assert_all_zero("field 024", zero024, reader.prev + 24)

    assert_eq("field 136", 1.0, one136, reader.prev + 136)
    assert_eq("field 140", 0.0, zero140, reader.prev + 140)
    assert_eq("field 144", 0.0, zero144, reader.prev + 144)
    assert_eq("field 148", 0.0, zero148, reader.prev + 148)
    assert_eq("field 152", 0.0, zero152, reader.prev + 152)

    assert_between("diffuse", 0.0, 1.0, diffuse, reader.prev + 156)
    assert_between("ambient", 0.0, 1.0, ambient, reader.prev + 160)

    assert_eq("color r", 1.0, color_r, reader.prev + 164)
    assert_eq("color g", 1.0, color_g, reader.prev + 168)
    assert_eq("color b", 1.0, color_b, reader.prev + 172)

    with assert_flag("flag", flag_raw, reader.prev + 176):
        flag = LightFlag.check(flag_raw)

    assert_eq("flag", LIGHT_FLAG, flag, reader.prev + 176)

    assert_gt("range min", 0.0, range_min, reader.prev + 180)
    assert_gt("range max", range_min, range_max, reader.prev + 184)
    expected = range_min * range_min
    assert_eq("range min sq", expected, range_min_sq, reader.prev + 188)
    expected = range_max * range_max
    assert_eq("range max sq", expected, range_max_sq, reader.prev + 192)
    expected = force_single_prec(1.0 / (range_max - range_min))
    assert_eq("range inv", expected, range_inv, reader.prev + 196)

    # if this was ever zero, field 208 wouldn't be read
    assert_eq("parent count", 1, parent_count, reader.prev + 200)
    assert_ne("parent ptr", 0, parent_ptr, reader.prev + 204)
    assert_eq("field 208", 0, zero208, reader.prev + 208)

    # light nodes always have an action priority of 9

    return Light(
        type="Light",
        direction=(direction_x, direction_y, direction_z),
        diffuse=diffuse,
        ambient=ambient,
        color=(color_r, color_g, color_b),
        range=(range_min, range_max),
        parent_ptr=parent_ptr,
    )
コード例 #29
0
ファイル: node_data_read.py プロジェクト: tobywf/mech3ax-py
def _read_node_data_camera(  # pylint: disable=too-many-locals
        reader: BinReader, ) -> Camera:
    (
        world_index,  # 000
        window_index,  # 004
        focus_node_xy,  # 008
        focus_node_xz,  # 012
        flag_raw,  # 016
        trans_x,  # 020
        trans_y,  # 024
        trans_z,  # 028
        rot_x,  # 032
        rot_y,  # 036
        rot_z,  # 040
        zero044,  # 132 zero bytes
        clip_near_z,  # 176
        clip_far_z,  # 180
        zero184,  # 24 zero bytes
        lod_multiplier,  # 208
        lod_inv_sq,  # 212
        fov_h_zoom_factor,  # 216
        fov_v_zoom_factor,  # 220
        fov_h_base,  # 224
        fov_v_base,  # 228
        fov_h,  # 232
        fov_v,  # 236
        fov_h_half,  # 240
        fov_v_half,  # 244
        one248,
        zero252,  # 60 zero bytes
        one312,
        zero316,  # 72 zero bytes
        one388,
        zero392,  # 72 zero bytes
        zero464,
        fov_h_tan_inv,  # 468
        fov_v_tan_inv,  # 472
        stride,
        zone_set,
        unk484,
    ) = reader.read(CAMERA)

    assert_eq("world index", 0, world_index, reader.prev + 0)
    assert_eq("window index", 1, window_index, reader.prev + 4)
    assert_eq("focus node xy", -1, focus_node_xy, reader.prev + 8)
    assert_eq("focus node xz", -1, focus_node_xz, reader.prev + 12)
    assert_eq("flag", 0, flag_raw, reader.prev + 16)

    assert_eq("trans x", 0.0, trans_x, reader.prev + 20)
    assert_eq("trans y", 0.0, trans_y, reader.prev + 24)
    assert_eq("trans z", 0.0, trans_z, reader.prev + 28)

    assert_eq("rot x", 0.0, rot_x, reader.prev + 32)
    assert_eq("rot y", 0.0, rot_y, reader.prev + 36)
    assert_eq("rot z", 0.0, rot_z, reader.prev + 40)

    # WorldTranslate: Vec3
    # WorldRotate: Vec3
    # MtwMatrix: Mat
    # Unk: Vec3
    # ViewVector: Vec3
    # Matrix: Mat
    # AltTranslate: Vec3
    assert_all_zero("field 044", zero044, reader.prev + 44)

    assert_gt("clip near z", 0.0, clip_near_z, reader.prev + 176)
    assert_gt("clip far z", clip_near_z, clip_far_z, reader.prev + 180)

    assert_all_zero("field 184", zero184, reader.prev + 184)

    assert_eq("LOD mul", 1.0, lod_multiplier, reader.prev + 208)
    assert_eq("LOD inv sq", 1.0, lod_inv_sq, reader.prev + 212)

    assert_eq("FOV H zoom factor", 1.0, fov_h_zoom_factor, reader.prev + 216)
    assert_eq("FOV V zoom factor", 1.0, fov_v_zoom_factor, reader.prev + 220)
    assert_gt("FOV H base", 0.0, fov_h_base, reader.prev + 224)
    assert_gt("FOV V base", 0.0, fov_v_base, reader.prev + 228)
    assert_eq("FOV H zoomed", fov_h_base, fov_h, reader.prev + 232)
    assert_eq("FOV V zoomed", fov_v_base, fov_v, reader.prev + 236)
    assert_eq("FOV H half", fov_h / 2.0, fov_h_half, reader.prev + 240)
    assert_eq("FOV V half", fov_v / 2.0, fov_v_half, reader.prev + 244)

    assert_eq("field 248", 1, one248, reader.prev + 248)
    assert_all_zero("field 252", zero252, reader.prev + 252)

    assert_eq("field 312", 1, one312, reader.prev + 312)
    assert_all_zero("field 316", zero316, reader.prev + 316)

    assert_eq("field 388", 1, one388, reader.prev + 388)
    assert_all_zero("field 392", zero392, reader.prev + 392)

    assert_eq("field 464", 0, zero464, reader.prev + 464)

    expected = force_single_prec(1.0 / tan(fov_h_half))
    assert_eq("FOV H tan inv", expected, fov_h_tan_inv, reader.prev + 468)
    expected = force_single_prec(1.0 / tan(fov_v_half))
    assert_eq("FOV V tan inv", expected, fov_v_tan_inv, reader.prev + 472)

    assert_eq("stride", 0, stride, reader.prev + 476)
    assert_eq("zone set", 0, zone_set, reader.prev + 480)
    assert_eq("field 484", -256, unk484, reader.prev + 484)

    return Camera(type="Camera",
                  clip=(clip_near_z, clip_far_z),
                  fov=(fov_h_base, fov_v_base))
コード例 #30
0
ファイル: node_data_read.py プロジェクト: tobywf/mech3ax-py
 def _assert_matrix(expected: Matrix) -> None:
     assert_eq("matrix 00", expected[0], matrix00, reader.prev + 48)
     assert_eq("matrix 01", expected[1], matrix01, reader.prev + 52)
     assert_eq("matrix 02", expected[2], matrix02, reader.prev + 56)
     assert_eq("matrix 10", expected[3], matrix10, reader.prev + 60)
     assert_eq("matrix 11", expected[4], matrix11, reader.prev + 64)
     assert_eq("matrix 12", expected[5], matrix12, reader.prev + 68)
     assert_eq("matrix 20", expected[6], matrix20, reader.prev + 72)
     assert_eq("matrix 21", expected[7], matrix21, reader.prev + 76)
     assert_eq("matrix 22", expected[8], matrix22, reader.prev + 80)