diff --git a/io_scene_halo/file_jma/build_scene.py b/io_scene_halo/file_jma/build_scene.py index c3371a7ef..1e77fc085 100644 --- a/io_scene_halo/file_jma/build_scene.py +++ b/io_scene_halo/file_jma/build_scene.py @@ -294,7 +294,7 @@ def get_pose_bone(arm: bpy.types.Object, node_name: str) -> bpy.types.PoseBone | for bone in arm.pose.bones: if bone.name.lower() == node_name.lower(): return bone - + # If a bone hasn't been found yet, test whether the node name matches a bone name stripped of prefixes for bone in arm.pose.bones: if remove_node_prefix(bone.name).lower() == node_name.lower(): diff --git a/io_scene_halo/file_jma/process_scene.py b/io_scene_halo/file_jma/process_scene.py index 389033b60..0a47a5252 100644 --- a/io_scene_halo/file_jma/process_scene.py +++ b/io_scene_halo/file_jma/process_scene.py @@ -104,7 +104,6 @@ def process_scene(context, extension, jma_version, game_title, generate_checksum node_list.append(obj) JMA.node_count = len(node_list) - sorted_list = global_functions.sort_list(node_list, armature, game_title, jma_version, True) joined_list = sorted_list[0] reversed_joined_list = sorted_list[1] diff --git a/io_scene_halo/file_qua/export_qua.py b/io_scene_halo/file_qua/export_qua.py index 290ea2835..04e0787ad 100644 --- a/io_scene_halo/file_qua/export_qua.py +++ b/io_scene_halo/file_qua/export_qua.py @@ -24,6 +24,7 @@ # # ##### END MIT LICENSE BLOCK ##### +import os import bpy from decimal import * diff --git a/io_scene_halo/file_tag/build_scene/build_bsp.py b/io_scene_halo/file_tag/build_scene/build_bsp.py index c1abf498f..8aa185581 100644 --- a/io_scene_halo/file_tag/build_scene/build_bsp.py +++ b/io_scene_halo/file_tag/build_scene/build_bsp.py @@ -28,10 +28,11 @@ import bpy import bmesh +from ... import config from sys import float_info from math import radians, log from mathutils import Matrix, Vector -from ..h2.file_scenario_structure_bsp.format import ClusterPortalFlags as H2ClusterPortalFlags, SurfaceFlags as H2SurfaceFlags, PartFlags +from ..h2.file_scenario_structure_bsp.format import ClusterPortalFlags as H2ClusterPortalFlags, SurfaceFlags as H2SurfaceFlags, PartFlags, PropertyTypeEnum from ...global_functions import shader_processing, mesh_processing, global_functions from ..h1.file_scenario_structure_bsp.format import ClusterPortalFlags as H1ClusterPortalFlags, SurfaceFlags as H1SurfaceFlags @@ -502,11 +503,46 @@ def build_scene(context, LEVEL, game_version, game_title, file_version, fix_rota mesh.materials.append(mat) else: + shader_collection_dic = {} + shader_collection_path = os.path.join(config.HALO_2_TAG_PATH, r"scenarios\shaders\shader_collections.shader_collections") + shader_collection_file = open(shader_collection_path, "r") + for line in shader_collection_file.readlines(): + if not global_functions.string_empty_check(line) and not line.startswith(";"): + split_result = line.split() + if len(split_result) == 2: + prefix = split_result[0] + path = split_result[1] + shader_collection_dic[path] = prefix + materials = [] for material in LEVEL.materials: - material_name = os.path.basename(material.shader.name) - if global_functions.string_empty_check(material_name): - material_name = os.path.basename(material.old_shader.name) + material_path = material.shader.name + if global_functions.string_empty_check(material_path): + material_path = material.old_shader.name + + material_directory = os.path.dirname(material_path) + material_name = os.path.basename(material_path) + + collection_prefix = shader_collection_dic.get(material_directory) + if not collection_prefix == None: + material_name = "%s %s" % (collection_prefix, material_name) + else: + print("Could not find a collection for: %s" % material_path) + + for material_property in material.properties: + property_enum = PropertyTypeEnum(material_property.property_type) + property_value = material_property.real_value + if PropertyTypeEnum.lightmap_resolution == property_enum: + material_name += " lm:%s" % property_value + + elif PropertyTypeEnum.lightmap_power == property_enum: + material_name += " lp:%s" % property_value + + elif PropertyTypeEnum.lightmap_half_life == property_enum: + material_name += " hl:%s" % property_value + + elif PropertyTypeEnum.lightmap_diffuse_scale == property_enum: + material_name += " ds:%s" % property_value mat = bpy.data.materials.new(name=material_name) shader_processing.generate_h2_shader(mat, material.shader, report) @@ -542,7 +578,7 @@ def build_scene(context, LEVEL, game_version, game_title, file_version, fix_rota object_mesh = bpy.data.objects.new(ob_name, mesh) object_mesh.tag_view.data_type_enum = '16' object_mesh.tag_view.instance_lightmap_policy_enum = str(instanced_geometry_instance.lightmapping_policy) - + object_mesh.parent = level_root cluster_collection_override.objects.link(object_mesh) @@ -652,11 +688,21 @@ def build_scene(context, LEVEL, game_version, game_title, file_version, fix_rota if not ngon_material_index == -1: if game_title == "halo1": - shader = mat.shader_tag_ref + shader_path = mat.shader_tag_ref.name + material_name = os.path.basename(shader_path) else: - shader = mat.new_shader + shader_path = mat.new_shader.name + + material_directory = os.path.dirname(shader_path) + material_name = os.path.basename(shader_path) + + collection_prefix = shader_collection_dic.get(material_directory) + if not collection_prefix == None: + material_name = "%s %s" % (collection_prefix, material_name) + else: + print("Could not find a collection for: %s" % material_path) - material_name = os.path.basename(shader.name) + if game_title == "halo1": if H1SurfaceFlags.two_sided in H1SurfaceFlags(surface.flags): material_name += "%" diff --git a/io_scene_halo/file_tag/build_scene/build_collision.py b/io_scene_halo/file_tag/build_scene/build_collision.py index 09588c955..fb31e05e3 100644 --- a/io_scene_halo/file_tag/build_scene/build_collision.py +++ b/io_scene_halo/file_tag/build_scene/build_collision.py @@ -39,14 +39,20 @@ def build_pathfinding_spheres(context, armature, COLLISION, fix_rotations, empty parent_idx = pathfinding_sphere.node object_name = '#pathfinding_sphere_%s' % pathfinding_sphere_idx - mesh = bpy.data.meshes.new(object_name) - object_mesh = bpy.data.objects.new(object_name, mesh) + if empty_markers: + object_mesh = bpy.data.objects.new(object_name, None) + + else: + mesh = bpy.data.meshes.new(object_name) + object_mesh = bpy.data.objects.new(object_name, mesh) + collection.objects.link(object_mesh) - bm = bmesh.new() - bmesh.ops.create_uvsphere(bm, u_segments=32, v_segments=16, radius=1) - bm.to_mesh(mesh) - bm.free() + if not empty_markers: + bm = bmesh.new() + bmesh.ops.create_uvsphere(bm, u_segments=32, v_segments=16, radius=1) + bm.to_mesh(mesh) + bm.free() matrix_translate = Matrix.Translation(pathfinding_sphere.center) @@ -77,7 +83,9 @@ def build_pathfinding_spheres(context, armature, COLLISION, fix_rotations, empty object_mesh.parent = armature object_mesh.matrix_world = transform_matrix - object_mesh.data.ass_jms.Object_Type = 'SPHERE' + if not empty_markers: + object_mesh.data.ass_jms.Object_Type = 'SPHERE' + object_mesh.ass_jms.marker_mask_type = '1' object_mesh.select_set(False) armature.select_set(False) diff --git a/io_scene_halo/file_tag/build_scene/build_physics.py b/io_scene_halo/file_tag/build_scene/build_physics.py index 5501dd0d2..f70ed6259 100644 --- a/io_scene_halo/file_tag/build_scene/build_physics.py +++ b/io_scene_halo/file_tag/build_scene/build_physics.py @@ -40,6 +40,8 @@ def build_scene(context, PHYSICS, game_version, game_title, file_version, fix_ro if armature: for mass_point_idx, mass_point in enumerate(PHYSICS.mass_points): + if game_title == "halo2" and mass_point.powered_mass_point < 0: + continue parent_idx = mass_point.model_node object_name_prefix = '#%s' % mass_point.name @@ -48,16 +50,22 @@ def build_scene(context, PHYSICS, game_version, game_title, file_version, fix_ro if context.scene.objects.get('#%s' % mass_point.name): marker_name_override = mass_point.name - mesh = bpy.data.meshes.new(object_name_prefix) - object_mesh = bpy.data.objects.new(object_name_prefix, mesh) + if empty_markers: + object_mesh = bpy.data.objects.new(object_name_prefix, None) + + else: + mesh = bpy.data.meshes.new(object_name_prefix) + object_mesh = bpy.data.objects.new(object_name_prefix, mesh) + collection.objects.link(object_mesh) object_mesh.ass_jms.name_override = marker_name_override - bm = bmesh.new() - bmesh.ops.create_uvsphere(bm, u_segments=32, v_segments=16, radius=1) - bm.to_mesh(mesh) - bm.free() + if not empty_markers: + bm = bmesh.new() + bmesh.ops.create_uvsphere(bm, u_segments=32, v_segments=16, radius=1) + bm.to_mesh(mesh) + bm.free() node_count = len(armature.data.bones) if not parent_idx == -1 and not parent_idx >= node_count: @@ -83,8 +91,12 @@ def build_scene(context, PHYSICS, game_version, game_title, file_version, fix_ro transform_matrix = matrix_translate @ matrix_rotation @ scale object_mesh.matrix_world = transform_matrix - object_mesh.data.ass_jms.Object_Type = 'SPHERE' - object_mesh.ass_jms.marker_mask_type = '2' + if not empty_markers: + object_mesh.data.ass_jms.Object_Type = 'SPHERE' + if game_title == "halo2": + object_mesh.ass_jms.marker_mask_type = '0' + else: + object_mesh.ass_jms.marker_mask_type = '2' else: report({'ERROR'}, "No valid armature is active. Import will now be aborted") diff --git a/io_scene_halo/file_tag/build_scene/generate_h2_scenario.py b/io_scene_halo/file_tag/build_scene/generate_h2_scenario.py index b6de7332b..ae6bffaf8 100644 --- a/io_scene_halo/file_tag/build_scene/generate_h2_scenario.py +++ b/io_scene_halo/file_tag/build_scene/generate_h2_scenario.py @@ -241,6 +241,9 @@ def generate_object_elements(level_root, collection_name, palette, tag_block, co root.parent = level_root root.location = element.position * 100 + ob_scale = element.scale + if ob_scale > 0.0: + root.scale = (ob_scale, ob_scale, ob_scale) get_data_type(collection_name, root, pallete_item.name, element) diff --git a/io_scene_halo/file_tag/h1/file_model/build_mesh.py b/io_scene_halo/file_tag/h1/file_model/build_mesh.py index 4b938f92d..619b2263a 100644 --- a/io_scene_halo/file_tag/h1/file_model/build_mesh.py +++ b/io_scene_halo/file_tag/h1/file_model/build_mesh.py @@ -117,6 +117,7 @@ def build_mesh_layout(asset, geometry, region_name, object_name, game_version, i region_attribute = mesh.get_custom_attribute() mesh.normals_split_custom_set_from_vertices(vertex_normals) + for vertex_idx, vertex in enumerate(vertex_data): node_0_index = vertex.node_0_index node_1_index = vertex.node_1_index diff --git a/io_scene_halo/file_tag/h1/file_model/process_file.py b/io_scene_halo/file_tag/h1/file_model/process_file.py index 42b869bd9..617bbac04 100644 --- a/io_scene_halo/file_tag/h1/file_model/process_file.py +++ b/io_scene_halo/file_tag/h1/file_model/process_file.py @@ -69,7 +69,7 @@ def process_file(input_stream, report): MODEL.mode_body.stubbs_unk_tag_block = TAG.TagBlock().read(input_stream, TAG, tag_format.XMLData(tag_node, "stubbs_unk_arr")) else: input_stream.read(12) # Padding? - + MODEL.mode_body.markers_tag_block = TAG.TagBlock().read(input_stream, TAG, tag_format.XMLData(tag_node, "markers")) MODEL.mode_body.nodes_tag_block = TAG.TagBlock().read(input_stream, TAG, tag_format.XMLData(tag_node, "nodes")) MODEL.mode_body.regions_tag_block = TAG.TagBlock().read(input_stream, TAG, tag_format.XMLData(tag_node, "regions")) diff --git a/io_scene_halo/file_tag/h1/file_physics/format.py b/io_scene_halo/file_tag/h1/file_physics/format.py index c3dee4387..3ee9edb27 100644 --- a/io_scene_halo/file_tag/h1/file_physics/format.py +++ b/io_scene_halo/file_tag/h1/file_physics/format.py @@ -56,8 +56,7 @@ def __init__(self): class PhysBody: def __init__(self, radius=0.0, moment_scale=0.0, mass=0.0, center_of_mass=Vector(), density=0.0, gravity_scale=0.0, ground_friction=0.0, ground_depth=0.0, ground_damp_fraction=0.0, ground_normal_k1=0.0, ground_normal_k0=0.0, water_friction=0.0, water_depth=0.0, water_density=0.0, air_friction=0.0, - xx_moment=0.0, yy_moment=0.0, zz_moment=0.0, inertial_matrix_and_inverse_tag_block=None, powered_mass_points_tag_block=None, mass_points_tag_block=None, - inertial_matrix_and_inverse=None, powered_mass_points=None, mass_points=None): + xx_moment=0.0, yy_moment=0.0, zz_moment=0.0, inertial_matrix_and_inverse_tag_block=None, powered_mass_points_tag_block=None, mass_points_tag_block=None): self.radius = radius self.moment_scale = moment_scale self.mass = mass @@ -79,9 +78,6 @@ def __init__(self, radius=0.0, moment_scale=0.0, mass=0.0, center_of_mass=Vector self.inertial_matrix_and_inverse_tag_block = inertial_matrix_and_inverse_tag_block self.powered_mass_points_tag_block = powered_mass_points_tag_block self.mass_points_tag_block = mass_points_tag_block - self.inertial_matrix_and_inverse = inertial_matrix_and_inverse - self.powered_mass_points = powered_mass_points - self.mass_points = mass_points class InertialMatrixAndInverse: def __init__(self, yy_zz_xy_zx=Vector(), xy_zz_xx_yz=Vector(), zx_yz_xx_yy=Vector()): diff --git a/io_scene_halo/file_tag/h1/file_scenario/upgrade_scenario.py b/io_scene_halo/file_tag/h1/file_scenario/upgrade_scenario.py index 0436335f7..6904946a5 100644 --- a/io_scene_halo/file_tag/h1/file_scenario/upgrade_scenario.py +++ b/io_scene_halo/file_tag/h1/file_scenario/upgrade_scenario.py @@ -25,6 +25,7 @@ # ##### END MIT LICENSE BLOCK ##### import bpy +import struct import random from math import radians @@ -248,7 +249,7 @@ def get_item_stats(collection_name): spawn_time = 110 respawn_on_empty_time = 90 - elif "turret" in collection_name: # We are assuming all turrets are Covenant cause that's all there is in vanilla + elif "turret" in collection_name: # We are assuming all turrets are Covenant cause that's all there is in the H1 sandbox classification = ClassificationEnum.secondary_turret.value spawn_time = 100 respawn_on_empty_time = 120 @@ -1403,6 +1404,185 @@ def block_mutation(TAG, H1_SCENARIO, SCENARIO): SCENARIO.sound_scenery_palette_header = TAG.TagBlockHeader("tbfd", 0, len(SCENARIO.sound_scenery_palette), 48) SCENARIO.crates_palette_header = TAG.TagBlockHeader("tbfd", 0, len(SCENARIO.crates_palette), 48) +def merge_by_name(base_tag_block, donor_tag_block, name_type=0): + base_entries = [] + for base_element in base_tag_block: + entry_name = "" + if name_type == 0: + entry_name = base_element.name + elif name_type == 1: + entry_name = base_element.animation_name + elif name_type == 2: + entry_name = base_element + + base_entries.append(entry_name) + + for donor_element in donor_tag_block: + entry_name = "" + if name_type == 0: + entry_name = donor_element.name + elif name_type == 1: + entry_name = donor_element.animation_name + elif name_type == 2: + entry_name = donor_element + + if not entry_name in base_entries: + base_tag_block.append(donor_element) + +def merge_scavenger_hunt_objects(base_scavenger_hunt_objects, donor_scavenger_hunt_objects, base_object_names, donor_object_names): + base_indices = [base_object.scenario_object_name_index for base_object in base_scavenger_hunt_objects] + base_names = [object_name for object_name in base_object_names] + donor_names = [object_name for object_name in donor_object_names] + + for donor_element in donor_scavenger_hunt_objects: + if donor_element.scenario_object_name_index >= 0: + donor_name = donor_names[donor_element.scenario_object_name_index] + base_name_index = base_names.index(donor_name) + donor_element.scenario_object_name_index = base_name_index + + if not base_name_index in base_indices: + base_scavenger_hunt_objects.append(donor_element) + +def merge_objects(base_objects, donor_object, base_palette, donor_palette, base_object_names=None, donor_object_names=None, base_device_groups=None, donor_device_groups=None): + base_palette_names = [base_element.name for base_element in base_palette] + donor_palette_names = [object_element.name for object_element in donor_palette] + base_names = [] + donor_names = [] + base_device_groups_names = [] + donor_device_groups_names = [] + if not base_object_names == None: + base_names = [object_name for object_name in base_object_names] + if not donor_object_names == None: + donor_names = [object_name for object_name in donor_object_names] + if not base_device_groups == None: + base_device_groups_names = [base_device_group.name for base_device_group in base_device_groups] + if not donor_device_groups == None: + donor_device_groups_names = [donor_device_group.name for donor_device_group in donor_device_groups] + + for donor_element in donor_palette: + if not donor_element.name in base_palette_names: + base_palette.append(donor_element) + + base_palette_names = [base_element.name for base_element in base_palette] + for donor_element in donor_object: + if not base_object_names == None: + donor_type_index = donor_palette_names[donor_element.type_index] + donor_element.type_index = base_palette_names.index(donor_type_index) + donor_name_index = donor_names[donor_element.name_index] + donor_element.name_index = base_names.index(donor_name_index) + if not base_device_groups == None: + if donor_element.power_group_index >= 0: + power_group_index = donor_device_groups_names[donor_element.power_group_index] + donor_element.power_group_index = base_device_groups_names.index(power_group_index) + + if not donor_device_groups == None: + if donor_element.position_group_index >= 0: + position_group_index = donor_device_groups_names[donor_element.position_group_index] + donor_element.position_group_index = base_device_groups_names.index(position_group_index) + + else: + donor_type_index = donor_palette_names[donor_element.palette_index] + donor_element.palette_index = base_palette_names.index(donor_type_index) + + base_objects.append(donor_element) + +def merge_by_location(base_tag_block, donor_tag_block): + location_checksums = [] + for base_element in base_tag_block: + x, y, z = base_element.position + x_int = struct.unpack('= 0: + use_this_object_index = donor_names[participant.use_this_object] + participant.use_this_object = base_names.index(use_this_object_index) + + if participant.set_new_name >= 0: + set_new_name_index = donor_names[participant.set_new_name] + participant.set_new_name = base_names.index(set_new_name_index) + + base_tag_block.append(donor_element) + +def merge_encounters(base_encounters, donor_encounters, base_palette, donor_palette): + base_encounters_names = [base_element.name for base_element in base_encounters] + base_palette_names = [base_element.name for base_element in base_palette] + donor_palette_names = [object_element.name for object_element in donor_palette] + for donor_element in donor_palette: + if not donor_element.name in base_palette_names: + base_palette.append(donor_element) + + base_palette_names = [base_element.name for base_element in base_palette] + for donor_element in donor_encounters: + if not donor_element.name in base_encounters_names: + for squad_element in donor_element.squads: + if squad_element.actor_type >= 0: + donor_actor_index = donor_palette_names[squad_element.actor_type] + squad_element.actor_type = base_palette_names.index(donor_actor_index) + + base_encounters.append(donor_element) + +def merge_child_scenarios(TAG, SCENARIO, report): + for child_scenario_element in SCENARIO.child_scenarios: + CHILD = child_scenario_element.parse_tag(report, "halo1", "retail") + + merge_by_name(SCENARIO.skies, CHILD.skies) + merge_by_name(SCENARIO.object_names, CHILD.object_names, 2) + merge_by_location(SCENARIO.comments, CHILD.comments) + merge_scavenger_hunt_objects(SCENARIO.scavenger_hunt_objects, CHILD.scavenger_hunt_objects, SCENARIO.object_names, CHILD.object_names) + merge_objects(SCENARIO.scenery, CHILD.scenery, SCENARIO.scenery_palette, CHILD.scenery_palette, SCENARIO.object_names, CHILD.object_names) + merge_objects(SCENARIO.bipeds, CHILD.bipeds, SCENARIO.biped_palette, CHILD.biped_palette, SCENARIO.object_names, CHILD.object_names) + merge_objects(SCENARIO.vehicles, CHILD.vehicles, SCENARIO.vehicle_palette, CHILD.vehicle_palette, SCENARIO.object_names, CHILD.object_names) + merge_objects(SCENARIO.equipment, CHILD.equipment, SCENARIO.equipment_palette, CHILD.equipment_palette, SCENARIO.object_names, CHILD.object_names) + merge_objects(SCENARIO.weapons, CHILD.weapons, SCENARIO.weapon_palette, CHILD.weapon_palette, SCENARIO.object_names, CHILD.object_names) + merge_by_name(SCENARIO.device_groups, CHILD.device_groups) + merge_objects(SCENARIO.device_machines, CHILD.device_machines, SCENARIO.device_machine_palette, CHILD.device_machine_palette, SCENARIO.object_names, CHILD.object_names, SCENARIO.device_groups, CHILD.device_groups) + merge_objects(SCENARIO.device_controls, CHILD.device_controls, SCENARIO.device_control_palette, CHILD.device_control_palette, SCENARIO.object_names, CHILD.object_names, SCENARIO.device_groups, CHILD.device_groups) + merge_objects(SCENARIO.device_light_fixtures, CHILD.device_light_fixtures, SCENARIO.device_light_fixtures_palette, CHILD.device_light_fixtures_palette, SCENARIO.object_names, CHILD.object_names, SCENARIO.device_groups, CHILD.device_groups) + merge_objects(SCENARIO.sound_scenery, CHILD.sound_scenery, SCENARIO.sound_scenery_palette, CHILD.sound_scenery_palette, SCENARIO.object_names, CHILD.object_names) + merge_by_name(SCENARIO.player_starting_profiles, CHILD.player_starting_profiles) + #merge_by_location(SCENARIO.player_starting_locations, CHILD.player_starting_locations) + merge_by_name(SCENARIO.trigger_volumes, CHILD.trigger_volumes) + merge_by_name(SCENARIO.recorded_animations, CHILD.recorded_animations) + merge_by_location(SCENARIO.netgame_flags, CHILD.netgame_flags) + merge_by_location(SCENARIO.netgame_equipment, CHILD.netgame_equipment) + merge_elements(SCENARIO.starting_equipment, CHILD.starting_equipment) + merge_elements(SCENARIO.bsp_switch_trigger_volumes, CHILD.bsp_switch_trigger_volumes) + merge_objects(SCENARIO.decals, CHILD.decals, SCENARIO.decal_palette, CHILD.decal_palette) + merge_by_name(SCENARIO.detail_object_collection_palette, CHILD.detail_object_collection_palette) + merge_encounters(SCENARIO.encounters, CHILD.encounters, SCENARIO.actor_palette, CHILD.actor_palette) + merge_by_name(SCENARIO.command_lists, CHILD.command_lists) + merge_by_name(SCENARIO.ai_animation_references, CHILD.ai_animation_references, 1) + merge_by_name(SCENARIO.ai_script_references, CHILD.ai_script_references, 2) + merge_ai_conversations(SCENARIO.ai_conversations, CHILD.ai_conversations, SCENARIO.object_names, CHILD.object_names) + merge_by_name(SCENARIO.source_files, CHILD.source_files) + merge_by_name(SCENARIO.cutscene_flags, CHILD.cutscene_flags) + merge_by_name(SCENARIO.cutscene_camera_points, CHILD.cutscene_camera_points) + merge_by_name(SCENARIO.cutscene_titles, CHILD.cutscene_titles) + merge_by_name(SCENARIO.structure_bsps, CHILD.structure_bsps) + def upgrade_h2_scenario(H1_ASSET, patch_txt_path, report): TAG = tag_format.TagAsset() SCENARIO = ScenarioAsset() @@ -1521,6 +1701,8 @@ def upgrade_h2_scenario(H1_ASSET, patch_txt_path, report): SCENARIO.screen_effect_references = [] SCENARIO.simulation_definition_table = [] + merge_child_scenarios(TAG, H1_ASSET, report) + block_mutation(TAG, H1_ASSET, SCENARIO) SCENARIO.skies_header, SCENARIO.skies = get_palette(TAG, H1_ASSET.skies, 16) diff --git a/io_scene_halo/file_tag/h1/file_shader_environment/upgrade_shader.py b/io_scene_halo/file_tag/h1/file_shader_environment/upgrade_shader.py index 3f87024bc..77870f22c 100644 --- a/io_scene_halo/file_tag/h1/file_shader_environment/upgrade_shader.py +++ b/io_scene_halo/file_tag/h1/file_shader_environment/upgrade_shader.py @@ -94,7 +94,7 @@ def add_parameter(SHADER, TAG, parameter_name="", enum=TypeEnum.bitmap, bitmap_n parameter.const_value = float_value parameter.const_color = rgba parameter.animation_properties = [] - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, 0, 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, 0, 28) parameter.animation_properties_tag_block = TAG.TagBlock() return parameter @@ -103,7 +103,7 @@ def generate_illum_opaque(shader_body, TAG, SHADER): parameter = add_parameter(SHADER, TAG, parameter_name="self_illum_color", enum=TypeEnum.color, rgba=(1.0, 1.0, 1.0, 1.0)) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.color, FunctionTypeEnum.constant, 32, shader_body.color_of_emitted_light) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -114,13 +114,13 @@ def generate_tex_bump(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) if not shader_body.base_map.name == "": parameter = add_parameter(SHADER, TAG, parameter_name="base_map", bitmap_name=shader_body.base_map.name, float_value=0.0) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -130,7 +130,7 @@ def generate_tex_bump(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -144,13 +144,13 @@ def generate_tex_bump_dprs_env(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) if not shader_body.base_map.name == "": parameter = add_parameter(SHADER, TAG, parameter_name="base_map", bitmap_name=shader_body.base_map.name, float_value=0.0) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -160,13 +160,13 @@ def generate_tex_bump_dprs_env(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) if not shader_body.reflection_cube_map.name == "": parameter = add_parameter(SHADER, TAG, parameter_name="environment_map", bitmap_name=shader_body.reflection_cube_map.name, float_value=0.0) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -184,7 +184,7 @@ def generate_tex_bump_env_alpha_test(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -197,7 +197,7 @@ def generate_tex_bump_env_alpha_test(shader_body, TAG, SHADER): if not shader_body.base_map.name == "": parameter = add_parameter(SHADER, TAG, parameter_name="base_map", bitmap_name=shader_body.base_map.name, float_value=0.0) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -207,13 +207,13 @@ def generate_tex_bump_env_alpha_test(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) if not shader_body.reflection_cube_map.name == "": parameter = add_parameter(SHADER, TAG, parameter_name="environment_map", bitmap_name=shader_body.reflection_cube_map.name, float_value=0.0) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -231,13 +231,13 @@ def generate_tex_bump_dprs_env_illum(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) if not shader_body.base_map.name == "": parameter = add_parameter(SHADER, TAG, parameter_name="base_map", bitmap_name=shader_body.base_map.name, float_value=0.0) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -247,13 +247,13 @@ def generate_tex_bump_dprs_env_illum(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) if not shader_body.reflection_cube_map.name == "": parameter = add_parameter(SHADER, TAG, parameter_name="environment_map", bitmap_name=shader_body.reflection_cube_map.name, float_value=0.0) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -270,7 +270,7 @@ def generate_tex_bump_dprs_env_illum(shader_body, TAG, SHADER): SHADER.parameters.append(add_parameter(SHADER, TAG, parameter_name="emissive_color", enum=TypeEnum.color, rgba=shader_body.color_of_emitted_light)) SHADER.parameters.append(add_parameter(SHADER, TAG, parameter_name="emissive_power", enum=TypeEnum.value, float_value=shader_body.power / 1000)) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -281,13 +281,13 @@ def generate_tex_bump_env_illum_3_channel(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) if not shader_body.base_map.name == "": parameter = add_parameter(SHADER, TAG, parameter_name="base_map", bitmap_name=shader_body.base_map.name, float_value=0.0) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -297,13 +297,13 @@ def generate_tex_bump_env_illum_3_channel(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) if not shader_body.reflection_cube_map.name == "": parameter = add_parameter(SHADER, TAG, parameter_name="environment_map", bitmap_name=shader_body.reflection_cube_map.name, float_value=0.0) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -320,7 +320,7 @@ def generate_tex_bump_env_illum_3_channel(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) SHADER.parameters.append(add_parameter(SHADER, TAG, parameter_name="lightmap_emmisive_map", bitmap_name=shader_body.map.name, float_value=0.0)) @@ -335,13 +335,13 @@ def generate_tex_bump_illum(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) if not shader_body.base_map.name == "": parameter = add_parameter(SHADER, TAG, parameter_name="base_map", bitmap_name=shader_body.base_map.name, float_value=0.0) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -351,7 +351,7 @@ def generate_tex_bump_illum(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -364,7 +364,7 @@ def generate_tex_bump_illum(shader_body, TAG, SHADER): SHADER.parameters.append(add_parameter(SHADER, TAG, parameter_name="emissive_color", enum=TypeEnum.color, rgba=shader_body.color_of_emitted_light)) SHADER.parameters.append(add_parameter(SHADER, TAG, parameter_name="emissive_power", enum=TypeEnum.value, float_value=shader_body.power / 1000)) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -375,13 +375,13 @@ def generate_tex_bump_illum_3_channel(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.bump_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) if not shader_body.base_map.name == "": parameter = add_parameter(SHADER, TAG, parameter_name="base_map", bitmap_name=shader_body.base_map.name, float_value=0.0) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -391,7 +391,7 @@ def generate_tex_bump_illum_3_channel(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.primary_detail_map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) @@ -404,7 +404,7 @@ def generate_tex_bump_illum_3_channel(shader_body, TAG, SHADER): add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_x, FunctionTypeEnum.constant, lower_bound=shader_body.map_scale) add_animation_property(SHADER, TAG, parameter, AnimationTypeEnum.bitmap_scale_y, FunctionTypeEnum.constant, lower_bound=shader_body.map_scale) - parameter.animation_properties_tag_block_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) + parameter.animation_properties_header = TAG.TagBlockHeader("tbfd", 0, len(parameter.animation_properties), 28) parameter.animation_properties_tag_block = TAG.TagBlock(len(parameter.animation_properties)) SHADER.parameters.append(parameter) SHADER.parameters.append(add_parameter(SHADER, TAG, parameter_name="lightmap_emmisive_map", bitmap_name=shader_body.map.name, float_value=0.0)) diff --git a/io_scene_halo/file_tag/h1/file_shader_model/process_file.py b/io_scene_halo/file_tag/h1/file_shader_model/process_file.py index aeed974f2..84ce982af 100644 --- a/io_scene_halo/file_tag/h1/file_shader_model/process_file.py +++ b/io_scene_halo/file_tag/h1/file_shader_model/process_file.py @@ -115,7 +115,6 @@ def process_file(input_stream, report): SHADER.shader_body.parallel_brightness = TAG.read_float(input_stream, TAG, tag_format.XMLData(tag_node, "parallel brightness")) SHADER.shader_body.parallel_tint_color = TAG.read_rgb(input_stream, TAG, tag_format.XMLData(tag_node, "parallel tint color")) SHADER.shader_body.reflection_cube_map = TAG.TagRef().read(input_stream, TAG, tag_format.XMLData(tag_node, "reflection cube map")) - input_stream.read(16) # Padding if is_stubbs_the_zombie: SHADER.shader_body.bump_scale = TAG.read_float(input_stream, TAG, tag_format.XMLData(tag_node, "bump scale")) diff --git a/io_scene_halo/file_tag/h2/file_biped/build_asset.py b/io_scene_halo/file_tag/h2/file_biped/build_asset.py new file mode 100644 index 000000000..1c3341222 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_biped/build_asset.py @@ -0,0 +1,431 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from math import radians +from ....global_functions import tag_format, shader_processing +from ....file_tag.h2.file_shader.format import FunctionTypeEnum +from ..file_object.build_asset import ( + write_ai_properties, + write_functions, + write_attachments, + write_tag_ref, + write_old_functions, + write_change_colors, + write_predicted_resources + ) +from ..file_unit.build_asset import ( + write_postures, + write_dialogue_variant, + write_powered_seats, + write_seats + ) + +def write_body(output_stream, TAG, BIPED): + BIPED.biped_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack('I', len(BIPED.biped_body.default_model_variant))) + BIPED.biped_body.model.write(output_stream, False, True) + BIPED.biped_body.crate_object.write(output_stream, False, True) + BIPED.biped_body.modifier_shader.write(output_stream, False, True) + BIPED.biped_body.creation_effect.write(output_stream, False, True) + BIPED.biped_body.material_effects.write(output_stream, False, True) + BIPED.biped_body.ai_properties_tag_block.write(output_stream, False) + BIPED.biped_body.functions_tag_block.write(output_stream, False) + output_stream.write(struct.pack('I', len(BIPED.biped_body.camera_marker_name))) + output_stream.write(struct.pack('>I', len(BIPED.biped_body.camera_submerged_marker_name))) + output_stream.write(struct.pack('I', len(BIPED.biped_body.right_hand_node))) + output_stream.write(struct.pack('>I', len(BIPED.biped_body.left_hand_node))) + output_stream.write(struct.pack('>I', len(BIPED.biped_body.preferred_gun_node))) + BIPED.biped_body.melee_damage.write(output_stream, False, True) + BIPED.biped_body.boarding_melee_damage.write(output_stream, False, True) + BIPED.biped_body.boarding_melee_response.write(output_stream, False, True) + BIPED.biped_body.landing_melee_damage.write(output_stream, False, True) + BIPED.biped_body.flurry_melee_damage.write(output_stream, False, True) + BIPED.biped_body.obstacle_smash_damage.write(output_stream, False, True) + output_stream.write(struct.pack('I', len(BIPED.biped_body.living_material_name))) + output_stream.write(struct.pack('>I', len(BIPED.biped_body.dead_material_name))) + output_stream.write(struct.pack('<4x')) + BIPED.biped_body.dead_sphere_shapes_tag_block.write(output_stream, False) + BIPED.biped_body.pill_shapes_tag_block.write(output_stream, False) + BIPED.biped_body.sphere_shapes_tag_block.write(output_stream, False) + output_stream.write(struct.pack(' 0: + dead_sphere_shapes_header.write(output_stream, TAG, True) + for dead_sphere_shape_element in dead_sphere_shapes: + output_stream.write(struct.pack('>I', len(dead_sphere_shape_element.name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(dead_sphere_shape_element.name, False))) + +def write_pill_shapes(output_stream, TAG, pill_shapes, pill_shapes_header): + if len(pill_shapes) > 0: + pill_shapes_header.write(output_stream, TAG, True) + for pill_shape_element in pill_shapes: + output_stream.write(struct.pack('>I', len(pill_shape_element.name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(pill_shape_element.name, False))) + +def write_contact_points(output_stream, TAG, contact_points, contact_points_header): + if len(contact_points) > 0: + contact_points_header.write(output_stream, TAG, True) + for contact_point_element in contact_points: + output_stream.write(struct.pack('>I', len(contact_point_element.name))) + + for contact_point_element in contact_points: + name_length = len(contact_point_element.name) + if name_length > 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(contact_point_element.name, False))) + +def build_asset(output_stream, BIPED, report): + TAG = tag_format.TagAsset() + TAG.is_legacy = False + TAG.big_endian = False + + BIPED.header.write(output_stream, False, True) + write_body(output_stream, TAG, BIPED) + + default_model_variant_name_length = len(BIPED.biped_body.default_model_variant) + if default_model_variant_name_length > 0: + output_stream.write(struct.pack('<%ss' % default_model_variant_name_length, TAG.string_to_bytes(BIPED.biped_body.default_model_variant, False))) + + model_name_length = len(BIPED.biped_body.model.name) + if model_name_length > 0: + output_stream.write(struct.pack('<%ssx' % model_name_length, TAG.string_to_bytes(BIPED.biped_body.model.name, False))) + + crate_object_name_length = len(BIPED.biped_body.crate_object.name) + if crate_object_name_length > 0: + output_stream.write(struct.pack('<%ssx' % crate_object_name_length, TAG.string_to_bytes(BIPED.biped_body.crate_object.name, False))) + + modifier_shader_name_length = len(BIPED.biped_body.modifier_shader.name) + if modifier_shader_name_length > 0: + output_stream.write(struct.pack('<%ssx' % modifier_shader_name_length, TAG.string_to_bytes(BIPED.biped_body.modifier_shader.name, False))) + + creation_effect_name_length = len(BIPED.biped_body.creation_effect.name) + if creation_effect_name_length > 0: + output_stream.write(struct.pack('<%ssx' % creation_effect_name_length, TAG.string_to_bytes(BIPED.biped_body.creation_effect.name, False))) + + material_effects_name_length = len(BIPED.biped_body.material_effects.name) + if material_effects_name_length > 0: + output_stream.write(struct.pack('<%ssx' % material_effects_name_length, TAG.string_to_bytes(BIPED.biped_body.material_effects.name, False))) + + write_ai_properties(output_stream, TAG, BIPED.ai_properties, BIPED.ai_properties_header) + write_functions(output_stream, TAG, BIPED.functions, BIPED.functions_header) + write_attachments(output_stream, TAG, BIPED.attachments, BIPED.attachments_header) + write_tag_ref(output_stream, TAG, BIPED.widgets, BIPED.widgets_header) + write_old_functions(output_stream, TAG, BIPED.old_functions, BIPED.old_functions_header) + write_change_colors(output_stream, TAG, BIPED.change_colors, BIPED.change_colors_header) + write_predicted_resources(output_stream, TAG, BIPED.predicted_resources, BIPED.predicted_resources_header) + + integrated_light_toggle_length = len(BIPED.biped_body.integrated_light_toggle.name) + if integrated_light_toggle_length > 0: + output_stream.write(struct.pack('<%ssx' % integrated_light_toggle_length, TAG.string_to_bytes(BIPED.biped_body.integrated_light_toggle.name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("uncs", True), 0, 1, 32)) + + camera_marker_name_length = len(BIPED.biped_body.camera_marker_name) + if camera_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % camera_marker_name_length, TAG.string_to_bytes(BIPED.biped_body.camera_marker_name, False))) + + camera_submerged_marker_name_length = len(BIPED.biped_body.camera_submerged_marker_name) + if camera_submerged_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % camera_submerged_marker_name_length, TAG.string_to_bytes(BIPED.biped_body.camera_submerged_marker_name, False))) + + write_tag_ref(output_stream, TAG, BIPED.camera_tracks, BIPED.camera_tracks_header) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("usas", True), 0, 1, 20)) + + spawned_turret_actor_length = len(BIPED.biped_body.spawned_turret_actor.name) + if spawned_turret_actor_length > 0: + output_stream.write(struct.pack('<%ssx' % spawned_turret_actor_length, TAG.string_to_bytes(BIPED.biped_body.spawned_turret_actor.name, False))) + + right_hand_node_length = len(BIPED.biped_body.right_hand_node) + if right_hand_node_length > 0: + output_stream.write(struct.pack('<%ss' % right_hand_node_length, TAG.string_to_bytes(BIPED.biped_body.right_hand_node, False))) + + left_hand_node_length = len(BIPED.biped_body.left_hand_node) + if left_hand_node_length > 0: + output_stream.write(struct.pack('<%ss' % left_hand_node_length, TAG.string_to_bytes(BIPED.biped_body.left_hand_node, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("uHnd", True), 1, 1, 4)) + + preferred_gun_node_length = len(BIPED.biped_body.preferred_gun_node) + if preferred_gun_node_length > 0: + output_stream.write(struct.pack('<%ss' % preferred_gun_node_length, TAG.string_to_bytes(BIPED.biped_body.preferred_gun_node, False))) + + melee_damage_length = len(BIPED.biped_body.melee_damage.name) + if melee_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % melee_damage_length, TAG.string_to_bytes(BIPED.biped_body.melee_damage.name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("ubms", True), 1, 1, 80)) + + boarding_melee_damage_length = len(BIPED.biped_body.boarding_melee_damage.name) + if boarding_melee_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % boarding_melee_damage_length, TAG.string_to_bytes(BIPED.biped_body.boarding_melee_damage.name, False))) + + boarding_melee_response_length = len(BIPED.biped_body.boarding_melee_response.name) + if boarding_melee_response_length > 0: + output_stream.write(struct.pack('<%ssx' % boarding_melee_response_length, TAG.string_to_bytes(BIPED.biped_body.boarding_melee_response.name, False))) + + landing_melee_damage_length = len(BIPED.biped_body.landing_melee_damage.name) + if landing_melee_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % landing_melee_damage_length, TAG.string_to_bytes(BIPED.biped_body.landing_melee_damage.name, False))) + + flurry_melee_damage_length = len(BIPED.biped_body.flurry_melee_damage.name) + if flurry_melee_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % flurry_melee_damage_length, TAG.string_to_bytes(BIPED.biped_body.flurry_melee_damage.name, False))) + + obstacle_smash_damage_length = len(BIPED.biped_body.obstacle_smash_damage.name) + if obstacle_smash_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % obstacle_smash_damage_length, TAG.string_to_bytes(BIPED.biped_body.obstacle_smash_damage.name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("cmtb", True), 0, 1, 2)) + + write_postures(output_stream, TAG, BIPED.postures, BIPED.postures_header) + write_tag_ref(output_stream, TAG, BIPED.new_hud_interface, BIPED.new_hud_interface_header) + write_dialogue_variant(output_stream, TAG, BIPED.dialogue_variants, BIPED.dialogue_variants_header) + write_powered_seats(output_stream, TAG, BIPED.powered_seats, BIPED.powered_seats_header) + write_tag_ref(output_stream, TAG, BIPED.weapons, BIPED.weapons_header) + write_seats(output_stream, TAG, BIPED.seats, BIPED.seats_header) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("!@#$", True), 0, 1, 20)) + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("ulYc", True), 1, 1, 8)) + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("blod", True), 1, 1, 8)) + + area_damage_effect_length = len(BIPED.biped_body.area_damage_effect.name) + if area_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % area_damage_effect_length, TAG.string_to_bytes(BIPED.biped_body.area_damage_effect.name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("chpy", True), 0, 1, 160)) + + living_material_name_length = len(BIPED.biped_body.living_material_name) + if living_material_name_length > 0: + output_stream.write(struct.pack('<%ss' % living_material_name_length, TAG.string_to_bytes(BIPED.biped_body.living_material_name, False))) + + dead_material_name_length = len(BIPED.biped_body.dead_material_name) + if dead_material_name_length > 0: + output_stream.write(struct.pack('<%ss' % dead_material_name_length, TAG.string_to_bytes(BIPED.biped_body.dead_material_name, False))) + + write_sphere_shapes(output_stream, TAG, BIPED.dead_sphere_shapes, BIPED.dead_sphere_shapes_header) + write_pill_shapes(output_stream, TAG, BIPED.pill_shapes, BIPED.pill_shapes_header) + write_sphere_shapes(output_stream, TAG, BIPED.sphere_shapes, BIPED.sphere_shapes_header) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("chgr", True), 0, 1, 48)) + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("chfl", True), 0, 1, 44)) + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("chdd", True), 0, 1, 0)) + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("chsn", True), 0, 1, 0)) + + write_contact_points(output_stream, TAG, BIPED.contact_points, BIPED.contact_points_header) + + reanimation_character_length = len(BIPED.biped_body.reanimation_character.name) + if reanimation_character_length > 0: + output_stream.write(struct.pack('<%ssx' % reanimation_character_length, TAG.string_to_bytes(BIPED.biped_body.reanimation_character.name, False))) + + death_spawn_character_length = len(BIPED.biped_body.death_spawn_character.name) + if death_spawn_character_length > 0: + output_stream.write(struct.pack('<%ssx' % death_spawn_character_length, TAG.string_to_bytes(BIPED.biped_body.death_spawn_character.name, False))) \ No newline at end of file diff --git a/io_scene_halo/file_tag/h2/file_biped/format.py b/io_scene_halo/file_tag/h2/file_biped/format.py index 33a727087..b6f7d57a9 100644 --- a/io_scene_halo/file_tag/h2/file_biped/format.py +++ b/io_scene_halo/file_tag/h2/file_biped/format.py @@ -26,154 +26,7 @@ from mathutils import Vector from enum import Flag, Enum, auto - -class ObjectFlags(Flag): - does_not_cast_shadow = auto() - search_cardinal_direction_lightmaps_on_failure = auto() - unused = auto() - not_a_pathfinding_obstacle = auto() - extension_of_parent = auto() - does_not_cause_collision_damage = auto() - early_mover = auto() - early_mover_localized_physics = auto() - use_static_massive_lightmap_sample = auto() - object_scales_attachments = auto() - inherits_players_appearance = auto() - dead_bipeds_cant_localize = auto() - attach_to_clusters_dynamic_sphere = auto() - effects_created_by_this_object_do_not_spawn_objects_in_multiplayer = auto() - prophet_is_not_displayed_in_pegasus_builds = auto() - -class LightmapShadowModeEnum(Enum): - default = 0 - never = auto() - always = auto() - -class SweetenerSizeEnum(Enum): - small = 0 - medium = auto() - large = auto() - -class UnitFlags(Flag): - circular_aiming = auto() - destroyed_after_dying = auto() - half_speed_interpolation = auto() - fires_from_camera = auto() - entrance_inside_bounding_sphere = auto() - doesnt_show_readid_weapon = auto() - causes_passenger_dialogue = auto() - resists_ping = auto() - melee_attack_is_fatal = auto() - dont_reface_during_pings = auto() - has_no_aiming = auto() - simple_creature = auto() - impact_melee_attaches_to_unit = auto() - impact_melee_dies_on_shield = auto() - cannot_open_doors_automatically = auto() - melee_attackers_cannot_attach = auto() - not_instantly_killed_by_melee = auto() - unused_17 = auto() - runs_around_flaming = auto() - inconsequential = auto() - special_cinematic_unit = auto() - ignored_by_autoaiming = auto() - shields_fry_infection_forms = auto() - unused_23 = auto() - unused_24 = auto() - acts_as_gunner_for_parent = auto() - controlled_by_parent_gunner = auto() - parents_primary_weapon = auto() - unit_has_boost = auto() - -class TeamsEnum(Enum): - default = 0 - player = auto() - human = auto() - covenant = auto() - flood = auto() - sentinel = auto() - heretic = auto() - prophet = auto() - unused_8 = auto() - unused_9 = auto() - unused_10 = auto() - unused_11 = auto() - unused_12 = auto() - unused_13 = auto() - unused_14 = auto() - unused_15 = auto() - -class ConstantSoundVolumeEnum(Enum): - silent = 0 - medium = auto() - loud = auto() - shout = auto() - quiet = auto() - -class MotionSensorBlipSizeEnum(Enum): - medium = 0 - small = auto() - large = auto() - -class MetaGameTypeEnum(Enum): - brute = 0 - grunt = auto() - jackal = auto() - skirmisher = auto() - marine = auto() - spartan = auto() - bugger = auto() - hunter = auto() - flood_infection = auto() - flood_carrier = auto() - flood_combat = auto() - flood_pure = auto() - sentinel = auto() - elite = auto() - engineer = auto() - mule = auto() - turret = auto() - mongoose = auto() - warthog = auto() - scorpion = auto() - hornet = auto() - pelican = auto() - revenant = auto() - seraph = auto() - shade = auto() - watchtower = auto() - ghost = auto() - chopper = auto() - mauler = auto() - wraith = auto() - banshee = auto() - phantom = auto() - scarab = auto() - guntower = auto() - tuning_fork = auto() - broadsword = auto() - mammoth = auto() - lich = auto() - mantis = auto() - wasp = auto() - phaeton = auto() - bishop = auto() - knight = auto() - pawn = auto() - -class MetaGameClassEnum(Enum): - infantry = 0 - leader = auto() - hero = auto() - specialist = auto() - light_vehicle = auto() - heavy_vehicle = auto() - giant_vehicle = auto() - standard_vehicle = auto() - -class GrenadeTypeEnum(Enum): - human_fragmentation = 0 - covenant_plasma = auto() +from ..file_unit.format import UnitAsset class BipedFlags(Flag): turns_without_animating = auto() @@ -203,40 +56,12 @@ class CollisionFlags(Flag): not_physical = auto() dead_character_collision_group = auto() -class BipedAsset(): +class BipedAsset(UnitAsset): def __init__(self): + super().__init__() self.header = None self.biped_body_header = None self.biped_body = None - self.ai_properties_header = None - self.ai_properties = None - self.functions_header = None - self.functions = None - self.attachments_header = None - self.attachments = None - self.widgets_header = None - self.widgets = None - self.old_functions_header = None - self.old_functions = None - self.change_colors_header = None - self.change_colors = None - self.predicted_resources_header = None - self.predicted_resources = None - self.camera_tracks_header = None - self.camera_tracks = None - self.camera_tracks_header = None - self.postures_header = None - self.postures = None - self.new_hud_interface_header = None - self.new_hud_interface = None - self.dialogue_variants_header = None - self.dialogue_variants = None - self.powered_seats_header = None - self.powered_seats = None - self.weapons_header = None - self.weapons = None - self.seats_header = None - self.seats = None self.dead_sphere_shapes_header = None self.dead_sphere_shapes = None self.pill_shapes_header = None @@ -246,135 +71,19 @@ def __init__(self): self.contact_points_header = None self.contact_points = None - class BipedBody: - def __init__(self, object_flags=0, bounding_radius=0.0, bounding_offset=Vector(), acceleration_scale=0.0, lightmap_shadow_mode=0, sweetner_size=0, - dynamic_light_sphere_radius=0.0, dynamic_light_sphere_offset=Vector(), default_model_variant="", default_model_variant_length=0, model=None, crate_object=None, - modifier_shader=None, creation_effect=None, material_effects=None, ai_properties_tag_block=None, functions_tag_block=None, apply_collision_damage_scale=0.0, - min_game_acc=0.0, max_game_acc=0.0, min_game_scale=0.0, max_game_scale=0.0, min_abs_acc=0.0, max_abs_acc=0.0, min_abs_scale=0.0, max_abs_scale=0.0, - hud_text_message_index=0, attachments_tag_block=None, widgets_tag_block=None, old_functions_tag_block=None, change_colors_tag_block=None, - predicted_resources_tag_block=None, unit_flags=0, default_team=0, constant_sound_volume=0, integrated_light_toggle=None, camera_field_of_view=0.0, - camera_stiffness=0.0, camera_marker_name="", camera_marker_name_length=0, camera_submerged_marker_name="", camera_submerged_marker_name_length=0, - pitch_auto_level=0.0, pitch_range=(0.0, 0.0), camera_tracks_tag_block=None, acceleration_range=Vector(), acceleration_action_scale=0.0, - acceleration_attach_scale=0.0, soft_ping_threshold=0.0, soft_ping_interrupt_time=0.0, hard_ping_threshold=0.0, hard_ping_interrupt_time=0.0, - hard_death_threshold=0.0, feign_death_threshold=0.0, feign_death_time=0.0, distance_of_evade_anim=0.0, distance_of_dive_anim=0.0, - stunned_movement_threshold=0.0, feign_death_chance=0.0, feign_repeat_chance=0.0, spawned_turret_actor=None, spawned_actor_count=(0, 0), - spawned_velocity=0.0, aiming_velocity_maximum=0.0, aiming_acceleration_maximum=0.0, casual_aiming_modifier=0.0, looking_velocity_maximum=0.0, - looking_acceleration_maximum=0.0, right_hand_node="", right_hand_node_length=0, left_hand_node="", left_hand_node_length=0, preferred_gun_node="", - preferred_gun_node_length=0, melee_damage=None, boarding_melee_damage=None, boarding_melee_response=None, landing_melee_damage=None, - flurry_melee_damage=None, obstacle_smash_damage=None, motion_sensor_blip_size=0, unit_type=0, unit_class=0, postures_tag_block=None, - new_hud_interfaces_tag_block=None, dialogue_variants_tag_block=None, grenade_velocity=0.0, grenade_type=0, grenade_count=0, powered_seats_tag_block=None, - weapons_tag_block=None, seats_tag_block=None, boost_peak_power=0.0, boost_rise_power=0.0, boost_peak_time=0.0, boost_fall_power=0.0, dead_time=0.0, - attack_weight=0.0, decay_weight=0.0, moving_turning_speed=0.0, biped_flags=0, stationary_turning_threshold=0.0, jump_velocity=0.0, - maximum_soft_landing_time=0.0, maximum_hard_landing_time=0.0, minimum_soft_landing_velocity=0.0, minimum_hard_landing_velocity=0.0, - maximum_hard_landing_velocity=0.0, death_hard_landing_velocity=0.0, stun_duration=0.0, standing_camera_height=0.0, crouching_camera_height=0.0, - crouching_transition_time=0.0, camera_interpolation_start=0.0, camera_interpolation_end=0.0, camera_forward_movement_scale=0.0, - camera_side_movement_scale=0.0, camera_vertical_movement_scale=0.0, camera_exclusion_distance=0.0, autoaim_width=0.0, lock_on_flags=0, lock_on_distance=0.0, - head_shot_acceleration_scale=0.0, area_damage_effect=None, collision_flags=0, height_standing=0.0, height_crouching=0.0, radius=0.0, mass=0.0, - living_material_name="", living_material_name_length=0, dead_material_name="", dead_material_name_length=0, dead_sphere_shapes_tag_block=None, - pill_shapes_tag_block=None, sphere_shapes_tag_block=None, maximum_slope_angle=0.0, downhill_falloff_angle=0.0, downhill_cuttoff_angle=0.0, - uphill_falloff_angle=0.0, uphill_cuttoff_angle=0.0, downhill_velocity_scale=0.0, uphill_velocity_scale=0.0, bank_angle=0.0, bank_apply_time=0.0, - bank_decay_time=0.0, pitch_ratio=0.0, max_velocity=0.0, max_sidestep_velocity=0.0, acceleration=0.0, deceleration=0.0, angular_velocity_maximum=0.0, - angular_acceleration_maximum=0.0, crouch_velocity_modifier=0.0, contact_points_tag_block=None, reanimation_character=None, death_spawn_character=None, - death_spawn_count=0): - self.object_flags = object_flags - self.bounding_radius = bounding_radius - self.bounding_offset = bounding_offset - self.acceleration_scale = acceleration_scale - self.lightmap_shadow_mode = lightmap_shadow_mode - self.sweetner_size = sweetner_size - self.dynamic_light_sphere_radius = dynamic_light_sphere_radius - self.dynamic_light_sphere_offset = dynamic_light_sphere_offset - self.default_model_variant = default_model_variant - self.default_model_variant_length = default_model_variant_length - self.model = model - self.crate_object = crate_object - self.modifier_shader = modifier_shader - self.creation_effect = creation_effect - self.material_effects = material_effects - self.ai_properties_tag_block = ai_properties_tag_block - self.functions_tag_block = functions_tag_block - self.apply_collision_damage_scale = apply_collision_damage_scale - self.min_game_acc = min_game_acc - self.max_game_acc = max_game_acc - self.min_game_scale = min_game_scale - self.max_game_scale = max_game_scale - self.min_abs_acc = min_abs_acc - self.max_abs_acc = max_abs_acc - self.min_abs_scale = min_abs_scale - self.max_abs_scale = max_abs_scale - self.hud_text_message_index = hud_text_message_index - self.attachments_tag_block = attachments_tag_block - self.widgets_tag_block = widgets_tag_block - self.old_functions_tag_block = old_functions_tag_block - self.change_colors_tag_block = change_colors_tag_block - self.predicted_resources_tag_block = predicted_resources_tag_block - self.unit_flags = unit_flags - self.default_team = default_team - self.constant_sound_volume = constant_sound_volume - self.integrated_light_toggle = integrated_light_toggle - self.camera_field_of_view = camera_field_of_view - self.camera_stiffness = camera_stiffness - self.camera_marker_name = camera_marker_name - self.camera_marker_name_length = camera_marker_name_length - self.camera_submerged_marker_name = camera_submerged_marker_name - self.camera_submerged_marker_name_length = camera_submerged_marker_name_length - self.pitch_auto_level = pitch_auto_level - self.pitch_range = pitch_range - self.camera_tracks_tag_block = camera_tracks_tag_block - self.acceleration_range = acceleration_range - self.acceleration_action_scale = acceleration_action_scale - self.acceleration_attach_scale = acceleration_attach_scale - self.soft_ping_threshold = soft_ping_threshold - self.soft_ping_interrupt_time = soft_ping_interrupt_time - self.hard_ping_threshold = hard_ping_threshold - self.hard_ping_interrupt_time = hard_ping_interrupt_time - self.hard_death_threshold = hard_death_threshold - self.feign_death_threshold = feign_death_threshold - self.feign_death_time = feign_death_time - self.distance_of_evade_anim = distance_of_evade_anim - self.distance_of_dive_anim = distance_of_dive_anim - self.stunned_movement_threshold = stunned_movement_threshold - self.feign_death_chance = feign_death_chance - self.feign_repeat_chance = feign_repeat_chance - self.spawned_turret_actor = spawned_turret_actor - self.spawned_actor_count = spawned_actor_count - self.spawned_velocity = spawned_velocity - self.aiming_velocity_maximum = aiming_velocity_maximum - self.aiming_acceleration_maximum = aiming_acceleration_maximum - self.casual_aiming_modifier = casual_aiming_modifier - self.looking_velocity_maximum = looking_velocity_maximum - self.looking_acceleration_maximum = looking_acceleration_maximum - self.right_hand_node = right_hand_node - self.right_hand_node_length = right_hand_node_length - self.left_hand_node = left_hand_node - self.left_hand_node_length = left_hand_node_length - self.preferred_gun_node = preferred_gun_node - self.preferred_gun_node_length = preferred_gun_node_length - self.melee_damage = melee_damage - self.boarding_melee_damage = boarding_melee_damage - self.boarding_melee_response = boarding_melee_response - self.landing_melee_damage = landing_melee_damage - self.flurry_melee_damage = flurry_melee_damage - self.obstacle_smash_damage = obstacle_smash_damage - self.motion_sensor_blip_size = motion_sensor_blip_size - self.unit_type = unit_type - self.unit_class = unit_class - self.postures_tag_block = postures_tag_block - self.new_hud_interfaces_tag_block = new_hud_interfaces_tag_block - self.dialogue_variants_tag_block = dialogue_variants_tag_block - self.grenade_velocity = grenade_velocity - self.grenade_type = grenade_type - self.grenade_count = grenade_count - self.powered_seats_tag_block = powered_seats_tag_block - self.weapons_tag_block = weapons_tag_block - self.seats_tag_block = seats_tag_block - self.boost_peak_power = boost_peak_power - self.boost_rise_power = boost_rise_power - self.boost_peak_time = boost_peak_time - self.boost_fall_power = boost_fall_power - self.dead_time = dead_time - self.attack_weight = attack_weight - self.decay_weight = decay_weight + class BipedBody(UnitAsset.UnitBody): + def __init__(self, moving_turning_speed=0.0, biped_flags=0, stationary_turning_threshold=0.0, jump_velocity=0.0, maximum_soft_landing_time=0.0, + maximum_hard_landing_time=0.0, minimum_soft_landing_velocity=0.0, minimum_hard_landing_velocity=0.0, maximum_hard_landing_velocity=0.0, + death_hard_landing_velocity=0.0, stun_duration=0.0, standing_camera_height=0.0, crouching_camera_height=0.0, crouching_transition_time=0.0, + camera_interpolation_start=0.0, camera_interpolation_end=0.0, camera_forward_movement_scale=0.0, camera_side_movement_scale=0.0, + camera_vertical_movement_scale=0.0, camera_exclusion_distance=0.0, autoaim_width=0.0, lock_on_flags=0, lock_on_distance=0.0, head_shot_acceleration_scale=0.0, + area_damage_effect=None, collision_flags=0, height_standing=0.0, height_crouching=0.0, radius=0.0, mass=0.0, living_material_name="", + living_material_name_length=0, dead_material_name="", dead_material_name_length=0, dead_sphere_shapes_tag_block=None, pill_shapes_tag_block=None, + sphere_shapes_tag_block=None, maximum_slope_angle=0.0, downhill_falloff_angle=0.0, downhill_cuttoff_angle=0.0, uphill_falloff_angle=0.0, + uphill_cuttoff_angle=0.0, downhill_velocity_scale=0.0, uphill_velocity_scale=0.0, bank_angle=0.0, bank_apply_time=0.0, bank_decay_time=0.0, pitch_ratio=0.0, + max_velocity=0.0, max_sidestep_velocity=0.0, acceleration=0.0, deceleration=0.0, angular_velocity_maximum=0.0, angular_acceleration_maximum=0.0, + crouch_velocity_modifier=0.0, contact_points_tag_block=None, reanimation_character=None, death_spawn_character=None, death_spawn_count=0): + super().__init__() self.moving_turning_speed = moving_turning_speed self.biped_flags = biped_flags self.stationary_turning_threshold = stationary_turning_threshold @@ -434,3 +143,30 @@ def __init__(self, object_flags=0, bounding_radius=0.0, bounding_offset=Vector() self.reanimation_character = reanimation_character self.death_spawn_character = death_spawn_character self.death_spawn_count = death_spawn_count + + class Shape: + def __init__(self, name="", name_length=0, material=-1, flags=0, relative_mass_scale=0.0, friction=0.0, restitution=0.0, volume=0.0, mass=0.0, phantom=-1, size_a=0, + count_a=0, radius=0.0, size_b=0, count_b=0, rotation_i=Vector(), rotation_j=Vector(), rotation_k=Vector(), translation=Vector(), bottom=Vector(), top=Vector()): + self.name = name + self.name_length = name_length + self.material = material + self.flags = flags + self.relative_mass_scale = relative_mass_scale + self.friction = friction + self.restitution = restitution + self.volume = volume + self.mass = mass + self.phantom = phantom + self.size_a = size_a + self.count_a = count_a + self.radius = radius + self.size_b = size_b + self.count_b = count_b + self.rotation_i = rotation_i + self.rotation_j = rotation_j + self.rotation_k = rotation_k + self.translation = translation + self.bottom = bottom + self.top = top + + diff --git a/io_scene_halo/file_tag/h2/file_biped/process_file.py b/io_scene_halo/file_tag/h2/file_biped/process_file.py index 20ce11bc7..ebd1cede8 100644 --- a/io_scene_halo/file_tag/h2/file_biped/process_file.py +++ b/io_scene_halo/file_tag/h2/file_biped/process_file.py @@ -26,18 +26,18 @@ from xml.dom import minidom from ....global_functions import tag_format -from .format import ( - BipedAsset, - ObjectFlags, - LightmapShadowModeEnum, - SweetenerSizeEnum, +from ..file_object.format import ObjectFlags, LightmapShadowModeEnum, SweetenerSizeEnum +from ..file_unit.format import ( UnitFlags, TeamsEnum, ConstantSoundVolumeEnum, MotionSensorBlipSizeEnum, MetaGameTypeEnum, MetaGameClassEnum, - GrenadeTypeEnum, + GrenadeTypeEnum + ) +from .format import ( + BipedAsset, BipedFlags, LockOnFlags, CollisionFlags diff --git a/io_scene_halo/file_tag/h2/file_camera_track/build_asset.py b/io_scene_halo/file_tag/h2/file_camera_track/build_asset.py new file mode 100644 index 000000000..01b23da98 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_camera_track/build_asset.py @@ -0,0 +1,53 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from math import radians +from ....global_functions import tag_format, shader_processing +from ....file_tag.h2.file_shader.format import FunctionTypeEnum + +def write_body(output_stream, TAG, CAMERATRACK): + CAMERATRACK.camera_track_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack('<4x')) + CAMERATRACK.camera_track_body.control_points_tag_block.write(output_stream, False) + +def write_control_points(output_stream, TAG, control_points, control_points_header): + if len(control_points) > 0: + control_points_header.write(output_stream, TAG, True) + for control_point_element in control_points: + output_stream.write(struct.pack(' 0: + point_states_header.write(output_stream, TAG, True) + for point_state_element in point_states: + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % physics_length, TAG.string_to_bytes(point_state_element.physics.name, False))) + +def build_asset(output_stream, CONTRAIL, report): + TAG = tag_format.TagAsset() + TAG.is_legacy = False + TAG.big_endian = False + + CONTRAIL.header.write(output_stream, False, True) + write_body(output_stream, TAG, CONTRAIL) + + render_bitmap_length = len(CONTRAIL.contrail_body.render_bitmap.name) + if render_bitmap_length > 0: + output_stream.write(struct.pack('<%ssx' % render_bitmap_length, TAG.string_to_bytes(CONTRAIL.contrail_body.render_bitmap.name, False))) + + secondary_bitmap_length = len(CONTRAIL.contrail_body.secondary_bitmap.name) + if secondary_bitmap_length > 0: + output_stream.write(struct.pack('<%ssx' % secondary_bitmap_length, TAG.string_to_bytes(CONTRAIL.contrail_body.secondary_bitmap.name, False))) + + write_point_states(output_stream, TAG, CONTRAIL.point_states, CONTRAIL.point_states_header) diff --git a/io_scene_halo/file_tag/h2/file_contrail/format.py b/io_scene_halo/file_tag/h2/file_contrail/format.py new file mode 100644 index 000000000..835413200 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_contrail/format.py @@ -0,0 +1,92 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto + +class ContrailAsset(): + def __init__(self): + self.header = None + self.contrail_body_header = None + self.contrail_body = None + self.point_states_header = None + self.point_states = None + + class ContrailBody: + def __init__(self, flags=0, scale_flags=0, point_generation_rate=0.0, point_velocity=(0.0, 0.0), point_velocity_cone_angle=(0.0, 0.0), inherited_velocity_fraction=0.0, + render_type=0, texture_repeats_u=0.0, texture_repeats_v=0.0, texture_animation_u=0.0, texture_animation_v=0.0, animation_rate=0.0, render_bitmap=None, + first_sequence_index=0, sequence_count=0, shader_flags=0, framebuffer_blend_function=0, framebuffer_fade_mode=0, map_flags=0, secondary_bitmap=None, anchor=0, + secondary_flags=0, u_animation_function=0, u_animation_period=0.0, u_animation_phase=0.0, u_animation_scale=0.0, v_animation_function=0, v_animation_period=0.0, + v_animation_phase=0.0, v_animation_scale=0.0, rotation_animation_function=0, rotation_animation_period=0.0, rotation_animation_phase=0.0, + rotation_animation_scale=0.0, rotation_animation_center=(0.0, 0.0), zsprite_radius_scale=0.0, point_states_tag_block=None): + self.flags = flags + self.scale_flags = scale_flags + self.point_generation_rate = point_generation_rate + self.point_velocity = point_velocity + self.point_velocity_cone_angle = point_velocity_cone_angle + self.inherited_velocity_fraction = inherited_velocity_fraction + self.render_type = render_type + self.texture_repeats_u = texture_repeats_u + self.texture_repeats_v = texture_repeats_v + self.texture_animation_u = texture_animation_u + self.texture_animation_v = texture_animation_v + self.animation_rate = animation_rate + self.render_bitmap = render_bitmap + self.first_sequence_index = first_sequence_index + self.sequence_count = sequence_count + self.shader_flags = shader_flags + self.framebuffer_blend_function = framebuffer_blend_function + self.framebuffer_fade_mode = framebuffer_fade_mode + self.map_flags = map_flags + self.secondary_bitmap = secondary_bitmap + self.anchor = anchor + self.secondary_flags = secondary_flags + self.u_animation_function = u_animation_function + self.u_animation_period = u_animation_period + self.u_animation_phase = u_animation_phase + self.u_animation_scale = u_animation_scale + self.v_animation_function = v_animation_function + self.v_animation_period = v_animation_period + self.v_animation_phase = v_animation_phase + self.v_animation_scale = v_animation_scale + self.rotation_animation_function = rotation_animation_function + self.rotation_animation_period = rotation_animation_period + self.rotation_animation_phase = rotation_animation_phase + self.rotation_animation_scale = rotation_animation_scale + self.rotation_animation_center = rotation_animation_center + self.zsprite_radius_scale = zsprite_radius_scale + self.point_states_tag_block = point_states_tag_block + + class PointState: + def __init__(self, duration=(0.0, 0.0), transition_duration=(0.0, 0.0), physics=None, width=0.0, color_lower_bound=(0.0, 0.0, 0.0, 0.0), color_upper_bound=(0.0, 0.0, 0.0, 0.0), + scale_flags=0): + self.duration = duration + self.transition_duration = transition_duration + self.physics = physics + self.width = width + self.color_lower_bound = color_lower_bound + self.color_upper_bound = color_upper_bound + self.scale_flags = scale_flags diff --git a/io_scene_halo/file_tag/h2/file_control/format.py b/io_scene_halo/file_tag/h2/file_control/format.py new file mode 100644 index 000000000..f1f7be080 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_control/format.py @@ -0,0 +1,58 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto +from ..file_device.format import DeviceAsset + +class ControlTypeEnum(Enum): + toggle_switch = 0 + on_button = auto() + off_button = auto() + call_button = auto() + +class TriggersWhenEnum(Enum): + touched_by_player = 0 + destroyed = auto() + +class ControlAsset(DeviceAsset): + def __init__(self): + super().__init__() + self.header = None + self.control_body_header = None + self.control_body = None + + class ControlBody(DeviceAsset.DeviceBody): + def __init__(self, control_type=0, triggers_when=0, call_value=0.0, action_string="", action_string_length=0, on=None, off=None, deny=None): + super().__init__() + self.control_type = control_type + self.triggers_when = triggers_when + self.call_value = call_value + self.action_string = action_string + self.action_string_length = action_string_length + self.on = on + self.off = off + self.deny = deny diff --git a/io_scene_halo/file_tag/h2/file_device_control/process_file.py b/io_scene_halo/file_tag/h2/file_control/process_file.py similarity index 98% rename from io_scene_halo/file_tag/h2/file_device_control/process_file.py rename to io_scene_halo/file_tag/h2/file_control/process_file.py index 14ff3ee83..9cd4c4fb9 100644 --- a/io_scene_halo/file_tag/h2/file_device_control/process_file.py +++ b/io_scene_halo/file_tag/h2/file_control/process_file.py @@ -26,16 +26,9 @@ from xml.dom import minidom from ....global_functions import tag_format -from .format import ( - ControlAsset, - ObjectFlags, - LightmapShadowModeEnum, - SweetenerSizeEnum, - DeviceFlags, - LightmapFlags, - ControlTypeEnum, - TriggersWhenEnum, - ) +from ..file_object.format import ObjectFlags, LightmapShadowModeEnum, SweetenerSizeEnum +from ..file_device.format import DeviceFlags, LightmapFlags +from .format import ControlAsset, ControlTypeEnum, TriggersWhenEnum XML_OUTPUT = False diff --git a/io_scene_halo/file_tag/h2/file_damage_effect/build_asset.py b/io_scene_halo/file_tag/h2/file_damage_effect/build_asset.py new file mode 100644 index 000000000..35f0f40bb --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_damage_effect/build_asset.py @@ -0,0 +1,147 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from math import radians +from ....global_functions import tag_format, shader_processing +from ....file_tag.h2.file_shader.format import FunctionTypeEnum + +def write_body(output_stream, TAG, DAMAGEFFECT): + DAMAGEFFECT.damage_effect_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack('I', len(DAMAGEFFECT.damage_effect_body.general_damage))) + output_stream.write(struct.pack('>I', len(DAMAGEFFECT.damage_effect_body.specific_damage))) + output_stream.write(struct.pack(' 0: + player_response_header.write(output_stream, TAG, True) + for player_response_element in player_responses: + output_stream.write(struct.pack('I', len(player_response_element.effect_name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % effect_name_length, TAG.string_to_bytes(player_response_element.effect_name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("dsfx", True), 0, 1, 20)) + + effect_name_length = len(player_response_element.effect_name) + if effect_name_length > 0: + output_stream.write(struct.pack('<%ss' % effect_name_length, TAG.string_to_bytes(player_response_element.effect_name, False))) + + shader_processing.write_function(output_stream, TAG, player_response_element.functions[2]) + +def build_asset(output_stream, DAMAGEFFECT, report): + TAG = tag_format.TagAsset() + TAG.is_legacy = False + TAG.big_endian = False + + DAMAGEFFECT.header.write(output_stream, False, True) + write_body(output_stream, TAG, DAMAGEFFECT) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("masd", True), 1, 1, 4)) + + general_damage_length = len(DAMAGEFFECT.damage_effect_body.general_damage) + if general_damage_length > 0: + output_stream.write(struct.pack('<%ss' % general_damage_length, TAG.string_to_bytes(DAMAGEFFECT.damage_effect_body.general_damage, False))) + + specific_damage_length = len(DAMAGEFFECT.damage_effect_body.specific_damage) + if specific_damage_length > 0: + output_stream.write(struct.pack('<%ss' % specific_damage_length, TAG.string_to_bytes(DAMAGEFFECT.damage_effect_body.specific_damage, False))) + + write_player_responses(output_stream, TAG, DAMAGEFFECT.player_responses, DAMAGEFFECT.player_response_header) + + sound_length = len(DAMAGEFFECT.damage_effect_body.sound.name) + if sound_length > 0: + output_stream.write(struct.pack('<%ssx' % sound_length, TAG.string_to_bytes(DAMAGEFFECT.damage_effect_body.sound.name, False))) \ No newline at end of file diff --git a/io_scene_halo/file_tag/h2/file_damage_effect/format.py b/io_scene_halo/file_tag/h2/file_damage_effect/format.py new file mode 100644 index 000000000..b91d946e8 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_damage_effect/format.py @@ -0,0 +1,119 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto + +class FunctionTypeEnum(Enum): + linear = 0 + late = auto() + very_late = auto() + early = auto() + very_early = auto() + cosine = auto() + zero = auto() + one = auto() + +class DamageEffectAsset(): + def __init__(self): + self.header = None + self.damage_effect_body_header = None + self.damage_effect_body = None + self.player_response_header = None + self.player_responses = None + + class DamageEffectBody: + def __init__(self, radius=(0.0, 0.0), cutoff_scale=0.0, flags=0, side_effect=0, category=0, damage_flags=0, aoe_core_radius=0.0, damage_lower_bound=0.0, damage_upper_bound=(0.0, 0.0), + dmg_inner_cone_angle=0.0, dmg_outer_cone_angle=0.0, active_camouflage_damage=0.0, stun=0.0, maximum_stun=0.0, stun_time=0.0, instantaneous_acceleration=0.0, + rider_direct_damage_scale=0.0, rider_maximum_transfer_damage_scale=0.0, rider_minimum_transfer_damage_scale=0.0, general_damage="", general_damage_length=0, + specific_damage="", specific_damage_length=0, ai_stun_radius=0.0, ai_stun_bounds=(0.0, 0.0), shake_radius=0.0, emp_radius=0, player_responses_tag_block=None, + impulse_duration=0.0, fade_function=0, rotation=0, pushback=0.0, jitter=(0.0, 0.0), shaking_duration=0.0, falloff_function=0, random_translation=0.0, random_rotation=0.0, + wobble_function=0, wobble_function_period=0.0, wobble_weight=0.0, sound=None, forward_velocity=0.0, forward_radius=0.0, forward_exponent=0.0, outward_velocity=0.0, + outward_radius=0.0, outward_exponent=0.0): + self.radius = radius + self.cutoff_scale = cutoff_scale + self.flags = flags + self.side_effect = side_effect + self.category = category + self.damage_flags = damage_flags + self.aoe_core_radius = aoe_core_radius + self.damage_lower_bound = damage_lower_bound + self.damage_upper_bound = damage_upper_bound + self.dmg_inner_cone_angle = dmg_inner_cone_angle + self.dmg_outer_cone_angle = dmg_outer_cone_angle + self.active_camouflage_damage = active_camouflage_damage + self.stun = stun + self.maximum_stun = maximum_stun + self.stun_time = stun_time + self.instantaneous_acceleration = instantaneous_acceleration + self.rider_direct_damage_scale = rider_direct_damage_scale + self.rider_maximum_transfer_damage_scale = rider_maximum_transfer_damage_scale + self.rider_minimum_transfer_damage_scale = rider_minimum_transfer_damage_scale + self.general_damage = general_damage + self.general_damage_length = general_damage_length + self.specific_damage = specific_damage + self.specific_damage_length = specific_damage_length + self.ai_stun_radius = ai_stun_radius + self.ai_stun_bounds = ai_stun_bounds + self.shake_radius = shake_radius + self.emp_radius = emp_radius + self.player_responses_tag_block = player_responses_tag_block + self.impulse_duration = impulse_duration + self.fade_function = fade_function + self.rotation = rotation + self.pushback = pushback + self.jitter = jitter + self.shaking_duration = shaking_duration + self.falloff_function = falloff_function + self.random_translation = random_translation + self.random_rotation = random_rotation + self.wobble_function = wobble_function + self.wobble_function_period = wobble_function_period + self.wobble_weight = wobble_weight + self.sound = sound + self.forward_velocity = forward_velocity + self.forward_radius = forward_radius + self.forward_exponent = forward_exponent + self.outward_velocity = outward_velocity + self.outward_radius = outward_radius + self.outward_exponent = outward_exponent + + class PlayerResponse: + def __init__(self, response_type=0, flash_type=0, priority=0, flash_duration=0.0, fade_function=0, maximum_intensity=0.0, color=(0.0, 0.0, 0.0, 0.0), low_vibration_duration=0.0, + high_vibration_duration=0.0, effect_name="", effect_name_length=0, sound_duration=0.0, functions=[]): + self.response_type = response_type + self.flash_type = flash_type + self.priority = priority + self.flash_duration = flash_duration + self.fade_function = fade_function + self.maximum_intensity = maximum_intensity + self.color = color + self.low_vibration_duration = low_vibration_duration + self.high_vibration_duration = high_vibration_duration + self.effect_name = effect_name + self.effect_name_length = effect_name_length + self.sound_duration = sound_duration + self.functions = functions diff --git a/io_scene_halo/file_tag/h2/file_decal/build_asset.py b/io_scene_halo/file_tag/h2/file_decal/build_asset.py new file mode 100644 index 000000000..7c535de1d --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_decal/build_asset.py @@ -0,0 +1,64 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from ....global_functions import tag_format, shader_processing + +def write_body(output_stream, TAG, DECAL): + DECAL.decal_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % next_decal_in_chain_length, TAG.string_to_bytes(DECAL.decal_body.next_decal_in_chain.name, False))) + + bitmap_length = len(DECAL.decal_body.bitmap.name) + if bitmap_length > 0: + output_stream.write(struct.pack('<%ssx' % bitmap_length, TAG.string_to_bytes(DECAL.decal_body.bitmap.name, False))) diff --git a/io_scene_halo/file_tag/h2/file_decal/format.py b/io_scene_halo/file_tag/h2/file_decal/format.py new file mode 100644 index 000000000..2583c6ef8 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_decal/format.py @@ -0,0 +1,61 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from enum import Flag, Enum, auto + +class LayerEnum(Enum): + lit_alpha_blend_prelight = 0 + lit_alpha_blend = auto() + double_multiply = auto() + multiply = auto() + max = auto() + add = auto() + error = auto() + +class DecalAsset(): + def __init__(self): + self.header = None + self.decal_body_header = None + self.decal_body = None + + class DecalBody: + def __init__(self, flags=0, decal_type=0, layer=3, max_overlapping_count=8, next_decal_in_chain=None, radius=(0.1, 0.1), radius_overlap_rejection=0.75, + color_lower_bounds=(1.0, 1.0, 1.0, 1.0), color_upper_bounds=(1.0, 1.0, 1.0, 1.0), lifetime=(10.0, 10.0), decay_time=(1.0, 1.0), bitmap=None, + maximum_sprite_extent=0.0): + self.flags = flags + self.decal_type = decal_type + self.layer = layer + self.max_overlapping_count = max_overlapping_count + self.next_decal_in_chain = next_decal_in_chain + self.radius = radius + self.radius_overlap_rejection = radius_overlap_rejection + self.color_lower_bounds = color_lower_bounds + self.color_upper_bounds = color_upper_bounds + self.lifetime = lifetime + self.decay_time = decay_time + self.bitmap = bitmap + self.maximum_sprite_extent = maximum_sprite_extent + diff --git a/io_scene_halo/file_tag/h2/file_device/format.py b/io_scene_halo/file_tag/h2/file_device/format.py new file mode 100644 index 000000000..53c0923fd --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_device/format.py @@ -0,0 +1,68 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto +from ..file_object.format import ObjectAsset + +class DeviceFlags(Flag): + position_loops = auto() + unused = auto() + allow_interpolation = auto() + +class LightmapFlags(Flag): + dont_use_in_lightmap = auto() + dont_use_in_lightprobe = auto() + +class DeviceAsset(ObjectAsset): + def __init__(self): + super().__init__() + self.header = None + self.device_body_header = None + self.device_body = None + + class DeviceBody(ObjectAsset.ObjectBody): + def __init__(self, device_flags=0, power_transition_time=0.0, power_acceleration_time=0.0, position_transition_time=0.0, position_acceleration_time=0.0, + depowered_position_transition_time=0.0, depowered_position_acceleration_time=0.0, lightmap_flags=0, open_up=None, close_down=None, opened=None, closed=None, + depowered=None, repowered=None, delay_time=0.0, delay_effect=None, automatic_activation_radius=0.0): + super().__init__() + self.device_flags = device_flags + self.power_transition_time = power_transition_time + self.power_acceleration_time = power_acceleration_time + self.position_transition_time = position_transition_time + self.position_acceleration_time = position_acceleration_time + self.depowered_position_transition_time = depowered_position_transition_time + self.depowered_position_acceleration_time = depowered_position_acceleration_time + self.lightmap_flags = lightmap_flags + self.open_up = open_up + self.close_down = close_down + self.opened = opened + self.closed = closed + self.depowered = depowered + self.repowered = repowered + self.delay_time = delay_time + self.delay_effect = delay_effect + self.automatic_activation_radius = automatic_activation_radius diff --git a/io_scene_halo/file_tag/h2/file_device_control/format.py b/io_scene_halo/file_tag/h2/file_device_control/format.py deleted file mode 100644 index 22600086f..000000000 --- a/io_scene_halo/file_tag/h2/file_device_control/format.py +++ /dev/null @@ -1,162 +0,0 @@ -# ##### BEGIN MIT LICENSE BLOCK ##### -# -# MIT License -# -# Copyright (c) 2023 Steven Garcia -# -# 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. -# -# ##### END MIT LICENSE BLOCK ##### - -from mathutils import Vector -from enum import Flag, Enum, auto - -class ObjectFlags(Flag): - does_not_cast_shadow = auto() - search_cardinal_direction_lightmaps_on_failure = auto() - unused = auto() - not_a_pathfinding_obstacle = auto() - extension_of_parent = auto() - does_not_cause_collision_damage = auto() - early_mover = auto() - early_mover_localized_physics = auto() - use_static_massive_lightmap_sample = auto() - object_scales_attachments = auto() - inherits_players_appearance = auto() - dead_bipdes_cant_localize = auto() - attach_to_clusters_by_dynamic_sphere = auto() - effects_created_by_this_object_do_not_spawn_objects_in_multiplayer = auto() - prophet_is_not_displayed_pegasus_builds = auto() - -class LightmapShadowModeEnum(Enum): - default = 0 - never = auto() - always = auto() - -class SweetenerSizeEnum(Enum): - small = 0 - medium = auto() - large = auto() - -class DeviceFlags(Flag): - position_loops = auto() - unused = auto() - allow_interpolation = auto() - -class LightmapFlags(Flag): - dont_use_in_lightmap = auto() - dont_use_in_lightprobe = auto() - -class ControlTypeEnum(Enum): - toggle_switch = 0 - on_button = auto() - off_button = auto() - call_button = auto() - -class TriggersWhenEnum(Enum): - touched_by_player = 0 - destroyed = auto() - -class ControlAsset(): - def __init__(self): - self.header = None - self.control_body_header = None - self.control_body = None - self.ai_properties_header = None - self.ai_properties = None - self.functions_header = None - self.functions = None - self.attachments_header = None - self.attachments = None - self.widgets_header = None - self.widgets = None - self.old_functions_header = None - self.old_functions = None - self.change_colors_header = None - self.change_colors = None - self.predicted_resources_header = None - self.predicted_resources = None - - class ControlBody: - def __init__(self, object_flags=0, bounding_radius=0.0, bounding_offset=Vector(), acceleration_scale=0.0, lightmap_shadow_mode=0, sweetner_size=0, - dynamic_light_sphere_radius=0.0, dynamic_light_sphere_offset=Vector(), default_model_variant="", default_model_variant_length=0, model=None, crate_object=None, - modifier_shader=None, creation_effect=None, material_effects=None, ai_properties_tag_block=None, functions_tag_block=None, apply_collision_damage_scale=0.0, - min_game_acc=0.0, max_game_acc=0.0, min_game_scale=0.0, max_game_scale=0.0, min_abs_acc=0.0, max_abs_acc=0.0, min_abs_scale=0.0, max_abs_scale=0.0, - hud_text_message_index=0, attachments_tag_block=None, widgets_tag_block=None, old_functions_tag_block=None, change_colors_tag_block=None, - predicted_resources_tag_block=None, device_flags=0, power_transition_time=0.0, power_acceleration_time=0.0, position_transition_time=0.0, - position_acceleration_time=0.0, depowered_position_transition_time=0.0, depowered_position_acceleration_time=0.0, lightmap_flags=0, open_up=None, - close_down=None, opened=None, closed=None, depowered=None, repowered=None, delay_time=0.0, delay_effect=None, automatic_activation_radius=0.0, control_type=0, - triggers_when=0, call_value=0.0, action_string="", action_string_length=0, on=None, off=None, deny=None): - self.object_flags = object_flags - self.bounding_radius = bounding_radius - self.bounding_offset = bounding_offset - self.acceleration_scale = acceleration_scale - self.lightmap_shadow_mode = lightmap_shadow_mode - self.sweetner_size = sweetner_size - self.dynamic_light_sphere_radius = dynamic_light_sphere_radius - self.dynamic_light_sphere_offset = dynamic_light_sphere_offset - self.default_model_variant = default_model_variant - self.default_model_variant_length = default_model_variant_length - self.model = model - self.crate_object = crate_object - self.modifier_shader = modifier_shader - self.creation_effect = creation_effect - self.material_effects = material_effects - self.ai_properties_tag_block = ai_properties_tag_block - self.functions_tag_block = functions_tag_block - self.apply_collision_damage_scale = apply_collision_damage_scale - self.min_game_acc = min_game_acc - self.max_game_acc = max_game_acc - self.min_game_scale = min_game_scale - self.max_game_scale = max_game_scale - self.min_abs_acc = min_abs_acc - self.max_abs_acc = max_abs_acc - self.min_abs_scale = min_abs_scale - self.max_abs_scale = max_abs_scale - self.hud_text_message_index = hud_text_message_index - self.attachments_tag_block = attachments_tag_block - self.widgets_tag_block = widgets_tag_block - self.old_functions_tag_block = old_functions_tag_block - self.change_colors_tag_block = change_colors_tag_block - self.predicted_resources_tag_block = predicted_resources_tag_block - self.device_flags = device_flags - self.power_transition_time = power_transition_time - self.power_acceleration_time = power_acceleration_time - self.position_transition_time = position_transition_time - self.position_acceleration_time = position_acceleration_time - self.depowered_position_transition_time = depowered_position_transition_time - self.depowered_position_acceleration_time = depowered_position_acceleration_time - self.lightmap_flags = lightmap_flags - self.open_up = open_up - self.close_down = close_down - self.opened = opened - self.closed = closed - self.depowered = depowered - self.repowered = repowered - self.delay_time = delay_time - self.delay_effect = delay_effect - self.automatic_activation_radius = automatic_activation_radius - self.control_type = control_type - self.triggers_when = triggers_when - self.call_value = call_value - self.action_string = action_string - self.action_string_length = action_string_length - self.on = on - self.off = off - self.deny = deny diff --git a/io_scene_halo/file_tag/h2/file_effect/build_asset.py b/io_scene_halo/file_tag/h2/file_effect/build_asset.py new file mode 100644 index 000000000..9a4e32d9a --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_effect/build_asset.py @@ -0,0 +1,186 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from math import radians +from ....global_functions import tag_format, shader_processing +from ....file_tag.h2.file_shader.format import FunctionTypeEnum + +def write_body(output_stream, TAG, EFFECT): + EFFECT.effect_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack(' 0: + locations_header.write(output_stream, TAG, True) + for location_element in locations: + output_stream.write(struct.pack('>I', len(location_element.name))) + + for location_element in locations: + name_length = len(location_element.name) + if name_length > 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(location_element.name, False))) + +def write_events(output_stream, TAG, events, events_header): + if len(events) > 0: + events_header.write(output_stream, TAG, True) + for event_element in events: + output_stream.write(struct.pack(' 0: + event_element.parts_header.write(output_stream, TAG, True) + for part_element in event_element.parts: + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % object_name_length, TAG.string_to_bytes(part_element.object_type.name, False))) + + if len(event_element.beams) > 0: + event_element.beams_header.write(output_stream, TAG, True) + for beam_element in event_element.beams: + beam_element.shader.write(output_stream, False) + for beam_property in beam_element.properties: + shader_processing.write_function_size(output_stream, beam_property) + for beam_property in beam_element.properties: + beam_property.function_header.write(output_stream, TAG, True) + shader_processing.write_function(output_stream, TAG, beam_property) + + if len(event_element.accelerations) > 0: + event_element.accelerations_header.write(output_stream, TAG, True) + for acceleration_element in event_element.accelerations: + output_stream.write(struct.pack(' 0: + event_element.particle_systems_header.write(output_stream, TAG, True) + for particle_system_element in event_element.particle_systems: + particle_system_element.particle.write(output_stream, False, True) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % particle_name_length, TAG.string_to_bytes(particle_system_element.particle.name, False))) + + if len(particle_system_element.emitters) > 0: + particle_system_element.emitters_header.write(output_stream, TAG, True) + for emitter_element in particle_system_element.emitters: + emitter_element.particle_physics.write(output_stream, False, True) + for particle_property in emitter_element.particle_properties: + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % particle_physics_length, TAG.string_to_bytes(emitter_element.particle_physics.name, False))) + + for particle_property in emitter_element.particle_properties: + particle_property.function_header.write(output_stream, TAG, True) + shader_processing.write_function(output_stream, TAG, particle_property) + + for emission_property in emitter_element.emission_properties: + emission_property.function_header.write(output_stream, TAG, True) + shader_processing.write_function(output_stream, TAG, emission_property) + +def build_asset(output_stream, EFFECT, report): + TAG = tag_format.TagAsset() + TAG.is_legacy = False + TAG.big_endian = False + + EFFECT.header.write(output_stream, False, True) + write_body(output_stream, TAG, EFFECT) + + write_locations(output_stream, TAG, EFFECT.locations, EFFECT.locations_header) + write_events(output_stream, TAG, EFFECT.events, EFFECT.events_header) + + looping_sound_name_length = len(EFFECT.effect_body.looping_sound.name) + if looping_sound_name_length > 0: + output_stream.write(struct.pack('<%ssx' % looping_sound_name_length, TAG.string_to_bytes(EFFECT.effect_body.looping_sound.name, False))) \ No newline at end of file diff --git a/io_scene_halo/file_tag/h2/file_effect/format.py b/io_scene_halo/file_tag/h2/file_effect/format.py new file mode 100644 index 000000000..4e027684c --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_effect/format.py @@ -0,0 +1,189 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto + +class EffectFlags(Flag): + deleted_when_attachment_deactivates = auto() + +class EventFlags(Flag): + disabled_for_debugging = auto() + +class EnvironmentEnum(Enum): + any_environment = 0 + air_only = auto() + water_only = auto() + space_only = auto() + +class ModeEnum(Enum): + either_mode = 0 + violent_mode_only = auto() + nonviolent_mode_only = auto() + +class PartFlags(Flag): + face_down_regardless_of_location_decals = auto() + offset_origin_away_from_geometry_lights = auto() + never_attached_to_object = auto() + disabled_for_debugging = auto() + draw_regardless_of_distance = auto() + +class ScaleFlags(Flag): + velocity = auto() + velocity_delta = auto() + velocity_cone_angle = auto() + angular_velocity = auto() + angular_velocity_delta = auto() + type_specific_scale = auto() + +class CoordinateSystemEnum(Enum): + world = 0 + local = auto() + parent = auto() + +class CameraModeEnum(Enum): + independent_of_camera_mode = 0 + only_in_first_person = auto() + only_in_third_person = auto() + both_first_and_third = auto() + +class ParticleSystemFlags(Flag): + glow = auto() + cinematics = auto() + looping_particle = auto() + disabled_for_debug = auto() + inherit_effect_velocity = auto() + dont_render_system = auto() + render_when_zoomed = auto() + spread_between_ticks = auto() + persistent_particle = auto() + expensive_visibility = auto() + +class EffectAsset(): + def __init__(self): + self.header = None + self.effect_body_header = None + self.effect_body = None + self.locations_header = None + self.locations = None + self.events_header = None + self.events = None + + class EffectBody: + def __init__(self, flags=0, loop_start_event=0, locations_tag_block=None, events_tag_block=None, looping_sound=None, location=0, always_play_distance=0.0, + never_play_distance=0.0): + self.flags = flags + self.loop_start_event = loop_start_event + self.locations_tag_block = locations_tag_block + self.events_tag_block = events_tag_block + self.looping_sound = looping_sound + self.location = location + self.always_play_distance = always_play_distance + self.never_play_distance = never_play_distance + + class Location: + def __init__(self, name="", name_length=0): + self.name = name + self.name_length = name_length + + class Event: + def __init__(self, flags=0, skip_fraction=0.0, delay_bounds=(0.0, 0.0), duration_bounds=(0.0, 0.0), parts_tag_block=None, parts_header=None, parts=None, + beams_tag_block=None, beams_header=None, beams=None, accelerations_tag_block=None, accelerations_header=None, accelerations=None, + particle_systems_tag_block=None, particle_systems_header=None, particle_systems=None): + self.flags = flags + self.skip_fraction = skip_fraction + self.delay_bounds = delay_bounds + self.duration_bounds = duration_bounds + self.parts_tag_block = parts_tag_block + self.parts_header = parts_header + self.parts = parts + self.beams_tag_block = beams_tag_block + self.beams_header = beams_header + self.beams = beams + self.accelerations_tag_block = accelerations_tag_block + self.accelerations_header = accelerations_header + self.accelerations = accelerations + self.particle_systems_tag_block = particle_systems_tag_block + self.particle_systems_header = particle_systems_header + self.particle_systems = particle_systems + + class Part: + def __init__(self, create_in_environment=0, create_in_mode=0, location=0, flags=0, object_type=None, velocity_bounds=(0.0, 0.0), velocity_cone_angle=0.0, + angular_velocity_bounds=(0.0, 0.0), radius_modifier_bounds=(0.0, 0.0), a_scales_value=0, b_scales_value=0): + self.create_in_environment = create_in_environment + self.create_in_mode = create_in_mode + self.location = location + self.flags = flags + self.object_type = object_type + self.velocity_bounds = velocity_bounds + self.velocity_cone_angle = velocity_cone_angle + self.angular_velocity_bounds = angular_velocity_bounds + self.radius_modifier_bounds = radius_modifier_bounds + self.a_scales_value = a_scales_value + self.b_scales_value = b_scales_value + + class Beam: + def __init__(self, shader=None, location=0, properties=None): + self.shader = shader + self.location = location + self.properties = properties + + class Acceleration: + def __init__(self, create_in_environment=0, create_in_mode=0, location=0, acceleration=0.0, inner_cone_angle=0.0, outer_cone_angle=0.0): + self.create_in_environment = create_in_environment + self.create_in_mode = create_in_mode + self.location = location + self.acceleration = acceleration + self.inner_cone_angle = inner_cone_angle + self.outer_cone_angle = outer_cone_angle + + class ParticleSystem: + def __init__(self, particle=None, location=0, coordinate_system=0, environment=0, disposition=0, camera_mode=0, sort_bias=0, flags=0, lod_in_distance=0.0, + lod_feather_in_delta=0.0, lod_out_distance=0.0, lod_feather_out_delta=0.0, emitters_tag_block=None, emitters_header=None, emitters=None): + self.particle = particle + self.location = location + self.coordinate_system = coordinate_system + self.environment = environment + self.disposition = disposition + self.camera_mode = camera_mode + self.sort_bias = sort_bias + self.flags = flags + self.lod_in_distance = lod_in_distance + self.lod_feather_in_delta = lod_feather_in_delta + self.lod_out_distance = lod_out_distance + self.lod_feather_out_delta = lod_feather_out_delta + self.emitters_tag_block = emitters_tag_block + self.emitters_header = emitters_header + self.emitters = emitters + + class Emitter: + def __init__(self, particle_physics=None, particle_properties=None, emission_shape=0, emission_properties=None, translational_offset=Vector(), relative_direction=(0.0, 0.0)): + self.particle_physics = particle_physics + self.particle_properties = particle_properties + self.emission_shape = emission_shape + self.emission_properties = emission_properties + self.translational_offset = translational_offset + self.relative_direction = relative_direction diff --git a/io_scene_halo/file_tag/h2/file_equipment/build_asset.py b/io_scene_halo/file_tag/h2/file_equipment/build_asset.py new file mode 100644 index 000000000..89b50bb33 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_equipment/build_asset.py @@ -0,0 +1,208 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from ....global_functions import tag_format, shader_processing +from ..file_object.build_asset import ( + write_ai_properties, + write_functions, + write_attachments, + write_tag_ref, + write_old_functions, + write_change_colors, + write_predicted_resources + ) +from ..file_item.build_asset import write_predicted_bitmaps + +def write_body(output_stream, TAG, EQUIPMENT): + EQUIPMENT.equipment_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack('I', len(EQUIPMENT.equipment_body.default_model_variant))) + EQUIPMENT.equipment_body.model.write(output_stream, False, True) + EQUIPMENT.equipment_body.crate_object.write(output_stream, False, True) + EQUIPMENT.equipment_body.modifier_shader.write(output_stream, False, True) + EQUIPMENT.equipment_body.creation_effect.write(output_stream, False, True) + EQUIPMENT.equipment_body.material_effects.write(output_stream, False, True) + EQUIPMENT.equipment_body.ai_properties_tag_block.write(output_stream, False) + EQUIPMENT.equipment_body.functions_tag_block.write(output_stream, False) + output_stream.write(struct.pack('I', len(EQUIPMENT.equipment_body.pickup_message))) + output_stream.write(struct.pack('>I', len(EQUIPMENT.equipment_body.swap_message))) + output_stream.write(struct.pack('>I', len(EQUIPMENT.equipment_body.pickup_or_dual_msg))) + output_stream.write(struct.pack('>I', len(EQUIPMENT.equipment_body.swap_or_dual_msg))) + output_stream.write(struct.pack('>I', len(EQUIPMENT.equipment_body.dual_only_msg))) + output_stream.write(struct.pack('>I', len(EQUIPMENT.equipment_body.picked_up_msg))) + output_stream.write(struct.pack('>I', len(EQUIPMENT.equipment_body.singluar_quantity_msg))) + output_stream.write(struct.pack('>I', len(EQUIPMENT.equipment_body.plural_quantity_msg))) + output_stream.write(struct.pack('>I', len(EQUIPMENT.equipment_body.switch_to_msg))) + output_stream.write(struct.pack('>I', len(EQUIPMENT.equipment_body.switch_to_from_ai_msg))) + EQUIPMENT.equipment_body.unused.write(output_stream, False, True) + EQUIPMENT.equipment_body.collision_sound.write(output_stream, False, True) + EQUIPMENT.equipment_body.predicted_bitmaps_tag_block.write(output_stream, False) + EQUIPMENT.equipment_body.detonation_damage_effect.write(output_stream, False, True) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % default_model_variant_name_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.default_model_variant, False))) + + model_name_length = len(EQUIPMENT.equipment_body.model.name) + if model_name_length > 0: + output_stream.write(struct.pack('<%ssx' % model_name_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.model.name, False))) + + crate_object_name_length = len(EQUIPMENT.equipment_body.crate_object.name) + if crate_object_name_length > 0: + output_stream.write(struct.pack('<%ssx' % crate_object_name_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.crate_object.name, False))) + + modifier_shader_name_length = len(EQUIPMENT.equipment_body.modifier_shader.name) + if modifier_shader_name_length > 0: + output_stream.write(struct.pack('<%ssx' % modifier_shader_name_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.modifier_shader.name, False))) + + creation_effect_name_length = len(EQUIPMENT.equipment_body.creation_effect.name) + if creation_effect_name_length > 0: + output_stream.write(struct.pack('<%ssx' % creation_effect_name_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.creation_effect.name, False))) + + material_effects_name_length = len(EQUIPMENT.equipment_body.material_effects.name) + if material_effects_name_length > 0: + output_stream.write(struct.pack('<%ssx' % material_effects_name_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.material_effects.name, False))) + + write_ai_properties(output_stream, TAG, EQUIPMENT.ai_properties, EQUIPMENT.ai_properties_header) + write_functions(output_stream, TAG, EQUIPMENT.functions, EQUIPMENT.functions_header) + write_attachments(output_stream, TAG, EQUIPMENT.attachments, EQUIPMENT.attachments_header) + write_tag_ref(output_stream, TAG, EQUIPMENT.widgets, EQUIPMENT.widgets_header) + write_old_functions(output_stream, TAG, EQUIPMENT.old_functions, EQUIPMENT.old_functions_header) + write_change_colors(output_stream, TAG, EQUIPMENT.change_colors, EQUIPMENT.change_colors_header) + write_predicted_resources(output_stream, TAG, EQUIPMENT.predicted_resources, EQUIPMENT.predicted_resources_header) + + pickup_message_length = len(EQUIPMENT.equipment_body.pickup_message) + if pickup_message_length > 0: + output_stream.write(struct.pack('<%ss' % pickup_message_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.pickup_message, False))) + + swap_message_length = len(EQUIPMENT.equipment_body.swap_message) + if swap_message_length > 0: + output_stream.write(struct.pack('<%ss' % swap_message_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.swap_message, False))) + + pickup_or_dual_msg_length = len(EQUIPMENT.equipment_body.pickup_or_dual_msg) + if pickup_or_dual_msg_length > 0: + output_stream.write(struct.pack('<%ss' % pickup_or_dual_msg_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.pickup_or_dual_msg, False))) + + swap_or_dual_msg_length = len(EQUIPMENT.equipment_body.swap_or_dual_msg) + if swap_or_dual_msg_length > 0: + output_stream.write(struct.pack('<%ss' % swap_or_dual_msg_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.swap_or_dual_msg, False))) + + dual_only_msg_length = len(EQUIPMENT.equipment_body.dual_only_msg) + if dual_only_msg_length > 0: + output_stream.write(struct.pack('<%ss' % dual_only_msg_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.dual_only_msg, False))) + + picked_up_msg_length = len(EQUIPMENT.equipment_body.picked_up_msg) + if picked_up_msg_length > 0: + output_stream.write(struct.pack('<%ss' % picked_up_msg_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.picked_up_msg, False))) + + singluar_quantity_msg_length = len(EQUIPMENT.equipment_body.singluar_quantity_msg) + if singluar_quantity_msg_length > 0: + output_stream.write(struct.pack('<%ss' % singluar_quantity_msg_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.singluar_quantity_msg, False))) + + plural_quantity_msg_length = len(EQUIPMENT.equipment_body.plural_quantity_msg) + if plural_quantity_msg_length > 0: + output_stream.write(struct.pack('<%ss' % plural_quantity_msg_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.plural_quantity_msg, False))) + + switch_to_msg_length = len(EQUIPMENT.equipment_body.switch_to_msg) + if switch_to_msg_length > 0: + output_stream.write(struct.pack('<%ss' % switch_to_msg_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.switch_to_msg, False))) + + switch_to_from_ai_msg_length = len(EQUIPMENT.equipment_body.switch_to_from_ai_msg) + if switch_to_from_ai_msg_length > 0: + output_stream.write(struct.pack('<%ss' % switch_to_from_ai_msg_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.switch_to_from_ai_msg, False))) + + unused_length = len(EQUIPMENT.equipment_body.unused.name) + if unused_length > 0: + output_stream.write(struct.pack('<%ssx' % unused_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.unused.name, False))) + + collision_sound_length = len(EQUIPMENT.equipment_body.collision_sound.name) + if collision_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % collision_sound_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.collision_sound.name, False))) + + write_predicted_bitmaps(output_stream, TAG, EQUIPMENT.predicted_bitmaps, EQUIPMENT.predicted_bitmaps_header) + + detonation_damage_effect_length = len(EQUIPMENT.equipment_body.detonation_damage_effect.name) + if detonation_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % detonation_damage_effect_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.detonation_damage_effect.name, False))) + + detonating_effect_length = len(EQUIPMENT.equipment_body.detonating_effect.name) + if detonating_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % detonating_effect_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.detonating_effect.name, False))) + + detonation_effect_length = len(EQUIPMENT.equipment_body.detonation_effect.name) + if detonation_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % detonation_effect_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.detonation_effect.name, False))) + + pickup_sound_length = len(EQUIPMENT.equipment_body.pickup_sound.name) + if pickup_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % pickup_sound_length, TAG.string_to_bytes(EQUIPMENT.equipment_body.pickup_sound.name, False))) diff --git a/io_scene_halo/file_tag/h2/file_equipment/format.py b/io_scene_halo/file_tag/h2/file_equipment/format.py index 7c9e54c4e..c3c85ec9d 100644 --- a/io_scene_halo/file_tag/h2/file_equipment/format.py +++ b/io_scene_halo/file_tag/h2/file_equipment/format.py @@ -26,38 +26,7 @@ from mathutils import Vector from enum import Flag, Enum, auto - -class ObjectFlags(Flag): - does_not_cast_shadow = auto() - search_cardinal_direction_lightmaps_on_failure = auto() - unused = auto() - not_a_pathfinding_obstacle = auto() - extension_of_parent = auto() - does_not_cause_collision_damage = auto() - early_mover = auto() - early_mover_localized_physics = auto() - use_static_massive_lightmap_sample = auto() - object_scales_attachments = auto() - inherits_players_appearance = auto() - dead_bipeds_cant_localize = auto() - attach_to_clusters_dynamic_sphere = auto() - effects_created_by_this_object_do_not_spawn_objects_in_multiplayer = auto() - prophet_is_not_displayed_in_pegasus_builds = auto() - -class LightmapShadowModeEnum(Enum): - default = 0 - never = auto() - always = auto() - -class SweetenerSizeEnum(Enum): - small = 0 - medium = auto() - large = auto() - -class ItemFlags(Flag): - always_maintains_z_up = auto() - destroyed_by_explosions = auto() - unaffected_by_gravity = auto() +from ..file_item.format import ItemAsset class PowerupTypeEnum(Enum): none = 0 @@ -72,105 +41,16 @@ class GrenadeTypeEnum(Enum): human_fragmentation = 0 covenant_plasma = auto() -class EquipmentAsset(): +class EquipmentAsset(ItemAsset): def __init__(self): + super().__init__() self.header = None self.equipment_body_header = None self.equipment_body = None - self.ai_properties_header = None - self.ai_properties = None - self.functions_header = None - self.functions = None - self.attachments_header = None - self.attachments = None - self.widgets_header = None - self.widgets = None - self.old_functions_header = None - self.old_functions = None - self.change_colors_header = None - self.change_colors = None - self.predicted_resources_header = None - self.predicted_resources = None - self.predicted_bitmaps_header = None - self.predicted_bitmaps = None - class EquipmentBody: - def __init__(self, object_flags=0, bounding_radius=0.0, bounding_offset=Vector(), acceleration_scale=0.0, lightmap_shadow_mode=0, sweetner_size=0, - dynamic_light_sphere_radius=0.0, dynamic_light_sphere_offset=Vector(), default_model_variant="", default_model_variant_length=0, model=None, crate_object=None, - modifier_shader=None, creation_effect=None, material_effects=None, ai_properties_tag_block=None, functions_tag_block=None, apply_collision_damage_scale=0.0, - min_game_acc=0.0, max_game_acc=0.0, min_game_scale=0.0, max_game_scale=0.0, min_abs_acc=0.0, max_abs_acc=0.0, min_abs_scale=0.0, max_abs_scale=0.0, - hud_text_message_index=0, attachments_tag_block=None, widgets_tag_block=None, old_functions_tag_block=None, change_colors_tag_block=None, - predicted_resources_tag_block=None, equipment_flags=0, old_message_index=0, sort_order=0, multiplayer_on_ground_scale=0.0, campaign_on_ground_scale=0.0, - pickup_message="", pickup_message_length=0, swap_message="", swap_message_length=0, pickup_or_dual_message="", pickup_or_dual_message_length=0, - swap_or_dual_message="", swap_or_dual_message_length=0, dual_only_message="", dual_only_message_length=0, picked_up_message="", picked_up_message_length=0, - singluar_quantity_message="", singluar_quantity_message_length=0, plural_quantity_message="", plural_quantity_message_length=0, switch_to_message="", - switch_to_message_length=0, switch_to_from_ai_message="", switch_to_from_ai_message_length=0, unused=None, collision_sound=None, - predicted_bitmaps_tag_block=None, detonation_damage_effect=None, detonation_delay=(0.0, 0.0), detonating_effect=None, detonation_effect=None, powerup_type=0, - grenade_type=0, powerup_time=0.0, pickup_sound=None): - self.object_flags = object_flags - self.bounding_radius = bounding_radius - self.bounding_offset = bounding_offset - self.acceleration_scale = acceleration_scale - self.lightmap_shadow_mode = lightmap_shadow_mode - self.sweetner_size = sweetner_size - self.dynamic_light_sphere_radius = dynamic_light_sphere_radius - self.dynamic_light_sphere_offset = dynamic_light_sphere_offset - self.default_model_variant = default_model_variant - self.default_model_variant_length = default_model_variant_length - self.model = model - self.crate_object = crate_object - self.modifier_shader = modifier_shader - self.creation_effect = creation_effect - self.material_effects = material_effects - self.ai_properties_tag_block = ai_properties_tag_block - self.functions_tag_block = functions_tag_block - self.apply_collision_damage_scale = apply_collision_damage_scale - self.min_game_acc = min_game_acc - self.max_game_acc = max_game_acc - self.min_game_scale = min_game_scale - self.max_game_scale = max_game_scale - self.min_abs_acc = min_abs_acc - self.max_abs_acc = max_abs_acc - self.min_abs_scale = min_abs_scale - self.max_abs_scale = max_abs_scale - self.hud_text_message_index = hud_text_message_index - self.attachments_tag_block = attachments_tag_block - self.widgets_tag_block = widgets_tag_block - self.old_functions_tag_block = old_functions_tag_block - self.change_colors_tag_block = change_colors_tag_block - self.predicted_resources_tag_block = predicted_resources_tag_block - self.equipment_flags = equipment_flags - self.old_message_index = old_message_index - self.sort_order = sort_order - self.multiplayer_on_ground_scale = multiplayer_on_ground_scale - self.campaign_on_ground_scale = campaign_on_ground_scale - self.pickup_message = pickup_message - self.pickup_message_length = pickup_message_length - self.swap_message = swap_message - self.swap_message_length = swap_message_length - self.pickup_or_dual_message = pickup_or_dual_message - self.pickup_or_dual_message_length = pickup_or_dual_message_length - self.swap_or_dual_message = swap_or_dual_message - self.swap_or_dual_message_length = swap_or_dual_message_length - self.dual_only_message = dual_only_message - self.dual_only_message_length = dual_only_message_length - self.picked_up_message = picked_up_message - self.picked_up_message_length = picked_up_message_length - self.singluar_quantity_message = singluar_quantity_message - self.singluar_quantity_message_length = singluar_quantity_message_length - self.plural_quantity_message = plural_quantity_message - self.plural_quantity_message_length = plural_quantity_message_length - self.switch_to_message = switch_to_message - self.switch_to_message_length = switch_to_message_length - self.switch_to_from_ai_message = switch_to_from_ai_message - self.switch_to_from_ai_message_length = switch_to_from_ai_message_length - self.unused = unused - self.collision_sound = collision_sound - self.predicted_bitmaps_tag_block = predicted_bitmaps_tag_block - self.detonation_damage_effect = detonation_damage_effect - self.detonation_delay = detonation_delay - self.detonating_effect = detonating_effect - self.detonation_effect = detonation_effect + class EquipmentBody(ItemAsset.ItemBody): + def __init__(self, powerup_type=0, grenade_type=0, powerup_time=0.0, pickup_sound=None): + super().__init__() self.powerup_type = powerup_type self.grenade_type = grenade_type self.powerup_time = powerup_time diff --git a/io_scene_halo/file_tag/h2/file_equipment/process_file.py b/io_scene_halo/file_tag/h2/file_equipment/process_file.py index 372d4390e..68b699b80 100644 --- a/io_scene_halo/file_tag/h2/file_equipment/process_file.py +++ b/io_scene_halo/file_tag/h2/file_equipment/process_file.py @@ -26,15 +26,9 @@ from xml.dom import minidom from ....global_functions import tag_format -from .format import ( - EquipmentAsset, - ObjectFlags, - LightmapShadowModeEnum, - SweetenerSizeEnum, - ItemFlags, - PowerupTypeEnum, - GrenadeTypeEnum - ) +from ..file_object.format import ObjectFlags, LightmapShadowModeEnum, SweetenerSizeEnum +from ..file_item.format import ItemFlags +from .format import EquipmentAsset, PowerupTypeEnum, GrenadeTypeEnum XML_OUTPUT = False @@ -104,25 +98,25 @@ def read_equipment_body_v0(EQUIPMENT, TAG, input_stream, tag_node, XML_OUTPUT): TAG.big_endian = True input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.pickup_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.pickup_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.swap_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.swap_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.pickup_or_dual_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.pickup_or_dual_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.swap_or_dual_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.swap_or_dual_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.dual_only_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.dual_only_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.picked_up_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.picked_up_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.singluar_quantity_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.singluar_quantity_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.plural_quantity_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.plural_quantity_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.switch_to_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.switch_to_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.switch_to_from_ai_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.switch_to_from_ai_msg_length = TAG.read_signed_short(input_stream, TAG) TAG.big_endian = False input_stream.read(148) # Padding? @@ -191,25 +185,25 @@ def read_equipment_body_retail(EQUIPMENT, TAG, input_stream, tag_node, XML_OUTPU TAG.big_endian = True input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.pickup_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.pickup_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.swap_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.swap_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.pickup_or_dual_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.pickup_or_dual_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.swap_or_dual_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.swap_or_dual_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.dual_only_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.dual_only_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.picked_up_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.picked_up_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.singluar_quantity_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.singluar_quantity_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.plural_quantity_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.plural_quantity_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.switch_to_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.switch_to_msg_length = TAG.read_signed_short(input_stream, TAG) input_stream.read(2) # Padding? - EQUIPMENT.equipment_body.switch_to_from_ai_message_length = TAG.read_signed_short(input_stream, TAG) + EQUIPMENT.equipment_body.switch_to_from_ai_msg_length = TAG.read_signed_short(input_stream, TAG) TAG.big_endian = False EQUIPMENT.equipment_body.unused = TAG.TagRef().read(input_stream, TAG, tag_format.XMLData(tag_node, "unused")) diff --git a/io_scene_halo/file_tag/h2/file_garbage/build_asset.py b/io_scene_halo/file_tag/h2/file_garbage/build_asset.py new file mode 100644 index 000000000..413ba8e33 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_garbage/build_asset.py @@ -0,0 +1,201 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from ....global_functions import tag_format, shader_processing +from ..file_object.build_asset import ( + write_ai_properties, + write_functions, + write_attachments, + write_tag_ref, + write_old_functions, + write_change_colors, + write_predicted_resources + ) +from ..file_item.build_asset import write_predicted_bitmaps + +def write_body(output_stream, TAG, GARBAGE): + GARBAGE.garbage_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack('I', len(GARBAGE.garbage_body.default_model_variant))) + GARBAGE.garbage_body.model.write(output_stream, False, True) + GARBAGE.garbage_body.crate_object.write(output_stream, False, True) + GARBAGE.garbage_body.modifier_shader.write(output_stream, False, True) + GARBAGE.garbage_body.creation_effect.write(output_stream, False, True) + GARBAGE.garbage_body.material_effects.write(output_stream, False, True) + GARBAGE.garbage_body.ai_properties_tag_block.write(output_stream, False) + GARBAGE.garbage_body.functions_tag_block.write(output_stream, False) + output_stream.write(struct.pack('I', len(GARBAGE.garbage_body.pickup_message))) + output_stream.write(struct.pack('>I', len(GARBAGE.garbage_body.swap_message))) + output_stream.write(struct.pack('>I', len(GARBAGE.garbage_body.pickup_or_dual_msg))) + output_stream.write(struct.pack('>I', len(GARBAGE.garbage_body.swap_or_dual_msg))) + output_stream.write(struct.pack('>I', len(GARBAGE.garbage_body.dual_only_msg))) + output_stream.write(struct.pack('>I', len(GARBAGE.garbage_body.picked_up_msg))) + output_stream.write(struct.pack('>I', len(GARBAGE.garbage_body.singluar_quantity_msg))) + output_stream.write(struct.pack('>I', len(GARBAGE.garbage_body.plural_quantity_msg))) + output_stream.write(struct.pack('>I', len(GARBAGE.garbage_body.switch_to_msg))) + output_stream.write(struct.pack('>I', len(GARBAGE.garbage_body.switch_to_from_ai_msg))) + GARBAGE.garbage_body.unused.write(output_stream, False, True) + GARBAGE.garbage_body.collision_sound.write(output_stream, False, True) + GARBAGE.garbage_body.predicted_bitmaps_tag_block.write(output_stream, False) + GARBAGE.garbage_body.detonation_damage_effect.write(output_stream, False, True) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % default_model_variant_name_length, TAG.string_to_bytes(GARBAGE.garbage_body.default_model_variant, False))) + + model_name_length = len(GARBAGE.garbage_body.model.name) + if model_name_length > 0: + output_stream.write(struct.pack('<%ssx' % model_name_length, TAG.string_to_bytes(GARBAGE.garbage_body.model.name, False))) + + crate_object_name_length = len(GARBAGE.garbage_body.crate_object.name) + if crate_object_name_length > 0: + output_stream.write(struct.pack('<%ssx' % crate_object_name_length, TAG.string_to_bytes(GARBAGE.garbage_body.crate_object.name, False))) + + modifier_shader_name_length = len(GARBAGE.garbage_body.modifier_shader.name) + if modifier_shader_name_length > 0: + output_stream.write(struct.pack('<%ssx' % modifier_shader_name_length, TAG.string_to_bytes(GARBAGE.garbage_body.modifier_shader.name, False))) + + creation_effect_name_length = len(GARBAGE.garbage_body.creation_effect.name) + if creation_effect_name_length > 0: + output_stream.write(struct.pack('<%ssx' % creation_effect_name_length, TAG.string_to_bytes(GARBAGE.garbage_body.creation_effect.name, False))) + + material_effects_name_length = len(GARBAGE.garbage_body.material_effects.name) + if material_effects_name_length > 0: + output_stream.write(struct.pack('<%ssx' % material_effects_name_length, TAG.string_to_bytes(GARBAGE.garbage_body.material_effects.name, False))) + + write_ai_properties(output_stream, TAG, GARBAGE.ai_properties, GARBAGE.ai_properties_header) + write_functions(output_stream, TAG, GARBAGE.functions, GARBAGE.functions_header) + write_attachments(output_stream, TAG, GARBAGE.attachments, GARBAGE.attachments_header) + write_tag_ref(output_stream, TAG, GARBAGE.widgets, GARBAGE.widgets_header) + write_old_functions(output_stream, TAG, GARBAGE.old_functions, GARBAGE.old_functions_header) + write_change_colors(output_stream, TAG, GARBAGE.change_colors, GARBAGE.change_colors_header) + write_predicted_resources(output_stream, TAG, GARBAGE.predicted_resources, GARBAGE.predicted_resources_header) + + pickup_message_length = len(GARBAGE.garbage_body.pickup_message) + if pickup_message_length > 0: + output_stream.write(struct.pack('<%ss' % pickup_message_length, TAG.string_to_bytes(GARBAGE.garbage_body.pickup_message, False))) + + swap_message_length = len(GARBAGE.garbage_body.swap_message) + if swap_message_length > 0: + output_stream.write(struct.pack('<%ss' % swap_message_length, TAG.string_to_bytes(GARBAGE.garbage_body.swap_message, False))) + + pickup_or_dual_msg_length = len(GARBAGE.garbage_body.pickup_or_dual_msg) + if pickup_or_dual_msg_length > 0: + output_stream.write(struct.pack('<%ss' % pickup_or_dual_msg_length, TAG.string_to_bytes(GARBAGE.garbage_body.pickup_or_dual_msg, False))) + + swap_or_dual_msg_length = len(GARBAGE.garbage_body.swap_or_dual_msg) + if swap_or_dual_msg_length > 0: + output_stream.write(struct.pack('<%ss' % swap_or_dual_msg_length, TAG.string_to_bytes(GARBAGE.garbage_body.swap_or_dual_msg, False))) + + dual_only_msg_length = len(GARBAGE.garbage_body.dual_only_msg) + if dual_only_msg_length > 0: + output_stream.write(struct.pack('<%ss' % dual_only_msg_length, TAG.string_to_bytes(GARBAGE.garbage_body.dual_only_msg, False))) + + picked_up_msg_length = len(GARBAGE.garbage_body.picked_up_msg) + if picked_up_msg_length > 0: + output_stream.write(struct.pack('<%ss' % picked_up_msg_length, TAG.string_to_bytes(GARBAGE.garbage_body.picked_up_msg, False))) + + singluar_quantity_msg_length = len(GARBAGE.garbage_body.singluar_quantity_msg) + if singluar_quantity_msg_length > 0: + output_stream.write(struct.pack('<%ss' % singluar_quantity_msg_length, TAG.string_to_bytes(GARBAGE.garbage_body.singluar_quantity_msg, False))) + + plural_quantity_msg_length = len(GARBAGE.garbage_body.plural_quantity_msg) + if plural_quantity_msg_length > 0: + output_stream.write(struct.pack('<%ss' % plural_quantity_msg_length, TAG.string_to_bytes(GARBAGE.garbage_body.plural_quantity_msg, False))) + + switch_to_msg_length = len(GARBAGE.garbage_body.switch_to_msg) + if switch_to_msg_length > 0: + output_stream.write(struct.pack('<%ss' % switch_to_msg_length, TAG.string_to_bytes(GARBAGE.garbage_body.switch_to_msg, False))) + + switch_to_from_ai_msg_length = len(GARBAGE.garbage_body.switch_to_from_ai_msg) + if switch_to_from_ai_msg_length > 0: + output_stream.write(struct.pack('<%ss' % switch_to_from_ai_msg_length, TAG.string_to_bytes(GARBAGE.garbage_body.switch_to_from_ai_msg, False))) + + unused_length = len(GARBAGE.garbage_body.unused.name) + if unused_length > 0: + output_stream.write(struct.pack('<%ssx' % unused_length, TAG.string_to_bytes(GARBAGE.garbage_body.unused.name, False))) + + collision_sound_length = len(GARBAGE.garbage_body.collision_sound.name) + if collision_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % collision_sound_length, TAG.string_to_bytes(GARBAGE.garbage_body.collision_sound.name, False))) + + write_predicted_bitmaps(output_stream, TAG, GARBAGE.predicted_bitmaps, GARBAGE.predicted_bitmaps_header) + + detonation_damage_effect_length = len(GARBAGE.garbage_body.detonation_damage_effect.name) + if detonation_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % detonation_damage_effect_length, TAG.string_to_bytes(GARBAGE.garbage_body.detonation_damage_effect.name, False))) + + detonating_effect_length = len(GARBAGE.garbage_body.detonating_effect.name) + if detonating_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % detonating_effect_length, TAG.string_to_bytes(GARBAGE.garbage_body.detonating_effect.name, False))) + + detonation_effect_length = len(GARBAGE.garbage_body.detonation_effect.name) + if detonation_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % detonation_effect_length, TAG.string_to_bytes(GARBAGE.garbage_body.detonation_effect.name, False))) \ No newline at end of file diff --git a/io_scene_halo/file_tag/h2/file_garbage/format.py b/io_scene_halo/file_tag/h2/file_garbage/format.py new file mode 100644 index 000000000..6e1f80366 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_garbage/format.py @@ -0,0 +1,41 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto +from ..file_item.format import ItemAsset + +class GarbageAsset(ItemAsset): + def __init__(self): + super().__init__() + self.header = None + self.garbage_body_header = None + self.garbage_body = None + + class GarbageBody(ItemAsset.ItemBody): + def __init__(self, ): + super().__init__() + diff --git a/io_scene_halo/file_tag/h2/file_item/build_asset.py b/io_scene_halo/file_tag/h2/file_item/build_asset.py new file mode 100644 index 000000000..f035c3d3c --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_item/build_asset.py @@ -0,0 +1,40 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from ....global_functions import shader_processing + +def write_predicted_bitmaps(output_stream, TAG, predicted_bitmaps, predicted_bitmaps_header): + if len(predicted_bitmaps) > 0: + predicted_bitmaps_header.write(output_stream, TAG, True) + for predicted_bitmap_element in predicted_bitmaps: + predicted_bitmap_element.write(output_stream, False, True) + + for predicted_bitmap_element in predicted_bitmaps: + predicted_bitmap_length = len(predicted_bitmap_element.name) + if predicted_bitmap_length > 0: + output_stream.write(struct.pack('<%ssx' % predicted_bitmap_length, TAG.string_to_bytes(predicted_bitmap_element.name, False))) diff --git a/io_scene_halo/file_tag/h2/file_item/format.py b/io_scene_halo/file_tag/h2/file_item/format.py new file mode 100644 index 000000000..02e95e06d --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_item/format.py @@ -0,0 +1,84 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto +from ..file_object.format import ObjectAsset + +class ItemFlags(Flag): + always_maintains_z_up = auto() + destroyed_by_explosions = auto() + unaffected_by_gravity = auto() + +class ItemAsset(ObjectAsset): + def __init__(self): + super().__init__() + self.header = None + self.item_body_header = None + self.item_body = None + self.predicted_bitmaps_header = None + self.predicted_bitmaps = None + + class ItemBody(ObjectAsset.ObjectBody): + def __init__(self, item_flags=0, old_message_index=0, sort_order=0, multiplayer_on_ground_scale=0.0, campaign_on_ground_scale=0.0, pickup_message="", + pickup_message_length=0, swap_message="", swap_message_length=0, pickup_or_dual_msg="", pickup_or_dual_msg_length=0, swap_or_dual_msg="", + swap_or_dual_msg_length=0, dual_only_msg="", dual_only_msg_length=0, picked_up_msg="", picked_up_msg_length=0, singluar_quantity_msg="", + singluar_quantity_msg_length=0, plural_quantity_msg="", plural_quantity_msg_length=0, switch_to_msg="", switch_to_msg_length=0, switch_to_from_ai_msg="", + switch_to_from_ai_msg_length=0, unused=None, collision_sound=None, predicted_bitmaps_tag_block=None, detonation_damage_effect=None, detonation_delay=(0.0, 0.0), + detonating_effect=None, detonation_effect=None): + super().__init__() + self.item_flags = item_flags + self.old_message_index = old_message_index + self.sort_order = sort_order + self.multiplayer_on_ground_scale = multiplayer_on_ground_scale + self.campaign_on_ground_scale = campaign_on_ground_scale + self.pickup_message = pickup_message + self.pickup_message_length = pickup_message_length + self.swap_message = swap_message + self.swap_message_length = swap_message_length + self.pickup_or_dual_msg = pickup_or_dual_msg + self.pickup_or_dual_msg_length = pickup_or_dual_msg_length + self.swap_or_dual_msg = swap_or_dual_msg + self.swap_or_dual_msg_length = swap_or_dual_msg_length + self.dual_only_msg = dual_only_msg + self.dual_only_msg_length = dual_only_msg_length + self.picked_up_msg = picked_up_msg + self.picked_up_msg_length = picked_up_msg_length + self.singluar_quantity_msg = singluar_quantity_msg + self.singluar_quantity_msg_length = singluar_quantity_msg_length + self.plural_quantity_msg = plural_quantity_msg + self.plural_quantity_msg_length = plural_quantity_msg_length + self.switch_to_msg = switch_to_msg + self.switch_to_msg_length = switch_to_msg_length + self.switch_to_from_ai_msg = switch_to_from_ai_msg + self.switch_to_from_ai_msg_length = switch_to_from_ai_msg_length + self.unused = unused + self.collision_sound = collision_sound + self.predicted_bitmaps_tag_block = predicted_bitmaps_tag_block + self.detonation_damage_effect = detonation_damage_effect + self.detonation_delay = detonation_delay + self.detonating_effect = detonating_effect + self.detonation_effect = detonation_effect diff --git a/io_scene_halo/file_tag/h2/file_lens_flare/build_asset.py b/io_scene_halo/file_tag/h2/file_lens_flare/build_asset.py new file mode 100644 index 000000000..e0c087917 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_lens_flare/build_asset.py @@ -0,0 +1,107 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from math import radians +from ....global_functions import tag_format, shader_processing + +def write_body(output_stream, TAG, LENSFLARE): + LENSFLARE.lens_flare_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack(' 0: + reflections_header.write(output_stream, TAG, True) + for reflection in reflections: + output_stream.write(struct.pack(' 0: + brightness_header.write(output_stream, TAG, True) + for brightness_property in brightness: + shader_processing.write_function_size(output_stream, brightness_property) + + for brightness_property in brightness: + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("SCFN", True), 0, 1, 12)) + shader_processing.write_function(output_stream, TAG, brightness_property) + +def write_color(output_stream, TAG, color, color_header): + if len(color) > 0: + color_header.write(output_stream, TAG, True) + for color_property in color: + shader_processing.write_function_size(output_stream, color_property) + + for color_property in color: + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("CLFN", True), 0, 1, 12)) + shader_processing.write_function(output_stream, TAG, color_property) + +def build_asset(output_stream, LENSFLARE, report): + TAG = tag_format.TagAsset() + TAG.is_legacy = False + TAG.big_endian = False + + LENSFLARE.header.write(output_stream, False, True) + write_body(output_stream, TAG, LENSFLARE) + + bitmap_length = len(LENSFLARE.lens_flare_body.bitmap.name) + if bitmap_length > 0: + output_stream.write(struct.pack('<%ssx' % bitmap_length, TAG.string_to_bytes(LENSFLARE.lens_flare_body.bitmap.name, False))) + + write_reflections(output_stream, TAG, LENSFLARE.reflections, LENSFLARE.reflections_header) + write_brightness(output_stream, TAG, LENSFLARE.brightness, LENSFLARE.brightness_header) + write_color(output_stream, TAG, LENSFLARE.color, LENSFLARE.color_header) \ No newline at end of file diff --git a/io_scene_halo/file_tag/h2/file_lens_flare/format.py b/io_scene_halo/file_tag/h2/file_lens_flare/format.py new file mode 100644 index 000000000..22736eb63 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_lens_flare/format.py @@ -0,0 +1,76 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto + +class LensFlareAsset(): + def __init__(self): + self.header = None + self.lens_flare_body_header = None + self.lens_flare_body = None + self.reflections_header = None + self.reflections = None + self.brightness_header = None + self.brightness = None + self.color_header = None + self.color = None + self.rotation_header = None + self.rotation = None + + class LensFlareBody: + def __init__(self, falloff_angle=0.0, cutoff_angle=0.0, occlusion_radius=0.05, occlusion_offset_direction=0, occlusion_inner_radius_scale=0, near_fade_distance=90.0, + far_fade_distance=100.0, bitmap=None, occlusion_flags=0, rotation_function=None, rotation_function_scale=0.0, corona_scale=(1.0, 1.0), falloff_function=0, + reflections_tag_block=None, flags=0, brightness_tag_block=None, color_tag_block=None, rotation_tag_block=None): + self.falloff_angle = falloff_angle + self.cutoff_angle = cutoff_angle + self.occlusion_radius = occlusion_radius + self.occlusion_offset_direction = occlusion_offset_direction + self.occlusion_inner_radius_scale = occlusion_inner_radius_scale + self.near_fade_distance = near_fade_distance + self.far_fade_distance = far_fade_distance + self.bitmap = bitmap + self.occlusion_flags = occlusion_flags + self.rotation_function = rotation_function + self.rotation_function_scale = rotation_function_scale + self.corona_scale = corona_scale + self.falloff_function = falloff_function + self.reflections_tag_block = reflections_tag_block + self.flags = flags + self.brightness_tag_block = brightness_tag_block + self.color_tag_block = color_tag_block + self.rotation_tag_block = rotation_tag_block + + class Reflection: + def __init__(self, flags=0, bitmap_index=0, position=0.0, rotation_offset=0.0, radius=(0.0, 0.1), brightness=(0.0, 1.0), modulation_factor=0.0, color=(1.0, 1.0, 1.0, 1.0)): + self.flags = flags + self.bitmap_index = bitmap_index + self.position = position + self.rotation_offset = rotation_offset + self.radius = radius + self.brightness = brightness + self.modulation_factor = modulation_factor + self.color = color diff --git a/io_scene_halo/file_tag/h2/file_light/build_asset.py b/io_scene_halo/file_tag/h2/file_light/build_asset.py new file mode 100644 index 000000000..a33bab9bb --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_light/build_asset.py @@ -0,0 +1,114 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from ....global_functions import tag_format, shader_processing + +def write_body(output_stream, TAG, LIGHT): + LIGHT.light_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack(' 0: + brightness_animation_header.write(output_stream, TAG, True) + for brightness_property in brightness_animation: + shader_processing.write_function_size(output_stream, brightness_property) + + for brightness_property in brightness_animation: + shader_processing.write_function(output_stream, TAG, brightness_property) + +def build_asset(output_stream, LIGHT, report): + TAG = tag_format.TagAsset() + TAG.is_legacy = False + TAG.big_endian = False + + LIGHT.header.write(output_stream, False, True) + write_body(output_stream, TAG, LIGHT) + + gel_map_length = len(LIGHT.light_body.gel_map.name) + if gel_map_length > 0: + output_stream.write(struct.pack('<%ssx' % gel_map_length, TAG.string_to_bytes(LIGHT.light_body.gel_map.name, False))) + + lens_flare_length = len(LIGHT.light_body.lens_flare.name) + if lens_flare_length > 0: + output_stream.write(struct.pack('<%ssx' % lens_flare_length, TAG.string_to_bytes(LIGHT.light_body.lens_flare.name, False))) + + light_volume_length = len(LIGHT.light_body.light_volume.name) + if light_volume_length > 0: + output_stream.write(struct.pack('<%ssx' % light_volume_length, TAG.string_to_bytes(LIGHT.light_body.light_volume.name, False))) + + write_brightness_animation(output_stream, TAG, LIGHT.brightness_animation, LIGHT.brightness_animation_header) + + shader_length = len(LIGHT.light_body.shader.name) + if shader_length > 0: + output_stream.write(struct.pack('<%ssx' % shader_length, TAG.string_to_bytes(LIGHT.light_body.shader.name, False))) diff --git a/io_scene_halo/file_tag/h2/file_light/format.py b/io_scene_halo/file_tag/h2/file_light/format.py new file mode 100644 index 000000000..8f0d89568 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_light/format.py @@ -0,0 +1,194 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto + +class LightFlags(Flag): + no_illumination = auto() + no_specular = auto() + force_cast_environment_shadows_through_portals = auto() + no_shadow = auto() + force_frustum_visibility_on_small_light = auto() + only_render_in_first_person = auto() + only_render_in_third_person = auto() + dont_fade_when_invisible = auto() + multiplayer_override = auto() + animated_gel = auto() + only_in_dynamic_envmap = auto() + ignore_parent_object = auto() + dont_shadow_parent = auto() + ignore_all_parents = auto() + march_milestone_hack = auto() + force_light_inside_world = auto() + environment_doesnt_cast_stencil_shadows = auto() + objects_dont_cast_stencil_shadows = auto() + first_person_from_camera = auto() + texture_camera_gel = auto() + light_framerate_killer = auto() + allowed_in_split_screen = auto() + only_on_parent_bipeds = auto() + +class ShapeTypeEnum(Enum): + sphere = 0 + orthogonal = auto() + projective = auto() + pyramid = auto() + +class ShadowTapBiasEnum(Enum): + _3_tap = 0 + unused = auto() + _1_tap = auto() + +class InterpolationFlags(Flag): + blend_in_hsv = auto() + more_colors = auto() + +class SpecularMaskEnum(Enum): + default = 0 + none_no_mask = auto() + gel_alpha = auto() + gel_color = auto() + +class FalloffFunctionEnum(Enum): + default = 0 + narrow = auto() + broad = auto() + very_broad = auto() + +class DiffuseContrastEnum(Enum): + default_linear = 0 + high = auto() + low = auto() + very_low = auto() + +class SpecularContrastEnum(Enum): + default_one = 0 + high_linear = auto() + low = auto() + very_low = auto() + +class FalloffGeometryEnum(Enum): + default = 0 + directional = auto() + spherical = auto() + +class DefaultLightmapSettingEnum(Enum): + dynamic_only = 0 + dynamic_with_lightmaps = auto() + lightmaps_only = auto() + +class EffectFalloffFunctionEnum(Enum): + linear = 0 + late = auto() + very_late = auto() + early = auto() + very_early = auto() + cosine = auto() + zero = auto() + one = auto() + +class EffectFalloffFunctionEnum(Enum): + linear = 0 + late = auto() + very_late = auto() + early = auto() + very_early = auto() + cosine = auto() + zero = auto() + one = auto() + +class FadeEnum(Enum): + fade_very_far = 0 + fade_far = auto() + fade_medium = auto() + fade_close = auto() + fade_very_close = auto() + +class AnimationFlags(Flag): + synchronized = auto() + +class LightAsset(): + def __init__(self): + self.header = None + self.light_body_header = None + self.light_body = None + self.brightness_animation_header = None + self.brightness_animation = None + self.color_animation_header = None + self.color_animation = None + self.gel_animation_header = None + self.gel_animation = None + + class LightBody: + def __init__(self, flags=0, shape_type=0, size_modifier=(0.0, 0.0), shadow_quality_bias=0.0, shadow_tap_bias=0, radius=0.0, specular_radius=0.0, near_width=0.0, + height_stretch=0.0, field_of_view=0.0, falloff_distance=0.0, cutoff_distance=0.0, interpolation_flags=0, bloom_bounds=(0.0, 0.0), + specular_lower_bound=(0.0, 0.0, 0.0, 1.0), specular_upper_bound=(0.0, 0.0, 0.0, 1.0), diffuse_lower_bound=(0.0, 0.0, 0.0, 1.0), + diffuse_upper_bound=(0.0, 0.0, 0.0, 1.0), brightness_bounds=(0.0, 0.0), gel_map=None, specular_mask=0, falloff_function=0, diffuse_contrast=0, + specular_contrast=0, falloff_geometry=0, lens_flare=None, bounding_radius=0.0, light_volume=None, default_lightmap_setting=0, lightmap_half_life=0.0, + lightmap_light_scale=0.0, duration=0.0, effect_falloff_function=0, illumination_fade=0, shadow_fade=0, specular_fade=0, animation_flags=0, + brightness_animation_tag_block=None, color_animation_tag_block=None, gel_animation_tag_block=None, shader=None): + self.flags = flags + self.shape_type = shape_type + self.size_modifier = size_modifier + self.shadow_quality_bias = shadow_quality_bias + self.shadow_tap_bias = shadow_tap_bias + self.radius = radius + self.specular_radius = specular_radius + self.near_width = near_width + self.height_stretch = height_stretch + self.field_of_view = field_of_view + self.falloff_distance = falloff_distance + self.cutoff_distance = cutoff_distance + self.interpolation_flags = interpolation_flags + self.bloom_bounds = bloom_bounds + self.specular_lower_bound = specular_lower_bound + self.specular_upper_bound = specular_upper_bound + self.diffuse_lower_bound = diffuse_lower_bound + self.diffuse_upper_bound = diffuse_upper_bound + self.brightness_bounds = brightness_bounds + self.gel_map = gel_map + self.specular_mask = specular_mask + self.falloff_function = falloff_function + self.diffuse_contrast = diffuse_contrast + self.specular_contrast = specular_contrast + self.falloff_geometry = falloff_geometry + self.lens_flare = lens_flare + self.bounding_radius = bounding_radius + self.light_volume = light_volume + self.default_lightmap_setting = default_lightmap_setting + self.lightmap_half_life = lightmap_half_life + self.lightmap_light_scale = lightmap_light_scale + self.duration = duration + self.effect_falloff_function = effect_falloff_function + self.illumination_fade = illumination_fade + self.shadow_fade = shadow_fade + self.specular_fade = specular_fade + self.animation_flags = animation_flags + self.brightness_animation_tag_block = brightness_animation_tag_block + self.color_animation_tag_block = color_animation_tag_block + self.gel_animation_tag_block = gel_animation_tag_block + self.shader = shader diff --git a/io_scene_halo/file_tag/h2/file_machine/build_asset.py b/io_scene_halo/file_tag/h2/file_machine/build_asset.py new file mode 100644 index 000000000..10d1156bc --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_machine/build_asset.py @@ -0,0 +1,169 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from ....global_functions import tag_format, shader_processing +from ..file_object.build_asset import ( + write_ai_properties, + write_functions, + write_attachments, + write_tag_ref, + write_old_functions, + write_change_colors, + write_predicted_resources + ) + +def write_body(output_stream, TAG, MACHINE): + MACHINE.machine_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack('I', len(MACHINE.machine_body.default_model_variant))) + MACHINE.machine_body.model.write(output_stream, False, True) + MACHINE.machine_body.crate_object.write(output_stream, False, True) + MACHINE.machine_body.modifier_shader.write(output_stream, False, True) + MACHINE.machine_body.creation_effect.write(output_stream, False, True) + MACHINE.machine_body.material_effects.write(output_stream, False, True) + MACHINE.machine_body.ai_properties_tag_block.write(output_stream, False) + MACHINE.machine_body.functions_tag_block.write(output_stream, False) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % default_model_variant_name_length, TAG.string_to_bytes(MACHINE.machine_body.default_model_variant, False))) + + model_name_length = len(MACHINE.machine_body.model.name) + if model_name_length > 0: + output_stream.write(struct.pack('<%ssx' % model_name_length, TAG.string_to_bytes(MACHINE.machine_body.model.name, False))) + + crate_object_name_length = len(MACHINE.machine_body.crate_object.name) + if crate_object_name_length > 0: + output_stream.write(struct.pack('<%ssx' % crate_object_name_length, TAG.string_to_bytes(MACHINE.machine_body.crate_object.name, False))) + + modifier_shader_name_length = len(MACHINE.machine_body.modifier_shader.name) + if modifier_shader_name_length > 0: + output_stream.write(struct.pack('<%ssx' % modifier_shader_name_length, TAG.string_to_bytes(MACHINE.machine_body.modifier_shader.name, False))) + + creation_effect_name_length = len(MACHINE.machine_body.creation_effect.name) + if creation_effect_name_length > 0: + output_stream.write(struct.pack('<%ssx' % creation_effect_name_length, TAG.string_to_bytes(MACHINE.machine_body.creation_effect.name, False))) + + material_effects_name_length = len(MACHINE.machine_body.material_effects.name) + if material_effects_name_length > 0: + output_stream.write(struct.pack('<%ssx' % material_effects_name_length, TAG.string_to_bytes(MACHINE.machine_body.material_effects.name, False))) + + write_ai_properties(output_stream, TAG, MACHINE.ai_properties, MACHINE.ai_properties_header) + write_functions(output_stream, TAG, MACHINE.functions, MACHINE.functions_header) + write_attachments(output_stream, TAG, MACHINE.attachments, MACHINE.attachments_header) + write_tag_ref(output_stream, TAG, MACHINE.widgets, MACHINE.widgets_header) + write_old_functions(output_stream, TAG, MACHINE.old_functions, MACHINE.old_functions_header) + write_change_colors(output_stream, TAG, MACHINE.change_colors, MACHINE.change_colors_header) + write_predicted_resources(output_stream, TAG, MACHINE.predicted_resources, MACHINE.predicted_resources_header) + + open_up_length = len(MACHINE.machine_body.open_up.name) + if open_up_length > 0: + output_stream.write(struct.pack('<%ssx' % open_up_length, TAG.string_to_bytes(MACHINE.machine_body.open_up.name, False))) + + close_down_length = len(MACHINE.machine_body.close_down.name) + if close_down_length > 0: + output_stream.write(struct.pack('<%ssx' % close_down_length, TAG.string_to_bytes(MACHINE.machine_body.close_down.name, False))) + + opened_length = len(MACHINE.machine_body.opened.name) + if opened_length > 0: + output_stream.write(struct.pack('<%ssx' % opened_length, TAG.string_to_bytes(MACHINE.machine_body.opened.name, False))) + + closed_length = len(MACHINE.machine_body.closed.name) + if closed_length > 0: + output_stream.write(struct.pack('<%ssx' % closed_length, TAG.string_to_bytes(MACHINE.machine_body.closed.name, False))) + + depowered_length = len(MACHINE.machine_body.depowered.name) + if depowered_length > 0: + output_stream.write(struct.pack('<%ssx' % depowered_length, TAG.string_to_bytes(MACHINE.machine_body.depowered.name, False))) + + repowered_length = len(MACHINE.machine_body.repowered.name) + if repowered_length > 0: + output_stream.write(struct.pack('<%ssx' % repowered_length, TAG.string_to_bytes(MACHINE.machine_body.repowered.name, False))) + + delay_effect_length = len(MACHINE.machine_body.delay_effect.name) + if delay_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % delay_effect_length, TAG.string_to_bytes(MACHINE.machine_body.delay_effect.name, False))) diff --git a/io_scene_halo/file_tag/h2/file_machine/format.py b/io_scene_halo/file_tag/h2/file_machine/format.py new file mode 100644 index 000000000..3ea29353c --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_machine/format.py @@ -0,0 +1,67 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto +from ..file_device.format import DeviceAsset + +class MachineTypeEnum(Enum): + door = 0 + platform = auto() + gear = auto() + +class MachineFlags(Flag): + pathfinding_obstacle = auto() + but_not_when_open = auto() + elevator = auto() + +class CollisionResponseEnum(Enum): + pause_until_crushed = 0 + reverse_directions = auto() + +class PathfindingPolicyEnum(Enum): + discs = 0 + sectors = auto() + cut_out = auto() + none = auto() + +class MachineAsset(DeviceAsset): + def __init__(self): + super().__init__() + self.header = None + self.machine_body_header = None + self.machine_body = None + + class MachineBody(DeviceAsset.DeviceBody): + def __init__(self, machine_type=0, machine_flags=0, door_open_time=0.0, door_occlusion_time=(0.0, 0.0), collision_response=0, elevator_node=0, pathfinding_policy=0): + super().__init__() + self.machine_type = machine_type + self.machine_flags = machine_flags + self.door_open_time = door_open_time + self.door_occlusion_time = door_occlusion_time + self.collision_response = collision_response + self.elevator_node = elevator_node + self.pathfinding_policy = pathfinding_policy diff --git a/io_scene_halo/file_tag/h2/file_device_machine/process_file.py b/io_scene_halo/file_tag/h2/file_machine/process_file.py similarity index 98% rename from io_scene_halo/file_tag/h2/file_device_machine/process_file.py rename to io_scene_halo/file_tag/h2/file_machine/process_file.py index 758b7860a..9036d35ca 100644 --- a/io_scene_halo/file_tag/h2/file_device_machine/process_file.py +++ b/io_scene_halo/file_tag/h2/file_machine/process_file.py @@ -26,18 +26,9 @@ from xml.dom import minidom from ....global_functions import tag_format -from .format import ( - MachineAsset, - ObjectFlags, - LightmapShadowModeEnum, - SweetenerSizeEnum, - DeviceFlags, - LightmapFlags, - MachineTypeEnum, - MachineFlags, - CollisionResponseEnum, - PathfindingPolicyEnum, - ) +from ..file_object.format import ObjectFlags, LightmapShadowModeEnum, SweetenerSizeEnum +from ..file_device.format import DeviceFlags, LightmapFlags +from .format import MachineAsset, MachineTypeEnum, MachineFlags, CollisionResponseEnum, PathfindingPolicyEnum XML_OUTPUT = False diff --git a/io_scene_halo/file_tag/h2/file_model/build_asset.py b/io_scene_halo/file_tag/h2/file_model/build_asset.py new file mode 100644 index 000000000..d266e2943 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_model/build_asset.py @@ -0,0 +1,384 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from ....global_functions import tag_format + +def write_body(output_stream, TAG, MODEL): + MODEL.model_body_header.write(output_stream, TAG, True) + MODEL.model_body.render_model.write(output_stream, False, True) + MODEL.model_body.collision_model.write(output_stream, False, True) + MODEL.model_body.animation.write(output_stream, False, True) + MODEL.model_body.physics.write(output_stream, False, True) + MODEL.model_body.physics_model.write(output_stream, False, True) + output_stream.write(struct.pack('I', len(MODEL.model_body.default_dialogue_effect))) + for salt_element in MODEL.model_body.salt_array: + output_stream.write(struct.pack('I', len(MODEL.model_body.hologram_control_function))) + +def write_variants(output_stream, TAG, variants, variants_header): + if len(variants) > 0: + variants_header.write(output_stream, TAG, True) + for variant_element in variants: + output_stream.write(struct.pack('>I', len(variant_element.name))) + output_stream.write(struct.pack('<16x')) + variant_element.regions_tag_block.write(output_stream, False) + variant_element.objects_tag_block.write(output_stream, False) + output_stream.write(struct.pack('<8x')) + output_stream.write(struct.pack('>I', len(variant_element.dialogue_sound_effect))) + variant_element.dialogue.write(output_stream, False, True) + + for variant_element in variants: + name_length = len(variant_element.name) + if name_length > 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(variant_element.name, False))) + + if len(variant_element.regions) > 0: + variant_element.regions_header.write(output_stream, TAG, True) + for region_element in variant_element.regions: + output_stream.write(struct.pack('>I', len(region_element.region_name))) + output_stream.write(struct.pack('<2x')) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % region_name_length, TAG.string_to_bytes(region_element.region_name, False))) + + if len(region_element.permutations) > 0: + region_element.permutations_header.write(output_stream, TAG, True) + for permutation_element in region_element.permutations: + output_stream.write(struct.pack('>I', len(permutation_element.permutation_name))) + output_stream.write(struct.pack('<1x')) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % permutation_name_length, TAG.string_to_bytes(permutation_element.permutation_name, False))) + + if len(permutation_element.states) > 0: + permutation_element.states_header.write(output_stream, TAG, True) + for state_element in permutation_element.states: + output_stream.write(struct.pack('>I', len(state_element.permutation_name))) + output_stream.write(struct.pack('<1x')) + output_stream.write(struct.pack('I', len(state_element.looping_effect_marker_name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % permutation_name_length, TAG.string_to_bytes(state_element.permutation_name, False))) + + looping_effect_length = len(state_element.looping_effect.name) + if looping_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % looping_effect_length, TAG.string_to_bytes(state_element.looping_effect.name, False))) + + looping_effect_marker_name_length = len(state_element.looping_effect_marker_name) + if looping_effect_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % looping_effect_marker_name_length, TAG.string_to_bytes(state_element.looping_effect_marker_name, False))) + + if len(variant_element.objects) > 0: + variant_element.objects_header.write(output_stream, TAG, True) + for object_element in variant_element.objects: + output_stream.write(struct.pack('>I', len(object_element.parent_marker_name))) + output_stream.write(struct.pack('>I', len(object_element.child_marker_name))) + object_element.child_object.write(output_stream, False, True) + + for object_element in variant_element.objects: + parent_marker_name_length = len(object_element.parent_marker_name) + if parent_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % parent_marker_name_length, TAG.string_to_bytes(object_element.parent_marker_name, False))) + + child_marker_name_length = len(object_element.child_marker_name) + if child_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % child_marker_name_length, TAG.string_to_bytes(object_element.child_marker_name, False))) + + child_object_length = len(object_element.child_object.name) + if child_object_length > 0: + output_stream.write(struct.pack('<%ssx' % child_object_length, TAG.string_to_bytes(object_element.child_object.name, False))) + + dialogue_sound_effect_length = len(variant_element.dialogue_sound_effect) + if dialogue_sound_effect_length > 0: + output_stream.write(struct.pack('<%ss' % dialogue_sound_effect_length, TAG.string_to_bytes(variant_element.dialogue_sound_effect, False))) + + dialogue_length = len(variant_element.dialogue.name) + if dialogue_length > 0: + output_stream.write(struct.pack('<%ssx' % dialogue_length, TAG.string_to_bytes(variant_element.dialogue.name, False))) + +def write_materials(output_stream, TAG, materials, materials_header): + if len(materials) > 0: + materials_header.write(output_stream, TAG, True) + for material_element in materials: + output_stream.write(struct.pack('>I', len(material_element.material_name))) + output_stream.write(struct.pack('I', len(material_element.global_material_name))) + output_stream.write(struct.pack('<4x')) + + for material_element in materials: + material_name_length = len(material_element.material_name) + if material_name_length > 0: + output_stream.write(struct.pack('<%ss' % material_name_length, TAG.string_to_bytes(material_element.material_name, False))) + + global_material_name_length = len(material_element.global_material_name) + if global_material_name_length > 0: + output_stream.write(struct.pack('<%ss' % global_material_name_length, TAG.string_to_bytes(material_element.global_material_name, False))) + +def write_new_damage_info(output_stream, TAG, new_damage_info, new_damage_info_header): + if len(new_damage_info) > 0: + new_damage_info_header.write(output_stream, TAG, True) + for new_damage_info_element in new_damage_info: + output_stream.write(struct.pack('I', len(new_damage_info_element.global_indirect_material_name))) + output_stream.write(struct.pack('I', len(new_damage_info_element.global_shield_material_name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % global_indirect_material_name_length, TAG.string_to_bytes(new_damage_info_element.global_indirect_material_name, False))) + + shield_damaged_first_person_shader_length = len(new_damage_info_element.shield_damaged_first_person_shader.name) + if shield_damaged_first_person_shader_length > 0: + output_stream.write(struct.pack('<%ssx' % shield_damaged_first_person_shader_length, TAG.string_to_bytes(new_damage_info_element.shield_damaged_first_person_shader.name, False))) + + shield_damaged_shader_length = len(new_damage_info_element.shield_damaged_shader.name) + if shield_damaged_shader_length > 0: + output_stream.write(struct.pack('<%ssx' % shield_damaged_shader_length, TAG.string_to_bytes(new_damage_info_element.shield_damaged_shader.name, False))) + + global_shield_material_name_length = len(new_damage_info_element.global_shield_material_name) + if global_shield_material_name_length > 0: + output_stream.write(struct.pack('<%ss' % global_shield_material_name_length, TAG.string_to_bytes(new_damage_info_element.global_shield_material_name, False))) + + shield_damaged_effect_length = len(new_damage_info_element.shield_damaged_effect.name) + if shield_damaged_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % shield_damaged_effect_length, TAG.string_to_bytes(new_damage_info_element.shield_damaged_effect.name, False))) + + shield_depleted_effect_length = len(new_damage_info_element.shield_depleted_effect.name) + if shield_depleted_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % shield_depleted_effect_length, TAG.string_to_bytes(new_damage_info_element.shield_depleted_effect.name, False))) + + shield_recharging_effect_length = len(new_damage_info_element.shield_recharging_effect.name) + if shield_recharging_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % shield_recharging_effect_length, TAG.string_to_bytes(new_damage_info_element.shield_recharging_effect.name, False))) + + if len(new_damage_info_element.damage_sections) > 0: + new_damage_info_element.damage_sections_header.write(output_stream, TAG, True) + for damage_section_element in new_damage_info_element.damage_sections: + output_stream.write(struct.pack('>I', len(damage_section_element.name))) + output_stream.write(struct.pack('I', len(damage_section_element.resurrection_restored_region_name))) + output_stream.write(struct.pack('<4x')) + + for damage_section_element in new_damage_info_element.damage_sections: + name_length = len(damage_section_element.name) + if name_length > 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(damage_section_element.name, False))) + + if len(damage_section_element.instant_responses) > 0: + damage_section_element.instant_responses_header.write(output_stream, TAG, True) + for instant_response_element in damage_section_element.instant_responses: + output_stream.write(struct.pack('I', len(instant_response_element.region))) + output_stream.write(struct.pack('I', len(instant_response_element.effect_marker_name))) + output_stream.write(struct.pack('>I', len(instant_response_element.damage_effect_marker_name))) + output_stream.write(struct.pack('I', len(instant_response_element.delay_effect_marker_name))) + output_stream.write(struct.pack('>I', len(instant_response_element.constraint_group_name))) + output_stream.write(struct.pack('>I', len(instant_response_element.ejecting_seat_label))) + output_stream.write(struct.pack('I', len(instant_response_element.destroyed_child_object_marker_name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % transition_effect_length, TAG.string_to_bytes(instant_response_element.transition_effect.name, False))) + + instant_response_element.ires_header.write(output_stream, TAG, True) + + transition_damage_effect_length = len(instant_response_element.transition_damage_effect.name) + if transition_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % transition_damage_effect_length, TAG.string_to_bytes(instant_response_element.transition_damage_effect.name, False))) + + region_length = len(instant_response_element.region) + if region_length > 0: + output_stream.write(struct.pack('<%ss' % region_length, TAG.string_to_bytes(instant_response_element.region, False))) + + effect_marker_name_length = len(instant_response_element.effect_marker_name) + if effect_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % effect_marker_name_length, TAG.string_to_bytes(instant_response_element.effect_marker_name, False))) + + instant_response_element.irem_header.write(output_stream, TAG, True) + + damage_effect_marker_name_length = len(instant_response_element.damage_effect_marker_name) + if damage_effect_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % damage_effect_marker_name_length, TAG.string_to_bytes(instant_response_element.damage_effect_marker_name, False))) + + delay_effect_length = len(instant_response_element.delay_effect.name) + if delay_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % delay_effect_length, TAG.string_to_bytes(instant_response_element.delay_effect.name, False))) + + delay_effect_marker_name_length = len(instant_response_element.delay_effect_marker_name) + if delay_effect_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % delay_effect_marker_name_length, TAG.string_to_bytes(instant_response_element.delay_effect_marker_name, False))) + + constraint_group_name_length = len(instant_response_element.constraint_group_name) + if constraint_group_name_length > 0: + output_stream.write(struct.pack('<%ss' % constraint_group_name_length, TAG.string_to_bytes(instant_response_element.constraint_group_name, False))) + + ejecting_seat_label_length = len(instant_response_element.ejecting_seat_label) + if ejecting_seat_label_length > 0: + output_stream.write(struct.pack('<%ss' % ejecting_seat_label_length, TAG.string_to_bytes(instant_response_element.ejecting_seat_label, False))) + + destroyed_child_object_marker_name_length = len(instant_response_element.destroyed_child_object_marker_name) + if destroyed_child_object_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % destroyed_child_object_marker_name_length, TAG.string_to_bytes(instant_response_element.destroyed_child_object_marker_name, False))) + + resurrection_restored_region_name_length = len(damage_section_element.resurrection_restored_region_name) + if resurrection_restored_region_name_length > 0: + output_stream.write(struct.pack('<%ss' % resurrection_restored_region_name_length, TAG.string_to_bytes(damage_section_element.resurrection_restored_region_name, False))) + + overshield_first_person_shader_length = len(new_damage_info_element.overshield_first_person_shader.name) + if overshield_first_person_shader_length > 0: + output_stream.write(struct.pack('<%ssx' % overshield_first_person_shader_length, TAG.string_to_bytes(new_damage_info_element.overshield_first_person_shader.name, False))) + + overshield_shader_length = len(new_damage_info_element.overshield_shader.name) + if overshield_shader_length > 0: + output_stream.write(struct.pack('<%ssx' % overshield_shader_length, TAG.string_to_bytes(new_damage_info_element.overshield_shader.name, False))) + +def build_asset(output_stream, MODEL, report): + TAG = tag_format.TagAsset() + TAG.is_legacy = False + TAG.big_endian = False + + MODEL.header.write(output_stream, False, True) + write_body(output_stream, TAG, MODEL) + + render_model_length = len(MODEL.model_body.render_model.name) + if render_model_length > 0: + output_stream.write(struct.pack('<%ssx' % render_model_length, TAG.string_to_bytes(MODEL.model_body.render_model.name, False))) + + collision_model_length = len(MODEL.model_body.collision_model.name) + if collision_model_length > 0: + output_stream.write(struct.pack('<%ssx' % collision_model_length, TAG.string_to_bytes(MODEL.model_body.collision_model.name, False))) + + animation_length = len(MODEL.model_body.animation.name) + if animation_length > 0: + output_stream.write(struct.pack('<%ssx' % animation_length, TAG.string_to_bytes(MODEL.model_body.animation.name, False))) + + physics_length = len(MODEL.model_body.physics.name) + if physics_length > 0: + output_stream.write(struct.pack('<%ssx' % physics_length, TAG.string_to_bytes(MODEL.model_body.physics.name, False))) + + physics_model_length = len(MODEL.model_body.physics_model.name) + if physics_model_length > 0: + output_stream.write(struct.pack('<%ssx' % physics_model_length, TAG.string_to_bytes(MODEL.model_body.physics_model.name, False))) + + write_variants(output_stream, TAG, MODEL.variants, MODEL.variants_header) + write_materials(output_stream, TAG, MODEL.materials, MODEL.materials_header) + write_new_damage_info(output_stream, TAG, MODEL.new_damage_info, MODEL.new_damage_info_header) \ No newline at end of file diff --git a/io_scene_halo/file_tag/h2/file_model/format.py b/io_scene_halo/file_tag/h2/file_model/format.py index 32cf951bc..970f58c1e 100644 --- a/io_scene_halo/file_tag/h2/file_model/format.py +++ b/io_scene_halo/file_tag/h2/file_model/format.py @@ -28,13 +28,13 @@ SALT_SIZE = 64 -class ShadowFadeDistance(Enum): +class ShadowFadeDistanceEnum(Enum): fade_at_super_high_detail_level = 0 fade_at_high_detail_level = auto() fade_at_medium_detail_level = auto() fade_at_low_detail_level = auto() fade_at_super_low_detail_level = auto() - afde_never = auto() + fade_never = auto() class ModelFlags(Flag): active_camo_always_on = auto() @@ -44,6 +44,35 @@ class ModelFlags(Flag): class RuntimeFlags(Flag): contains_runtime_nodes = auto() +class StateEnum(Enum): + default = 0 + minor_damage = auto() + medium_damage = auto() + major_damage = auto() + destroyed = auto() + +class NewDamageInfoFlags(Flag): + takes_shield_damage_for_children = auto() + takes_body_damage_for_children = auto() + always_shields_friendly_damage = auto() + passes_area_damage_to_children = auto() + parent_never_takes_body_damage_for_us = auto() + only_damaged_by_explosives = auto() + parent_never_takes_shield_damage_for_us = auto() + cannot_die_from_damage = auto() + passes_attached_damage_to_riders = auto() + +class DamageSectionFlags(Flag): + absorbs_body_damage = auto() + takes_full_dmg_when_object_dies = auto() + cannot_die_with_riders = auto() + takes_full_dmg_when_obj_dstryd = auto() + restored_on_ressurection = auto() + unused_5 = auto() + unused_6 = auto() + heatshottable = auto() + ignores_shields = auto() + class ModelAsset(): def __init__(self): self.header = None @@ -104,3 +133,198 @@ def __init__(self, render_model=None, collision_model=None, animation=None, phys self.hologram_shader = hologram_shader self.hologram_control_function = hologram_control_function self.hologram_control_function_length = hologram_control_function_length + + class Variant: + def __init__(self, name="", name_length=0, regions_tag_block=None, regions_header=None, regions=None, objects_tag_block=None, objects_header=None, + objects=None, dialogue_sound_effect="", dialogue_sound_effect_length=0, dialogue=None): + self.name = name + self.name_length = name_length + self.regions_tag_block = regions_tag_block + self.regions_header = regions_header + self.regions = regions + self.objects_tag_block = objects_tag_block + self.objects_header = objects_header + self.objects = objects + self.dialogue_sound_effect = dialogue_sound_effect + self.dialogue_sound_effect_length = dialogue_sound_effect_length + self.dialogue = dialogue + + class Region: + def __init__(self, region_name="", region_name_length=0, parent_variant=0, permutations_tag_block=None, permutations_header=None, permutations=None, + sort_order=0): + self.region_name = region_name + self.region_name_length = region_name_length + self.parent_variant = parent_variant + self.permutations_tag_block = permutations_tag_block + self.permutations_header = permutations_header + self.permutations = permutations + self.sort_order = sort_order + + class Permutation: + def __init__(self, permutation_name="", permutation_name_length=0, flags=0, probability=0.0, states_tag_block=None, states_header=None, + states=None): + self.permutation_name = permutation_name + self.permutation_name_length = permutation_name_length + self.flags = flags + self.probability = probability + self.states_tag_block = states_tag_block + self.states_header = states_header + self.states = states + + class State: + def __init__(self, permutation_name="", permutation_name_length=0, property_flags=0, state=0, looping_effect=None, looping_effect_marker_name="", + looping_effect_marker_name_length=0, initial_probability=0.0): + self.permutation_name = permutation_name + self.permutation_name_length = permutation_name_length + self.property_flags = property_flags + self.state = state + self.looping_effect = looping_effect + self.looping_effect_marker_name = looping_effect_marker_name + self.looping_effect_marker_name_length = looping_effect_marker_name_length + self.initial_probability = initial_probability + + class Object: + def __init__(self, parent_marker_name="", parent_marker_name_length=0, child_marker_name="", child_marker_name_length=0, child_object=None): + self.parent_marker_name = parent_marker_name + self.parent_marker_name_length = parent_marker_name_length + self.child_marker_name = child_marker_name + self.child_marker_name_length = child_marker_name_length + self.child_object = child_object + + class Materials: + def __init__(self, material_name="", material_name_length=0, material_type=0, damage_section=0, global_material_name="", + global_material_name_length=0): + self.material_name = material_name + self.material_name_length = material_name_length + self.material_type = material_type + self.damage_section = damage_section + self.global_material_name = global_material_name + self.global_material_name_length = global_material_name_length + + class NewDamageInfo: + def __init__(self, flags=0, global_indirect_material_name="", global_indirect_material_name_length=0, indirect_damage_section=0, + collision_damage_reporting_type=0, response_damage_reporting_type=0, maximum_vitality=0.0, body_minimum_stun_damage=0.0, + body_stun_time=0.0, body_recharge_time=0.0, recharge_fraction=0.0, shield_damaged_first_person_shader=None, shield_damaged_shader=None, + maximum_shield_vitality=0.0, global_shield_material_name="", global_shield_material_name_length=0, shield_minimum_stun_damage=0.0, + shield_stun_time=0.0, shield_recharge_time=0.0, shield_damaged_threshold=0.0, shield_damaged_effect=None, shield_depleted_effect=None, + shield_recharging_effect=None, damage_sections_tag_block=None, damage_sections_header=None, damage_sections=None, nodes_tag_block=None, + nodes_header=None, nodes=None, damage_seats_tag_block=None, damage_seats_header=None, damage_seats=None, + damage_constraints_tag_block=None, damage_constraints_header=None, damage_constraints=None, overshield_first_person_shader=None, + overshield_shader=None): + self.flags = flags + self.global_indirect_material_name = global_indirect_material_name + self.global_indirect_material_name_length = global_indirect_material_name_length + self.indirect_damage_section = indirect_damage_section + self.collision_damage_reporting_type = collision_damage_reporting_type + self.response_damage_reporting_type = response_damage_reporting_type + self.maximum_vitality = maximum_vitality + self.body_minimum_stun_damage = body_minimum_stun_damage + self.body_stun_time = body_stun_time + self.body_recharge_time = body_recharge_time + self.recharge_fraction = recharge_fraction + self.shield_damaged_first_person_shader = shield_damaged_first_person_shader + self.shield_damaged_shader = shield_damaged_shader + self.maximum_shield_vitality = maximum_shield_vitality + self.global_shield_material_name = global_shield_material_name + self.global_shield_material_name_length = global_shield_material_name_length + self.shield_minimum_stun_damage = shield_minimum_stun_damage + self.shield_stun_time = shield_stun_time + self.shield_recharge_time = shield_recharge_time + self.shield_damaged_threshold = shield_damaged_threshold + self.shield_damaged_effect = shield_damaged_effect + self.shield_depleted_effect = shield_depleted_effect + self.shield_recharging_effect = shield_recharging_effect + self.damage_sections_tag_block = damage_sections_tag_block + self.damage_sections_header = damage_sections_header + self.damage_sections = damage_sections + self.nodes_tag_block = nodes_tag_block + self.nodes_header = nodes_header + self.nodes = nodes + self.damage_seats_tag_block = damage_seats_tag_block + self.damage_seats_header = damage_seats_header + self.damage_seats = damage_seats + self.damage_constraints_tag_block = damage_constraints_tag_block + self.damage_constraints_header = damage_constraints_header + self.damage_constraints = damage_constraints + self.overshield_first_person_shader = overshield_first_person_shader + self.overshield_shader = overshield_shader + + class DamageSection: + def __init__(self, name="", name_length=0, flags=0, vitality_percentage=0.0, instant_responses_tag_block=None, instant_responses_header=None, + instant_responses=None, unknown_0_tag_block=None, unknown_0_header=None, unknown_0=None, unknown_1_tag_block=None, + unknown_1_header=None, unknown_1=None, stun_time=0.0, recharge_time=0.0, resurrection_restored_region_name="", + resurrection_restored_region_name_length=0): + self.name = name + self.name_length = name_length + self.flags = flags + self.vitality_percentage = vitality_percentage + self.instant_responses_tag_block = instant_responses_tag_block + self.instant_responses_header = instant_responses_header + self.instant_responses = instant_responses + self.unknown_0_tag_block = unknown_0_tag_block + self.unknown_0_header = unknown_0_header + self.unknown_0 = unknown_0 + self.unknown_1_tag_block = unknown_1_tag_block + self.unknown_1_header = unknown_1_header + self.unknown_1 = unknown_1 + self.stun_time = stun_time + self.recharge_time = recharge_time + self.resurrection_restored_region_name = resurrection_restored_region_name + self.resurrection_restored_region_name_length = resurrection_restored_region_name_length + + class InstantResponse: + def __init__(self, response_type=0, constraint_damage_type=0, flags=0, damage_threshold=0.0, transition_effect=None, transition_damage_effect=None, + region="", region_length=0, new_state=0, runtime_region_index=0, effect_marker_name="", effect_marker_name_length=0, + damage_effect_marker_name="", damage_effect_marker_name_length=0, response_delay=0.0, delay_effect=None, delay_effect_marker_name="", + delay_effect_marker_name_length=0, constraint_group_name="", constraint_group_name_length=0, ejecting_seat_label="", + ejecting_seat_label_length=0, skip_fraction=0.0, destroyed_child_object_marker_name="", destroyed_child_object_marker_name_length=0, + total_damage_threshold=0.0, ires_header=None, irem_header=None): + self.response_type = response_type + self.constraint_damage_type = constraint_damage_type + self.flags = flags + self.damage_threshold = damage_threshold + self.transition_effect = transition_effect + self.transition_damage_effect = transition_damage_effect + self.region = region + self.region_length = region_length + self.new_state = new_state + self.runtime_region_index = runtime_region_index + self.effect_marker_name = effect_marker_name + self.effect_marker_name_length = effect_marker_name_length + self.damage_effect_marker_name = damage_effect_marker_name + self.damage_effect_marker_name_length = damage_effect_marker_name_length + self.response_delay = response_delay + self.delay_effect = delay_effect + self.delay_effect_marker_name = delay_effect_marker_name + self.delay_effect_marker_name_length = delay_effect_marker_name_length + self.constraint_group_name = constraint_group_name + self.constraint_group_name_length = constraint_group_name_length + self.ejecting_seat_label = ejecting_seat_label + self.ejecting_seat_label_length = ejecting_seat_label_length + self.skip_fraction = skip_fraction + self.destroyed_child_object_marker_name = destroyed_child_object_marker_name + self.destroyed_child_object_marker_name_length = destroyed_child_object_marker_name_length + self.total_damage_threshold = total_damage_threshold + self.ires_header = ires_header + self.irem_header = irem_header + + class DamageSeat: + def __init__(self, seat_label="", seat_label_length=0, direct_damage_scale=0.0, damage_transfer_fall_off=0.0, maximum_transfer_damage_scale=0.0, + minimum_transfer_damage_scale=0.0): + self.seat_label = seat_label + self.seat_label_length = seat_label_length + self.direct_damage_scale = direct_damage_scale + self.damage_transfer_fall_off = damage_transfer_fall_off + self.maximum_transfer_damage_scale = maximum_transfer_damage_scale + self.minimum_transfer_damage_scale = minimum_transfer_damage_scale + + class DamageConstraint: + def __init__(self, physics_model_constraint_name="", physics_model_constraint_name_length=0, damage_constraint_name="", + damage_constraint_name_length=0, damage_constraint_group_name="", damage_constraint_group_name_length=0, group_probability_scale=0.0): + self.physics_model_constraint_name = physics_model_constraint_name + self.physics_model_constraint_name_length = physics_model_constraint_name_length + self.damage_constraint_name = damage_constraint_name + self.damage_constraint_name_length = damage_constraint_name_length + self.damage_constraint_group_name = damage_constraint_group_name + self.damage_constraint_group_name_length = damage_constraint_group_name_length + self.group_probability_scale = group_probability_scale diff --git a/io_scene_halo/file_tag/h2/file_model/process_file.py b/io_scene_halo/file_tag/h2/file_model/process_file.py index 18fa05f01..f1cc7e2e3 100644 --- a/io_scene_halo/file_tag/h2/file_model/process_file.py +++ b/io_scene_halo/file_tag/h2/file_model/process_file.py @@ -28,7 +28,7 @@ from ....global_functions import tag_format from .format import ( ModelAsset, - ShadowFadeDistance, + ShadowFadeDistanceEnum, ModelFlags, RuntimeFlags, SALT_SIZE @@ -63,7 +63,7 @@ def read_model_body_v0(MODEL, TAG, input_stream, tag_node, XML_OUTPUT): MODEL.model_body.reduce_to_l4 = TAG.read_float(input_stream, TAG, tag_format.XMLData(tag_node, "reduce to l4")) MODEL.model_body.reduce_to_l5 = TAG.read_float(input_stream, TAG, tag_format.XMLData(tag_node, "reduce to l5")) input_stream.read(4) # Padding? - MODEL.model_body.shadow_fade_distance = TAG.read_enum_unsigned_short(input_stream, TAG, tag_format.XMLData(tag_node, "shadow fade distance", ShadowFadeDistance)) + MODEL.model_body.shadow_fade_distance = TAG.read_enum_unsigned_short(input_stream, TAG, tag_format.XMLData(tag_node, "shadow fade distance", ShadowFadeDistanceEnum)) input_stream.read(2) # Padding? MODEL.model_body.variants_tag_block = TAG.TagBlock().read(input_stream, TAG, tag_format.XMLData(tag_node, "variants")) MODEL.model_body.materials_tag_block = TAG.TagBlock().read(input_stream, TAG, tag_format.XMLData(tag_node, "materials")) @@ -131,7 +131,7 @@ def read_model_body_retail(MODEL, TAG, input_stream, tag_node, XML_OUTPUT): MODEL.model_body.reduce_to_l4 = TAG.read_float(input_stream, TAG, tag_format.XMLData(tag_node, "reduce to l4")) MODEL.model_body.reduce_to_l5 = TAG.read_float(input_stream, TAG, tag_format.XMLData(tag_node, "reduce to l5")) input_stream.read(4) # Padding? - MODEL.model_body.shadow_fade_distance = TAG.read_enum_unsigned_short(input_stream, TAG, tag_format.XMLData(tag_node, "shadow fade distance", ShadowFadeDistance)) + MODEL.model_body.shadow_fade_distance = TAG.read_enum_unsigned_short(input_stream, TAG, tag_format.XMLData(tag_node, "shadow fade distance", ShadowFadeDistanceEnum)) input_stream.read(2) # Padding? MODEL.model_body.variants_tag_block = TAG.TagBlock().read(input_stream, TAG, tag_format.XMLData(tag_node, "variants")) MODEL.model_body.materials_tag_block = TAG.TagBlock().read(input_stream, TAG, tag_format.XMLData(tag_node, "materials")) diff --git a/io_scene_halo/file_tag/h2/file_object/build_asset.py b/io_scene_halo/file_tag/h2/file_object/build_asset.py new file mode 100644 index 000000000..51227b5ea --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_object/build_asset.py @@ -0,0 +1,175 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from ....global_functions import shader_processing + +def write_ai_properties(output_stream, TAG, ai_properties, ai_properties_header): + if len(ai_properties) > 0: + ai_properties_header.write(output_stream, TAG, True) + for ai_property_element in ai_properties: + output_stream.write(struct.pack('I', len(ai_property_element.ai_type_name))) + output_stream.write(struct.pack('<4x')) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(ai_property_element.ai_type_name, False))) + +def write_functions(output_stream, TAG, functions, functions_header): + if len(functions) > 0: + functions_header.write(output_stream, TAG, True) + for function_element in functions: + output_stream.write(struct.pack('I', len(function_element.import_name))) + output_stream.write(struct.pack('>I', len(function_element.export_name))) + output_stream.write(struct.pack('>I', len(function_element.turn_off_with))) + output_stream.write(struct.pack('I', len(function_element.scale_by))) + + for function_element in functions: + import_name_length = len(function_element.import_name) + if import_name_length > 0: + output_stream.write(struct.pack('<%ss' % import_name_length, TAG.string_to_bytes(function_element.import_name, False))) + + export_name_length = len(function_element.export_name) + if export_name_length > 0: + output_stream.write(struct.pack('<%ss' % export_name_length, TAG.string_to_bytes(function_element.export_name, False))) + + turn_off_with_name_length = len(function_element.turn_off_with) + if turn_off_with_name_length > 0: + output_stream.write(struct.pack('<%ss' % turn_off_with_name_length, TAG.string_to_bytes(function_element.turn_off_with, False))) + + shader_processing.write_function(output_stream, TAG, function_element.function_property) + + scale_by_name_length = len(function_element.scale_by) + if scale_by_name_length > 0: + output_stream.write(struct.pack('<%ss' % scale_by_name_length, TAG.string_to_bytes(function_element.scale_by, False))) + +def write_attachments(output_stream, TAG, attachments, attachments_header): + if len(attachments) > 0: + attachments_header.write(output_stream, TAG, True) + for attachment_element in attachments: + attachment_element.attachment_type.write(output_stream, False, True) + output_stream.write(struct.pack('>I', len(attachment_element.marker))) + output_stream.write(struct.pack('I', len(attachment_element.primary_scale))) + output_stream.write(struct.pack('>I', len(attachment_element.secondary_scale))) + + for attachment_element in attachments: + attachment_type_name_length = len(attachment_element.attachment_type.name) + if attachment_type_name_length > 0: + output_stream.write(struct.pack('<%ssx' % attachment_type_name_length, TAG.string_to_bytes(attachment_element.attachment_type.name, False))) + + marker_name_length = len(attachment_element.marker) + if marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % marker_name_length, TAG.string_to_bytes(attachment_element.marker, False))) + + primary_scale_name_length = len(attachment_element.primary_scale) + if primary_scale_name_length > 0: + output_stream.write(struct.pack('<%ss' % primary_scale_name_length, TAG.string_to_bytes(attachment_element.primary_scale, False))) + + secondary_scale_name_length = len(attachment_element.secondary_scale) + if secondary_scale_name_length > 0: + output_stream.write(struct.pack('<%ss' % secondary_scale_name_length, TAG.string_to_bytes(attachment_element.secondary_scale, False))) + +def write_tag_ref(output_stream, TAG, tag_refs, tag_ref_header): + if len(tag_refs) > 0: + tag_ref_header.write(output_stream, TAG, True) + for tag_ref_element in tag_refs: + tag_ref_element.write(output_stream, False, True) + + for tag_ref_element in tag_refs: + tag_ref_name_length = len(tag_ref_element.name) + if tag_ref_name_length > 0: + output_stream.write(struct.pack('<%ssx' % tag_ref_name_length, TAG.string_to_bytes(tag_ref_element.name, False))) + +def write_old_functions(output_stream, TAG, old_functions, old_functions_header): + if len(old_functions) > 0: + old_functions_header.write(output_stream, TAG, True) + for old_function_element in old_functions: + output_stream.write(struct.pack('<76x')) + output_stream.write(struct.pack('>I', len(old_function_element.name))) + + for old_function_element in old_functions: + old_function_name_length = len(old_function_element.name) + if old_function_name_length > 0: + output_stream.write(struct.pack('<%ss' % old_function_name_length, TAG.string_to_bytes(old_function_element.name, False))) + +def write_change_colors(output_stream, TAG, change_colors, change_colors_header): + if len(change_colors) > 0: + change_colors_header.write(output_stream, TAG, True) + for change_colors_element in change_colors: + change_colors_element.initial_permutations_tag_block.write(output_stream, False) + change_colors_element.functions_tag_block.write(output_stream, False) + + for change_colors_element in change_colors: + if len(change_colors_element.initial_permutations) > 0: + change_colors_element.initial_permutations_header.write(output_stream, TAG, True) + for initial_permutation_element in change_colors_element.initial_permutations: + output_stream.write(struct.pack('I', len(initial_permutation_element.variant_name))) + + for initial_permutation_element in change_colors_element.initial_permutations: + variant_name_length = len(initial_permutation_element.variant_name) + if variant_name_length > 0: + output_stream.write(struct.pack('<%ss' % variant_name_length, TAG.string_to_bytes(initial_permutation_element.variant_name, False))) + + if len(change_colors_element.functions) > 0: + change_colors_element.functions_header.write(output_stream, TAG, True) + for function_element in change_colors_element.functions: + output_stream.write(struct.pack('<4x')) + output_stream.write(struct.pack('I', len(function_element.darken_by))) + output_stream.write(struct.pack('>I', len(function_element.scale_by))) + + for function_element in change_colors_element.functions: + darken_by_name_length = len(function_element.darken_by) + if darken_by_name_length > 0: + output_stream.write(struct.pack('<%ss' % darken_by_name_length, TAG.string_to_bytes(function_element.darken_by, False))) + + scale_by_name_length = len(function_element.scale_by) + if scale_by_name_length > 0: + output_stream.write(struct.pack('<%ss' % scale_by_name_length, TAG.string_to_bytes(function_element.scale_by, False))) + +def write_predicted_resources(output_stream, TAG, predicted_resources, predicted_resources_header): + if len(predicted_resources) > 0: + predicted_resources_header.write(output_stream, TAG, True) + for predicted_resource_element in predicted_resources: + output_stream.write(struct.pack(' 0: + locations_header.write(output_stream, TAG, True) + for location_element in locations: + output_stream.write(struct.pack('>I', len(location_element.name))) + + for location_element in locations: + name_length = len(location_element.name) + if name_length > 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(location_element.name, False))) + +def write_events(output_stream, TAG, events, events_header): + if len(events) > 0: + events_header.write(output_stream, TAG, True) + for event_element in events: + output_stream.write(struct.pack(' 0: + event_element.parts_header.write(output_stream, TAG, True) + for part_element in event_element.parts: + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % object_name_length, TAG.string_to_bytes(part_element.object_type.name, False))) + + if len(event_element.beams) > 0: + event_element.beams_header.write(output_stream, TAG, True) + for beam_element in event_element.beams: + beam_element.shader.write(output_stream, False) + for beam_property in beam_element.properties: + shader_processing.write_function_size(output_stream, beam_property) + + for beam_property in beam_element.properties: + beam_property.function_header.write(output_stream, TAG, True) + shader_processing.write_function(output_stream, TAG, beam_property) + + if len(event_element.accelerations) > 0: + event_element.accelerations_header.write(output_stream, TAG, True) + for acceleration_element in event_element.accelerations: + output_stream.write(struct.pack(' 0: + event_element.particle_systems_header.write(output_stream, TAG, True) + for particle_system_element in event_element.particle_systems: + particle_system_element.particle.write(output_stream, False) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % particle_name_length, TAG.string_to_bytes(particle_system_element.particle.name, False))) + if len(particle_system_element.emitters) > 0: + particle_system_element.emitters_header.write(output_stream, TAG, True) + for emitter_element in particle_system_element.emitters: + emitter_element.particle_physics.write(output_stream, False) + for particle_property in emitter_element.particle_properties: + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % shader_template_name_length, TAG.string_to_bytes(PARTICLE.particle_body.shader_template.name, False))) + + shader_processing.write_parameters(output_stream, TAG, PARTICLE.parameters, PARTICLE.parameters_header) + for particle_property in PARTICLE.particle_body.properties: + particle_property.function_header.write(output_stream, TAG, True) + shader_processing.write_function(output_stream, TAG, particle_property) + + collision_effect_name_length = len(PARTICLE.particle_body.collision_effect.name) + if collision_effect_name_length > 0: + output_stream.write(struct.pack('<%ssx' % collision_effect_name_length, TAG.string_to_bytes(PARTICLE.particle_body.collision_effect.name, False))) + + death_effect_name_length = len(PARTICLE.particle_body.death_effect.name) + if death_effect_name_length > 0: + output_stream.write(struct.pack('<%ssx' % death_effect_name_length, TAG.string_to_bytes(PARTICLE.particle_body.death_effect.name, False))) + + write_locations(output_stream, TAG, PARTICLE.locations, PARTICLE.locations_header) + + + + + + diff --git a/io_scene_halo/file_tag/h2/file_particle/format.py b/io_scene_halo/file_tag/h2/file_particle/format.py new file mode 100644 index 000000000..cfb8724fa --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_particle/format.py @@ -0,0 +1,188 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto + +class ParticleFlags(Flag): + spins = auto() + random_u_mirror = auto() + random_v_mirror = auto() + frame_animation_one_shot = auto() + select_random_sequence = auto() + disable_frame_blending = auto() + can_animate_backwards = auto() + receive_lightmap_lighting = auto() + tint_from_diffuse_texture = auto() + dies_at_rest = auto() + dies_on_structure_collision = auto() + dies_in_media = auto() + dies_in_air = auto() + bitmap_authored_vertically = auto() + has_sweetener = auto() + +class ParticleBillboardStyleEnum(Enum): + screen_facing = 0 + parallel_to_direction = auto() + perpendicular_to_direction = auto() + vertical = auto() + horizontal = auto() + +class EnvironmentEnum(Enum): + any_environment = 0 + air_only = auto() + water_only = auto() + space_only = auto() + +class ModeEnum(Enum): + either_mode = 0 + violent_mode_only = auto() + nonviolent_mode_only = auto() + +class PartFlags(Flag): + face_down_regardless_of_location_decals = auto() + offset_origin_away_from_geometry_lights = auto() + never_attached_to_object = auto() + disabled_for_debugging = auto() + draw_regardless_of_distance = auto() + +class ScaleFlags(Flag): + velocity = auto() + velocity_delta = auto() + velocity_cone_angle = auto() + angular_velocity = auto() + angular_velocity_delta = auto() + type_specific_scale = auto() + +class OutputModifierInputEnum(Enum): + particle_age = 0 + particle_emit_time = auto() + particle_random_1 = auto() + particle_random_2 = auto() + emitter_age = auto() + emitter_random_1 = auto() + emitter_random_2 = auto() + system_lod = auto() + game_time = auto() + effect_a_scale = auto() + effect_b_scale = auto() + particle_rotation = auto() + explosion_animation = auto() + explosion_rotation = auto() + particle_random_3 = auto() + particle_random_4 = auto() + location_random = auto() + +class OutputModifierEnum(Enum): + none = 0 + plus = auto() + times = auto() + +class CoordinateSystemEnum(Enum): + world = 0 + local = auto() + parent = auto() + +class CameraModeEnum(Enum): + independent_of_camera_mode = 0 + only_in_first_person = auto() + only_in_third_person = auto() + both_first_and_third = auto() + +class AttachedParticleSystemFlags(Flag): + glow = auto() + cinematics = auto() + looping_particle = auto() + disabled_for_debug = auto() + inherit_effect_velocity = auto() + dont_render_system = auto() + render_when_zoomed = auto() + spread_between_ticks = auto() + persistent_particle = auto() + expensive_visibility = auto() + +class ParticleAsset(): + def __init__(self): + self.header = None + self.particle_body_header = None + self.particle_body = None + self.parameters_header = None + self.parameters = None + self.locations_header = None + self.locations = None + self.attached_particle_systems_header = None + self.attached_particle_systems = None + self.shader_postprocess_definitions_header = None + self.shader_postprocess_definitions = None + + class ParticleBody: + def __init__(self, flags=0, particle_billboard_style=0, first_sequence_index=0, sequence_count=0, shader_template=None, parameters_tag_block=None, properties=None, + collision_effect=None, death_effect=None, locations_tag_block=None, attached_particle_systems_tag_block=None, shader_postprocess_definitions_tag_block=None): + self.flags = flags + self.particle_billboard_style = particle_billboard_style + self.first_sequence_index = first_sequence_index + self.sequence_count = sequence_count + self.shader_template = shader_template + self.parameters_tag_block = parameters_tag_block + self.properties = properties + self.collision_effect = collision_effect + self.death_effect = death_effect + self.locations_tag_block = locations_tag_block + self.attached_particle_systems_tag_block = attached_particle_systems_tag_block + self.shader_postprocess_definitions_tag_block = shader_postprocess_definitions_tag_block + + class Location: + def __init__(self, name="", name_length=0): + self.name = name + self.name_length = name_length + + class AttachedParticleSystem: + def __init__(self, particle=None, location=0, coordinate_system=0, environment=0, disposition=0, camera_mode=0, sort_bias=0, flags=0, lod_in_distance=0.0, + lod_feather_in_delta=0.0, lod_out_distance=0.0, lod_feather_out_delta=0.0, emitters_tag_block=None, emitters_header=None, emitters=None): + self.particle = particle + self.location = location + self.coordinate_system = coordinate_system + self.environment = environment + self.disposition = disposition + self.camera_mode = camera_mode + self.sort_bias = sort_bias + self.flags = flags + self.lod_in_distance = lod_in_distance + self.lod_feather_in_delta = lod_feather_in_delta + self.lod_out_distance = lod_out_distance + self.lod_feather_out_delta = lod_feather_out_delta + self.emitters_tag_block = emitters_tag_block + self.emitters_header = emitters_header + self.emitters = emitters + + class Emitter: + def __init__(self, particle_physics=None, particle_properties=None, emission_shape=0, emission_properties=None, translational_offset=Vector(), relative_direction=(0.0, 0.0)): + self.particle_physics = particle_physics + self.particle_properties = particle_properties + self.emission_shape = emission_shape + self.emission_properties = emission_properties + self.translational_offset = translational_offset + self.relative_direction = relative_direction diff --git a/io_scene_halo/file_tag/h2/file_particle_physics/build_asset.py b/io_scene_halo/file_tag/h2/file_particle_physics/build_asset.py new file mode 100644 index 000000000..90a787b06 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_particle_physics/build_asset.py @@ -0,0 +1,73 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from ....global_functions import tag_format, shader_processing + +def write_body(output_stream, TAG, PARTICLEPHYSICS): + PARTICLEPHYSICS.particle_physics_body_header.write(output_stream, TAG, True) + PARTICLEPHYSICS.particle_physics_body.template.write(output_stream, False, True) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % template_name_length, TAG.string_to_bytes(PARTICLEPHYSICS.particle_physics_body.template.name, False))) + +def write_movements(output_stream, TAG, movements, movements_header): + if len(movements) > 0: + movements_header.write(output_stream, TAG, True) + for movement_element in movements: + output_stream.write(struct.pack(' 0: + movement_element.parameters_header.write(output_stream, TAG, True) + for parameter_element in movement_element.parameters: + output_stream.write(struct.pack('I', len(PROJECTILE.projectile_body.default_model_variant))) + PROJECTILE.projectile_body.model.write(output_stream, False, True) + PROJECTILE.projectile_body.crate_object.write(output_stream, False, True) + PROJECTILE.projectile_body.modifier_shader.write(output_stream, False, True) + PROJECTILE.projectile_body.creation_effect.write(output_stream, False, True) + PROJECTILE.projectile_body.material_effects.write(output_stream, False, True) + PROJECTILE.projectile_body.ai_properties_tag_block.write(output_stream, False) + PROJECTILE.projectile_body.functions_tag_block.write(output_stream, False) + output_stream.write(struct.pack(' 0: + material_responses_header.write(output_stream, TAG, True) + for material_response_element in material_responses: + output_stream.write(struct.pack('I', len(material_response_element.material_name))) + output_stream.write(struct.pack('<4x')) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % result_effect_length, TAG.string_to_bytes(material_response_element.result_effect.name, False))) + + material_name_length = len(material_response_element.material_name) + if material_name_length > 0: + output_stream.write(struct.pack('<%ss' % material_name_length, TAG.string_to_bytes(material_response_element.material_name, False))) + + potential_result_effect_length = len(material_response_element.potential_result_effect.name) + if potential_result_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % potential_result_effect_length, TAG.string_to_bytes(material_response_element.potential_result_effect.name, False))) + + detonation_effect_length = len(material_response_element.detonation_effect.name) + if detonation_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % detonation_effect_length, TAG.string_to_bytes(material_response_element.detonation_effect.name, False))) + +def build_asset(output_stream, PROJECTILE, report): + TAG = tag_format.TagAsset() + TAG.is_legacy = False + TAG.big_endian = False + + PROJECTILE.header.write(output_stream, False, True) + write_body(output_stream, TAG, PROJECTILE) + + default_model_variant_name_length = len(PROJECTILE.projectile_body.default_model_variant) + if default_model_variant_name_length > 0: + output_stream.write(struct.pack('<%ss' % default_model_variant_name_length, TAG.string_to_bytes(PROJECTILE.projectile_body.default_model_variant, False))) + + model_name_length = len(PROJECTILE.projectile_body.model.name) + if model_name_length > 0: + output_stream.write(struct.pack('<%ssx' % model_name_length, TAG.string_to_bytes(PROJECTILE.projectile_body.model.name, False))) + + crate_object_name_length = len(PROJECTILE.projectile_body.crate_object.name) + if crate_object_name_length > 0: + output_stream.write(struct.pack('<%ssx' % crate_object_name_length, TAG.string_to_bytes(PROJECTILE.projectile_body.crate_object.name, False))) + + modifier_shader_name_length = len(PROJECTILE.projectile_body.modifier_shader.name) + if modifier_shader_name_length > 0: + output_stream.write(struct.pack('<%ssx' % modifier_shader_name_length, TAG.string_to_bytes(PROJECTILE.projectile_body.modifier_shader.name, False))) + + creation_effect_name_length = len(PROJECTILE.projectile_body.creation_effect.name) + if creation_effect_name_length > 0: + output_stream.write(struct.pack('<%ssx' % creation_effect_name_length, TAG.string_to_bytes(PROJECTILE.projectile_body.creation_effect.name, False))) + + material_effects_name_length = len(PROJECTILE.projectile_body.material_effects.name) + if material_effects_name_length > 0: + output_stream.write(struct.pack('<%ssx' % material_effects_name_length, TAG.string_to_bytes(PROJECTILE.projectile_body.material_effects.name, False))) + + write_ai_properties(output_stream, TAG, PROJECTILE.ai_properties, PROJECTILE.ai_properties_header) + write_functions(output_stream, TAG, PROJECTILE.functions, PROJECTILE.functions_header) + write_attachments(output_stream, TAG, PROJECTILE.attachments, PROJECTILE.attachments_header) + write_tag_ref(output_stream, TAG, PROJECTILE.widgets, PROJECTILE.widgets_header) + write_old_functions(output_stream, TAG, PROJECTILE.old_functions, PROJECTILE.old_functions_header) + write_change_colors(output_stream, TAG, PROJECTILE.change_colors, PROJECTILE.change_colors_header) + write_predicted_resources(output_stream, TAG, PROJECTILE.predicted_resources, PROJECTILE.predicted_resources_header) + + detonation_started_length = len(PROJECTILE.projectile_body.detonation_started.name) + if detonation_started_length > 0: + output_stream.write(struct.pack('<%ssx' % detonation_started_length, TAG.string_to_bytes(PROJECTILE.projectile_body.detonation_started.name, False))) + + detonation_effect_airborne_length = len(PROJECTILE.projectile_body.detonation_effect_airborne.name) + if detonation_effect_airborne_length > 0: + output_stream.write(struct.pack('<%ssx' % detonation_effect_airborne_length, TAG.string_to_bytes(PROJECTILE.projectile_body.detonation_effect_airborne.name, False))) + + detonation_effect_ground_length = len(PROJECTILE.projectile_body.detonation_effect_ground.name) + if detonation_effect_ground_length > 0: + output_stream.write(struct.pack('<%ssx' % detonation_effect_ground_length, TAG.string_to_bytes(PROJECTILE.projectile_body.detonation_effect_ground.name, False))) + + detonation_damage_length = len(PROJECTILE.projectile_body.detonation_damage.name) + if detonation_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % detonation_damage_length, TAG.string_to_bytes(PROJECTILE.projectile_body.detonation_damage.name, False))) + + attached_detonation_damage_length = len(PROJECTILE.projectile_body.attached_detonation_damage.name) + if attached_detonation_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % attached_detonation_damage_length, TAG.string_to_bytes(PROJECTILE.projectile_body.attached_detonation_damage.name, False))) + + super_detonation_length = len(PROJECTILE.projectile_body.super_detonation.name) + if super_detonation_length > 0: + output_stream.write(struct.pack('<%ssx' % super_detonation_length, TAG.string_to_bytes(PROJECTILE.projectile_body.super_detonation.name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("sd2s", True), 1, 1, 16)) + + super_detonation_damage_length = len(PROJECTILE.projectile_body.super_detonation_damage.name) + if super_detonation_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % super_detonation_damage_length, TAG.string_to_bytes(PROJECTILE.projectile_body.super_detonation_damage.name, False))) + + detonation_sound_length = len(PROJECTILE.projectile_body.detonation_sound.name) + if detonation_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % detonation_sound_length, TAG.string_to_bytes(PROJECTILE.projectile_body.detonation_sound.name, False))) + + super_attached_detonation_damage_effect_length = len(PROJECTILE.projectile_body.super_attached_detonation_damage_effect.name) + if super_attached_detonation_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % super_attached_detonation_damage_effect_length, TAG.string_to_bytes(PROJECTILE.projectile_body.super_attached_detonation_damage_effect.name, False))) + + flyby_sound_length = len(PROJECTILE.projectile_body.flyby_sound.name) + if flyby_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % flyby_sound_length, TAG.string_to_bytes(PROJECTILE.projectile_body.flyby_sound.name, False))) + + impact_effect_length = len(PROJECTILE.projectile_body.impact_effect.name) + if impact_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % impact_effect_length, TAG.string_to_bytes(PROJECTILE.projectile_body.impact_effect.name, False))) + + impact_damage_length = len(PROJECTILE.projectile_body.impact_damage.name) + if impact_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % impact_damage_length, TAG.string_to_bytes(PROJECTILE.projectile_body.impact_damage.name, False))) + + boarding_detonation_damage_effect_length = len(PROJECTILE.projectile_body.boarding_detonation_damage_effect.name) + if boarding_detonation_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % boarding_detonation_damage_effect_length, TAG.string_to_bytes(PROJECTILE.projectile_body.boarding_detonation_damage_effect.name, False))) + + boarding_attached_detonation_damage_effect_length = len(PROJECTILE.projectile_body.boarding_attached_detonation_damage_effect.name) + if boarding_attached_detonation_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % boarding_attached_detonation_damage_effect_length, TAG.string_to_bytes(PROJECTILE.projectile_body.boarding_attached_detonation_damage_effect.name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("avlb", True), 1, 1, 4)) + + write_material_responses(output_stream, TAG, PROJECTILE.material_responses, PROJECTILE.material_responses_header) \ No newline at end of file diff --git a/io_scene_halo/file_tag/h2/file_projectile/format.py b/io_scene_halo/file_tag/h2/file_projectile/format.py new file mode 100644 index 000000000..1ca93279d --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_projectile/format.py @@ -0,0 +1,122 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto +from ..file_object.format import ObjectAsset + +class ResponseEnum(Enum): + impact_detonate = 0 + fizzle = auto() + overpenetrate = auto() + attach = auto() + bounce = auto() + bounce_dud = auto() + fizzle_ricochet = auto() + +class ProjectileAsset(ObjectAsset): + def __init__(self): + super().__init__() + self.header = None + self.projectile_body_header = None + self.projectile_body = None + self.material_responses_header = None + self.material_responses = None + + class ProjectileBody(ObjectAsset.ObjectBody): + def __init__(self, projectile_flags=0, detonation_timer_starts=0, impact_noise=0, ai_perception_radius=0.0, collision_radius=0.0, arming_time=0.0, danger_radius=0.0, + timer=(0.0, 0.0), minimum_velocity=0.0, maximum_range=0.0, detonation_noise=0, super_detonation_projectile_count=0, detonation_started=None, + detonation_effect_airborne=None, detonation_effect_ground=None, detonation_damage=None, attached_detonation_damage=None, super_detonation=None, + super_detonation_damage=None, detonation_sound=None, damage_reporting_type=0, super_attached_detonation_damage_effect=None, material_effect_radius=0.0, + flyby_sound=None, impact_effect=None, impact_damage=None, boarding_detonation_time=0.0, boarding_detonation_damage_effect=None, + boarding_attached_detonation_damage_effect=None, air_gravity_scale=0.0, air_damage_range=(0.0, 0.0), water_gravity_scale=0.0, water_damage_range=(0.0, 0.0), + initial_velocity=0.0, final_velocity=0.0, guided_angular_velocity_lower=0.0, guided_angular_velocity_upper=0.0, acceleration_range=(0.0, 0.0), + targeted_leading_fraction=0.0, material_responses_tag_block=None): + super().__init__() + self.projectile_flags = projectile_flags + self.detonation_timer_starts = detonation_timer_starts + self.impact_noise = impact_noise + self.ai_perception_radius = ai_perception_radius + self.collision_radius = collision_radius + self.arming_time = arming_time + self.danger_radius = danger_radius + self.timer = timer + self.minimum_velocity = minimum_velocity + self.maximum_range = maximum_range + self.detonation_noise = detonation_noise + self.super_detonation_projectile_count = super_detonation_projectile_count + self.detonation_started = detonation_started + self.detonation_effect_airborne = detonation_effect_airborne + self.detonation_effect_ground = detonation_effect_ground + self.detonation_damage = detonation_damage + self.attached_detonation_damage = attached_detonation_damage + self.super_detonation = super_detonation + self.super_detonation_damage = super_detonation_damage + self.detonation_sound = detonation_sound + self.damage_reporting_type = damage_reporting_type + self.super_attached_detonation_damage_effect = super_attached_detonation_damage_effect + self.material_effect_radius = material_effect_radius + self.flyby_sound = flyby_sound + self.impact_effect = impact_effect + self.impact_damage = impact_damage + self.boarding_detonation_time = boarding_detonation_time + self.boarding_detonation_damage_effect = boarding_detonation_damage_effect + self.boarding_attached_detonation_damage_effect = boarding_attached_detonation_damage_effect + self.air_gravity_scale = air_gravity_scale + self.air_damage_range = air_damage_range + self.water_gravity_scale = water_gravity_scale + self.water_damage_range = water_damage_range + self.initial_velocity = initial_velocity + self.final_velocity = final_velocity + self.guided_angular_velocity_lower = guided_angular_velocity_lower + self.guided_angular_velocity_upper = guided_angular_velocity_upper + self.acceleration_range = acceleration_range + self.targeted_leading_fraction = targeted_leading_fraction + self.material_responses_tag_block = material_responses_tag_block + + class MaterialResponse: + def __init__(self, flags=0, result_response=0, result_effect=None, material_name="", material_name_length=0, potential_result_response=0, potential_result_flags=0, + chance_fraction=0.0, between=(0.0, 0.0), and_bounds=(0.0, 0.0), potential_result_effect=None, scale_effects_by=0, angular_noise=0.0, velocity_noise=0.0, + detonation_effect=None, initial_friction=0.0, maximum_distance=0.0, parallel_friction=0.0, perpendicular_friction=0.0): + self.flags = flags + self.result_response = result_response + self.result_effect = result_effect + self.material_name = material_name + self.material_name_length = material_name_length + self.potential_result_response = potential_result_response + self.potential_result_flags = potential_result_flags + self.chance_fraction = chance_fraction + self.between = between + self.and_bounds = and_bounds + self.potential_result_effect = potential_result_effect + self.scale_effects_by = scale_effects_by + self.angular_noise = angular_noise + self.velocity_noise = velocity_noise + self.detonation_effect = detonation_effect + self.initial_friction = initial_friction + self.maximum_distance = maximum_distance + self.parallel_friction = parallel_friction + self.perpendicular_friction = perpendicular_friction diff --git a/io_scene_halo/file_tag/h2/file_render_model/build_mesh.py b/io_scene_halo/file_tag/h2/file_render_model/build_mesh.py index 78dad2d56..0f4ef0a98 100644 --- a/io_scene_halo/file_tag/h2/file_render_model/build_mesh.py +++ b/io_scene_halo/file_tag/h2/file_render_model/build_mesh.py @@ -28,13 +28,14 @@ import bpy import bmesh +from .... import config from ....global_functions import shader_processing, global_functions, mesh_processing -from .format import PartFlags, GeometryClassificationEnum +from .format import PartFlags, GeometryClassificationEnum, PropertyTypeEnum def build_mesh_layout(context, import_file, geometry, current_region_permutation, armature, random_color_gen, materials): vertex_groups = [] active_region_permutations = [] - + materials_count = len(import_file.materials) full_mesh = bpy.data.meshes.new(current_region_permutation) object_mesh = bpy.data.objects.new(current_region_permutation, full_mesh) @@ -52,7 +53,6 @@ def build_mesh_layout(context, import_file, geometry, current_region_permutation triangle_mat_indices = [] vertices = [raw_vertex.position for raw_vertex in section_data.raw_vertices] vertex_normals = [raw_vertex.normal for raw_vertex in section_data.raw_vertices] - for part_idx, part in enumerate(section_data.parts): triangle_part = [] @@ -229,6 +229,7 @@ def build_mesh_layout(context, import_file, geometry, current_region_permutation group_index = node_set[0] vertex_index = node_set[1] node_weight = node_set[2] + object_mesh.vertex_groups[group_index].add([vertex_weights_set_idx], node_weight, 'ADD') object_mesh.parent = armature @@ -248,10 +249,45 @@ def build_object(context, collection, geometry, armature, LOD, region_name, perm def get_geometry_layout(context, collection, import_file, armature, report): random_color_gen = global_functions.RandomColorGenerator() # generates a random sequence of colors materials = [] + shader_collection_dic = {} + shader_collection_path = os.path.join(config.HALO_2_TAG_PATH, r"scenarios\shaders\shader_collections.shader_collections") + shader_collection_file = open(shader_collection_path, "r") + for line in shader_collection_file.readlines(): + if not global_functions.string_empty_check(line) and not line.startswith(";"): + split_result = line.split() + if len(split_result) == 2: + prefix = split_result[0] + path = split_result[1] + shader_collection_dic[path] = prefix + for material in import_file.materials: - material_name = os.path.basename(material.shader.name) - if global_functions.string_empty_check(material_name): - material_name = os.path.basename(material.old_shader.name) + material_path = material.shader.name + if global_functions.string_empty_check(material_path): + material_path = material.old_shader.name + + material_directory = os.path.dirname(material_path) + material_name = os.path.basename(material_path) + + collection_prefix = shader_collection_dic.get(material_directory) + if not collection_prefix == None: + material_name = "%s %s" % (collection_prefix, material_name) + else: + print("Could not find a collection for: %s" % material_path) + + for material_property in material.properties: + property_enum = PropertyTypeEnum(material_property.property_type) + property_value = material_property.real_value + if PropertyTypeEnum.lightmap_resolution == property_enum: + material_name += " lm:%s" % property_value + + elif PropertyTypeEnum.lightmap_power == property_enum: + material_name += " lp:%s" % property_value + + elif PropertyTypeEnum.lightmap_half_life == property_enum: + material_name += " hl:%s" % property_value + + elif PropertyTypeEnum.lightmap_diffuse_scale == property_enum: + material_name += " ds:%s" % property_value mat = bpy.data.materials.new(name=material_name) shader_processing.generate_h2_shader(mat, material.shader, report) diff --git a/io_scene_halo/file_tag/h2/file_render_model/format.py b/io_scene_halo/file_tag/h2/file_render_model/format.py index e22d9b3bc..da2c5698f 100644 --- a/io_scene_halo/file_tag/h2/file_render_model/format.py +++ b/io_scene_halo/file_tag/h2/file_render_model/format.py @@ -499,8 +499,8 @@ def __init__(self, old_shader=None, shader=None, properties_tag_block_header=Non self.properties = properties class Property: - def __init__(self, type=0, int_value=0, real_value=0.0): - self.type = type + def __init__(self, property_type=0, int_value=0, real_value=0.0): + self.property_type = property_type self.int_value = int_value self.real_value = real_value diff --git a/io_scene_halo/file_tag/h2/file_render_model/process_file.py b/io_scene_halo/file_tag/h2/file_render_model/process_file.py index 8b0c43c18..754c5389b 100644 --- a/io_scene_halo/file_tag/h2/file_render_model/process_file.py +++ b/io_scene_halo/file_tag/h2/file_render_model/process_file.py @@ -677,7 +677,6 @@ def read_sections_v0(RENDER, TAG, input_stream, tag_node, XML_OUTPUT): section_data_node.appendChild(section_data_element_node) section_data = RENDER.LegacySectionData() - if section.section_data_header.version == 0: if section.section_data_header.size == 524: section_data.raw_vertices_tag_block = TAG.TagBlock().read(input_stream, TAG, tag_format.XMLData(section_data_element_node, "raw vertices")) @@ -1330,6 +1329,7 @@ def read_materials(RENDER, TAG, input_stream, tag_node, XML_OUTPUT): material_node.appendChild(material_element_node) material = RENDER.Material() + material.old_shader = TAG.TagRef().read(input_stream, TAG, tag_format.XMLData(material_element_node, "old shader")) material.shader = TAG.TagRef().read(input_stream, TAG, tag_format.XMLData(material_element_node, "shader")) material.properties_tag_block = TAG.TagBlock().read(input_stream, TAG, tag_format.XMLData(material_element_node, "properties")) @@ -1370,7 +1370,7 @@ def read_materials(RENDER, TAG, input_stream, tag_node, XML_OUTPUT): property_node.appendChild(property_element_node) property = RENDER.Property() - property.type = TAG.read_enum_unsigned_short(input_stream, TAG, tag_format.XMLData(property_element_node, "type", PropertyTypeEnum)) + property.property_type = TAG.read_enum_unsigned_short(input_stream, TAG, tag_format.XMLData(property_element_node, "type", PropertyTypeEnum)) property.int_value = TAG.read_signed_short(input_stream, TAG, tag_format.XMLData(property_element_node, "int value")) property.real_value = TAG.read_float(input_stream, TAG, tag_format.XMLData(property_element_node, "real value")) diff --git a/io_scene_halo/file_tag/h2/file_scenario/mesh_helper/build_mesh.py b/io_scene_halo/file_tag/h2/file_scenario/mesh_helper/build_mesh.py index d1d663d4c..cbc742c76 100644 --- a/io_scene_halo/file_tag/h2/file_scenario/mesh_helper/build_mesh.py +++ b/io_scene_halo/file_tag/h2/file_scenario/mesh_helper/build_mesh.py @@ -46,7 +46,6 @@ def build_mesh_layout(asset, section, region_name, random_color_gen, object_mesh triangle_mat_indices = [] vertices = [raw_vertex.position for raw_vertex in section_data.raw_vertices] vertex_normals = [raw_vertex.normal for raw_vertex in section_data.raw_vertices] - for part_idx, part in enumerate(section_data.parts): triangle_part = [] diff --git a/io_scene_halo/file_tag/h2/file_scenario/process_file.py b/io_scene_halo/file_tag/h2/file_scenario/process_file.py index 9ad67e9e7..031027bed 100644 --- a/io_scene_halo/file_tag/h2/file_scenario/process_file.py +++ b/io_scene_halo/file_tag/h2/file_scenario/process_file.py @@ -3436,7 +3436,7 @@ def read_scenario_cluster_data(SCENARIO, TAG, input_stream, tag_node, XML_OUTPUT scenario_cluster_data_element_node = None if XML_OUTPUT: scenario_cluster_data_element_node = scenario_cluster_data_node.childNodes[scenario_cluster_data_idx] - + bsp_name_length = scenario_cluster_data.bsp.name_length if bsp_name_length > 0: scenario_cluster_data.bsp.name = TAG.read_variable_string(input_stream, bsp_name_length, TAG) diff --git a/io_scene_halo/file_tag/h2/file_scenario_structure_bsp/format.py b/io_scene_halo/file_tag/h2/file_scenario_structure_bsp/format.py index be8568d80..9397be030 100644 --- a/io_scene_halo/file_tag/h2/file_scenario_structure_bsp/format.py +++ b/io_scene_halo/file_tag/h2/file_scenario_structure_bsp/format.py @@ -787,8 +787,8 @@ def __init__(self, old_shader=None, shader=None, properties_header=None, propert self.properties = properties class Property: - def __init__(self, type=0, int_value=0, real_value=0.0): - self.type = type + def __init__(self, property_type=0, int_value=0, real_value=0.0): + self.property_type = property_type self.int_value = int_value self.real_value = real_value diff --git a/io_scene_halo/file_tag/h2/file_scenario_structure_bsp/process_file.py b/io_scene_halo/file_tag/h2/file_scenario_structure_bsp/process_file.py index 1c730b9a0..d55f46b63 100644 --- a/io_scene_halo/file_tag/h2/file_scenario_structure_bsp/process_file.py +++ b/io_scene_halo/file_tag/h2/file_scenario_structure_bsp/process_file.py @@ -1121,7 +1121,7 @@ def read_materials(LEVEL, TAG, input_stream, tag_node, XML_OUTPUT): property_node.appendChild(property_element_node) property = LEVEL.Property() - property.type = TAG.read_enum_unsigned_short(input_stream, TAG, tag_format.XMLData(property_element_node, "type", PropertyTypeEnum)) + property.property_type = TAG.read_enum_unsigned_short(input_stream, TAG, tag_format.XMLData(property_element_node, "type", PropertyTypeEnum)) property.int_value = TAG.read_signed_short(input_stream, TAG, tag_format.XMLData(property_element_node, "int value")) property.real_value = TAG.read_float(input_stream, TAG, tag_format.XMLData(property_element_node, "real value")) diff --git a/io_scene_halo/file_tag/h2/file_scenery/build_asset.py b/io_scene_halo/file_tag/h2/file_scenery/build_asset.py new file mode 100644 index 000000000..6be5ae424 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_scenery/build_asset.py @@ -0,0 +1,121 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from ....global_functions import tag_format, shader_processing +from ....file_tag.h2.file_shader.format import FunctionTypeEnum +from ..file_object.build_asset import ( + write_ai_properties, + write_functions, + write_attachments, + write_tag_ref, + write_old_functions, + write_change_colors, + write_predicted_resources + ) + +def write_body(output_stream, TAG, SCENERY): + SCENERY.scenery_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack('I', len(SCENERY.scenery_body.default_model_variant))) + SCENERY.scenery_body.model.write(output_stream, False, True) + SCENERY.scenery_body.crate_object.write(output_stream, False, True) + SCENERY.scenery_body.modifier_shader.write(output_stream, False, True) + SCENERY.scenery_body.creation_effect.write(output_stream, False, True) + SCENERY.scenery_body.material_effects.write(output_stream, False, True) + SCENERY.scenery_body.ai_properties_tag_block.write(output_stream, False) + SCENERY.scenery_body.functions_tag_block.write(output_stream, False) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % default_model_variant_name_length, TAG.string_to_bytes(SCENERY.scenery_body.default_model_variant, False))) + + model_name_length = len(SCENERY.scenery_body.model.name) + if model_name_length > 0: + output_stream.write(struct.pack('<%ssx' % model_name_length, TAG.string_to_bytes(SCENERY.scenery_body.model.name, False))) + + crate_object_name_length = len(SCENERY.scenery_body.crate_object.name) + if crate_object_name_length > 0: + output_stream.write(struct.pack('<%ssx' % crate_object_name_length, TAG.string_to_bytes(SCENERY.scenery_body.crate_object.name, False))) + + modifier_shader_name_length = len(SCENERY.scenery_body.modifier_shader.name) + if modifier_shader_name_length > 0: + output_stream.write(struct.pack('<%ssx' % modifier_shader_name_length, TAG.string_to_bytes(SCENERY.scenery_body.modifier_shader.name, False))) + + creation_effect_name_length = len(SCENERY.scenery_body.creation_effect.name) + if creation_effect_name_length > 0: + output_stream.write(struct.pack('<%ssx' % creation_effect_name_length, TAG.string_to_bytes(SCENERY.scenery_body.creation_effect.name, False))) + + material_effects_name_length = len(SCENERY.scenery_body.material_effects.name) + if material_effects_name_length > 0: + output_stream.write(struct.pack('<%ssx' % material_effects_name_length, TAG.string_to_bytes(SCENERY.scenery_body.material_effects.name, False))) + + write_ai_properties(output_stream, TAG, SCENERY.ai_properties, SCENERY.ai_properties_header) + write_functions(output_stream, TAG, SCENERY.functions, SCENERY.functions_header) + write_attachments(output_stream, TAG, SCENERY.attachments, SCENERY.attachments_header) + write_tag_ref(output_stream, TAG, SCENERY.widgets, SCENERY.widgets_header) + write_old_functions(output_stream, TAG, SCENERY.old_functions, SCENERY.old_functions_header) + write_change_colors(output_stream, TAG, SCENERY.change_colors, SCENERY.change_colors_header) + write_predicted_resources(output_stream, TAG, SCENERY.predicted_resources, SCENERY.predicted_resources_header) + diff --git a/io_scene_halo/file_tag/h2/file_scenery/format.py b/io_scene_halo/file_tag/h2/file_scenery/format.py index aa7d093ac..458db7e5a 100644 --- a/io_scene_halo/file_tag/h2/file_scenery/format.py +++ b/io_scene_halo/file_tag/h2/file_scenery/format.py @@ -26,33 +26,7 @@ from mathutils import Vector from enum import Flag, Enum, auto - -class ObjectFlags(Flag): - does_not_cast_shadow = auto() - search_cardinal_direction_lightmaps_on_failure = auto() - unused = auto() - not_a_pathfinding_obstacle = auto() - extension_of_parent = auto() - does_not_cause_collision_damage = auto() - early_mover = auto() - early_mover_localized_physics = auto() - use_static_massive_lightmap_sample = auto() - object_scales_attachments = auto() - inherits_players_appearance = auto() - dead_bipdes_cant_localize = auto() - attach_to_clusters_by_dynamic_sphere = auto() - effects_created_by_this_object_do_not_spawn_objects_in_multiplayer = auto() - prophet_is_not_displayed_pegasus_builds = auto() - -class LightmapShadowModeEnum(Enum): - default = 0 - never = auto() - always = auto() - -class SweetenerSizeEnum(Enum): - small = 0 - medium = auto() - large = auto() +from ..file_object.format import ObjectAsset class PathfindingPolicyEnum(Enum): pathfinding_cut_out = 0 @@ -68,65 +42,16 @@ class LightmappingPolicyEnum(Enum): per_pixel = auto() dynamic = auto() -class SceneryAsset(): +class SceneryAsset(ObjectAsset): def __init__(self): + super().__init__() self.header = None self.scenery_body_header = None self.scenery_body = None - self.ai_properties_header = None - self.ai_properties = None - self.functions_header = None - self.functions = None - self.attachments_header = None - self.attachments = None - self.widgets_header = None - self.widgets = None - self.old_functions_header = None - self.old_functions = None - self.change_colors_header = None - self.change_colors = None - self.predicted_resources_header = None - self.predicted_resources = None - class SceneryBody: - def __init__(self, object_flags=0, bounding_radius=0.0, bounding_offset=Vector(), acceleration_scale=0.0, lightmap_shadow_mode=0, sweetner_size=0, - dynamic_light_sphere_radius=0.0, dynamic_light_sphere_offset=Vector(), default_model_variant="", default_model_variant_length=0, model=None, crate_object=None, - modifier_shader=None, creation_effect=None, material_effects=None, ai_properties_tag_block=None, functions_tag_block=None, apply_collision_damage_scale=0.0, - min_game_acc=0.0, max_game_acc=0.0, min_game_scale=0.0, max_game_scale=0.0, min_abs_acc=0.0, max_abs_acc=0.0, min_abs_scale=0.0, max_abs_scale=0.0, - hud_text_message_index=0, attachments_tag_block=None, widgets_tag_block=None, old_functions_tag_block=None, change_colors_tag_block=None, - predicted_resources_tag_block=None, pathfinding_policy=0, scenery_flags=0, lightmapping_policy=0): - self.object_flags = object_flags - self.bounding_radius = bounding_radius - self.bounding_offset = bounding_offset - self.acceleration_scale = acceleration_scale - self.lightmap_shadow_mode = lightmap_shadow_mode - self.sweetner_size = sweetner_size - self.dynamic_light_sphere_radius = dynamic_light_sphere_radius - self.dynamic_light_sphere_offset = dynamic_light_sphere_offset - self.default_model_variant = default_model_variant - self.default_model_variant_length = default_model_variant_length - self.model = model - self.crate_object = crate_object - self.modifier_shader = modifier_shader - self.creation_effect = creation_effect - self.material_effects = material_effects - self.ai_properties_tag_block = ai_properties_tag_block - self.functions_tag_block = functions_tag_block - self.apply_collision_damage_scale = apply_collision_damage_scale - self.min_game_acc = min_game_acc - self.max_game_acc = max_game_acc - self.min_game_scale = min_game_scale - self.max_game_scale = max_game_scale - self.min_abs_acc = min_abs_acc - self.max_abs_acc = max_abs_acc - self.min_abs_scale = min_abs_scale - self.max_abs_scale = max_abs_scale - self.hud_text_message_index = hud_text_message_index - self.attachments_tag_block = attachments_tag_block - self.widgets_tag_block = widgets_tag_block - self.old_functions_tag_block = old_functions_tag_block - self.change_colors_tag_block = change_colors_tag_block - self.predicted_resources_tag_block = predicted_resources_tag_block + class SceneryBody(ObjectAsset.ObjectBody): + def __init__(self, pathfinding_policy=0, scenery_flags=0, lightmapping_policy=0): + super().__init__() self.pathfinding_policy = pathfinding_policy self.scenery_flags = scenery_flags self.lightmapping_policy = lightmapping_policy diff --git a/io_scene_halo/file_tag/h2/file_scenery/process_file.py b/io_scene_halo/file_tag/h2/file_scenery/process_file.py index 29f0f2e0e..358696d0c 100644 --- a/io_scene_halo/file_tag/h2/file_scenery/process_file.py +++ b/io_scene_halo/file_tag/h2/file_scenery/process_file.py @@ -26,15 +26,8 @@ from xml.dom import minidom from ....global_functions import tag_format -from .format import ( - SceneryAsset, - ObjectFlags, - LightmapShadowModeEnum, - SweetenerSizeEnum, - PathfindingPolicyEnum, - SceneryFlags, - LightmappingPolicyEnum, - ) +from ..file_object.format import ObjectFlags, LightmapShadowModeEnum, SweetenerSizeEnum +from .format import SceneryAsset, PathfindingPolicyEnum, SceneryFlags, LightmappingPolicyEnum XML_OUTPUT = False diff --git a/io_scene_halo/file_tag/h2/file_shader/build_asset.py b/io_scene_halo/file_tag/h2/file_shader/build_asset.py index bd9ebc3f2..ee7688ded 100644 --- a/io_scene_halo/file_tag/h2/file_shader/build_asset.py +++ b/io_scene_halo/file_tag/h2/file_shader/build_asset.py @@ -27,7 +27,7 @@ import struct from .format import FunctionTypeEnum -from ....global_functions import tag_format +from ....global_functions import tag_format, shader_processing def write_body(output_stream, TAG, SHADER): SHADER.shader_body_header.write(output_stream, TAG, True) @@ -58,121 +58,6 @@ def write_body(output_stream, TAG, SHADER): if material_name_length > 0: output_stream.write(struct.pack('<%ss' % material_name_length, TAG.string_to_bytes(SHADER.shader_body.material_name, False))) -def write_identity(output_stream, TAG, animation_properties): - output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("tbfd", True), 0, 20, 1)) - output_stream.write(struct.pack(' 1: - output_stream.write(struct.pack(' 0: - parameters_header.write(output_stream, TAG, True) - for parameter_element in parameters: - output_stream.write(struct.pack('>I', len(parameter_element.name))) - output_stream.write(struct.pack(' 0: - output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(parameter_element.name, False))) - if bitmap_name_length > 0: - output_stream.write(struct.pack('<%ssx' % bitmap_name_length, TAG.string_to_bytes(parameter_element.bitmap.name, False))) - - if len(parameter_element.animation_properties) > 0: - parameter_element.animation_properties_tag_block_header.write(output_stream, TAG, True) - for animation_properties in parameter_element.animation_properties: - output_stream.write(struct.pack('I', len(animation_properties.input_name))) - output_stream.write(struct.pack('>I', len(animation_properties.range_name))) - output_stream.write(struct.pack(' 0: - output_stream.write(struct.pack('<%ss' % input_name_length, TAG.string_to_bytes(animation_properties.input_name, False))) - if range_name_length > 0: - output_stream.write(struct.pack('<%ss' % range_name_length, TAG.string_to_bytes(animation_properties.range_name, False))) - - animation_properties.map_property_header.write(output_stream, TAG, True) - function_type = FunctionTypeEnum(animation_properties.function_type) - if FunctionTypeEnum.identity == function_type: - write_identity(output_stream, TAG, animation_properties) - elif FunctionTypeEnum.constant == function_type: - write_constant(output_stream, TAG, animation_properties) - elif FunctionTypeEnum.transition == function_type: - write_constant(output_stream, TAG, animation_properties) - elif FunctionTypeEnum.periodic == function_type: - write_constant(output_stream, TAG, animation_properties) - elif FunctionTypeEnum.linear == function_type: - write_constant(output_stream, TAG, animation_properties) - elif FunctionTypeEnum.linear_key == function_type: - write_constant(output_stream, TAG, animation_properties) - elif FunctionTypeEnum.multi_linear_key == function_type: - write_constant(output_stream, TAG, animation_properties) - elif FunctionTypeEnum.spline == function_type: - write_constant(output_stream, TAG, animation_properties) - elif FunctionTypeEnum.multi_spline == function_type: - write_constant(output_stream, TAG, animation_properties) - elif FunctionTypeEnum.exponent == function_type: - write_constant(output_stream, TAG, animation_properties) - elif FunctionTypeEnum.spline2 == function_type: - write_constant(output_stream, TAG, animation_properties) - def build_asset(output_stream, SHADER, report): TAG = tag_format.TagAsset() TAG.is_legacy = False @@ -181,4 +66,4 @@ def build_asset(output_stream, SHADER, report): SHADER.header.write(output_stream, False, True) write_body(output_stream, TAG, SHADER) - write_parameters(output_stream, TAG, SHADER.parameters, SHADER.parameters_header) + shader_processing.write_parameters(output_stream, TAG, SHADER.parameters, SHADER.parameters_header) diff --git a/io_scene_halo/file_tag/h2/file_shader/format.py b/io_scene_halo/file_tag/h2/file_shader/format.py index c95e141bc..9a643e551 100644 --- a/io_scene_halo/file_tag/h2/file_shader/format.py +++ b/io_scene_halo/file_tag/h2/file_shader/format.py @@ -71,7 +71,7 @@ class AnimationTypeEnum(Enum): bitmap_rotation_axis_x = auto() bitmap_rotation_axis_y = auto() bitmap_rotation_axis_z = auto() - value = auto() + _value = auto() color = auto() bitmap_index = auto() @@ -88,8 +88,11 @@ class FunctionTypeEnum(Enum): exponent = auto() spline2 = auto() -class OutputTypeEnum(Enum): - scalar_intensity = 0 +class OutputTypeFlags(Flag): + range = auto() + _2_color = 32 + _3_color = 48 + _4_color = 64 class TransitionExponentEnum(Enum): linear = 0 @@ -184,15 +187,20 @@ def __init__(self, name="", name_length=0, type=0, bitmap=None, const_value=0.0, self.animation_properties = animation_properties class AnimationProperty: - def __init__(self, type=0, input_name="", input_name_length=0, range_name="", range_name_length=0, time_period=0.0, map_property_header=None, - function_header=None, function_type=0, range_check=False, input_function_data=None, range_function_data=None, output_type=0, upper_bound=1.0, - lower_bound=0.0, color_a=(0.0, 0.0, 0.0, 1.0), color_b=(0.0, 0.0, 0.0, 1.0), color_c=(0.0, 0.0, 0.0, 1.0), color_d=(0.0, 0.0, 0.0, 1.0)): + def __init__(self, type=0, input_name="", input_name_length=0, input_type=0, range_name="", range_name_length=0, range_type=0, time_period=0.0, output_modifier=0, + output_modifier_input=0, map_property_header=None, function_header=None, function_type=0, range_check=False, input_function_data=None, range_function_data=None, + output_type=0, upper_bound=1.0, lower_bound=0.0, range_upper_bound=1.0, range_lower_bound=1.0, color_a=(0.0, 0.0, 0.0, 1.0), color_b=(0.0, 0.0, 0.0, 1.0), + color_c=(0.0, 0.0, 0.0, 1.0), color_d=(0.0, 0.0, 0.0, 1.0)): self.type = type self.input_name = input_name self.input_name_length = input_name_length + self.input_type = input_type self.range_name = range_name self.range_name_length = range_name_length + self.range_type = range_type self.time_period = time_period + self.output_modifier = output_modifier + self.output_modifier_input = output_modifier_input self.map_property_header = map_property_header self.function_header = function_header self.function_type = function_type @@ -202,6 +210,8 @@ def __init__(self, type=0, input_name="", input_name_length=0, range_name="", ra self.output_type = output_type self.upper_bound = upper_bound self.lower_bound = lower_bound + self.range_upper_bound = range_upper_bound + self.range_lower_bound = range_lower_bound self.color_a = color_a self.color_b = color_b self.color_c = color_c diff --git a/io_scene_halo/file_tag/h2/file_shader/process_file.py b/io_scene_halo/file_tag/h2/file_shader/process_file.py index eb1f4a7c6..389ad66ee 100644 --- a/io_scene_halo/file_tag/h2/file_shader/process_file.py +++ b/io_scene_halo/file_tag/h2/file_shader/process_file.py @@ -73,6 +73,7 @@ def read_shader_body_v0(SHADER, TAG, input_stream, tag_node, XML_OUTPUT): SHADER.shader_body.lightmap_ambient_bias = TAG.read_float(input_stream, TAG, tag_format.XMLData(tag_node, "lightmap ambient bias")) SHADER.shader_body.postprocess_properties_tag_block = TAG.TagBlock().read(input_stream, TAG, tag_format.XMLData(tag_node, "predicted resources")) + def read_shader_body_retail(SHADER, TAG, input_stream, tag_node, XML_OUTPUT): SHADER.shader_body_header = TAG.TagBlockHeader().read(input_stream, TAG) SHADER.shader_body = SHADER.ShaderBody() diff --git a/io_scene_halo/file_tag/h2/file_sky/build_asset.py b/io_scene_halo/file_tag/h2/file_sky/build_asset.py new file mode 100644 index 000000000..d58a9fe43 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_sky/build_asset.py @@ -0,0 +1,194 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from math import radians +from ....global_functions import tag_format + +def write_body(output_stream, TAG, SKY): + SKY.sky_body_header.write(output_stream, TAG, True) + SKY.sky_body.render_model.write(output_stream, False, True) + SKY.sky_body.animation_graph.write(output_stream, False, True) + output_stream.write(struct.pack(' 0: + cubemap_header.write(output_stream, TAG, True) + for cubemap_element in cubemap: + cubemap_element.cubemap_reference.write(output_stream, False, True) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % cubemap_reference_length, TAG.string_to_bytes(cubemap_element.cubemap_reference.name, False))) + +def write_fog(output_stream, TAG, fog, fog_header): + if len(fog) > 0: + fog_header.write(output_stream, TAG, True) + for fog_element in fog: + output_stream.write(struct.pack(' 0: + sky_fog_header.write(output_stream, TAG, True) + for sky_fog_element in sky_fog: + output_stream.write(struct.pack(' 0: + patchy_fog_header.write(output_stream, TAG, True) + for patchy_fog_element in patchy_fog: + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % patchy_fog_length, TAG.string_to_bytes(patchy_fog_element.patchy_fog.name, False))) + +def write_lights(output_stream, TAG, lights, lights_header): + if len(lights) > 0: + lights_header.write(output_stream, TAG, True) + for light_element in lights: + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % lens_flare_length, TAG.string_to_bytes(light_element.lens_flare.name, False))) + + if len(light_element.fog) > 0: + light_element.fog_header.write(output_stream, TAG, True) + for fog_element in light_element.fog: + output_stream.write(struct.pack(' 0: + light_element.fog_opposite_header.write(output_stream, TAG, True) + for fog_opposite_element in light_element.fog_opposite: + output_stream.write(struct.pack(' 0: + light_element.radiosity_header.write(output_stream, TAG, True) + for radiosity_element in light_element.radiosity: + output_stream.write(struct.pack(' 0: + shader_functions_header.write(output_stream, TAG, True) + for shader_function_element in shader_functions: + output_stream.write(struct.pack('<4x')) + output_stream.write(struct.pack('<31sx', TAG.string_to_bytes(shader_function_element, False))) + +def write_animations(output_stream, TAG, animations, animations_header): + if len(animations) > 0: + animations_header.write(output_stream, TAG, True) + for animation_element in animations: + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % render_model_length, TAG.string_to_bytes(SKY.sky_body.render_model.name, False))) + + animation_graph_length = len(SKY.sky_body.animation_graph.name) + if animation_graph_length > 0: + output_stream.write(struct.pack('<%ssx' % animation_graph_length, TAG.string_to_bytes(SKY.sky_body.animation_graph.name, False))) + + write_cubemaps(output_stream, TAG, SKY.cubemap, SKY.cubemap_header) + write_fog(output_stream, TAG, SKY.atmospheric_fog, SKY.atmospheric_fog_header) + write_fog(output_stream, TAG, SKY.secondary_fog, SKY.secondary_fog_header) + write_sky_fog(output_stream, TAG, SKY.sky_fog, SKY.sky_fog_header) + write_sky_fog(output_stream, TAG, SKY.sky_fog, SKY.sky_fog_header) + write_patchy_fog(output_stream, TAG, SKY.patchy_fog, SKY.patchy_fog_header) + write_lights(output_stream, TAG, SKY.lights, SKY.lights_header) + write_shader_functions(output_stream, TAG, SKY.shader_functions, SKY.shader_functions_header) + write_animations(output_stream, TAG, SKY.animations, SKY.animations_header) diff --git a/io_scene_halo/file_tag/h2/file_sky/format.py b/io_scene_halo/file_tag/h2/file_sky/format.py index 9f95d9344..4c8a8abfa 100644 --- a/io_scene_halo/file_tag/h2/file_sky/format.py +++ b/io_scene_halo/file_tag/h2/file_sky/format.py @@ -149,4 +149,4 @@ def __init__(self, flags=0, color=(0.0, 0.0, 0.0, 1.0), power=0.0, test_distance class Animation: def __init__(self, animation_index=0, period=0.0): self.animation_index = animation_index - self.period = period + self.period = period \ No newline at end of file diff --git a/io_scene_halo/file_tag/h2/file_sky/process_file.py b/io_scene_halo/file_tag/h2/file_sky/process_file.py index d0a69ed56..db1ce626c 100644 --- a/io_scene_halo/file_tag/h2/file_sky/process_file.py +++ b/io_scene_halo/file_tag/h2/file_sky/process_file.py @@ -49,8 +49,7 @@ def read_sky_body_v0(SKY, TAG, input_stream, tag_node, XML_OUTPUT): SKY.sky_body = SKY.SkyBody() SKY.sky_body.render_model = TAG.TagRef().read(input_stream, TAG, tag_format.XMLData(tag_node, "render model")) SKY.sky_body.animation_graph = TAG.TagRef().read(input_stream, TAG, tag_format.XMLData(tag_node, "animation graph")) - SKY.sky_body.flags = TAG.read_flag_unsigned_short(input_stream, TAG, tag_format.XMLData(tag_node, "flags", SkyFlags)) - input_stream.read(2) # Padding? + SKY.sky_body.flags = TAG.read_flag_unsigned_integer(input_stream, TAG, tag_format.XMLData(tag_node, "flags", SkyFlags)) SKY.sky_body.render_model_scale = TAG.read_float(input_stream, TAG, tag_format.XMLData(tag_node, "bounding radius")) SKY.sky_body.movement_scale = TAG.read_float(input_stream, TAG, tag_format.XMLData(tag_node, "bounding radius")) SKY.sky_body.cubemap_tag_block = TAG.TagBlock().read(input_stream, TAG, tag_format.XMLData(tag_node, "cubemap")) @@ -84,8 +83,7 @@ def read_sky_body_retail(SKY, TAG, input_stream, tag_node, XML_OUTPUT): SKY.sky_body = SKY.SkyBody() SKY.sky_body.render_model = TAG.TagRef().read(input_stream, TAG, tag_format.XMLData(tag_node, "render model")) SKY.sky_body.animation_graph = TAG.TagRef().read(input_stream, TAG, tag_format.XMLData(tag_node, "animation graph")) - SKY.sky_body.flags = TAG.read_flag_unsigned_short(input_stream, TAG, tag_format.XMLData(tag_node, "flags", SkyFlags)) - input_stream.read(2) # Padding? + SKY.sky_body.flags = TAG.read_flag_unsigned_integer(input_stream, TAG, tag_format.XMLData(tag_node, "flags", SkyFlags)) SKY.sky_body.render_model_scale = TAG.read_float(input_stream, TAG, tag_format.XMLData(tag_node, "bounding radius")) SKY.sky_body.movement_scale = TAG.read_float(input_stream, TAG, tag_format.XMLData(tag_node, "bounding radius")) SKY.sky_body.cubemap_tag_block = TAG.TagBlock().read(input_stream, TAG, tag_format.XMLData(tag_node, "cubemap")) @@ -304,8 +302,7 @@ def read_lights(SKY, TAG, input_stream, tag_node, XML_OUTPUT): radiosity_node.appendChild(radiosity_element_node) radiosity = SKY.Radiosity() - radiosity.flags = TAG.read_flag_unsigned_short(input_stream, TAG, tag_format.XMLData(radiosity_element_node, "flags", LightFlags)) - input_stream.read(2) # Padding? + radiosity.flags = TAG.read_flag_unsigned_integer(input_stream, TAG, tag_format.XMLData(radiosity_element_node, "flags", LightFlags)) radiosity.color = TAG.read_rgb(input_stream, TAG, tag_format.XMLData(radiosity_element_node, "color")) radiosity.power = TAG.read_float(input_stream, TAG, tag_format.XMLData(radiosity_element_node, "power")) radiosity.test_distance = TAG.read_float(input_stream, TAG, tag_format.XMLData(radiosity_element_node, "test distance")) diff --git a/io_scene_halo/file_tag/h2/file_sound/build_asset.py b/io_scene_halo/file_tag/h2/file_sound/build_asset.py new file mode 100644 index 000000000..25ad212d7 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_sound/build_asset.py @@ -0,0 +1,174 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from math import radians +from ....global_functions import tag_format, shader_processing + +def write_body(output_stream, TAG, SOUND): + SOUND.sound_body_header.write(output_stream, TAG, True) + SOUND.sound_body.remastered_sound.write(output_stream, False, True) + output_stream.write(struct.pack(' 0: + pitch_ranges_header.write(output_stream, TAG, True) + for pitch_range_element in pitch_ranges: + output_stream.write(struct.pack('>I', len(pitch_range_element.name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(pitch_range_element.name, False))) + + if len(pitch_range_element.permutations) > 0: + pitch_range_element.permutations_header.write(output_stream, TAG, True) + for permutation_element in pitch_range_element.permutations: + output_stream.write(struct.pack('>I', len(permutation_element.name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(permutation_element.name, False))) + +def write_extra_sound_info(output_stream, TAG, extra_sound_info, extra_sound_info_header): + if len(extra_sound_info) > 0: + extra_sound_info_header.write(output_stream, TAG, True) + for extra_sound_info_element in extra_sound_info: + extra_sound_info_element.language_permutation_info_tag_block.write(output_stream, False) + extra_sound_info_element.encoded_permutation_section_tag_block.write(output_stream, False) + output_stream.write(struct.pack(' 0: + extra_sound_info_element.language_permutation_info_header.write(output_stream, TAG, True) + for language_permutation_info_element in extra_sound_info_element.language_permutation_info: + language_permutation_info_element.raw_info_block_tag_block.write(output_stream, False) + + for language_permutation_info_element in extra_sound_info_element.language_permutation_info: + if len(language_permutation_info_element.raw_info_block) > 0: + language_permutation_info_element.raw_info_block_header.write(output_stream, TAG, True) + for raw_info_block_element in language_permutation_info_element.raw_info_block: + output_stream.write(struct.pack('>I', len(raw_info_block_element.skip_fraction_name))) + raw_info_block_element.samples_tag_data.write(output_stream, False) + raw_info_block_element.unk_1_tag_data.write(output_stream, False) + raw_info_block_element.unk_2_tag_data.write(output_stream, False) + raw_info_block_element.markers_tag_block.write(output_stream, False) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % skip_fraction_name_length, TAG.string_to_bytes(raw_info_block_element.skip_fraction_name, False))) + + output_stream.write(raw_info_block_element.samples_tag_data.data) + output_stream.write(raw_info_block_element.unk_1_tag_data.data) + output_stream.write(raw_info_block_element.unk_2_tag_data.data) + + extra_sound_info[0].blok_header.write(output_stream, TAG, True) + +def build_asset(output_stream, SOUND, report): + TAG = tag_format.TagAsset() + TAG.is_legacy = False + TAG.big_endian = False + + SOUND.header.write(output_stream, False, True) + write_body(output_stream, TAG, SOUND) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("snpl", True), 0, 1, 56)) + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("snsc", True), 0, 1, 20)) + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("snpr", True), 1, 1, 36)) + + write_pitch_ranges(output_stream, TAG, SOUND.pitch_ranges, SOUND.pitch_ranges_header) + write_extra_sound_info(output_stream, TAG, SOUND.extra_sound_info, SOUND.extra_sound_info_header) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("srpr", True), 0, 1, 12)) + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("slpp", True), 0, 1, 20)) + for parameter in SOUND.sound_body.parameters: + shader_processing.write_function(output_stream, TAG, parameter) diff --git a/io_scene_halo/file_tag/h2/file_sound/format.py b/io_scene_halo/file_tag/h2/file_sound/format.py new file mode 100644 index 000000000..ceccf7867 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_sound/format.py @@ -0,0 +1,251 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto + +class SoundFlags(Flag): + fit_to_adpcm_blocksize = auto() + split_long_sound_into_permutations = auto() + always_spatialize = auto() + never_obstruct = auto() + internal_dont_touch = auto() + use_huge_sound_transmission = auto() + link_count_to_owner_unit = auto() + pitch_range_is_language = auto() + dont_use_sound_class_speaker_flag = auto() + dont_use_lipsync_data = auto() + play_only_in_legacy_mode = auto() + play_only_in_remastered_mode = auto() + +class ClassEnum(Enum): + projectile_impact = 0 + projectile_detonation = auto() + projectile_flyby = auto() + unk_3 = auto() + weapon_fire = auto() + weapon_ready = auto() + weapon_reload = auto() + weapon_empty = auto() + weapon_charge = auto() + weapon_overheat = auto() + weapon_idle = auto() + weapon_melee = auto() + weapon_animation = auto() + object_impacts = auto() + particle_impacts = auto() + unk_15 = auto() + unk_16 = auto() + unk_17 = auto() + unit_footsteps = auto() + unit_dialog = auto() + unit_animation = auto() + unk_21 = auto() + vehicle_collision = auto() + vehicle_engine = auto() + vehicle_animation = auto() + unk_25 = auto() + device_door = auto() + unk_27 = auto() + device_machinery = auto() + device_stationary = auto() + unk_30 = auto() + unk_31 = auto() + music = auto() + ambient_nature = auto() + ambient_machinery = auto() + unk_35 = auto() + huge_ass = auto() + object_looping = auto() + cinematic_music = auto() + unk_39 = auto() + unk_40 = auto() + unk_41 = auto() + unk_42 = auto() + unk_43 = auto() + unk_44 = auto() + cortana_mission = auto() + cortana_cinematic = auto() + mission_dialog = auto() + cinematic_dialog = auto() + scripted_cinematic_foley = auto() + game_event = auto() + ui = auto() + test = auto() + multilingual_test = auto() + +class SoundAsset(): + def __init__(self): + self.header = None + self.sound_body_header = None + self.sound_body = None + self.promotion_rules_header = None + self.promotion_rules = None + self.runtime_timers_header = None + self.runtime_timers = None + self.pitch_ranges_header = None + self.pitch_ranges = None + self.platform_parameters_header = None + self.platform_parameters = None + self.extra_sound_info_header = None + self.extra_sound_info = None + self.reflections_header = None + self.reflections = None + + class SoundBody: + def __init__(self, remastered_sound=None, flags=0, class_type=0, sample_rate=0, output_effect=0, import_type=0, minimum_distance=0.0, maximum_distance=0.0, skip_fraction=0.0, + maximum_bend_per_second=0.0, gain_base=0.0, gain_variance=0.0, random_pitch_bounds=(0, 0), inner_cone_angle=0.0, outer_cone_angle=0.0, outer_cone_gain=0.0, + override_flags=0, azimuth=0.0, positional_gain=0.0, first_person_gain=0.0, gain_modifier=(0.0, 0.0), pitch_modifier=(0, 0), skip_fraction_modifier=(0.0, 0.0), + encoding=0, compression=0, promotion_rules_tag_block=None, runtime_timers_tag_block=None, runtime_active_promotion_index=0, runtime_last_promotion_time=0, + runtime_suppression_timeout=0, inner_silence_distance=0.0, pitch_ranges_tag_block=None, platform_parameters_tag_block=None, extra_sound_info_tag_block=None, + reflections_tag_block=None, low_pass_minimum_distance=0.0, low_pass_maximum_distance=1.0, parameters=None): + self.remastered_sound = remastered_sound + self.flags = flags + self.class_type = class_type + self.sample_rate = sample_rate + self.output_effect = output_effect + self.import_type = import_type + self.minimum_distance = minimum_distance + self.maximum_distance = maximum_distance + self.skip_fraction = skip_fraction + self.maximum_bend_per_second = maximum_bend_per_second + self.gain_base = gain_base + self.gain_variance = gain_variance + self.random_pitch_bounds = random_pitch_bounds + self.inner_cone_angle = inner_cone_angle + self.outer_cone_angle = outer_cone_angle + self.outer_cone_gain = outer_cone_gain + self.override_flags = override_flags + self.azimuth = azimuth + self.positional_gain = positional_gain + self.first_person_gain = first_person_gain + self.gain_modifier = gain_modifier + self.pitch_modifier = pitch_modifier + self.skip_fraction_modifier = skip_fraction_modifier + self.encoding = encoding + self.compression = compression + self.promotion_rules_tag_block = promotion_rules_tag_block + self.runtime_timers_tag_block = runtime_timers_tag_block + self.runtime_active_promotion_index = runtime_active_promotion_index + self.runtime_last_promotion_time = runtime_last_promotion_time + self.runtime_suppression_timeout = runtime_suppression_timeout + self.inner_silence_distance = inner_silence_distance + self.pitch_ranges_tag_block = pitch_ranges_tag_block + self.platform_parameters_tag_block = platform_parameters_tag_block + self.extra_sound_info_tag_block = extra_sound_info_tag_block + self.reflections_tag_block = reflections_tag_block + self.low_pass_minimum_distance = low_pass_minimum_distance + self.low_pass_maximum_distance = low_pass_maximum_distance + self.parameters = parameters + + class PitchRange: + def __init__(self, name="", name_length=0, natural_pitch=0, bend_bounds=(0, 0), unk_0=(0, 0), permutations_tag_block=None, permutations_header=None, permutations=None): + self.name = name + self.name_length = name_length + self.natural_pitch = natural_pitch + self.bend_bounds = bend_bounds + self.unk_0 = unk_0 + self.permutations_tag_block = permutations_tag_block + self.permutations_header = permutations_header + self.permutations = permutations + + class Permutation: + def __init__(self, name="", name_length=0, skip_fraction=0.0, gain=0.0, sound_data=0, sound_index=0, lipsync_data=0, sound_permutation_chunk_tag_block=None, + sound_permutation_chunk_header=None, sound_permutation_chunk=None): + self.name = name + self.name_length = name_length + self.skip_fraction = skip_fraction + self.gain = gain + self.sound_data = sound_data + self.sound_index = sound_index + self.lipsync_data = lipsync_data + self.sound_permutation_chunk_tag_block = sound_permutation_chunk_tag_block + self.sound_permutation_chunk_header = sound_permutation_chunk_header + self.sound_permutation_chunk = sound_permutation_chunk + + class SoundExtraInfo: + def __init__(self, language_permutation_info_tag_block=None, language_permutation_info_header=None, language_permutation_info=None, encoded_permutation_section_tag_block=None, + encoded_permutation_section_header=None, encoded_permutation_section=None, block_offset=0, block_size=0, section_data_size=0, resource_data_size=0, + resources_tag_block=None, resources_header=None, resources=None, owner_tag_section_offset=0): + self.language_permutation_info_tag_block = language_permutation_info_tag_block + self.language_permutation_info_header = language_permutation_info_header + self.language_permutation_info = language_permutation_info + self.encoded_permutation_section_tag_block = encoded_permutation_section_tag_block + self.encoded_permutation_section_header = encoded_permutation_section_header + self.encoded_permutation_section = encoded_permutation_section + self.block_offset = block_offset + self.block_size = block_size + self.section_data_size = section_data_size + self.resource_data_size = resource_data_size + self.resources_tag_block = resources_tag_block + self.resources_header = resources_header + self.resources = resources + self.owner_tag_section_offset = owner_tag_section_offset + + class SoundExtraInfo: + def __init__(self, language_permutation_info_tag_block=None, language_permutation_info_header=None, language_permutation_info=None, encoded_permutation_section_tag_block=None, + encoded_permutation_section_header=None, encoded_permutation_section=None, block_offset=0, block_size=0, section_data_size=0, resource_data_size=0, + resources_tag_block=None, resources_header=None, resources=None, owner_tag_section_offset=0, blok_header=None): + self.language_permutation_info_tag_block = language_permutation_info_tag_block + self.language_permutation_info_header = language_permutation_info_header + self.language_permutation_info = language_permutation_info + self.encoded_permutation_section_tag_block = encoded_permutation_section_tag_block + self.encoded_permutation_section_header = encoded_permutation_section_header + self.encoded_permutation_section = encoded_permutation_section + self.block_offset = block_offset + self.block_size = block_size + self.section_data_size = section_data_size + self.resource_data_size = resource_data_size + self.resources_tag_block = resources_tag_block + self.resources_header = resources_header + self.resources = resources + self.owner_tag_section_offset = owner_tag_section_offset + self.blok_header = blok_header + + class LanguagePermutationInfo: + def __init__(self, raw_info_block_tag_block=None, raw_info_block_header=None, raw_info_block=None): + self.raw_info_block_tag_block = raw_info_block_tag_block + self.raw_info_block_header = raw_info_block_header + self.raw_info_block = raw_info_block + + class RawInfoBlock: + def __init__(self, skip_fraction_name="", skip_fraction_name_length=0, samples_tag_data=None, unk_1_tag_data=None, unk_2_tag_data=None, markers_tag_block=None, + markers_header=None, markers=None, compression=0, language=0, sound_permutation_chunk_tag_block=None, sound_permutation_chunk_header=None, + sound_permutation_chunk=None, remaining_data=0): + self.skip_fraction_name = skip_fraction_name + self.skip_fraction_name_length = skip_fraction_name_length + self.samples_tag_data = samples_tag_data + self.unk_1_tag_data = unk_1_tag_data + self.unk_2_tag_data = unk_2_tag_data + self.markers_tag_block = markers_tag_block + self.markers_header = markers_header + self.markers = markers + self.compression = compression + self.language = language + self.sound_permutation_chunk_tag_block = sound_permutation_chunk_tag_block + self.sound_permutation_chunk_header = sound_permutation_chunk_header + self.sound_permutation_chunk = sound_permutation_chunk + self.remaining_data = remaining_data diff --git a/io_scene_halo/file_tag/h2/file_sound_environment/build_asset.py b/io_scene_halo/file_tag/h2/file_sound_environment/build_asset.py new file mode 100644 index 000000000..f8abec7e1 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_sound_environment/build_asset.py @@ -0,0 +1,61 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from ....global_functions import tag_format, shader_processing + +def write_body(output_stream, TAG, SOUNDENV): + SOUNDENV.sound_environment_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack('<4x')) + output_stream.write(struct.pack('I', len(SOUNDENV.sound_environment_body.reflection_type))) + output_stream.write(struct.pack('<12x')) + +def build_asset(output_stream, SOUNDENV, report): + TAG = tag_format.TagAsset() + TAG.is_legacy = False + TAG.big_endian = False + + SOUNDENV.header.write(output_stream, False, True) + write_body(output_stream, TAG, SOUNDENV) + + reflection_type_length = len(SOUNDENV.sound_environment_body.reflection_type) + if reflection_type_length > 0: + output_stream.write(struct.pack('<%ss' % reflection_type_length, TAG.string_to_bytes(SOUNDENV.sound_environment_body.reflection_type, False))) diff --git a/io_scene_halo/file_tag/h2/file_sound_environment/format.py b/io_scene_halo/file_tag/h2/file_sound_environment/format.py new file mode 100644 index 000000000..4adaff749 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_sound_environment/format.py @@ -0,0 +1,50 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +class SoundEnvironmentAsset(): + def __init__(self): + self.header = None + self.sound_environment_body_header = None + self.sound_environment_body = None + + class SoundEnvironmentBody: + def __init__(self, priority=0, room_intensity=0.0, room_intensity_hf=0.0, room_rolloff=0.0, decay_time=0.0, decay_hf_ratio=0.0, reflections_intensity=0.0, + reflections_delay=0.0, reverb_intensity=0.0, reverb_delay=0.0, diffusion=0.0, density=0.0, hf_reference=0.0, reflection_type="", reflection_type_length=0): + self.priority = priority + self.room_intensity = room_intensity + self.room_intensity_hf = room_intensity_hf + self.room_rolloff = room_rolloff + self.decay_time = decay_time + self.decay_hf_ratio = decay_hf_ratio + self.reflections_intensity = reflections_intensity + self.reflections_delay = reflections_delay + self.reverb_intensity = reverb_intensity + self.reverb_delay = reverb_delay + self.diffusion = diffusion + self.density = density + self.hf_reference = hf_reference + self.reflection_type = reflection_type + self.reflection_type_length = reflection_type_length diff --git a/io_scene_halo/file_tag/h2/file_sound_looping/build_asset.py b/io_scene_halo/file_tag/h2/file_sound_looping/build_asset.py new file mode 100644 index 000000000..25d756d63 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_sound_looping/build_asset.py @@ -0,0 +1,138 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from math import radians +from ....global_functions import tag_format, shader_processing +from ....file_tag.h2.file_shader.format import FunctionTypeEnum + +def write_body(output_stream, TAG, SOUNDLOOP): + SOUNDLOOP.sound_looping_body_header.write(output_stream, TAG, True) + SOUNDLOOP.sound_looping_body.remastered_looping_sound.write(output_stream, False, True) + output_stream.write(struct.pack(' 0: + tracks_header.write(output_stream, TAG, True) + for track_element in tracks: + output_stream.write(struct.pack('>I', len(track_element.name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(track_element.name, False))) + + in_sound_length = len(track_element.in_sound.name) + if in_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % in_sound_length, TAG.string_to_bytes(track_element.in_sound.name, False))) + + loop_sound_length = len(track_element.loop_sound.name) + if loop_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % loop_sound_length, TAG.string_to_bytes(track_element.loop_sound.name, False))) + + out_sound_length = len(track_element.out_sound.name) + if out_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % out_sound_length, TAG.string_to_bytes(track_element.out_sound.name, False))) + + alt_loop_length = len(track_element.alt_loop.name) + if alt_loop_length > 0: + output_stream.write(struct.pack('<%ssx' % alt_loop_length, TAG.string_to_bytes(track_element.alt_loop.name, False))) + + alt_out_length = len(track_element.alt_out.name) + if alt_out_length > 0: + output_stream.write(struct.pack('<%ssx' % alt_out_length, TAG.string_to_bytes(track_element.alt_out.name, False))) + + alt_trans_in_length = len(track_element.alt_trans_in.name) + if alt_trans_in_length > 0: + output_stream.write(struct.pack('<%ssx' % alt_trans_in_length, TAG.string_to_bytes(track_element.alt_trans_in.name, False))) + + alt_trans_out_length = len(track_element.alt_trans_out.name) + if alt_trans_out_length > 0: + output_stream.write(struct.pack('<%ssx' % alt_trans_out_length, TAG.string_to_bytes(track_element.alt_trans_out.name, False))) + +def write_detail_sounds(output_stream, TAG, detail_sounds, detail_sounds_header): + if len(detail_sounds) > 0: + detail_sounds_header.write(output_stream, TAG, True) + for detail_sound_element in detail_sounds: + output_stream.write(struct.pack('>I', len(detail_sound_element.name))) + detail_sound_element.sound.write(output_stream, False, True) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(detail_sound_element.name, False))) + + sound_length = len(detail_sound_element.sound.name) + if sound_length > 0: + output_stream.write(struct.pack('<%ssx' % sound_length, TAG.string_to_bytes(detail_sound_element.sound.name, False))) + +def build_asset(output_stream, SOUNDLOOP, report): + TAG = tag_format.TagAsset() + TAG.is_legacy = False + TAG.big_endian = False + + SOUNDLOOP.header.write(output_stream, False, True) + write_body(output_stream, TAG, SOUNDLOOP) + + remastered_looping_sound_length = len(SOUNDLOOP.sound_looping_body.remastered_looping_sound.name) + if remastered_looping_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % remastered_looping_sound_length, TAG.string_to_bytes(SOUNDLOOP.sound_looping_body.remastered_looping_sound.name, False))) + + unk_1_length = len(SOUNDLOOP.sound_looping_body.unk_1.name) + if unk_1_length > 0: + output_stream.write(struct.pack('<%ssx' % unk_1_length, TAG.string_to_bytes(SOUNDLOOP.sound_looping_body.unk_1.name, False))) + + write_tracks(output_stream, TAG, SOUNDLOOP.tracks, SOUNDLOOP.tracks_header) + write_detail_sounds(output_stream, TAG, SOUNDLOOP.detail_sounds, SOUNDLOOP.detail_sounds_header) + diff --git a/io_scene_halo/file_tag/h2/file_sound_looping/format.py b/io_scene_halo/file_tag/h2/file_sound_looping/format.py new file mode 100644 index 000000000..151b2f2be --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_sound_looping/format.py @@ -0,0 +1,81 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto + +class SoundLoopingAsset(): + def __init__(self): + self.header = None + self.sound_looping_body_header = None + self.sound_looping_body = None + self.tracks_header = None + self.tracks = None + self.detail_sounds_header = None + self.detail_sounds = None + + class SoundLoopingBody: + def __init__(self, remastered_looping_sound=None, flags=0, martys_music_time=0.0, unk_0=0.0, unk_1=None, tracks_tag_block=None, detail_sounds_tag_block=None): + self.remastered_looping_sound = remastered_looping_sound + self.flags = flags + self.martys_music_time = martys_music_time + self.unk_0 = unk_0 + self.unk_1 = unk_1 + self.tracks_tag_block = tracks_tag_block + self.detail_sounds_tag_block = detail_sounds_tag_block + + class Track: + def __init__(self, name="", name_length=0, flags=0, gain=0.0, fade_in_duration=0.0, fade_out_duration=0.0, in_sound=None, loop_sound=None, out_sound=None, alt_loop=None, + alt_out=None, output_effect=0, alt_trans_in=None, alt_trans_out=None, alt_crossfade_duration=0.0, alt_fade_out_duration=0.0): + self.name = name + self.name_length = name_length + self.flags = flags + self.gain = gain + self.fade_in_duration = fade_in_duration + self.fade_out_duration = fade_out_duration + self.in_sound = in_sound + self.loop_sound = loop_sound + self.out_sound = out_sound + self.alt_loop = alt_loop + self.alt_out = alt_out + self.output_effect = output_effect + self.alt_trans_in = alt_trans_in + self.alt_trans_out = alt_trans_out + self.alt_crossfade_duration = alt_crossfade_duration + self.alt_fade_out_duration = alt_fade_out_duration + + class DetailSound: + def __init__(self, name="", name_length=0, sound=None, random_period_bounds=(0.0, 0.0), unk_0=0.0, flags=0, yaw_bounds=(0.0, 0.0), pitch_bounds=(0.0, 0.0), + distance_bounds=(0.0, 0.0)): + self.name = name + self.name_length = name_length + self.sound = sound + self.random_period_bounds = random_period_bounds + self.unk_0 = unk_0 + self.flags = flags + self.yaw_bounds = yaw_bounds + self.pitch_bounds = pitch_bounds + self.distance_bounds = distance_bounds diff --git a/io_scene_halo/file_tag/h2/file_sound_scenery/format.py b/io_scene_halo/file_tag/h2/file_sound_scenery/format.py index 02be48fc0..913e6dc74 100644 --- a/io_scene_halo/file_tag/h2/file_sound_scenery/format.py +++ b/io_scene_halo/file_tag/h2/file_sound_scenery/format.py @@ -24,92 +24,16 @@ # # ##### END MIT LICENSE BLOCK ##### -from mathutils import Vector -from enum import Flag, Enum, auto +from ..file_object.format import ObjectAsset -class ObjectFlags(Flag): - does_not_cast_shadow = auto() - search_cardinal_direction_lightmaps_on_failure = auto() - unused = auto() - not_a_pathfinding_obstacle = auto() - extension_of_parent = auto() - does_not_cause_collision_damage = auto() - early_mover = auto() - early_mover_localized_physics = auto() - use_static_massive_lightmap_sample = auto() - object_scales_attachments = auto() - inherits_players_appearance = auto() - dead_bipdes_cant_localize = auto() - attach_to_clusters_by_dynamic_sphere = auto() - effects_created_by_this_object_do_not_spawn_objects_in_multiplayer = auto() - prophet_is_not_displayed_pegasus_builds = auto() - -class LightmapShadowModeEnum(Enum): - default = 0 - never = auto() - always = auto() - -class SweetenerSizeEnum(Enum): - small = 0 - medium = auto() - large = auto() - -class SoundSceneryAsset(): +class SoundSceneryAsset(ObjectAsset): def __init__(self): + super().__init__() self.header = None self.sound_scenery_body_header = None self.sound_scenery_body = None - self.ai_properties_header = None - self.ai_properties = None - self.functions_header = None - self.functions = None - self.attachments_header = None - self.attachments = None - self.widgets_header = None - self.widgets = None - self.old_functions_header = None - self.old_functions = None - self.change_colors_header = None - self.change_colors = None - self.predicted_resources_header = None - self.predicted_resources = None - class SoundSceneryBody: - def __init__(self, object_flags=0, bounding_radius=0.0, bounding_offset=Vector(), acceleration_scale=0.0, lightmap_shadow_mode=0, sweetner_size=0, - dynamic_light_sphere_radius=0.0, dynamic_light_sphere_offset=Vector(), default_model_variant="", default_model_variant_length=0, model=None, crate_object=None, - modifier_shader=None, creation_effect=None, material_effects=None, ai_properties_tag_block=None, functions_tag_block=None, apply_collision_damage_scale=0.0, - min_game_acc=0.0, max_game_acc=0.0, min_game_scale=0.0, max_game_scale=0.0, min_abs_acc=0.0, max_abs_acc=0.0, min_abs_scale=0.0, max_abs_scale=0.0, - hud_text_message_index=0, attachments_tag_block=None, widgets_tag_block=None, old_functions_tag_block=None, change_colors_tag_block=None, - predicted_resources_tag_block=None): - self.object_flags = object_flags - self.bounding_radius = bounding_radius - self.bounding_offset = bounding_offset - self.acceleration_scale = acceleration_scale - self.lightmap_shadow_mode = lightmap_shadow_mode - self.sweetner_size = sweetner_size - self.dynamic_light_sphere_radius = dynamic_light_sphere_radius - self.dynamic_light_sphere_offset = dynamic_light_sphere_offset - self.default_model_variant = default_model_variant - self.default_model_variant_length = default_model_variant_length - self.model = model - self.crate_object = crate_object - self.modifier_shader = modifier_shader - self.creation_effect = creation_effect - self.material_effects = material_effects - self.ai_properties_tag_block = ai_properties_tag_block - self.functions_tag_block = functions_tag_block - self.apply_collision_damage_scale = apply_collision_damage_scale - self.min_game_acc = min_game_acc - self.max_game_acc = max_game_acc - self.min_game_scale = min_game_scale - self.max_game_scale = max_game_scale - self.min_abs_acc = min_abs_acc - self.max_abs_acc = max_abs_acc - self.min_abs_scale = min_abs_scale - self.max_abs_scale = max_abs_scale - self.hud_text_message_index = hud_text_message_index - self.attachments_tag_block = attachments_tag_block - self.widgets_tag_block = widgets_tag_block - self.old_functions_tag_block = old_functions_tag_block - self.change_colors_tag_block = change_colors_tag_block - self.predicted_resources_tag_block = predicted_resources_tag_block + class SoundSceneryBody(ObjectAsset.ObjectBody): + def __init__(self): + super().__init__() + diff --git a/io_scene_halo/file_tag/h2/file_sound_scenery/process_file.py b/io_scene_halo/file_tag/h2/file_sound_scenery/process_file.py index a21d2af1e..014533271 100644 --- a/io_scene_halo/file_tag/h2/file_sound_scenery/process_file.py +++ b/io_scene_halo/file_tag/h2/file_sound_scenery/process_file.py @@ -26,12 +26,8 @@ from xml.dom import minidom from ....global_functions import tag_format -from .format import ( - SoundSceneryAsset, - ObjectFlags, - LightmapShadowModeEnum, - SweetenerSizeEnum, - ) +from ..file_object.format import ObjectFlags, LightmapShadowModeEnum, SweetenerSizeEnum +from .format import SoundSceneryAsset XML_OUTPUT = False diff --git a/io_scene_halo/file_tag/h2/file_unit/build_asset.py b/io_scene_halo/file_tag/h2/file_unit/build_asset.py new file mode 100644 index 000000000..acfa6fc06 --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_unit/build_asset.py @@ -0,0 +1,156 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from math import radians +from ....global_functions import shader_processing +from ..file_object.build_asset import write_tag_ref + +def write_postures(output_stream, TAG, postures, postures_header): + if len(postures) > 0: + postures_header.write(output_stream, TAG, True) + for posture_element in postures: + output_stream.write(struct.pack('>I', len(posture_element.name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(posture_element.name, False))) + +def write_dialogue_variant(output_stream, TAG, dialogue_variants, dialogue_variants_header): + if len(dialogue_variants) > 0: + dialogue_variants_header.write(output_stream, TAG, True) + for dialogue_variant_element in dialogue_variants: + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % dialogue_length, TAG.string_to_bytes(dialogue_variant_element.dialogue.name, False))) + +def write_powered_seats(output_stream, TAG, powered_seats, powered_seats_header): + if len(powered_seats) > 0: + powered_seats_header.write(output_stream, TAG, True) + for powered_seat_element in powered_seats: + output_stream.write(struct.pack(' 0: + seats_header.write(output_stream, TAG, True) + for seat_element in seats: + output_stream.write(struct.pack('I', len(seat_element.label))) + output_stream.write(struct.pack('>I', len(seat_element.marker_name))) + output_stream.write(struct.pack('>I', len(seat_element.entry_marker_name))) + output_stream.write(struct.pack('>I', len(seat_element.boarding_grenade_marker))) + output_stream.write(struct.pack('>I', len(seat_element.boarding_grenade_string))) + output_stream.write(struct.pack('>I', len(seat_element.boarding_melee_string))) + output_stream.write(struct.pack('I', len(seat_element.camera_marker_name))) + output_stream.write(struct.pack('>I', len(seat_element.camera_submerged_marker_name))) + output_stream.write(struct.pack('I', len(seat_element.enter_seat_string))) + output_stream.write(struct.pack('I', len(seat_element.invisible_seat_region))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % label_length, TAG.string_to_bytes(seat_element.label, False))) + + marker_name_length = len(seat_element.marker_name) + if marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % marker_name_length, TAG.string_to_bytes(seat_element.marker_name, False))) + + entry_marker_name_length = len(seat_element.entry_marker_name) + if entry_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % entry_marker_name_length, TAG.string_to_bytes(seat_element.entry_marker_name, False))) + + boarding_grenade_marker_length = len(seat_element.boarding_grenade_marker) + if boarding_grenade_marker_length > 0: + output_stream.write(struct.pack('<%ss' % boarding_grenade_marker_length, TAG.string_to_bytes(seat_element.boarding_grenade_marker, False))) + + boarding_grenade_string_length = len(seat_element.boarding_grenade_string) + if boarding_grenade_string_length > 0: + output_stream.write(struct.pack('<%ss' % boarding_grenade_string_length, TAG.string_to_bytes(seat_element.boarding_grenade_string, False))) + + boarding_melee_string_length = len(seat_element.boarding_melee_string) + if boarding_melee_string_length > 0: + output_stream.write(struct.pack('<%ss' % boarding_melee_string_length, TAG.string_to_bytes(seat_element.boarding_melee_string, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("usas", True), 0, 1, 20)) + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("uncs", True), 0, 1, 32)) + + camera_marker_name_length = len(seat_element.camera_marker_name) + if camera_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % camera_marker_name_length, TAG.string_to_bytes(seat_element.camera_marker_name, False))) + + camera_submerged_marker_name_length = len(seat_element.camera_submerged_marker_name) + if camera_submerged_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % camera_submerged_marker_name_length, TAG.string_to_bytes(seat_element.camera_submerged_marker_name, False))) + + write_tag_ref(output_stream, TAG, seat_element.camera_tracks, seat_element.camera_tracks_header) + write_tag_ref(output_stream, TAG, seat_element.unit_hud_interface, seat_element.unit_hud_interface_header) + + enter_seat_string_length = len(seat_element.enter_seat_string) + if enter_seat_string_length > 0: + output_stream.write(struct.pack('<%ss' % enter_seat_string_length, TAG.string_to_bytes(seat_element.enter_seat_string, False))) + + built_in_gunner_length = len(seat_element.built_in_gunner.name) + if built_in_gunner_length > 0: + output_stream.write(struct.pack('<%ssx' % built_in_gunner_length, TAG.string_to_bytes(seat_element.built_in_gunner.name, False))) + + invisible_seat_region_length = len(seat_element.invisible_seat_region) + if invisible_seat_region_length > 0: + output_stream.write(struct.pack('<%ss' % invisible_seat_region_length, TAG.string_to_bytes(seat_element.invisible_seat_region, False))) diff --git a/io_scene_halo/file_tag/h2/file_unit/format.py b/io_scene_halo/file_tag/h2/file_unit/format.py new file mode 100644 index 000000000..ac5d4356d --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_unit/format.py @@ -0,0 +1,331 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +from mathutils import Vector +from enum import Flag, Enum, auto +from ..file_object.format import ObjectAsset + +class UnitFlags(Flag): + circular_aiming = auto() + destroyed_after_dying = auto() + half_speed_interpolation = auto() + fires_from_camera = auto() + entrance_inside_bounding_sphere = auto() + doesnt_show_readid_weapon = auto() + causes_passenger_dialogue = auto() + resists_ping = auto() + melee_attack_is_fatal = auto() + dont_reface_during_pings = auto() + has_no_aiming = auto() + simple_creature = auto() + impact_melee_attaches_to_unit = auto() + impact_melee_dies_on_shield = auto() + cannot_open_doors_automatically = auto() + melee_attackers_cannot_attach = auto() + not_instantly_killed_by_melee = auto() + unused_17 = auto() + runs_around_flaming = auto() + inconsequential = auto() + special_cinematic_unit = auto() + ignored_by_autoaiming = auto() + shields_fry_infection_forms = auto() + unused_23 = auto() + unused_24 = auto() + acts_as_gunner_for_parent = auto() + controlled_by_parent_gunner = auto() + parents_primary_weapon = auto() + unit_has_boost = auto() + +class TeamsEnum(Enum): + default = 0 + player = auto() + human = auto() + covenant = auto() + flood = auto() + sentinel = auto() + heretic = auto() + prophet = auto() + unused_8 = auto() + unused_9 = auto() + unused_10 = auto() + unused_11 = auto() + unused_12 = auto() + unused_13 = auto() + unused_14 = auto() + unused_15 = auto() + +class ConstantSoundVolumeEnum(Enum): + silent = 0 + medium = auto() + loud = auto() + shout = auto() + quiet = auto() + +class MotionSensorBlipSizeEnum(Enum): + medium = 0 + small = auto() + large = auto() + +class MetaGameTypeEnum(Enum): + brute = 0 + grunt = auto() + jackal = auto() + skirmisher = auto() + marine = auto() + spartan = auto() + bugger = auto() + hunter = auto() + flood_infection = auto() + flood_carrier = auto() + flood_combat = auto() + flood_pure = auto() + sentinel = auto() + elite = auto() + engineer = auto() + mule = auto() + turret = auto() + mongoose = auto() + warthog = auto() + scorpion = auto() + hornet = auto() + pelican = auto() + revenant = auto() + seraph = auto() + shade = auto() + watchtower = auto() + ghost = auto() + chopper = auto() + mauler = auto() + wraith = auto() + banshee = auto() + phantom = auto() + scarab = auto() + guntower = auto() + tuning_fork = auto() + broadsword = auto() + mammoth = auto() + lich = auto() + mantis = auto() + wasp = auto() + phaeton = auto() + bishop = auto() + knight = auto() + pawn = auto() + +class MetaGameClassEnum(Enum): + infantry = 0 + leader = auto() + hero = auto() + specialist = auto() + light_vehicle = auto() + heavy_vehicle = auto() + giant_vehicle = auto() + standard_vehicle = auto() + +class GrenadeTypeEnum(Enum): + human_fragmentation = 0 + covenant_plasma = auto() + +class UnitAsset(ObjectAsset): + def __init__(self): + super().__init__() + self.header = None + self.unit_body_header = None + self.unit_body = None + self.camera_tracks_header = None + self.camera_tracks = None + self.postures_header = None + self.postures = None + self.new_hud_interface_header = None + self.new_hud_interface = None + self.dialogue_variants_header = None + self.dialogue_variants = None + self.powered_seats_header = None + self.powered_seats = None + self.weapons_header = None + self.weapons = None + self.seats_header = None + self.seats = None + + class UnitBody(ObjectAsset.ObjectBody): + def __init__(self, unit_flags=0, default_team=0, constant_sound_volume=0, integrated_light_toggle=None, camera_field_of_view=0.0, camera_stiffness=0.0, + camera_marker_name="", camera_marker_name_length=0, camera_submerged_marker_name="", camera_submerged_marker_name_length=0, pitch_auto_level=0.0, + pitch_range=(0.0, 0.0), camera_tracks_tag_block=None, acceleration_range=Vector(), acceleration_action_scale=0.0, acceleration_attach_scale=0.0, + soft_ping_threshold=0.0, soft_ping_interrupt_time=0.0, hard_ping_threshold=0.0, hard_ping_interrupt_time=0.0, hard_death_threshold=0.0, + feign_death_threshold=0.0, feign_death_time=0.0, distance_of_evade_anim=0.0, distance_of_dive_anim=0.0, stunned_movement_threshold=0.0, + feign_death_chance=0.0, feign_repeat_chance=0.0, spawned_turret_actor=None, spawned_actor_count=(0, 0), spawned_velocity=0.0, aiming_velocity_maximum=0.0, + aiming_acceleration_maximum=0.0, casual_aiming_modifier=0.0, looking_velocity_maximum=0.0, looking_acceleration_maximum=0.0, right_hand_node="", + right_hand_node_length=0, left_hand_node="", left_hand_node_length=0, preferred_gun_node="", preferred_gun_node_length=0, melee_damage=None, + boarding_melee_damage=None, boarding_melee_response=None, landing_melee_damage=None, flurry_melee_damage=None, obstacle_smash_damage=None, + motion_sensor_blip_size=0, unit_type=0, unit_class=0, postures_tag_block=None, new_hud_interfaces_tag_block=None, dialogue_variants_tag_block=None, + grenade_velocity=0.0, grenade_type=0, grenade_count=0, powered_seats_tag_block=None, weapons_tag_block=None, seats_tag_block=None, boost_peak_power=0.0, + boost_rise_power=0.0, boost_peak_time=0.0, boost_fall_power=0.0, dead_time=0.0, attack_weight=0.0, decay_weight=0.0): + super().__init__() + self.unit_flags = unit_flags + self.default_team = default_team + self.constant_sound_volume = constant_sound_volume + self.integrated_light_toggle = integrated_light_toggle + self.camera_field_of_view = camera_field_of_view + self.camera_stiffness = camera_stiffness + self.camera_marker_name = camera_marker_name + self.camera_marker_name_length = camera_marker_name_length + self.camera_submerged_marker_name = camera_submerged_marker_name + self.camera_submerged_marker_name_length = camera_submerged_marker_name_length + self.pitch_auto_level = pitch_auto_level + self.pitch_range = pitch_range + self.camera_tracks_tag_block = camera_tracks_tag_block + self.acceleration_range = acceleration_range + self.acceleration_action_scale = acceleration_action_scale + self.acceleration_attach_scale = acceleration_attach_scale + self.soft_ping_threshold = soft_ping_threshold + self.soft_ping_interrupt_time = soft_ping_interrupt_time + self.hard_ping_threshold = hard_ping_threshold + self.hard_ping_interrupt_time = hard_ping_interrupt_time + self.hard_death_threshold = hard_death_threshold + self.feign_death_threshold = feign_death_threshold + self.feign_death_time = feign_death_time + self.distance_of_evade_anim = distance_of_evade_anim + self.distance_of_dive_anim = distance_of_dive_anim + self.stunned_movement_threshold = stunned_movement_threshold + self.feign_death_chance = feign_death_chance + self.feign_repeat_chance = feign_repeat_chance + self.spawned_turret_actor = spawned_turret_actor + self.spawned_actor_count = spawned_actor_count + self.spawned_velocity = spawned_velocity + self.aiming_velocity_maximum = aiming_velocity_maximum + self.aiming_acceleration_maximum = aiming_acceleration_maximum + self.casual_aiming_modifier = casual_aiming_modifier + self.looking_velocity_maximum = looking_velocity_maximum + self.looking_acceleration_maximum = looking_acceleration_maximum + self.right_hand_node = right_hand_node + self.right_hand_node_length = right_hand_node_length + self.left_hand_node = left_hand_node + self.left_hand_node_length = left_hand_node_length + self.preferred_gun_node = preferred_gun_node + self.preferred_gun_node_length = preferred_gun_node_length + self.melee_damage = melee_damage + self.boarding_melee_damage = boarding_melee_damage + self.boarding_melee_response = boarding_melee_response + self.landing_melee_damage = landing_melee_damage + self.flurry_melee_damage = flurry_melee_damage + self.obstacle_smash_damage = obstacle_smash_damage + self.motion_sensor_blip_size = motion_sensor_blip_size + self.unit_type = unit_type + self.unit_class = unit_class + self.postures_tag_block = postures_tag_block + self.new_hud_interfaces_tag_block = new_hud_interfaces_tag_block + self.dialogue_variants_tag_block = dialogue_variants_tag_block + self.grenade_velocity = grenade_velocity + self.grenade_type = grenade_type + self.grenade_count = grenade_count + self.powered_seats_tag_block = powered_seats_tag_block + self.weapons_tag_block = weapons_tag_block + self.seats_tag_block = seats_tag_block + self.boost_peak_power = boost_peak_power + self.boost_rise_power = boost_rise_power + self.boost_peak_time = boost_peak_time + self.boost_fall_power = boost_fall_power + self.dead_time = dead_time + self.attack_weight = attack_weight + self.decay_weight = decay_weight + + class Posture: + def __init__(self, name="", name_length=0, pill_offset=Vector()): + self.name = name + self.name_length = name_length + self.pill_offset = pill_offset + + class DialogueVariant: + def __init__(self, variant_number=0, dialogue=None): + self.variant_number = variant_number + self.dialogue = dialogue + + class PoweredSeat: + def __init__(self, driver_powerup_time=0.0, driver_powerdown_time=0.0): + self.driver_powerup_time = driver_powerup_time + self.driver_powerdown_time = driver_powerdown_time + + class Seat: + def __init__(self, flags=0, label="", label_length=0, marker_name="", marker_name_length=0, entry_marker_name="", entry_marker_name_length=0, boarding_grenade_marker="", + boarding_grenade_marker_length=0, boarding_grenade_string="", boarding_grenade_string_length=0, boarding_melee_string="", boarding_melee_string_length=0, + ping_scale=0.0, turnover_time=0.0, acceleration_range=Vector(), acceleration_action_scale=0.0, acceleration_attach_scale=0.0, ai_scariness=0.0, + ai_seat_type=0, boarding_seat=-1, listener_interpolation_factor=0.0, yaw_rate_bounds=(0.0, 0.0), pitch_rate_bounds=(0.0, 0.0), min_speed_reference=0.0, + max_speed_reference=0.0, speed_exponent=0.0, camera_marker_name="", camera_marker_name_length=0, camera_submerged_marker_name="", + camera_submerged_marker_name_length=0, pitch_auto_level=0.0, pitch_range=(0.0, 0.0), camera_tracks_tag_block=None, camera_tracks_header=None, camera_tracks=None, + unit_hud_interface_tag_block=None, unit_hud_interface_header=None, unit_hud_interface=None, enter_seat_string="", enter_seat_string_length=0, yaw_minimum=0.0, + yaw_maximum=0.0, built_in_gunner=None, entry_radius=0.0, entry_marker_cone_angle=0.0, entry_marker_facing_angle=0.0, maximum_relative_velocity=0.0, + invisible_seat_region="", invisible_seat_region_length=0, runtime_invisible_seat_region_index=0): + self.flags = flags + self.label = label + self.label_length = label_length + self.marker_name = marker_name + self.marker_name_length = marker_name_length + self.entry_marker_name = entry_marker_name + self.entry_marker_name_length = entry_marker_name_length + self.boarding_grenade_marker = boarding_grenade_marker + self.boarding_grenade_marker_length = boarding_grenade_marker_length + self.boarding_grenade_string = boarding_grenade_string + self.boarding_grenade_string_length = boarding_grenade_string_length + self.boarding_melee_string = boarding_melee_string + self.boarding_melee_string_length = boarding_melee_string_length + self.ping_scale = ping_scale + self.turnover_time = turnover_time + self.acceleration_range = acceleration_range + self.acceleration_action_scale = acceleration_action_scale + self.acceleration_attach_scale = acceleration_attach_scale + self.ai_scariness = ai_scariness + self.ai_seat_type = ai_seat_type + self.boarding_seat = boarding_seat + self.listener_interpolation_factor = listener_interpolation_factor + self.yaw_rate_bounds = yaw_rate_bounds + self.pitch_rate_bounds = pitch_rate_bounds + self.min_speed_reference = min_speed_reference + self.max_speed_reference = max_speed_reference + self.speed_exponent = speed_exponent + self.camera_marker_name = camera_marker_name + self.camera_marker_name_length = camera_marker_name_length + self.camera_submerged_marker_name = camera_submerged_marker_name + self.camera_submerged_marker_name_length = camera_submerged_marker_name_length + self.pitch_auto_level = pitch_auto_level + self.pitch_range = pitch_range + self.camera_tracks_tag_block = camera_tracks_tag_block + self.camera_tracks_header = camera_tracks_header + self.camera_tracks = camera_tracks + self.unit_hud_interface_tag_block = unit_hud_interface_tag_block + self.unit_hud_interface_header = unit_hud_interface_header + self.unit_hud_interface = unit_hud_interface + self.enter_seat_string = enter_seat_string + self.enter_seat_string_length = enter_seat_string_length + self.yaw_minimum = yaw_minimum + self.yaw_maximum = yaw_maximum + self.built_in_gunner = built_in_gunner + self.entry_radius = entry_radius + self.entry_marker_cone_angle = entry_marker_cone_angle + self.entry_marker_facing_angle = entry_marker_facing_angle + self.maximum_relative_velocity = maximum_relative_velocity + self.invisible_seat_region = invisible_seat_region + self.invisible_seat_region_length = invisible_seat_region_length + self.runtime_invisible_seat_region_index = runtime_invisible_seat_region_index diff --git a/io_scene_halo/file_tag/h2/file_vehicle/build_asset.py b/io_scene_halo/file_tag/h2/file_vehicle/build_asset.py new file mode 100644 index 000000000..36852573d --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_vehicle/build_asset.py @@ -0,0 +1,485 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from math import radians +from ....global_functions import tag_format, shader_processing +from ....file_tag.h2.file_shader.format import FunctionTypeEnum +from ..file_object.build_asset import ( + write_ai_properties, + write_functions, + write_attachments, + write_tag_ref, + write_old_functions, + write_change_colors, + write_predicted_resources + ) +from ..file_unit.build_asset import ( + write_postures, + write_dialogue_variant, + write_powered_seats, + write_seats + ) + +def write_body(output_stream, TAG, VEHICLE): + VEHICLE.vehicle_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack('I', len(VEHICLE.vehicle_body.default_model_variant))) + VEHICLE.vehicle_body.model.write(output_stream, False, True) + VEHICLE.vehicle_body.crate_object.write(output_stream, False, True) + VEHICLE.vehicle_body.modifier_shader.write(output_stream, False, True) + VEHICLE.vehicle_body.creation_effect.write(output_stream, False, True) + VEHICLE.vehicle_body.material_effects.write(output_stream, False, True) + VEHICLE.vehicle_body.ai_properties_tag_block.write(output_stream, False) + VEHICLE.vehicle_body.functions_tag_block.write(output_stream, False) + output_stream.write(struct.pack('I', len(VEHICLE.vehicle_body.camera_marker_name))) + output_stream.write(struct.pack('>I', len(VEHICLE.vehicle_body.camera_submerged_marker_name))) + output_stream.write(struct.pack('I', len(VEHICLE.vehicle_body.right_hand_node))) + output_stream.write(struct.pack('>I', len(VEHICLE.vehicle_body.left_hand_node))) + output_stream.write(struct.pack('>I', len(VEHICLE.vehicle_body.preferred_gun_node))) + VEHICLE.vehicle_body.melee_damage.write(output_stream, False, True) + VEHICLE.vehicle_body.boarding_melee_damage.write(output_stream, False, True) + VEHICLE.vehicle_body.boarding_melee_response.write(output_stream, False, True) + VEHICLE.vehicle_body.landing_melee_damage.write(output_stream, False, True) + VEHICLE.vehicle_body.flurry_melee_damage.write(output_stream, False, True) + VEHICLE.vehicle_body.obstacle_smash_damage.write(output_stream, False, True) + output_stream.write(struct.pack('I', len(VEHICLE.vehicle_body.flip_message))) + output_stream.write(struct.pack(' 0: + gears_header.write(output_stream, TAG, True) + for gear_element in gears: + output_stream.write(struct.pack(' 0: + anti_gravity_point_header.write(output_stream, TAG, True) + for anti_gravity_point_element in anti_gravity_point: + output_stream.write(struct.pack('>I', len(anti_gravity_point_element.marker_name))) + output_stream.write(struct.pack('I', len(anti_gravity_point_element.damage_source_region_name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % marker_name_length, TAG.string_to_bytes(anti_gravity_point_element.marker_name, False))) + + damage_source_region_name_length = len(anti_gravity_point_element.damage_source_region_name) + if damage_source_region_name_length > 0: + output_stream.write(struct.pack('<%ss' % damage_source_region_name_length, TAG.string_to_bytes(anti_gravity_point_element.damage_source_region_name, False))) + +def write_friction_points(output_stream, TAG, friction_points, friction_points_header): + if len(friction_points) > 0: + friction_points_header.write(output_stream, TAG, True) + for friction_point_element in friction_points: + output_stream.write(struct.pack('>I', len(friction_point_element.marker_name))) + output_stream.write(struct.pack('I', len(friction_point_element.collision_global_material_name))) + output_stream.write(struct.pack('<2x')) + output_stream.write(struct.pack('I', len(friction_point_element.region_name))) + output_stream.write(struct.pack('<4x')) + + for friction_point_element in friction_points: + marker_name_length = len(friction_point_element.marker_name) + if marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % marker_name_length, TAG.string_to_bytes(friction_point_element.marker_name, False))) + + collision_global_material_name_length = len(friction_point_element.collision_global_material_name) + if collision_global_material_name_length > 0: + output_stream.write(struct.pack('<%ss' % collision_global_material_name_length, TAG.string_to_bytes(friction_point_element.collision_global_material_name, False))) + + region_name_length = len(friction_point_element.region_name) + if region_name_length > 0: + output_stream.write(struct.pack('<%ss' % region_name_length, TAG.string_to_bytes(friction_point_element.region_name, False))) + +def write_phantom_shapes(output_stream, TAG, phantom_shapes, phantom_shapes_header): + if len(phantom_shapes) > 0: + phantom_shapes_header.write(output_stream, TAG, True) + for phantom_shape_element in phantom_shapes: + output_stream.write(struct.pack('<4x')) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % default_model_variant_name_length, TAG.string_to_bytes(VEHICLE.vehicle_body.default_model_variant, False))) + + model_name_length = len(VEHICLE.vehicle_body.model.name) + if model_name_length > 0: + output_stream.write(struct.pack('<%ssx' % model_name_length, TAG.string_to_bytes(VEHICLE.vehicle_body.model.name, False))) + + crate_object_name_length = len(VEHICLE.vehicle_body.crate_object.name) + if crate_object_name_length > 0: + output_stream.write(struct.pack('<%ssx' % crate_object_name_length, TAG.string_to_bytes(VEHICLE.vehicle_body.crate_object.name, False))) + + modifier_shader_name_length = len(VEHICLE.vehicle_body.modifier_shader.name) + if modifier_shader_name_length > 0: + output_stream.write(struct.pack('<%ssx' % modifier_shader_name_length, TAG.string_to_bytes(VEHICLE.vehicle_body.modifier_shader.name, False))) + + creation_effect_name_length = len(VEHICLE.vehicle_body.creation_effect.name) + if creation_effect_name_length > 0: + output_stream.write(struct.pack('<%ssx' % creation_effect_name_length, TAG.string_to_bytes(VEHICLE.vehicle_body.creation_effect.name, False))) + + material_effects_name_length = len(VEHICLE.vehicle_body.material_effects.name) + if material_effects_name_length > 0: + output_stream.write(struct.pack('<%ssx' % material_effects_name_length, TAG.string_to_bytes(VEHICLE.vehicle_body.material_effects.name, False))) + + write_ai_properties(output_stream, TAG, VEHICLE.ai_properties, VEHICLE.ai_properties_header) + write_functions(output_stream, TAG, VEHICLE.functions, VEHICLE.functions_header) + write_attachments(output_stream, TAG, VEHICLE.attachments, VEHICLE.attachments_header) + write_tag_ref(output_stream, TAG, VEHICLE.widgets, VEHICLE.widgets_header) + write_old_functions(output_stream, TAG, VEHICLE.old_functions, VEHICLE.old_functions_header) + write_change_colors(output_stream, TAG, VEHICLE.change_colors, VEHICLE.change_colors_header) + write_predicted_resources(output_stream, TAG, VEHICLE.predicted_resources, VEHICLE.predicted_resources_header) + + integrated_light_toggle_length = len(VEHICLE.vehicle_body.integrated_light_toggle.name) + if integrated_light_toggle_length > 0: + output_stream.write(struct.pack('<%ssx' % integrated_light_toggle_length, TAG.string_to_bytes(VEHICLE.vehicle_body.integrated_light_toggle.name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("uncs", True), 0, 1, 32)) + + camera_marker_name_length = len(VEHICLE.vehicle_body.camera_marker_name) + if camera_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % camera_marker_name_length, TAG.string_to_bytes(VEHICLE.vehicle_body.camera_marker_name, False))) + + camera_submerged_marker_name_length = len(VEHICLE.vehicle_body.camera_submerged_marker_name) + if camera_submerged_marker_name_length > 0: + output_stream.write(struct.pack('<%ss' % camera_submerged_marker_name_length, TAG.string_to_bytes(VEHICLE.vehicle_body.camera_submerged_marker_name, False))) + + write_tag_ref(output_stream, TAG, VEHICLE.camera_tracks, VEHICLE.camera_tracks_header) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("usas", True), 0, 1, 20)) + + spawned_turret_actor_length = len(VEHICLE.vehicle_body.spawned_turret_actor.name) + if spawned_turret_actor_length > 0: + output_stream.write(struct.pack('<%ssx' % spawned_turret_actor_length, TAG.string_to_bytes(VEHICLE.vehicle_body.spawned_turret_actor.name, False))) + + right_hand_node_length = len(VEHICLE.vehicle_body.right_hand_node) + if right_hand_node_length > 0: + output_stream.write(struct.pack('<%ss' % right_hand_node_length, TAG.string_to_bytes(VEHICLE.vehicle_body.right_hand_node, False))) + + left_hand_node_length = len(VEHICLE.vehicle_body.left_hand_node) + if left_hand_node_length > 0: + output_stream.write(struct.pack('<%ss' % left_hand_node_length, TAG.string_to_bytes(VEHICLE.vehicle_body.left_hand_node, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("uHnd", True), 1, 1, 4)) + + preferred_gun_node_length = len(VEHICLE.vehicle_body.preferred_gun_node) + if preferred_gun_node_length > 0: + output_stream.write(struct.pack('<%ss' % preferred_gun_node_length, TAG.string_to_bytes(VEHICLE.vehicle_body.preferred_gun_node, False))) + + melee_damage_length = len(VEHICLE.vehicle_body.melee_damage.name) + if melee_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % melee_damage_length, TAG.string_to_bytes(VEHICLE.vehicle_body.melee_damage.name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("ubms", True), 1, 1, 80)) + + boarding_melee_damage_length = len(VEHICLE.vehicle_body.boarding_melee_damage.name) + if boarding_melee_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % boarding_melee_damage_length, TAG.string_to_bytes(VEHICLE.vehicle_body.boarding_melee_damage.name, False))) + + boarding_melee_response_length = len(VEHICLE.vehicle_body.boarding_melee_response.name) + if boarding_melee_response_length > 0: + output_stream.write(struct.pack('<%ssx' % boarding_melee_response_length, TAG.string_to_bytes(VEHICLE.vehicle_body.boarding_melee_response.name, False))) + + landing_melee_damage_length = len(VEHICLE.vehicle_body.landing_melee_damage.name) + if landing_melee_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % landing_melee_damage_length, TAG.string_to_bytes(VEHICLE.vehicle_body.landing_melee_damage.name, False))) + + flurry_melee_damage_length = len(VEHICLE.vehicle_body.flurry_melee_damage.name) + if flurry_melee_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % flurry_melee_damage_length, TAG.string_to_bytes(VEHICLE.vehicle_body.flurry_melee_damage.name, False))) + + obstacle_smash_damage_length = len(VEHICLE.vehicle_body.obstacle_smash_damage.name) + if obstacle_smash_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % obstacle_smash_damage_length, TAG.string_to_bytes(VEHICLE.vehicle_body.obstacle_smash_damage.name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("cmtb", True), 0, 1, 2)) + + write_postures(output_stream, TAG, VEHICLE.postures, VEHICLE.postures_header) + write_tag_ref(output_stream, TAG, VEHICLE.new_hud_interface, VEHICLE.new_hud_interface_header) + write_dialogue_variant(output_stream, TAG, VEHICLE.dialogue_variants, VEHICLE.dialogue_variants_header) + write_powered_seats(output_stream, TAG, VEHICLE.powered_seats, VEHICLE.powered_seats_header) + write_tag_ref(output_stream, TAG, VEHICLE.weapons, VEHICLE.weapons_header) + write_seats(output_stream, TAG, VEHICLE.seats, VEHICLE.seats_header) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("!@#$", True), 0, 1, 20)) + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("ulYc", True), 1, 1, 8)) + + flip_message_length = len(VEHICLE.vehicle_body.flip_message) + if flip_message_length > 0: + output_stream.write(struct.pack('<%ss' % flip_message_length, TAG.string_to_bytes(VEHICLE.vehicle_body.flip_message, False))) + + write_gears(output_stream, TAG, VEHICLE.gears, VEHICLE.gears_header) + + suspension_sound_length = len(VEHICLE.vehicle_body.suspension_sound.name) + if suspension_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % suspension_sound_length, TAG.string_to_bytes(VEHICLE.vehicle_body.suspension_sound.name, False))) + + crash_sound_length = len(VEHICLE.vehicle_body.crash_sound.name) + if crash_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % crash_sound_length, TAG.string_to_bytes(VEHICLE.vehicle_body.crash_sound.name, False))) + + unused_length = len(VEHICLE.vehicle_body.unused.name) + if unused_length > 0: + output_stream.write(struct.pack('<%ssx' % unused_length, TAG.string_to_bytes(VEHICLE.vehicle_body.unused.name, False))) + + special_effect_length = len(VEHICLE.vehicle_body.special_effect.name) + if special_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % special_effect_length, TAG.string_to_bytes(VEHICLE.vehicle_body.special_effect.name, False))) + + unused_effect_length = len(VEHICLE.vehicle_body.unused_effect.name) + if unused_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % unused_effect_length, TAG.string_to_bytes(VEHICLE.vehicle_body.unused_effect.name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("HVPH", True), 0, 1, 96)) + + write_anti_gravity_point(output_stream, TAG, VEHICLE.anti_gravity_point, VEHICLE.anti_gravity_point_header) + write_friction_points(output_stream, TAG, VEHICLE.friction_points, VEHICLE.friction_points_header) + write_phantom_shapes(output_stream, TAG, VEHICLE.phantom_shapes, VEHICLE.phantom_shapes_header) \ No newline at end of file diff --git a/io_scene_halo/file_tag/h2/file_vehicle/format.py b/io_scene_halo/file_tag/h2/file_vehicle/format.py index 06ec35f5a..536515748 100644 --- a/io_scene_halo/file_tag/h2/file_vehicle/format.py +++ b/io_scene_halo/file_tag/h2/file_vehicle/format.py @@ -26,154 +26,7 @@ from mathutils import Vector from enum import Flag, Enum, auto - -class ObjectFlags(Flag): - does_not_cast_shadow = auto() - search_cardinal_direction_lightmaps_on_failure = auto() - unused = auto() - not_a_pathfinding_obstacle = auto() - extension_of_parent = auto() - does_not_cause_collision_damage = auto() - early_mover = auto() - early_mover_localized_physics = auto() - use_static_massive_lightmap_sample = auto() - object_scales_attachments = auto() - inherits_players_appearance = auto() - dead_bipeds_cant_localize = auto() - attach_to_clusters_dynamic_sphere = auto() - effects_created_by_this_object_do_not_spawn_objects_in_multiplayer = auto() - prophet_is_not_displayed_in_pegasus_builds = auto() - -class LightmapShadowModeEnum(Enum): - default = 0 - never = auto() - always = auto() - -class SweetenerSizeEnum(Enum): - small = 0 - medium = auto() - large = auto() - -class UnitFlags(Flag): - circular_aiming = auto() - destroyed_after_dying = auto() - half_speed_interpolation = auto() - fires_from_camera = auto() - entrance_inside_bounding_sphere = auto() - doesnt_show_readid_weapon = auto() - causes_passenger_dialogue = auto() - resists_ping = auto() - melee_attack_is_fatal = auto() - dont_reface_during_pings = auto() - has_no_aiming = auto() - simple_creature = auto() - impact_melee_attaches_to_unit = auto() - impact_melee_dies_on_shield = auto() - cannot_open_doors_automatically = auto() - melee_attackers_cannot_attach = auto() - not_instantly_killed_by_melee = auto() - unused_17 = auto() - runs_around_flaming = auto() - inconsequential = auto() - special_cinematic_unit = auto() - ignored_by_autoaiming = auto() - shields_fry_infection_forms = auto() - unused_23 = auto() - unused_24 = auto() - acts_as_gunner_for_parent = auto() - controlled_by_parent_gunner = auto() - parents_primary_weapon = auto() - unit_has_boost = auto() - -class TeamsEnum(Enum): - default = 0 - player = auto() - human = auto() - covenant = auto() - flood = auto() - sentinel = auto() - heretic = auto() - prophet = auto() - unused_8 = auto() - unused_9 = auto() - unused_10 = auto() - unused_11 = auto() - unused_12 = auto() - unused_13 = auto() - unused_14 = auto() - unused_15 = auto() - -class ConstantSoundVolumeEnum(Enum): - silent = 0 - medium = auto() - loud = auto() - shout = auto() - quiet = auto() - -class MotionSensorBlipSizeEnum(Enum): - medium = 0 - small = auto() - large = auto() - -class MetaGameTypeEnum(Enum): - brute = 0 - grunt = auto() - jackal = auto() - skirmisher = auto() - marine = auto() - spartan = auto() - bugger = auto() - hunter = auto() - flood_infection = auto() - flood_carrier = auto() - flood_combat = auto() - flood_pure = auto() - sentinel = auto() - elite = auto() - engineer = auto() - mule = auto() - turret = auto() - mongoose = auto() - warthog = auto() - scorpion = auto() - hornet = auto() - pelican = auto() - revenant = auto() - seraph = auto() - shade = auto() - watchtower = auto() - ghost = auto() - chopper = auto() - mauler = auto() - wraith = auto() - banshee = auto() - phantom = auto() - scarab = auto() - guntower = auto() - tuning_fork = auto() - broadsword = auto() - mammoth = auto() - lich = auto() - mantis = auto() - wasp = auto() - phaeton = auto() - bishop = auto() - knight = auto() - pawn = auto() - -class MetaGameClassEnum(Enum): - infantry = 0 - leader = auto() - hero = auto() - specialist = auto() - light_vehicle = auto() - heavy_vehicle = auto() - giant_vehicle = auto() - standard_vehicle = auto() - -class GrenadeTypeEnum(Enum): - human_fragmentation = 0 - covenant_plasma = auto() +from ..file_unit.format import UnitAsset class VehicleFlags(Flag): speed_wake_physics = auto() @@ -236,40 +89,12 @@ class VehicleSizeEnum(Enum): class PhysicsFlags(Flag): invalid = auto() -class VehicleAsset(): +class VehicleAsset(UnitAsset): def __init__(self): + super().__init__() self.header = None self.vehicle_body_header = None self.vehicle_body = None - self.ai_properties_header = None - self.ai_properties = None - self.functions_header = None - self.functions = None - self.attachments_header = None - self.attachments = None - self.widgets_header = None - self.widgets = None - self.old_functions_header = None - self.old_functions = None - self.change_colors_header = None - self.change_colors = None - self.predicted_resources_header = None - self.predicted_resources = None - self.camera_tracks_header = None - self.camera_tracks = None - self.camera_tracks_header = None - self.postures_header = None - self.postures = None - self.new_hud_interface_header = None - self.new_hud_interface = None - self.dialogue_variants_header = None - self.dialogue_variants = None - self.powered_seats_header = None - self.powered_seats = None - self.weapons_header = None - self.weapons = None - self.seats_header = None - self.seats = None self.gears_header = None self.gears = None self.anti_gravity_point_header = None @@ -279,134 +104,18 @@ def __init__(self): self.phantom_shapes_header = None self.phantom_shapes = None - class VehicleBody: - def __init__(self, object_flags=0, bounding_radius=0.0, bounding_offset=Vector(), acceleration_scale=0.0, lightmap_shadow_mode=0, sweetner_size=0, - dynamic_light_sphere_radius=0.0, dynamic_light_sphere_offset=Vector(), default_model_variant="", default_model_variant_length=0, model=None, crate_object=None, - modifier_shader=None, creation_effect=None, material_effects=None, ai_properties_tag_block=None, functions_tag_block=None, apply_collision_damage_scale=0.0, - min_game_acc=0.0, max_game_acc=0.0, min_game_scale=0.0, max_game_scale=0.0, min_abs_acc=0.0, max_abs_acc=0.0, min_abs_scale=0.0, max_abs_scale=0.0, - hud_text_message_index=0, attachments_tag_block=None, widgets_tag_block=None, old_functions_tag_block=None, change_colors_tag_block=None, - predicted_resources_tag_block=None, unit_flags=0, default_team=0, constant_sound_volume=0, integrated_light_toggle=None, camera_field_of_view=0.0, - camera_stiffness=0.0, camera_marker_name="", camera_marker_name_length=0, camera_submerged_marker_name="", camera_submerged_marker_name_length=0, - pitch_auto_level=0.0, pitch_range=(0.0, 0.0), camera_tracks_tag_block=None, acceleration_range=Vector(), acceleration_action_scale=0.0, - acceleration_attach_scale=0.0, soft_ping_threshold=0.0, soft_ping_interrupt_time=0.0, hard_ping_threshold=0.0, hard_ping_interrupt_time=0.0, - hard_death_threshold=0.0, feign_death_threshold=0.0, feign_death_time=0.0, distance_of_evade_anim=0.0, distance_of_dive_anim=0.0, - stunned_movement_threshold=0.0, feign_death_chance=0.0, feign_repeat_chance=0.0, spawned_turret_actor=None, spawned_actor_count=(0, 0), - spawned_velocity=0.0, aiming_velocity_maximum=0.0, aiming_acceleration_maximum=0.0, casual_aiming_modifier=0.0, looking_velocity_maximum=0.0, - looking_acceleration_maximum=0.0, right_hand_node="", right_hand_node_length=0, left_hand_node="", left_hand_node_length=0, preferred_gun_node="", - preferred_gun_node_length=0, melee_damage=None, boarding_melee_damage=None, boarding_melee_response=None, landing_melee_damage=None, - flurry_melee_damage=None, obstacle_smash_damage=None, motion_sensor_blip_size=0, unit_type=0, unit_class=0, postures_tag_block=None, - new_hud_interfaces_tag_block=None, dialogue_variants_tag_block=None, grenade_velocity=0.0, grenade_type=0, grenade_count=0, powered_seats_tag_block=None, - weapons_tag_block=None, seats_tag_block=None, boost_peak_power=0.0, boost_rise_power=0.0, boost_peak_time=0.0, boost_fall_power=0.0, dead_time=0.0, - attack_weight=0.0, decay_weight=0.0, vehicle_flags=0, vehicle_type=0, vehicle_control=0, maximum_forward_speed=0.0, maximum_reverse_speed=0.0, - speed_acceleration=0.0, speed_deceleration=0.0, maximum_left_turn=0.0, maximum_right_turn=0.0, wheel_circumference=0.0, turn_rate=0.0, blur_speed=0.0, - specific_type=0, player_training_vehicle_type=0, flip_message="", flip_message_length=0, turn_scale=0.0, speed_turn_penalty_power=0.0, speed_turn_penalty=0.0, - maximum_left_slide=0.0, maximum_right_slide=0.0, slide_acceleration=0.0, slide_deceleration=0.0, minimum_flipping_angular_velocity=0.0, - maximum_flipping_angular_velocity=0.0, vehicle_size=0, fixed_gun_yaw=0.0, fixed_gun_pitch=0.0, overdampen_cusp_angle=0.0, overdampen_exponent=0.0, - crouch_transition_time=0.0, engine_moment=0.0, engine_max_angular_velocity=0.0, gears_tag_block=None, gears_header=None, flying_torque_scale=0.0, - seat_enterance_acceleration_scale=0.0, seat_exit_acceleration_scale=0.0, air_friction_deceleration=0.0, thrust_scale=0.0, suspension_sound=None, - crash_sound=None, unused=None, special_effect=None, unused_effect=None, physics_flags=0, ground_fricton=0.0, ground_depth=0.0, ground_damp_factor=0.0, - ground_moving_friction=0.0, ground_maximum_slope_0=0.0, ground_maximum_slope_1=0.0, anti_gravity_bank_lift=0.0, steering_bank_reaction_scale=0.0, - gravity_scale=0.0, radius=0.0, anti_gravity_point_tag_block=None, anti_gravity_point_header=None, friction_points_tag_block=None, friction_points_header=None, - phantom_shapes_tag_block=None, phantom_shapes_header=None, gears=None, anti_gravity_point=None, friction_points=None, phantom_shapes=None): - self.object_flags = object_flags - self.bounding_radius = bounding_radius - self.bounding_offset = bounding_offset - self.acceleration_scale = acceleration_scale - self.lightmap_shadow_mode = lightmap_shadow_mode - self.sweetner_size = sweetner_size - self.dynamic_light_sphere_radius = dynamic_light_sphere_radius - self.dynamic_light_sphere_offset = dynamic_light_sphere_offset - self.default_model_variant = default_model_variant - self.default_model_variant_length = default_model_variant_length - self.model = model - self.crate_object = crate_object - self.modifier_shader = modifier_shader - self.creation_effect = creation_effect - self.material_effects = material_effects - self.ai_properties_tag_block = ai_properties_tag_block - self.functions_tag_block = functions_tag_block - self.apply_collision_damage_scale = apply_collision_damage_scale - self.min_game_acc = min_game_acc - self.max_game_acc = max_game_acc - self.min_game_scale = min_game_scale - self.max_game_scale = max_game_scale - self.min_abs_acc = min_abs_acc - self.max_abs_acc = max_abs_acc - self.min_abs_scale = min_abs_scale - self.max_abs_scale = max_abs_scale - self.hud_text_message_index = hud_text_message_index - self.attachments_tag_block = attachments_tag_block - self.widgets_tag_block = widgets_tag_block - self.old_functions_tag_block = old_functions_tag_block - self.change_colors_tag_block = change_colors_tag_block - self.predicted_resources_tag_block = predicted_resources_tag_block - self.unit_flags = unit_flags - self.default_team = default_team - self.constant_sound_volume = constant_sound_volume - self.integrated_light_toggle = integrated_light_toggle - self.camera_field_of_view = camera_field_of_view - self.camera_stiffness = camera_stiffness - self.camera_marker_name = camera_marker_name - self.camera_marker_name_length = camera_marker_name_length - self.camera_submerged_marker_name = camera_submerged_marker_name - self.camera_submerged_marker_name_length = camera_submerged_marker_name_length - self.pitch_auto_level = pitch_auto_level - self.pitch_range = pitch_range - self.camera_tracks_tag_block = camera_tracks_tag_block - self.acceleration_range = acceleration_range - self.acceleration_action_scale = acceleration_action_scale - self.acceleration_attach_scale = acceleration_attach_scale - self.soft_ping_threshold = soft_ping_threshold - self.soft_ping_interrupt_time = soft_ping_interrupt_time - self.hard_ping_threshold = hard_ping_threshold - self.hard_ping_interrupt_time = hard_ping_interrupt_time - self.hard_death_threshold = hard_death_threshold - self.feign_death_threshold = feign_death_threshold - self.feign_death_time = feign_death_time - self.distance_of_evade_anim = distance_of_evade_anim - self.distance_of_dive_anim = distance_of_dive_anim - self.stunned_movement_threshold = stunned_movement_threshold - self.feign_death_chance = feign_death_chance - self.feign_repeat_chance = feign_repeat_chance - self.spawned_turret_actor = spawned_turret_actor - self.spawned_actor_count = spawned_actor_count - self.spawned_velocity = spawned_velocity - self.aiming_velocity_maximum = aiming_velocity_maximum - self.aiming_acceleration_maximum = aiming_acceleration_maximum - self.casual_aiming_modifier = casual_aiming_modifier - self.looking_velocity_maximum = looking_velocity_maximum - self.looking_acceleration_maximum = looking_acceleration_maximum - self.right_hand_node = right_hand_node - self.right_hand_node_length = right_hand_node_length - self.left_hand_node = left_hand_node - self.left_hand_node_length = left_hand_node_length - self.preferred_gun_node = preferred_gun_node - self.preferred_gun_node_length = preferred_gun_node_length - self.melee_damage = melee_damage - self.boarding_melee_damage = boarding_melee_damage - self.boarding_melee_response = boarding_melee_response - self.landing_melee_damage = landing_melee_damage - self.flurry_melee_damage = flurry_melee_damage - self.obstacle_smash_damage = obstacle_smash_damage - self.motion_sensor_blip_size = motion_sensor_blip_size - self.unit_type = unit_type - self.unit_class = unit_class - self.postures_tag_block = postures_tag_block - self.new_hud_interfaces_tag_block = new_hud_interfaces_tag_block - self.dialogue_variants_tag_block = dialogue_variants_tag_block - self.grenade_velocity = grenade_velocity - self.grenade_type = grenade_type - self.grenade_count = grenade_count - self.powered_seats_tag_block = powered_seats_tag_block - self.weapons_tag_block = weapons_tag_block - self.seats_tag_block = seats_tag_block - self.boost_peak_power = boost_peak_power - self.boost_rise_power = boost_rise_power - self.boost_peak_time = boost_peak_time - self.boost_fall_power = boost_fall_power - self.dead_time = dead_time - self.attack_weight = attack_weight - self.decay_weight = decay_weight + class VehicleBody(UnitAsset.UnitBody): + def __init__(self, vehicle_flags=0, vehicle_type=0, vehicle_control=0, maximum_forward_speed=0.0, maximum_reverse_speed=0.0, speed_acceleration=0.0, speed_deceleration=0.0, + maximum_left_turn=0.0, maximum_right_turn=0.0, wheel_circumference=0.0, turn_rate=0.0, blur_speed=0.0, specific_type=0, player_training_vehicle_type=0, + flip_message="", flip_message_length=0, turn_scale=0.0, speed_turn_penalty_power=0.0, speed_turn_penalty=0.0, maximum_left_slide=0.0, maximum_right_slide=0.0, + slide_acceleration=0.0, slide_deceleration=0.0, minimum_flipping_angular_velocity=0.0, maximum_flipping_angular_velocity=0.0, vehicle_size=0, + fixed_gun_yaw=0.0, fixed_gun_pitch=0.0, overdampen_cusp_angle=0.0, overdampen_exponent=0.0, crouch_transition_time=0.0, engine_moment=0.0, + engine_max_angular_velocity=0.0, gears_tag_block=None, flying_torque_scale=0.0, seat_enterance_acceleration_scale=0.0, seat_exit_acceleration_scale=0.0, + air_friction_deceleration=0.0, thrust_scale=0.0, suspension_sound=None, crash_sound=None, unused=None, special_effect=None, unused_effect=None, + physics_flags=0, ground_fricton=0.0, ground_depth=0.0, ground_damp_factor=0.0, ground_moving_friction=0.0, ground_maximum_slope_0=0.0, + ground_maximum_slope_1=0.0, anti_gravity_bank_lift=0.0, steering_bank_reaction_scale=0.0, gravity_scale=0.0, radius=0.0, anti_gravity_point_tag_block=None, + friction_points_tag_block=None, phantom_shapes_tag_block=None): + super().__init__() self.vehicle_flags = vehicle_flags self.vehicle_type = vehicle_type self.vehicle_control = vehicle_control @@ -465,3 +174,101 @@ def __init__(self, object_flags=0, bounding_radius=0.0, bounding_offset=Vector() self.anti_gravity_point_tag_block = anti_gravity_point_tag_block self.friction_points_tag_block = friction_points_tag_block self.phantom_shapes_tag_block = phantom_shapes_tag_block + + class Gear: + def __init__(self, a_min_torque=0.0, a_max_torque=0.0, a_peak_torque_scale=0.0, a_past_peak_torque_exponent=0.0, a_torque_at_max_angular_velocity=0.0, + a_torque_at_2x_max_angular_velocity=0.0, b_min_torque=0.0, b_max_torque=0.0, b_peak_torque_scale=0.0, b_past_peak_torque_exponent=0.0, + b_torque_at_max_angular_velocity=0.0, b_torque_at_2x_max_angular_velocity=0.0, min_time_to_upshift=0.0, engine_up_shift_scale=0.0, gear_ratio=0.0, + min_time_to_downshift=0.0, engine_down_shift_scale=0.0): + self.a_min_torque = a_min_torque + self.a_max_torque = a_max_torque + self.a_peak_torque_scale = a_peak_torque_scale + self.a_past_peak_torque_exponent = a_past_peak_torque_exponent + self.a_torque_at_max_angular_velocity = a_torque_at_max_angular_velocity + self.a_torque_at_2x_max_angular_velocity = a_torque_at_2x_max_angular_velocity + self.b_min_torque = b_min_torque + self.b_max_torque = b_max_torque + self.b_peak_torque_scale = b_peak_torque_scale + self.b_past_peak_torque_exponent = b_past_peak_torque_exponent + self.b_torque_at_max_angular_velocity = b_torque_at_max_angular_velocity + self.b_torque_at_2x_max_angular_velocity = b_torque_at_2x_max_angular_velocity + self.min_time_to_upshift = min_time_to_upshift + self.engine_up_shift_scale = engine_up_shift_scale + self.gear_ratio = gear_ratio + self.min_time_to_downshift = min_time_to_downshift + self.engine_down_shift_scale = engine_down_shift_scale + + class AntiGravityPoint: + def __init__(self, marker_name="", marker_name_length=0, flags=0, antigrav_strength=0.0, antigrav_offset=0.0, antigrav_height=0.0, antigrav_damp_factor=0.0, + antigrav_normal_k1=0.0, antigrav_normal_k0=0.0, radius=0.0, damage_source_region_name="", damage_source_region_name_length=0, default_state_error=0.0, + minor_damage_error=0.0, medium_damage_error=0.0, major_damage_error=0.0, destroyed_state_error=0.0): + self.marker_name = marker_name + self.marker_name_length = marker_name_length + self.flags = flags + self.antigrav_strength = antigrav_strength + self.antigrav_offset = antigrav_offset + self.antigrav_height = antigrav_height + self.antigrav_damp_factor = antigrav_damp_factor + self.antigrav_normal_k1 = antigrav_normal_k1 + self.antigrav_normal_k0 = antigrav_normal_k0 + self.radius = radius + self.damage_source_region_name = damage_source_region_name + self.damage_source_region_name_length = damage_source_region_name_length + self.default_state_error = default_state_error + self.minor_damage_error = minor_damage_error + self.medium_damage_error = medium_damage_error + self.major_damage_error = major_damage_error + self.destroyed_state_error = destroyed_state_error + + class FrictionPoint: + def __init__(self, marker_name="", marker_name_length=0, flags=0, fraction_of_total_mass=0.0, radius=0.0, damaged_radius=0.0, friction_type=0, + moving_friction_velocity_diff=0.0, e_brake_moving_friction=0.0, e_brake_friction=0.0, e_brake_moving_friction_vel_dif=0.0, collision_global_material_name="", + collision_global_material_name_length=0, model_state_destroyed=0, region_name="", region_name_length=0): + self.marker_name = marker_name + self.marker_name_length = marker_name_length + self.flags = flags + self.fraction_of_total_mass = fraction_of_total_mass + self.radius = radius + self.damaged_radius = damaged_radius + self.friction_type = friction_type + self.moving_friction_velocity_diff = moving_friction_velocity_diff + self.e_brake_moving_friction = e_brake_moving_friction + self.e_brake_friction = e_brake_friction + self.e_brake_moving_friction_vel_dif = e_brake_moving_friction_vel_dif + self.collision_global_material_name = collision_global_material_name + self.collision_global_material_name_length = collision_global_material_name_length + self.model_state_destroyed = model_state_destroyed + self.region_name = region_name + self.region_name_length = region_name_length + + class PhantomVolume: + def __init__(self, size=0, count=0, child_shapes_size=0, child_shapes_capacity=0, multisphere_count=0, flags=0, x0=0.0, x1=0.0, y0=0.0, y1=0.0, z0=0.0, z1=0.0, + spheres=None): + self.size = size + self.count = count + self.child_shapes_size = child_shapes_size + self.child_shapes_capacity = child_shapes_capacity + self.multisphere_count = multisphere_count + self.flags = flags + self.x0 = x0 + self.x1 = x1 + self.y0 = y0 + self.y1 = y1 + self.z0 = z0 + self.z1 = z1 + self.spheres = spheres + + class Sphere: + def __init__(self, size=0, count=0, num_spheres=0, sphere_0=Vector(), sphere_1=Vector(), sphere_2=Vector(), sphere_3=Vector(), sphere_4=Vector(), sphere_5=Vector(), + sphere_6=Vector(), sphere_7=Vector()): + self.size = size + self.count = count + self.num_spheres = num_spheres + self.sphere_0 = sphere_0 + self.sphere_1 = sphere_1 + self.sphere_2 = sphere_2 + self.sphere_3 = sphere_3 + self.sphere_4 = sphere_4 + self.sphere_5 = sphere_5 + self.sphere_6 = sphere_6 + self.sphere_7 = sphere_7 diff --git a/io_scene_halo/file_tag/h2/file_vehicle/process_file.py b/io_scene_halo/file_tag/h2/file_vehicle/process_file.py index c70934428..e591849d1 100644 --- a/io_scene_halo/file_tag/h2/file_vehicle/process_file.py +++ b/io_scene_halo/file_tag/h2/file_vehicle/process_file.py @@ -26,18 +26,18 @@ from xml.dom import minidom from ....global_functions import tag_format -from .format import ( - VehicleAsset, - ObjectFlags, - LightmapShadowModeEnum, - SweetenerSizeEnum, +from ..file_object.format import ObjectFlags, LightmapShadowModeEnum, SweetenerSizeEnum +from ..file_unit.format import ( UnitFlags, TeamsEnum, ConstantSoundVolumeEnum, MotionSensorBlipSizeEnum, MetaGameTypeEnum, MetaGameClassEnum, - GrenadeTypeEnum, + GrenadeTypeEnum + ) +from .format import ( + VehicleAsset, VehicleFlags, VehicleTypeEnum, VehicleControlEnum, diff --git a/io_scene_halo/file_tag/h2/file_weapon/build_asset.py b/io_scene_halo/file_tag/h2/file_weapon/build_asset.py new file mode 100644 index 000000000..6fd23a55c --- /dev/null +++ b/io_scene_halo/file_tag/h2/file_weapon/build_asset.py @@ -0,0 +1,620 @@ +# ##### BEGIN MIT LICENSE BLOCK ##### +# +# MIT License +# +# Copyright (c) 2023 Steven Garcia +# +# 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. +# +# ##### END MIT LICENSE BLOCK ##### + +import struct + +from math import radians +from ....global_functions import tag_format, shader_processing +from ..file_object.build_asset import ( + write_ai_properties, + write_functions, + write_attachments, + write_tag_ref, + write_old_functions, + write_change_colors, + write_predicted_resources + ) +from ..file_item.build_asset import write_predicted_bitmaps + +def write_body(output_stream, TAG, WEAPON): + WEAPON.weapon_body_header.write(output_stream, TAG, True) + output_stream.write(struct.pack('I', len(WEAPON.weapon_body.default_model_variant))) + WEAPON.weapon_body.model.write(output_stream, False, True) + WEAPON.weapon_body.crate_object.write(output_stream, False, True) + WEAPON.weapon_body.modifier_shader.write(output_stream, False, True) + WEAPON.weapon_body.creation_effect.write(output_stream, False, True) + WEAPON.weapon_body.material_effects.write(output_stream, False, True) + WEAPON.weapon_body.ai_properties_tag_block.write(output_stream, False) + WEAPON.weapon_body.functions_tag_block.write(output_stream, False) + output_stream.write(struct.pack('I', len(WEAPON.weapon_body.pickup_message))) + output_stream.write(struct.pack('>I', len(WEAPON.weapon_body.swap_message))) + output_stream.write(struct.pack('>I', len(WEAPON.weapon_body.pickup_or_dual_msg))) + output_stream.write(struct.pack('>I', len(WEAPON.weapon_body.swap_or_dual_msg))) + output_stream.write(struct.pack('>I', len(WEAPON.weapon_body.dual_only_msg))) + output_stream.write(struct.pack('>I', len(WEAPON.weapon_body.picked_up_msg))) + output_stream.write(struct.pack('>I', len(WEAPON.weapon_body.singluar_quantity_msg))) + output_stream.write(struct.pack('>I', len(WEAPON.weapon_body.plural_quantity_msg))) + output_stream.write(struct.pack('>I', len(WEAPON.weapon_body.switch_to_msg))) + output_stream.write(struct.pack('>I', len(WEAPON.weapon_body.switch_to_from_ai_msg))) + WEAPON.weapon_body.unused.write(output_stream, False, True) + WEAPON.weapon_body.collision_sound.write(output_stream, False, True) + WEAPON.weapon_body.predicted_bitmaps_tag_block.write(output_stream, False) + WEAPON.weapon_body.detonation_damage_effect.write(output_stream, False, True) + output_stream.write(struct.pack('I', len(WEAPON.weapon_body.unknown))) + output_stream.write(struct.pack('I', len(WEAPON.weapon_body.handle_node))) + output_stream.write(struct.pack('>I', len(WEAPON.weapon_body.weapon_class))) + output_stream.write(struct.pack('>I', len(WEAPON.weapon_body.weapon_name))) + output_stream.write(struct.pack(' 0: + first_person_header.write(output_stream, TAG, True) + for first_person_element in first_person: + first_person_element.first_person_model.write(output_stream, False, True) + first_person_element.first_person_animations.write(output_stream, False, True) + + for first_person_element in first_person: + first_person_model_length = len(first_person_element.first_person_model.name) + if first_person_model_length > 0: + output_stream.write(struct.pack('<%ssx' % first_person_model_length, TAG.string_to_bytes(first_person_element.first_person_model.name, False))) + + first_person_animations_length = len(first_person_element.first_person_animations.name) + if first_person_animations_length > 0: + output_stream.write(struct.pack('<%ssx' % first_person_animations_length, TAG.string_to_bytes(first_person_element.first_person_animations.name, False))) + +def write_magazines(output_stream, TAG, magazines, magazines_header): + if len(magazines) > 0: + magazines_header.write(output_stream, TAG, True) + for magazine_stat_element in magazines: + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % reloading_effect_length, TAG.string_to_bytes(magazine_stat_element.reloading_effect.name, False))) + + reloading_damage_effect_length = len(magazine_stat_element.reloading_damage_effect.name) + if reloading_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % reloading_damage_effect_length, TAG.string_to_bytes(magazine_stat_element.reloading_damage_effect.name, False))) + + chambering_effect_length = len(magazine_stat_element.chambering_effect.name) + if chambering_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % chambering_effect_length, TAG.string_to_bytes(magazine_stat_element.chambering_effect.name, False))) + + chambering_damage_effect_length = len(magazine_stat_element.chambering_damage_effect.name) + if chambering_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % chambering_damage_effect_length, TAG.string_to_bytes(magazine_stat_element.chambering_damage_effect.name, False))) + + if len(magazine_stat_element.magazines) > 0: + magazine_stat_element.magazines_header.write(output_stream, TAG, True) + for magazine_element in magazine_stat_element.magazines: + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % equipment_length, TAG.string_to_bytes(magazine_element.equipment.name, False))) + +def write_new_triggers(output_stream, TAG, new_triggers, new_triggers_header): + if len(new_triggers) > 0: + new_triggers_header.write(output_stream, TAG, True) + for new_trigger_element in new_triggers: + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % charging_effect_length, TAG.string_to_bytes(new_trigger_element.charging_effect.name, False))) + + charging_damage_effect_length = len(new_trigger_element.charging_damage_effect.name) + if charging_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % charging_damage_effect_length, TAG.string_to_bytes(new_trigger_element.charging_damage_effect.name, False))) + +def write_barrels(output_stream, TAG, barrels, barrels_header): + if len(barrels) > 0: + barrels_header.write(output_stream, TAG, True) + for barrel_element in barrels: + output_stream.write(struct.pack('I', len(barrel_element.optional_barrel_marker_name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % optional_barrel_marker_name_length, TAG.string_to_bytes(barrel_element.optional_barrel_marker_name, False))) + + projectile_length = len(barrel_element.projectile.name) + if projectile_length > 0: + output_stream.write(struct.pack('<%ssx' % projectile_length, TAG.string_to_bytes(barrel_element.projectile.name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("wbde", True), 1, 1, 16)) + + damage_effect_length = len(barrel_element.damage_effect.name) + if damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % damage_effect_length, TAG.string_to_bytes(barrel_element.damage_effect.name, False))) + + if len(barrel_element.firing_effects) > 0: + barrel_element.firing_effects_header.write(output_stream, TAG, True) + for firing_effect_element in barrel_element.firing_effects: + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ssx' % firing_effect_length, TAG.string_to_bytes(firing_effect_element.firing_effect.name, False))) + + misfire_effect_length = len(firing_effect_element.misfire_effect.name) + if misfire_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % misfire_effect_length, TAG.string_to_bytes(firing_effect_element.misfire_effect.name, False))) + + empty_effect_length = len(firing_effect_element.empty_effect.name) + if empty_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % empty_effect_length, TAG.string_to_bytes(firing_effect_element.empty_effect.name, False))) + + firing_damage_length = len(firing_effect_element.firing_damage.name) + if firing_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % firing_damage_length, TAG.string_to_bytes(firing_effect_element.firing_damage.name, False))) + + misfire_damage_length = len(firing_effect_element.misfire_damage.name) + if misfire_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % misfire_damage_length, TAG.string_to_bytes(firing_effect_element.misfire_damage.name, False))) + + empty_damage_length = len(firing_effect_element.empty_damage.name) + if empty_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % empty_damage_length, TAG.string_to_bytes(firing_effect_element.empty_damage.name, False))) + +def build_asset(output_stream, WEAPON, report): + TAG = tag_format.TagAsset() + TAG.is_legacy = False + TAG.big_endian = False + + WEAPON.header.write(output_stream, False, True) + write_body(output_stream, TAG, WEAPON) + + default_model_variant_name_length = len(WEAPON.weapon_body.default_model_variant) + if default_model_variant_name_length > 0: + output_stream.write(struct.pack('<%ss' % default_model_variant_name_length, TAG.string_to_bytes(WEAPON.weapon_body.default_model_variant, False))) + + model_name_length = len(WEAPON.weapon_body.model.name) + if model_name_length > 0: + output_stream.write(struct.pack('<%ssx' % model_name_length, TAG.string_to_bytes(WEAPON.weapon_body.model.name, False))) + + crate_object_name_length = len(WEAPON.weapon_body.crate_object.name) + if crate_object_name_length > 0: + output_stream.write(struct.pack('<%ssx' % crate_object_name_length, TAG.string_to_bytes(WEAPON.weapon_body.crate_object.name, False))) + + modifier_shader_name_length = len(WEAPON.weapon_body.modifier_shader.name) + if modifier_shader_name_length > 0: + output_stream.write(struct.pack('<%ssx' % modifier_shader_name_length, TAG.string_to_bytes(WEAPON.weapon_body.modifier_shader.name, False))) + + creation_effect_name_length = len(WEAPON.weapon_body.creation_effect.name) + if creation_effect_name_length > 0: + output_stream.write(struct.pack('<%ssx' % creation_effect_name_length, TAG.string_to_bytes(WEAPON.weapon_body.creation_effect.name, False))) + + material_effects_name_length = len(WEAPON.weapon_body.material_effects.name) + if material_effects_name_length > 0: + output_stream.write(struct.pack('<%ssx' % material_effects_name_length, TAG.string_to_bytes(WEAPON.weapon_body.material_effects.name, False))) + + write_ai_properties(output_stream, TAG, WEAPON.ai_properties, WEAPON.ai_properties_header) + write_functions(output_stream, TAG, WEAPON.functions, WEAPON.functions_header) + write_attachments(output_stream, TAG, WEAPON.attachments, WEAPON.attachments_header) + write_tag_ref(output_stream, TAG, WEAPON.widgets, WEAPON.widgets_header) + write_old_functions(output_stream, TAG, WEAPON.old_functions, WEAPON.old_functions_header) + write_change_colors(output_stream, TAG, WEAPON.change_colors, WEAPON.change_colors_header) + write_predicted_resources(output_stream, TAG, WEAPON.predicted_resources, WEAPON.predicted_resources_header) + + pickup_message_length = len(WEAPON.weapon_body.pickup_message) + if pickup_message_length > 0: + output_stream.write(struct.pack('<%ss' % pickup_message_length, TAG.string_to_bytes(WEAPON.weapon_body.pickup_message, False))) + + swap_message_length = len(WEAPON.weapon_body.swap_message) + if swap_message_length > 0: + output_stream.write(struct.pack('<%ss' % swap_message_length, TAG.string_to_bytes(WEAPON.weapon_body.swap_message, False))) + + pickup_or_dual_msg_length = len(WEAPON.weapon_body.pickup_or_dual_msg) + if pickup_or_dual_msg_length > 0: + output_stream.write(struct.pack('<%ss' % pickup_or_dual_msg_length, TAG.string_to_bytes(WEAPON.weapon_body.pickup_or_dual_msg, False))) + + swap_or_dual_msg_length = len(WEAPON.weapon_body.swap_or_dual_msg) + if swap_or_dual_msg_length > 0: + output_stream.write(struct.pack('<%ss' % swap_or_dual_msg_length, TAG.string_to_bytes(WEAPON.weapon_body.swap_or_dual_msg, False))) + + dual_only_msg_length = len(WEAPON.weapon_body.dual_only_msg) + if dual_only_msg_length > 0: + output_stream.write(struct.pack('<%ss' % dual_only_msg_length, TAG.string_to_bytes(WEAPON.weapon_body.dual_only_msg, False))) + + picked_up_msg_length = len(WEAPON.weapon_body.picked_up_msg) + if picked_up_msg_length > 0: + output_stream.write(struct.pack('<%ss' % picked_up_msg_length, TAG.string_to_bytes(WEAPON.weapon_body.picked_up_msg, False))) + + singluar_quantity_msg_length = len(WEAPON.weapon_body.singluar_quantity_msg) + if singluar_quantity_msg_length > 0: + output_stream.write(struct.pack('<%ss' % singluar_quantity_msg_length, TAG.string_to_bytes(WEAPON.weapon_body.singluar_quantity_msg, False))) + + plural_quantity_msg_length = len(WEAPON.weapon_body.plural_quantity_msg) + if plural_quantity_msg_length > 0: + output_stream.write(struct.pack('<%ss' % plural_quantity_msg_length, TAG.string_to_bytes(WEAPON.weapon_body.plural_quantity_msg, False))) + + switch_to_msg_length = len(WEAPON.weapon_body.switch_to_msg) + if switch_to_msg_length > 0: + output_stream.write(struct.pack('<%ss' % switch_to_msg_length, TAG.string_to_bytes(WEAPON.weapon_body.switch_to_msg, False))) + + switch_to_from_ai_msg_length = len(WEAPON.weapon_body.switch_to_from_ai_msg) + if switch_to_from_ai_msg_length > 0: + output_stream.write(struct.pack('<%ss' % switch_to_from_ai_msg_length, TAG.string_to_bytes(WEAPON.weapon_body.switch_to_from_ai_msg, False))) + + unused_length = len(WEAPON.weapon_body.unused.name) + if unused_length > 0: + output_stream.write(struct.pack('<%ssx' % unused_length, TAG.string_to_bytes(WEAPON.weapon_body.unused.name, False))) + + collision_sound_length = len(WEAPON.weapon_body.collision_sound.name) + if collision_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % collision_sound_length, TAG.string_to_bytes(WEAPON.weapon_body.collision_sound.name, False))) + + write_predicted_bitmaps(output_stream, TAG, WEAPON.predicted_bitmaps, WEAPON.predicted_bitmaps_header) + + detonation_damage_effect_length = len(WEAPON.weapon_body.detonation_damage_effect.name) + if detonation_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % detonation_damage_effect_length, TAG.string_to_bytes(WEAPON.weapon_body.detonation_damage_effect.name, False))) + + detonating_effect_length = len(WEAPON.weapon_body.detonating_effect.name) + if detonating_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % detonating_effect_length, TAG.string_to_bytes(WEAPON.weapon_body.detonating_effect.name, False))) + + detonation_effect_length = len(WEAPON.weapon_body.detonation_effect.name) + if detonation_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % detonation_effect_length, TAG.string_to_bytes(WEAPON.weapon_body.detonation_effect.name, False))) + + unknown_length = len(WEAPON.weapon_body.unknown) + if unknown_length > 0: + output_stream.write(struct.pack('<%ss' % unknown_length, TAG.string_to_bytes(WEAPON.weapon_body.unknown, False))) + + ready_effect_length = len(WEAPON.weapon_body.ready_effect.name) + if ready_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % ready_effect_length, TAG.string_to_bytes(WEAPON.weapon_body.ready_effect.name, False))) + + ready_damage_effect_length = len(WEAPON.weapon_body.ready_damage_effect.name) + if ready_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % ready_damage_effect_length, TAG.string_to_bytes(WEAPON.weapon_body.ready_damage_effect.name, False))) + + overheated_length = len(WEAPON.weapon_body.overheated.name) + if overheated_length > 0: + output_stream.write(struct.pack('<%ssx' % overheated_length, TAG.string_to_bytes(WEAPON.weapon_body.overheated.name, False))) + + overheated_damage_effect_length = len(WEAPON.weapon_body.overheated_damage_effect.name) + if overheated_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % overheated_damage_effect_length, TAG.string_to_bytes(WEAPON.weapon_body.overheated_damage_effect.name, False))) + + detonation_length = len(WEAPON.weapon_body.detonation.name) + if detonation_length > 0: + output_stream.write(struct.pack('<%ssx' % detonation_length, TAG.string_to_bytes(WEAPON.weapon_body.detonation.name, False))) + + weapon_detonation_damage_effect_length = len(WEAPON.weapon_body.weapon_detonation_damage_effect.name) + if weapon_detonation_damage_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % weapon_detonation_damage_effect_length, TAG.string_to_bytes(WEAPON.weapon_body.weapon_detonation_damage_effect.name, False))) + + player_melee_damage_length = len(WEAPON.weapon_body.player_melee_damage.name) + if player_melee_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % player_melee_damage_length, TAG.string_to_bytes(WEAPON.weapon_body.player_melee_damage.name, False))) + + player_melee_response_length = len(WEAPON.weapon_body.player_melee_response.name) + if player_melee_response_length > 0: + output_stream.write(struct.pack('<%ssx' % player_melee_response_length, TAG.string_to_bytes(WEAPON.weapon_body.player_melee_response.name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("masd", True), 1, 1, 20)) + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("mdps", True), 1, 1, 140)) + + first_hit_melee_damage_length = len(WEAPON.weapon_body.first_hit_melee_damage.name) + if first_hit_melee_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % first_hit_melee_damage_length, TAG.string_to_bytes(WEAPON.weapon_body.first_hit_melee_damage.name, False))) + + first_hit_melee_response_length = len(WEAPON.weapon_body.first_hit_melee_response.name) + if first_hit_melee_response_length > 0: + output_stream.write(struct.pack('<%ssx' % first_hit_melee_response_length, TAG.string_to_bytes(WEAPON.weapon_body.first_hit_melee_response.name, False))) + + second_hit_melee_damage_length = len(WEAPON.weapon_body.second_hit_melee_damage.name) + if second_hit_melee_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % second_hit_melee_damage_length, TAG.string_to_bytes(WEAPON.weapon_body.second_hit_melee_damage.name, False))) + + second_hit_melee_response_length = len(WEAPON.weapon_body.second_hit_melee_response.name) + if second_hit_melee_response_length > 0: + output_stream.write(struct.pack('<%ssx' % second_hit_melee_response_length, TAG.string_to_bytes(WEAPON.weapon_body.second_hit_melee_response.name, False))) + + third_hit_melee_damage_length = len(WEAPON.weapon_body.third_hit_melee_damage.name) + if third_hit_melee_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % third_hit_melee_damage_length, TAG.string_to_bytes(WEAPON.weapon_body.third_hit_melee_damage.name, False))) + + third_hit_melee_response_length = len(WEAPON.weapon_body.third_hit_melee_response.name) + if third_hit_melee_response_length > 0: + output_stream.write(struct.pack('<%ssx' % third_hit_melee_response_length, TAG.string_to_bytes(WEAPON.weapon_body.third_hit_melee_response.name, False))) + + lunge_melee_damage_length = len(WEAPON.weapon_body.lunge_melee_damage.name) + if lunge_melee_damage_length > 0: + output_stream.write(struct.pack('<%ssx' % lunge_melee_damage_length, TAG.string_to_bytes(WEAPON.weapon_body.lunge_melee_damage.name, False))) + + lunge_melee_response_length = len(WEAPON.weapon_body.lunge_melee_response.name) + if lunge_melee_response_length > 0: + output_stream.write(struct.pack('<%ssx' % lunge_melee_response_length, TAG.string_to_bytes(WEAPON.weapon_body.lunge_melee_response.name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("easd", True), 0, 1, 36)) + weapon_power_on_effect_length = len(WEAPON.weapon_body.weapon_power_on_effect.name) + if weapon_power_on_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % weapon_power_on_effect_length, TAG.string_to_bytes(WEAPON.weapon_body.weapon_power_on_effect.name, False))) + + weapon_power_off_effect_length = len(WEAPON.weapon_body.weapon_power_off_effect.name) + if weapon_power_off_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % weapon_power_off_effect_length, TAG.string_to_bytes(WEAPON.weapon_body.weapon_power_off_effect.name, False))) + + pickup_sound_length = len(WEAPON.weapon_body.pickup_sound.name) + if pickup_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % pickup_sound_length, TAG.string_to_bytes(WEAPON.weapon_body.pickup_sound.name, False))) + + zoom_in_sound_length = len(WEAPON.weapon_body.zoom_in_sound.name) + if zoom_in_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % zoom_in_sound_length, TAG.string_to_bytes(WEAPON.weapon_body.zoom_in_sound.name, False))) + + zoom_out_sound_length = len(WEAPON.weapon_body.zoom_out_sound.name) + if zoom_out_sound_length > 0: + output_stream.write(struct.pack('<%ssx' % zoom_out_sound_length, TAG.string_to_bytes(WEAPON.weapon_body.zoom_out_sound.name, False))) + + handle_node_length = len(WEAPON.weapon_body.handle_node) + if handle_node_length > 0: + output_stream.write(struct.pack('<%ss' % handle_node_length, TAG.string_to_bytes(WEAPON.weapon_body.handle_node, False))) + + weapon_class_length = len(WEAPON.weapon_body.weapon_class) + if weapon_class_length > 0: + output_stream.write(struct.pack('<%ss' % weapon_class_length, TAG.string_to_bytes(WEAPON.weapon_body.weapon_class, False))) + + weapon_name_length = len(WEAPON.weapon_body.weapon_name) + if weapon_name_length > 0: + output_stream.write(struct.pack('<%ss' % weapon_name_length, TAG.string_to_bytes(WEAPON.weapon_body.weapon_name, False))) + + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("wtsf", True), 1, 1, 4)) + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("wItS", True), 0, 1, 44)) + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("wSiS", True), 0, 1, 16)) + + write_first_person(output_stream, TAG, WEAPON.first_person, WEAPON.first_person_header) + + new_hud_interface_length = len(WEAPON.weapon_body.new_hud_interface.name) + if new_hud_interface_length > 0: + output_stream.write(struct.pack('<%ssx' % new_hud_interface_length, TAG.string_to_bytes(WEAPON.weapon_body.new_hud_interface.name, False))) + + write_magazines(output_stream, TAG, WEAPON.magazines, WEAPON.magazines_header) + write_new_triggers(output_stream, TAG, WEAPON.new_triggers, WEAPON.new_triggers_header) + write_barrels(output_stream, TAG, WEAPON.barrels, WEAPON.barrels_header) + + deployed_vehicle_length = len(WEAPON.weapon_body.deployed_vehicle.name) + if deployed_vehicle_length > 0: + output_stream.write(struct.pack('<%ssx' % deployed_vehicle_length, TAG.string_to_bytes(WEAPON.weapon_body.deployed_vehicle.name, False))) + + age_effect_length = len(WEAPON.weapon_body.age_effect.name) + if age_effect_length > 0: + output_stream.write(struct.pack('<%ssx' % age_effect_length, TAG.string_to_bytes(WEAPON.weapon_body.age_effect.name, False))) + + aged_weapon_length = len(WEAPON.weapon_body.aged_weapon.name) + if aged_weapon_length > 0: + output_stream.write(struct.pack('<%ssx' % aged_weapon_length, TAG.string_to_bytes(WEAPON.weapon_body.aged_weapon.name, False))) \ No newline at end of file diff --git a/io_scene_halo/file_tag/h2/file_weapon/format.py b/io_scene_halo/file_tag/h2/file_weapon/format.py index bd976af4c..8059b7b43 100644 --- a/io_scene_halo/file_tag/h2/file_weapon/format.py +++ b/io_scene_halo/file_tag/h2/file_weapon/format.py @@ -26,38 +26,7 @@ from mathutils import Vector from enum import Flag, Enum, auto - -class ObjectFlags(Flag): - does_not_cast_shadow = auto() - search_cardinal_direction_lightmaps_on_failure = auto() - unused = auto() - not_a_pathfinding_obstacle = auto() - extension_of_parent = auto() - does_not_cause_collision_damage = auto() - early_mover = auto() - early_mover_localized_physics = auto() - use_static_massive_lightmap_sample = auto() - object_scales_attachments = auto() - inherits_players_appearance = auto() - dead_bipeds_cant_localize = auto() - attach_to_clusters_dynamic_sphere = auto() - effects_created_by_this_object_do_not_spawn_objects_in_multiplayer = auto() - prophet_is_not_displayed_in_pegasus_builds = auto() - -class LightmapShadowModeEnum(Enum): - default = 0 - never = auto() - always = auto() - -class SweetenerSizeEnum(Enum): - small = 0 - medium = auto() - large = auto() - -class ItemFlags(Flag): - always_maintains_z_up = auto() - destroyed_by_explosions = auto() - unaffected_by_gravity = auto() +from ..file_item.format import ItemAsset class WeaponFlags(Flag): unused_0 = auto() @@ -171,27 +140,47 @@ class TrackingTypeEnum(Enum): human_tracking = auto() plasma_tracking = auto() -class WeaponAsset(): +class BehaviorEnum(Enum): + spew = 0 + latch = auto() + latch_autofire = auto() + charge = auto() + latch_zoom = auto() + latch_rocketlauncher = auto() + +class TriggerPredictionEnum(Enum): + none = 0 + spew = auto() + charge = auto() + +class BarrelFlags(Flag): + tracks_fired_projectile = auto() + random_firing_effects = auto() + can_fire_with_partial_ammo = auto() + projectiles_use_weapon_origin = auto() + ejects_during_chamber = auto() + use_error_when_unzoomed = auto() + projectile_vector_cannot_be_adjusted = auto() + projectiles_have_identical_error = auto() + projectiles_fire_parallel = auto() + cant_fire_when_others_firing_ = auto() + cant_fire_when_others_recovering = auto() + dont_clear_fire_bit_after_recovering = auto() + stagger_fire_across_multiple_markers = auto() + fires_locked_projectiles = auto() + can_fire_at_maximum_age = auto() + +class BarrelPredictionEnum(Enum): + none = 0 + continuous = auto() + instant = auto() + +class WeaponAsset(ItemAsset): def __init__(self): + super().__init__() self.header = None self.weapon_body_header = None self.weapon_body = None - self.ai_properties_header = None - self.ai_properties = None - self.functions_header = None - self.functions = None - self.attachments_header = None - self.attachments = None - self.widgets_header = None - self.widgets = None - self.old_functions_header = None - self.old_functions = None - self.change_colors_header = None - self.change_colors = None - self.predicted_resources_header = None - self.predicted_resources = None - self.predicted_bitmaps_header = None - self.predicted_bitmaps = None self.first_person_header = None self.first_person = None self.weapon_predicted_resources_header = None @@ -203,97 +192,23 @@ def __init__(self): self.barrels_header = None self.barrels = None - class WeaponBody: - def __init__(self, object_flags=0, bounding_radius=0.0, bounding_offset=Vector(), acceleration_scale=0.0, lightmap_shadow_mode=0, sweetner_size=0, - dynamic_light_sphere_radius=0.0, dynamic_light_sphere_offset=Vector(), default_model_variant="", default_model_variant_length=0, model=None, crate_object=None, - modifier_shader=None, creation_effect=None, material_effects=None, ai_properties_tag_block=None, functions_tag_block=None, apply_collision_damage_scale=0.0, - min_game_acc=0.0, max_game_acc=0.0, min_game_scale=0.0, max_game_scale=0.0, min_abs_acc=0.0, max_abs_acc=0.0, min_abs_scale=0.0, max_abs_scale=0.0, - hud_text_message_index=0, attachments_tag_block=None, widgets_tag_block=None, old_functions_tag_block=None, change_colors_tag_block=None, - predicted_resources_tag_block=None, equipment_flags=0, old_message_index=0, sort_order=0, multiplayer_on_ground_scale=0.0, campaign_on_ground_scale=0.0, - pickup_message="", pickup_message_length=0, swap_message="", swap_message_length=0, pickup_or_dual_message="", pickup_or_dual_message_length=0, - swap_or_dual_message="", swap_or_dual_message_length=0, dual_only_message="", dual_only_message_length=0, picked_up_message="", picked_up_message_length=0, - singluar_quantity_message="", singluar_quantity_message_length=0, plural_quantity_message="", plural_quantity_message_length=0, switch_to_message="", - switch_to_message_length=0, switch_to_from_ai_message="", switch_to_from_ai_message_length=0, unused=None, collision_sound=None, - predicted_bitmaps_tag_block=None, detonation_damage_effect=None, detonation_delay=(0.0, 0.0), detonating_effect=None, detonation_effect=None, weapon_flags=0, - unknown="", unknown_length=0, secondary_trigger_mode=0, maximum_alternate_shots_loaded=0, turn_on_time=0.0, ready_time=0.0, ready_effect=None, - ready_damage_effect=None, heat_recovery_threshold=0.0, overheated_threshold=0.0, heat_detonation_threshold=0.0, heat_detonation_fraction=0.0, + class WeaponBody(ItemAsset.ItemBody): + def __init__(self, weapon_flags=0, unknown="", unknown_length=0, secondary_trigger_mode=0, maximum_alternate_shots_loaded=0, turn_on_time=0.0, ready_time=0.0, + ready_effect=None, ready_damage_effect=None, heat_recovery_threshold=0.0, overheated_threshold=0.0, heat_detonation_threshold=0.0, heat_detonation_fraction=0.0, heat_loss_per_second=0.0, heat_illumination=0.0, overheated_loss_per_second=0.0, overheated=None, overheated_damage_effect=None, detonation=None, weapon_detonation_damage_effect=None, player_melee_damage=None, player_melee_response=None, magnetism_angle=0.0, magnetism_range=0.0, throttle_magnitude=0.0, throttle_minimum_distance=0.0, throttle_maximum_adjustment_angle=0.0, damage_pyramid_angles=(0.0, 0.0), damage_pyramid_depth=0.0, first_hit_melee_damage=None, first_hit_melee_response=None, second_hit_melee_damage=None, second_hit_melee_response=None, third_hit_melee_damage=None, third_hit_melee_response=None, lunge_melee_damage=None, lunge_melee_response=None, melee_damage_reporting_type=0, magnification_levels=0, magnification_range=(0.0, 0.0), autoaim_angle=0.0, autoaim_range=0.0, weapon_aim_assist_magnetism_angle=0.0, weapon_aim_assist_magnetism_range=0.0, deviation_angle=0.0, movement_penalized=0, - forward_movement_penalty=0.0, sideways_movement_penalty=0.0, ai_scariness=0.0, weapon_power_on_time=0.0, weapon_power_off_time=0.0, - weapon_power_on_effect=None, weapon_power_off_effect=None, age_heat_recovery_penalty=0.0, age_rate_of_fire_penalty=0.0, age_misfire_start=0.0, - age_misfire_chance=0.0, pickup_sound=None, zoom_in_sound=None, zoom_out_sound=None, active_camo_ding=0.0, active_camo_regrowth_rate=0.0, handle_node="", - handle_node_length=0, weapon_class="", weapon_class_length=0, weapon_name="", weapon_name_length=0, multiplayer_weapon_type=0, weapon_type=0, - tracking_type=0, first_person_tag_block=None, new_hud_interface=None, weapon_predicted_resources_tag_block=None, magazines_tag_block=None, - new_triggers_tag_block=None, barrels_tag_block=None, max_movement_acceleration=0.0, max_movement_velocity=0.0, max_turning_acceleration=0.0, - max_turning_velocity=0.0, deployed_vehicle=None, age_effect=None, aged_weapon=None, first_person_weapon_offset=Vector(), first_person_scope_size=(0.0, 0.0)): - self.object_flags = object_flags - self.bounding_radius = bounding_radius - self.bounding_offset = bounding_offset - self.acceleration_scale = acceleration_scale - self.lightmap_shadow_mode = lightmap_shadow_mode - self.sweetner_size = sweetner_size - self.dynamic_light_sphere_radius = dynamic_light_sphere_radius - self.dynamic_light_sphere_offset = dynamic_light_sphere_offset - self.default_model_variant = default_model_variant - self.default_model_variant_length = default_model_variant_length - self.model = model - self.crate_object = crate_object - self.modifier_shader = modifier_shader - self.creation_effect = creation_effect - self.material_effects = material_effects - self.ai_properties_tag_block = ai_properties_tag_block - self.functions_tag_block = functions_tag_block - self.apply_collision_damage_scale = apply_collision_damage_scale - self.min_game_acc = min_game_acc - self.max_game_acc = max_game_acc - self.min_game_scale = min_game_scale - self.max_game_scale = max_game_scale - self.min_abs_acc = min_abs_acc - self.max_abs_acc = max_abs_acc - self.min_abs_scale = min_abs_scale - self.max_abs_scale = max_abs_scale - self.hud_text_message_index = hud_text_message_index - self.attachments_tag_block = attachments_tag_block - self.widgets_tag_block = widgets_tag_block - self.old_functions_tag_block = old_functions_tag_block - self.change_colors_tag_block = change_colors_tag_block - self.predicted_resources_tag_block = predicted_resources_tag_block - self.equipment_flags = equipment_flags - self.old_message_index = old_message_index - self.sort_order = sort_order - self.multiplayer_on_ground_scale = multiplayer_on_ground_scale - self.campaign_on_ground_scale = campaign_on_ground_scale - self.pickup_message = pickup_message - self.pickup_message_length = pickup_message_length - self.swap_message = swap_message - self.swap_message_length = swap_message_length - self.pickup_or_dual_message = pickup_or_dual_message - self.pickup_or_dual_message_length = pickup_or_dual_message_length - self.swap_or_dual_message = swap_or_dual_message - self.swap_or_dual_message_length = swap_or_dual_message_length - self.dual_only_message = dual_only_message - self.dual_only_message_length = dual_only_message_length - self.picked_up_message = picked_up_message - self.picked_up_message_length = picked_up_message_length - self.singluar_quantity_message = singluar_quantity_message - self.singluar_quantity_message_length = singluar_quantity_message_length - self.plural_quantity_message = plural_quantity_message - self.plural_quantity_message_length = plural_quantity_message_length - self.switch_to_message = switch_to_message - self.switch_to_message_length = switch_to_message_length - self.switch_to_from_ai_message = switch_to_from_ai_message - self.switch_to_from_ai_message_length = switch_to_from_ai_message_length - self.unused = unused - self.collision_sound = collision_sound - self.predicted_bitmaps_tag_block = predicted_bitmaps_tag_block - self.detonation_damage_effect = detonation_damage_effect - self.detonation_delay = detonation_delay - self.detonating_effect = detonating_effect - self.detonation_effect = detonation_effect + forward_movement_penalty=0.0, sideways_movement_penalty=0.0, ai_scariness=0.0, weapon_power_on_time=0.0, weapon_power_off_time=0.0, weapon_power_on_effect=None, + weapon_power_off_effect=None, age_heat_recovery_penalty=0.0, age_rate_of_fire_penalty=0.0, age_misfire_start=0.0, age_misfire_chance=0.0, pickup_sound=None, + zoom_in_sound=None, zoom_out_sound=None, active_camo_ding=0.0, active_camo_regrowth_rate=0.0, handle_node="", handle_node_length=0, weapon_class="", + weapon_class_length=0, weapon_name="", weapon_name_length=0, multiplayer_weapon_type=0, weapon_type=0, tracking_type=0, first_person_tag_block=None, + new_hud_interface=None, weapon_predicted_resources_tag_block=None, magazines_tag_block=None, new_triggers_tag_block=None, barrels_tag_block=None, + max_movement_acceleration=0.0, max_movement_velocity=0.0, max_turning_acceleration=0.0, max_turning_velocity=0.0, deployed_vehicle=None, age_effect=None, + aged_weapon=None, first_person_weapon_offset=Vector(), first_person_scope_size=(0.0, 0.0)): + super().__init__() self.weapon_flags = weapon_flags self.unknown = unknown self.unknown_length = unknown_length @@ -380,3 +295,125 @@ def __init__(self, object_flags=0, bounding_radius=0.0, bounding_offset=Vector() self.aged_weapon = aged_weapon self.first_person_weapon_offset = first_person_weapon_offset self.first_person_scope_size = first_person_scope_size + + class FirstPerson: + def __init__(self, first_person_model=None, first_person_animations=None): + self.first_person_model = first_person_model + self.first_person_animations = first_person_animations + + class MagazineStats: + def __init__(self, flags=0, rounds_recharged=0, rounds_total_initial=0, rounds_total_maximum=0, rounds_loaded_maximum=0, reload_time=0.0, + rounds_reloaded=0, chamber_time=0.0, reloading_effect=None, reloading_damage_effect=None, chambering_effect=None, + chambering_damage_effect=None, magazines_tag_block=None, magazines_header=None, magazines=None): + self.flags = flags + self.rounds_recharged = rounds_recharged + self.rounds_total_initial = rounds_total_initial + self.rounds_total_maximum = rounds_total_maximum + self.rounds_loaded_maximum = rounds_loaded_maximum + self.reload_time = reload_time + self.rounds_reloaded = rounds_reloaded + self.chamber_time = chamber_time + self.reloading_effect = reloading_effect + self.reloading_damage_effect = reloading_damage_effect + self.chambering_effect = chambering_effect + self.chambering_damage_effect = chambering_damage_effect + self.magazines_tag_block = magazines_tag_block + self.magazines_header = magazines_header + self.magazines = magazines + + class Magazine: + def __init__(self, rounds=0, equipment=None): + self.rounds = rounds + self.equipment = equipment + + class NewTrigger: + def __init__(self, flags=0, input_type=0, behavior=0, primary_barrel=0, secondary_barrel=0, prediction=0, autofire_time=0.0, autofire_throw=0.0, + secondary_action=0, primary_action=0, charging_time=0.0, charged_time=0.0, overcharged_action=0, charged_illumination=0.0, spew_time=0.0, + charging_effect=None, charging_damage_effect=None): + self.flags = flags + self.input_type = input_type + self.behavior = behavior + self.primary_barrel = primary_barrel + self.secondary_barrel = secondary_barrel + self.prediction = prediction + self.autofire_time = autofire_time + self.autofire_throw = autofire_throw + self.secondary_action = secondary_action + self.primary_action = primary_action + self.charging_time = charging_time + self.charged_time = charged_time + self.overcharged_action = overcharged_action + self.charged_illumination = charged_illumination + self.spew_time = spew_time + self.charging_effect = charging_effect + self.charging_damage_effect = charging_damage_effect + + class Barrel: + def __init__(self, flags=0, rounds_per_second=(0.0, 0.0), firing_acceleration_time=0.0, firing_deceleration_time=0.0, barrel_spin_scale=0.0, + blurred_rate_of_fire=0.0, shots_per_fire=(0, 0), fire_recovery_time=0.0, soft_recovery_fraction=0.0, magazine=0, rounds_per_shot=0, + minimum_rounds_loaded=0, rounds_between_tracers=0, optional_barrel_marker_name="", optional_barrel_marker_name_length=0, prediction_type=0, + firing_noise=0, error_acceleration_time=0.0, error_deceleration_time=0.0, damage_error=(0.0, 0.0), dual_acceleration_time=0.0, + dual_deceleration_time=0.0, dual_minimum_error=0.0, dual_error_angle=(0.0, 0.0), dual_wield_damage_scale=0.0, distribution_function=0, + projectiles_per_shot=0, distribution_angle=0.0, projectile_minimum_error=0.0, projectile_error_angle=(0.0, 0.0), + first_person_offset=Vector(), damage_effect_reporting_type=0, projectile=None, damage_effect=None, ejection_port_recovery_time=0.0, + illumination_recovery_time=0.0, heat_generated_per_round=0.0, age_generated_per_round=0.0, overload_time=0.0, + angle_change_per_shot=(0.0, 0.0), recoil_acceleration_time=0.0, recoil_deceleration_time=0.0, angle_change_function=0, + firing_effects_tag_block=None, firing_effects_header=None, firing_effects=None): + self.flags = flags + self.rounds_per_second = rounds_per_second + self.firing_acceleration_time = firing_acceleration_time + self.firing_deceleration_time = firing_deceleration_time + self.barrel_spin_scale = barrel_spin_scale + self.blurred_rate_of_fire = blurred_rate_of_fire + self.shots_per_fire = shots_per_fire + self.fire_recovery_time = fire_recovery_time + self.soft_recovery_fraction = soft_recovery_fraction + self.magazine = magazine + self.rounds_per_shot = rounds_per_shot + self.minimum_rounds_loaded = minimum_rounds_loaded + self.rounds_between_tracers = rounds_between_tracers + self.optional_barrel_marker_name = optional_barrel_marker_name + self.optional_barrel_marker_name_length = optional_barrel_marker_name_length + self.prediction_type = prediction_type + self.firing_noise = firing_noise + self.error_acceleration_time = error_acceleration_time + self.error_deceleration_time = error_deceleration_time + self.damage_error = damage_error + self.dual_acceleration_time = dual_acceleration_time + self.dual_deceleration_time = dual_deceleration_time + self.dual_minimum_error = dual_minimum_error + self.dual_error_angle = dual_error_angle + self.dual_wield_damage_scale = dual_wield_damage_scale + self.distribution_function = distribution_function + self.projectiles_per_shot = projectiles_per_shot + self.distribution_angle = distribution_angle + self.projectile_minimum_error = projectile_minimum_error + self.projectile_error_angle = projectile_error_angle + self.first_person_offset = first_person_offset + self.damage_effect_reporting_type = damage_effect_reporting_type + self.projectile = projectile + self.damage_effect = damage_effect + self.ejection_port_recovery_time = ejection_port_recovery_time + self.illumination_recovery_time = illumination_recovery_time + self.heat_generated_per_round = heat_generated_per_round + self.age_generated_per_round = age_generated_per_round + self.overload_time = overload_time + self.angle_change_per_shot = angle_change_per_shot + self.recoil_acceleration_time = recoil_acceleration_time + self.recoil_deceleration_time = recoil_deceleration_time + self.angle_change_function = angle_change_function + self.firing_effects_tag_block = firing_effects_tag_block + self.firing_effects_header = firing_effects_header + self.firing_effects = firing_effects + + class FiringEffect: + def __init__(self, shot_count_lower_bound=0, shot_count_upper_bound=0, firing_effect=None, misfire_effect=None, empty_effect=None, firing_damage=None, + misfire_damage=None, empty_damage=None): + self.shot_count_lower_bound = shot_count_lower_bound + self.shot_count_upper_bound = shot_count_upper_bound + self.firing_effect = firing_effect + self.misfire_effect = misfire_effect + self.empty_effect = empty_effect + self.firing_damage = firing_damage + self.misfire_damage = misfire_damage + self.empty_damage = empty_damage diff --git a/io_scene_halo/file_tag/h2/file_weapon/process_file.py b/io_scene_halo/file_tag/h2/file_weapon/process_file.py index dabc23cc2..3505cbdf9 100644 --- a/io_scene_halo/file_tag/h2/file_weapon/process_file.py +++ b/io_scene_halo/file_tag/h2/file_weapon/process_file.py @@ -26,12 +26,10 @@ from xml.dom import minidom from ....global_functions import tag_format +from ..file_object.format import ObjectFlags, LightmapShadowModeEnum, SweetenerSizeEnum +from ..file_item.format import ItemFlags from .format import ( WeaponAsset, - ObjectFlags, - LightmapShadowModeEnum, - SweetenerSizeEnum, - ItemFlags, WeaponFlags, SecondaryTriggerModeEnum, MeleeDamageReportingTypeEnum, @@ -138,8 +136,7 @@ def read_weapon_body(WEAPON, TAG, input_stream, tag_node, XML_OUTPUT): WEAPON.weapon_body.detonating_effect = TAG.TagRef().read(input_stream, TAG, tag_format.XMLData(tag_node, "detonating effect")) WEAPON.weapon_body.detonation_effect = TAG.TagRef().read(input_stream, TAG, tag_format.XMLData(tag_node, "detonation effect")) - WEAPON.weapon_body.weapon_flags = TAG.read_flag_unsigned_short(input_stream, TAG, tag_format.XMLData(tag_node, "flags", WeaponFlags)) - input_stream.read(2) # Padding? + WEAPON.weapon_body.weapon_flags = TAG.read_flag_unsigned_integer(input_stream, TAG, tag_format.XMLData(tag_node, "flags", WeaponFlags)) TAG.big_endian = True input_stream.read(2) # Padding? @@ -180,7 +177,8 @@ def read_weapon_body(WEAPON, TAG, input_stream, tag_node, XML_OUTPUT): WEAPON.weapon_body.third_hit_melee_response = TAG.TagRef().read(input_stream, TAG, tag_format.XMLData(tag_node, "third hit melee response")) WEAPON.weapon_body.lunge_melee_damage = TAG.TagRef().read(input_stream, TAG, tag_format.XMLData(tag_node, "lunge melee damage")) WEAPON.weapon_body.lunge_melee_response = TAG.TagRef().read(input_stream, TAG, tag_format.XMLData(tag_node, "lunge melee response")) - WEAPON.weapon_body.melee_damage_reporting_type = TAG.read_enum_unsigned_short(input_stream, TAG, tag_format.XMLData(tag_node, "melee damage reporting type", MeleeDamageReportingTypeEnum)) + WEAPON.weapon_body.melee_damage_reporting_type = TAG.read_enum_unsigned_byte(input_stream, TAG, tag_format.XMLData(tag_node, "melee damage reporting type", MeleeDamageReportingTypeEnum)) + input_stream.read(1) # Padding? WEAPON.weapon_body.magnification_levels = TAG.read_signed_short(input_stream, TAG, tag_format.XMLData(tag_node, "magnification levels")) WEAPON.weapon_body.magnification_range = TAG.read_min_max(input_stream, TAG, tag_format.XMLData(tag_node, "magnification range")) WEAPON.weapon_body.autoaim_angle = TAG.read_degree(input_stream, TAG, tag_format.XMLData(tag_node, "autoaim angle")) diff --git a/io_scene_halo/global_functions/mesh_processing.py b/io_scene_halo/global_functions/mesh_processing.py index 90d7b29bd..248e64273 100644 --- a/io_scene_halo/global_functions/mesh_processing.py +++ b/io_scene_halo/global_functions/mesh_processing.py @@ -32,6 +32,8 @@ from math import radians from mathutils import Vector, Matrix from ..global_functions import global_functions, mesh_processing +from ..file_tag.h2.file_render_model.format import DetailLevelsFlags + class Surface: def __init__(self, material_index=0, surface_normal=Vector(), vertices=None): @@ -574,6 +576,7 @@ def generate_mesh_object_retail(asset, object_vertices, object_triangles, object object_vertices, object_triangles = optimize_geo(object_vertices, object_triangles) verts = [vertex.translation for vertex in object_vertices] + vertex_normals = [vertex.normal for vertex in object_vertices] tris = [(triangles.v0, triangles.v1, triangles.v2) for triangles in object_triangles] mesh = bpy.data.meshes.new(object_name) @@ -583,6 +586,7 @@ def generate_mesh_object_retail(asset, object_vertices, object_triangles, object poly.use_smooth = True region_attribute = mesh.get_custom_attribute() + mesh.normals_split_custom_set_from_vertices(vertex_normals) for vertex_idx, vertex in enumerate(object_vertices): for node_values in vertex.node_set: node_index = node_values[0] @@ -866,12 +870,12 @@ def process_mesh_export_vert(vertex_data, loop_data, loop_normals, file_type, or final_translation = original_geo_matrix @ translation if loop_normals: - final_normal = (original_geo_matrix @ (translation + loop_data.normal) - final_translation).normalized() + final_normal = (original_geo_matrix.to_3x3() @ loop_data.normal).normalized() if final_normal.length == 0.0: - final_normal = (original_geo_matrix @ (translation + vertex_data.normal) - final_translation).normalized() + final_normal = (original_geo_matrix.to_3x3() @ vertex_data.normal).normalized() else: - final_normal = (original_geo_matrix @ (translation + vertex_data.normal) - final_translation).normalized() + final_normal = (original_geo_matrix.to_3x3() @ vertex_data.normal).normalized() if negative_matrix and original_geo_matrix.determinant() < 0.0 and file_type == 'JMS': invert_normal_x = final_normal[0] * -1 @@ -983,6 +987,7 @@ def get_mesh_data(ASSET, section_data, mesh, material_count, materials, random_c triangles = [] triangle_mat_indices = [] vertices = [raw_vertex.position for raw_vertex in section_data.raw_vertices] + vertex_normals = [raw_vertex.normal for raw_vertex in section_data.raw_vertices] for part_idx, part in enumerate(section_data.parts): triangle_part = [] @@ -1023,6 +1028,7 @@ def get_mesh_data(ASSET, section_data, mesh, material_count, materials, random_c for tri_idx, poly in enumerate(mesh.polygons): poly.use_smooth = True + mesh.normals_split_custom_set_from_vertices(vertex_normals) for triangle_idx, triangle in enumerate(triangles): triangle_material_index = triangle_mat_indices[triangle_idx] if not triangle_material_index == -1 and triangle_material_index < material_count: diff --git a/io_scene_halo/global_functions/shader_processing.py b/io_scene_halo/global_functions/shader_processing.py index 9f282a729..4e508efe1 100644 --- a/io_scene_halo/global_functions/shader_processing.py +++ b/io_scene_halo/global_functions/shader_processing.py @@ -27,13 +27,30 @@ import os import bpy import zlib +import struct import numpy as np from .. import config from mathutils import Vector +from enum import Flag, Enum, auto from ..file_tag.h1.file_shader_environment.format import EnvironmentTypeEnum, EnvironmentFlags, DiffuseFlags, ReflectionFlags from ..file_tag.h1.file_shader_model.format import ModelFlags, DetailFumctionEnum, DetailMaskEnum, FunctionEnum from ..file_tag.h1.file_bitmap.format import FormatEnum +from ..file_tag.h2.file_shader.format import ( + ShaderAsset, + ShaderFlags, + TypeEnum, + ShaderLODBiasEnum, + SpecularTypeEnum, + LightmapTypeEnum, + AnimationTypeEnum, + FunctionTypeEnum, + OutputTypeFlags, + TransitionExponentEnum, + PeriodicExponentEnum, + ) +from ..file_tag.h2.file_particle.format import OutputModifierInputEnum +from . import global_functions try: from PIL import Image @@ -1939,3 +1956,621 @@ def generate_h2_shader(mat, tag_ref, report): else: print("Skipping shader") #generate_shader_environment(mat, shader, report) + +class OpaqueTemplateEnum(Enum): + active_camo_opaque = r'shaders\shader_templates\opaque\active_camo_opaque' + bloom = r'shaders\shader_templates\opaque\bloom' + decal_simple = r'shaders\shader_templates\opaque\decal_simple' + emblem_flag = r'shaders\shader_templates\opaque\emblem_flag' + emblem_opaque = r'shaders\shader_templates\opaque\emblem_opaque' + emblem_overlay = r'shaders\shader_templates\opaque\emblem_overlay' + emblem_overlay_simple = r'shaders\shader_templates\opaque\emblem_overlay_simple' + illum = r'shaders\shader_templates\opaque\illum' + illum_3_channel = r'shaders\shader_templates\opaque\illum_3_channel' + illum_3_channel_opaque = r'shaders\shader_templates\opaque\illum_3_channel_opaque' + illum_3_channel_plasma = r'shaders\shader_templates\opaque\illum_3_channel_plasma' + illum_bloom = r'shaders\shader_templates\opaque\illum_bloom' + illum_bloom_3_channel = r'shaders\shader_templates\opaque\illum_bloom_3_channel' + illum_bloom_3_channel_opaque = r'shaders\shader_templates\opaque\illum_bloom_3_channel_opaque' + illum_bloom_masked = r'shaders\shader_templates\opaque\illum_bloom_masked' + illum_bloom_opaque = r'shaders\shader_templates\opaque\illum_bloom_opaque' + illum_clamped = r'shaders\shader_templates\opaque\illum_clamped' + illum_detail = r'shaders\shader_templates\opaque\illum_detail' + illum_opaque = r'shaders\shader_templates\opaque\illum_opaque' + illum_opaque_index = r'shaders\shader_templates\opaque\illum_opaque_index' + illum_wrap = r'shaders\shader_templates\opaque\illum_wrap' + overlay = r'shaders\shader_templates\opaque\overlay' + prt_lightmap = r'shaders\shader_templates\opaque\prt_lightmap' + prt_scarab = r'shaders\shader_templates\opaque\prt_scarab' + prt_simple = r'shaders\shader_templates\opaque\prt_simple' + prt_simple_lm_emissive = r'shaders\shader_templates\opaque\prt_simple_lm_emissive' + render_layer_disabled = r'shaders\shader_templates\opaque\render_layer_disabled' + tex_alpha_test = r'shaders\shader_templates\opaque\tex_alpha_test' + tex_alpha_test_clamped = r'shaders\shader_templates\opaque\tex_alpha_test_clamped' + tex_bump = r'shaders\shader_templates\opaque\tex_bump' + tex_bump_active_camo = r'shaders\shader_templates\opaque\tex_bump_active_camo' + tex_bump_alpha_test = r'shaders\shader_templates\opaque\tex_bump_alpha_test' + tex_bump_alpha_test_clamped = r'shaders\shader_templates\opaque\tex_bump_alpha_test_clamped' + tex_bump_alpha_test_clamped_single_pass = r'shaders\shader_templates\opaque\tex_bump_alpha_test_clamped_single_pass' + tex_bump_alpha_test_detail = r'shaders\shader_templates\opaque\tex_bump_alpha_test_detail' + tex_bump_alpha_test_single_pass = r'shaders\shader_templates\opaque\tex_bump_alpha_test_single_pass' + tex_bump_bloom = r'shaders\shader_templates\opaque\tex_bump_bloom' + tex_bump_clamped_multiply_map = r'shaders\shader_templates\opaque\tex_bump_clamped_multiply_map' + tex_bump_detail_blend = r'shaders\shader_templates\opaque\tex_bump_detail_blend' + tex_bump_detail_blend_detail = r'shaders\shader_templates\opaque\tex_bump_detail_blend_detail' + tex_bump_detail_blend_specular = r'shaders\shader_templates\opaque\tex_bump_detail_blend_specular' + tex_bump_detail_blend_specular_combined = r'shaders\shader_templates\opaque\tex_bump_detail_blend_specular_combined' + tex_bump_detail_blend_specular_dblmult = r'shaders\shader_templates\opaque\tex_bump_detail_blend_specular_dblmult' + tex_bump_detail_keep = r'shaders\shader_templates\opaque\tex_bump_detail_keep' + tex_bump_detail_keep_blend = r'shaders\shader_templates\opaque\tex_bump_detail_keep_blend' + tex_bump_detail_mask = r'shaders\shader_templates\opaque\tex_bump_detail_mask' + tex_bump_detail_overlay = r'shaders\shader_templates\opaque\tex_bump_detail_overlay' + tex_bump_dprs_env = r'shaders\shader_templates\opaque\tex_bump_dprs_env' + tex_bump_dprs_env_illum = r'shaders\shader_templates\opaque\tex_bump_dprs_env_illum' + tex_bump_dprs_env_illum_emissive = r'shaders\shader_templates\opaque\tex_bump_dprs_env_illum_emissive' + tex_bump_env = r'shaders\shader_templates\opaque\tex_bump_env' + tex_bump_env_alpha_test = r'shaders\shader_templates\opaque\tex_bump_env_alpha_test' + tex_bump_env_alpha_test_combined = r'shaders\shader_templates\opaque\tex_bump_env_alpha_test_combined' + tex_bump_env_alpha_test_indexed = r'shaders\shader_templates\opaque\tex_bump_env_alpha_test_indexed' + tex_bump_env_clamped = r'shaders\shader_templates\opaque\tex_bump_env_clamped' + tex_bump_env_clamped_combined = r'shaders\shader_templates\opaque\tex_bump_env_clamped_combined' + tex_bump_env_combined = r'shaders\shader_templates\opaque\tex_bump_env_combined' + tex_bump_env_dbl_spec = r'shaders\shader_templates\opaque\tex_bump_env_dbl_spec' + tex_bump_env_detail_blend = r'shaders\shader_templates\opaque\tex_bump_env_detail_blend' + tex_bump_env_detail_blend_specular = r'shaders\shader_templates\opaque\tex_bump_env_detail_blend_specular' + tex_bump_env_detail_mask = r'shaders\shader_templates\opaque\tex_bump_env_detail_mask' + tex_bump_env_detail_mask_combined = r'shaders\shader_templates\opaque\tex_bump_env_detail_mask_combined' + tex_bump_env_detail_overlay = r'shaders\shader_templates\opaque\tex_bump_env_detail_overlay' + tex_bump_env_detail_overlay_combined = r'shaders\shader_templates\opaque\tex_bump_env_detail_overlay_combined' + tex_bump_env_fast_masked = r'shaders\shader_templates\opaque\tex_bump_env_fast_masked' + tex_bump_env_four_change_color = r'shaders\shader_templates\opaque\tex_bump_env_four_change_color' + tex_bump_env_four_change_color_combined = r'shaders\shader_templates\opaque\tex_bump_env_four_change_color_combined' + tex_bump_env_illum = r'shaders\shader_templates\opaque\tex_bump_env_illum' + tex_bump_env_illum_3_channel = r'shaders\shader_templates\opaque\tex_bump_env_illum_3_channel' + tex_bump_env_illum_3_channel_combined = r'shaders\shader_templates\opaque\tex_bump_env_illum_3_channel_combined' + tex_bump_env_illum_3_channel_combined_unfucked = r'shaders\shader_templates\opaque\tex_bump_env_illum_3_channel_combined_unfucked' + tex_bump_env_illum_3_channel_occlusion = r'shaders\shader_templates\opaque\tex_bump_env_illum_3_channel_occlusion' + tex_bump_env_illum_3_channel_occlusion_combined = r'shaders\shader_templates\opaque\tex_bump_env_illum_3_channel_occlusion_combined' + tex_bump_env_illum_combined = r'shaders\shader_templates\opaque\tex_bump_env_illum_combined' + tex_bump_env_illum_combined_emmisive_map = r'shaders\shader_templates\opaque\tex_bump_env_illum_combined_emmisive_map' + tex_bump_env_illum_detail_honor_guard = r'shaders\shader_templates\opaque\tex_bump_env_illum_detail_honor_guard' + tex_bump_env_illum_detail_honor_guard_base = r'shaders\shader_templates\opaque\tex_bump_env_illum_detail_honor_guard_base' + tex_bump_env_illum_emmisive_map = r'shaders\shader_templates\opaque\tex_bump_env_illum_emmisive_map' + tex_bump_env_illum_four_change_color = r'shaders\shader_templates\opaque\tex_bump_env_illum_four_change_color' + tex_bump_env_illum_four_change_color_no_lod = r'shaders\shader_templates\opaque\tex_bump_env_illum_four_change_color_no_lod' + tex_bump_env_illum_tiling_specular = r'shaders\shader_templates\opaque\tex_bump_env_illum_tiling_specular' + tex_bump_env_illum_trace = r'shaders\shader_templates\opaque\tex_bump_env_illum_trace' + tex_bump_env_illum_two_change_color = r'shaders\shader_templates\opaque\tex_bump_env_illum_two_change_color' + tex_bump_env_illum_two_change_color_combined = r'shaders\shader_templates\opaque\tex_bump_env_illum_two_change_color_combined' + tex_bump_env_no_detail = r'shaders\shader_templates\opaque\tex_bump_env_no_detail' + tex_bump_env_tiling_specular = r'shaders\shader_templates\opaque\tex_bump_env_tiling_specular' + tex_bump_env_two_change_color = r'shaders\shader_templates\opaque\tex_bump_env_two_change_color' + tex_bump_env_two_change_color_combined = r'shaders\shader_templates\opaque\tex_bump_env_two_change_color_combined' + tex_bump_env_two_change_color_indexed = r'shaders\shader_templates\opaque\tex_bump_env_two_change_color_indexed' + tex_bump_env_two_change_color_multiply_map_self_illum = r'shaders\shader_templates\opaque\tex_bump_env_two_change_color_multiply_map_self_illum' + tex_bump_env_two_detail = r'shaders\shader_templates\opaque\tex_bump_env_two_detail' + tex_bump_env_two_detail_combined = r'shaders\shader_templates\opaque\tex_bump_env_two_detail_combined' + tex_bump_foliage = r'shaders\shader_templates\opaque\tex_bump_foliage' + tex_bump_four_change_color = r'shaders\shader_templates\opaque\tex_bump_four_change_color' + tex_bump_illum = r'shaders\shader_templates\opaque\tex_bump_illum' + tex_bump_illum_3_channel = r'shaders\shader_templates\opaque\tex_bump_illum_3_channel' + tex_bump_illum_alpha_test = r'shaders\shader_templates\opaque\tex_bump_illum_alpha_test' + tex_bump_illum_alpha_test_illum = r'shaders\shader_templates\opaque\tex_bump_illum_alpha_test_illum' + tex_bump_illum_alpha_test_single_pass = r'shaders\shader_templates\opaque\tex_bump_illum_alpha_test_single_pass' + tex_bump_illum_bloom = r'shaders\shader_templates\opaque\tex_bump_illum_bloom' + tex_bump_illum_bloom_3_channel = r'shaders\shader_templates\opaque\tex_bump_illum_bloom_3_channel' + tex_bump_illum_detail = r'shaders\shader_templates\opaque\tex_bump_illum_detail' + tex_bump_illum_detail_honor_guard = r'shaders\shader_templates\opaque\tex_bump_illum_detail_honor_guard' + tex_bump_illum_no_specular = r'shaders\shader_templates\opaque\tex_bump_illum_no_specular' + tex_bump_illum_trace = r'shaders\shader_templates\opaque\tex_bump_illum_trace' + tex_bump_illum_two_detail = r'shaders\shader_templates\opaque\tex_bump_illum_two_detail' + tex_bump_meter_illum = r'shaders\shader_templates\opaque\tex_bump_meter_illum' + tex_bump_multiply_map = r'shaders\shader_templates\opaque\tex_bump_multiply_map' + tex_bump_no_alpha = r'shaders\shader_templates\opaque\tex_bump_no_alpha' + tex_bump_no_specular = r'shaders\shader_templates\opaque\tex_bump_no_specular' + tex_bump_one_change_color = r'shaders\shader_templates\opaque\tex_bump_one_change_color' + tex_bump_plasma = r'shaders\shader_templates\opaque\tex_bump_plasma' + tex_bump_plasma_one_channel_illum = r'shaders\shader_templates\opaque\tex_bump_plasma_one_channel_illum' + tex_bump_shiny = r'shaders\shader_templates\opaque\tex_bump_shiny' + tex_bump_terrain = r'shaders\shader_templates\opaque\tex_bump_terrain' + tex_bump_three_detail_blend = r'shaders\shader_templates\opaque\tex_bump_three_detail_blend' + tex_bump_tiling_specular = r'shaders\shader_templates\opaque\tex_bump_tiling_specular' + tex_bump_two_change_color = r'shaders\shader_templates\opaque\tex_bump_two_change_color' + tex_bump_two_change_color_multiply_map = r'shaders\shader_templates\opaque\tex_bump_two_change_color_multiply_map' + tex_bump_two_change_color_multiply_map_self_illum = r'shaders\shader_templates\opaque\tex_bump_two_change_color_multiply_map_self_illum' + tex_bump_two_detail = r'shaders\shader_templates\opaque\tex_bump_two_detail' + tex_bump_two_detail_tint = r'shaders\shader_templates\opaque\tex_bump_two_detail_tint' + tex_detail_blend = r'shaders\shader_templates\opaque\tex_detail_blend' + tex_env = r'shaders\shader_templates\opaque\tex_env' + tex_env_3_channel_illum = r'shaders\shader_templates\opaque\tex_env_3_channel_illum' + tex_illum = r'shaders\shader_templates\opaque\tex_illum' + tex_illum_bloom = r'shaders\shader_templates\opaque\tex_illum_bloom' + +class TransparentTemplateEnum(Enum): + add_illum_detail = r'shaders\shader_templates\transparent\add_illum_detail' + bumped_environment_additive = r'shaders\shader_templates\transparent\bumped_environment_additive' + bumped_environment_blended = r'shaders\shader_templates\transparent\bumped_environment_blended' + bumped_environment_darkened = r'shaders\shader_templates\transparent\bumped_environment_darkened' + bumped_environment_masked = r'shaders\shader_templates\transparent\bumped_environment_masked' + bumped_environment_mask_colored = r'shaders\shader_templates\transparent\bumped_environment_mask_colored' + cortana = r'shaders\shader_templates\transparent\cortana' + cortana_holographic_active_camo = r'shaders\shader_templates\transparent\cortana_holographic_active_camo' + lit = r'shaders\shader_templates\transparent\lit' + meter = r'shaders\shader_templates\transparent\meter' + meter_active_camo = r'shaders\shader_templates\transparent\meter_active_camo' + one_add_changecolor_screenspace_xform2 = r'shaders\shader_templates\transparent\one_add_changecolor_screenspace_xform2' + one_add_env_illum = r'shaders\shader_templates\transparent\one_add_env_illum' + one_add_env_illum_clamped = r'shaders\shader_templates\transparent\one_add_env_illum_clamped' + one_add_env_illum_trace = r'shaders\shader_templates\transparent\one_add_env_illum_trace' + one_add_illum = r'shaders\shader_templates\transparent\one_add_illum' + one_add_illum_detail = r'shaders\shader_templates\transparent\one_add_illum_detail' + one_add_illum_no_fog = r'shaders\shader_templates\transparent\one_add_illum_no_fog' + one_add_illum_screenspace_xform2 = r'shaders\shader_templates\transparent\one_add_illum_screenspace_xform2' + one_add_two_plus_two = r'shaders\shader_templates\transparent\one_add_two_plus_two' + one_alpha_env = r'shaders\shader_templates\transparent\one_alpha_env' + one_alpha_env_active_camo = r'shaders\shader_templates\transparent\one_alpha_env_active_camo' + one_alpha_env_clamped = r'shaders\shader_templates\transparent\one_alpha_env_clamped' + one_alpha_env_fixed = r'shaders\shader_templates\transparent\one_alpha_env_fixed' + one_alpha_env_illum = r'shaders\shader_templates\transparent\one_alpha_env_illum' + one_alpha_env_illum_specular_mask = r'shaders\shader_templates\transparent\one_alpha_env_illum_specular_mask' + one_alpha_env_plasma = r'shaders\shader_templates\transparent\one_alpha_env_plasma' + one_alpha_env_trace = r'shaders\shader_templates\transparent\one_alpha_env_trace' + overshield = r'shaders\shader_templates\transparent\overshield' + overshield_tartarus = r'shaders\shader_templates\transparent\overshield_tartarus' + particle_additive = r'shaders\shader_templates\transparent\particle_additive' + particle_additive_tint = r'shaders\shader_templates\transparent\particle_additive_tint' + particle_alpha_blend = r'shaders\shader_templates\transparent\particle_alpha_blend' + particle_alpha_blend_tint = r'shaders\shader_templates\transparent\particle_alpha_blend_tint' + particle_alpha_multiply_add = r'shaders\shader_templates\transparent\particle_alpha_multiply_add' + particle_alpha_multiply_add_tint = r'shaders\shader_templates\transparent\particle_alpha_multiply_add_tint' + particle_alpha_test = r'shaders\shader_templates\transparent\particle_alpha_test' + particle_alpha_test_no_lighting = r'shaders\shader_templates\transparent\particle_alpha_test_no_lighting' + particle_alpha_test_no_lighting_fixed_for_glass = r'shaders\shader_templates\transparent\particle_alpha_test_no_lighting_fixed_for_glass' + particle_alpha_test_tint = r'shaders\shader_templates\transparent\particle_alpha_test_tint' + particle_alpha_test_tint_no_lighting = r'shaders\shader_templates\transparent\particle_alpha_test_tint_no_lighting' + particle_plasma = r'shaders\shader_templates\transparent\particle_plasma' + plasma_1_channel = r'shaders\shader_templates\transparent\plasma_1_channel' + plasma_alpha = r'shaders\shader_templates\transparent\plasma_alpha' + plasma_alpha_active_camo = r'shaders\shader_templates\transparent\plasma_alpha_active_camo' + plasma_mask_offset = r'shaders\shader_templates\transparent\plasma_mask_offset' + plasma_mask_offset_active_camo = r'shaders\shader_templates\transparent\plasma_mask_offset_active_camo' + plasma_shield = r'shaders\shader_templates\transparent\plasma_shield' + plasma_shield_change_color = r'shaders\shader_templates\transparent\plasma_shield_change_color' + plasma_time = r'shaders\shader_templates\transparent\plasma_time' + roadsign_glass_newmombasa = r'shaders\shader_templates\transparent\roadsign_glass_newmombasa' + sky_one_add_illum_detail = r'shaders\shader_templates\transparent\sky_one_add_illum_detail' + sky_one_add_two_plus_two = r'shaders\shader_templates\transparent\sky_one_add_two_plus_two' + sky_one_alpha_env = r'shaders\shader_templates\transparent\sky_one_alpha_env' + sky_one_alpha_env_clamped = r'shaders\shader_templates\transparent\sky_one_alpha_env_clamped' + sky_one_alpha_env_illum = r'shaders\shader_templates\transparent\sky_one_alpha_env_illum' + sky_two_add_clouds = r'shaders\shader_templates\transparent\sky_two_add_clouds' + sky_two_add_clouds_clamped = r'shaders\shader_templates\transparent\sky_two_add_clouds_clamped' + sky_two_add_detail_masked = r'shaders\shader_templates\transparent\sky_two_add_detail_masked' + sky_two_alpha_clouds = r'shaders\shader_templates\transparent\sky_two_alpha_clouds' + tartarus_shield = r'shaders\shader_templates\transparent\tartarus_shield' + trace = r'shaders\shader_templates\transparent\trace' + transparent_glass = r'shaders\shader_templates\transparent\transparent_glass' + two_add_clouds = r'shaders\shader_templates\transparent\two_add_clouds' + two_add_detail_masked = r'shaders\shader_templates\transparent\two_add_detail_masked' + two_add_detail_masked_prepass = r'shaders\shader_templates\transparent\two_add_detail_masked_prepass' + two_add_detail_masked_trace = r'shaders\shader_templates\transparent\two_add_detail_masked_trace' + two_add_env_illum = r'shaders\shader_templates\transparent\two_add_env_illum' + two_add_env_illum_3_channel = r'shaders\shader_templates\transparent\two_add_env_illum_3_channel' + two_add_env_illum_active_camo = r'shaders\shader_templates\transparent\two_add_env_illum_active_camo' + two_add_tint = r'shaders\shader_templates\transparent\two_add_tint' + two_alpha_clouds = r'shaders\shader_templates\transparent\two_alpha_clouds' + two_alpha_detail_masked = r'shaders\shader_templates\transparent\two_alpha_detail_masked' + two_alpha_env_detail = r'shaders\shader_templates\transparent\two_alpha_env_detail' + two_alpha_env_illum = r'shaders\shader_templates\transparent\two_alpha_env_illum' + two_alpha_env_illum_bumped_environment_masked = r'shaders\shader_templates\transparent\two_alpha_env_illum_bumped_environment_masked' + two_alpha_env_multichannel = r'shaders\shader_templates\transparent\two_alpha_env_multichannel' + two_alpha_env_two_change_color = r'shaders\shader_templates\transparent\two_alpha_env_two_change_color' + two_alpha_two_change_color = r'shaders\shader_templates\transparent\two_alpha_two_change_color' + waterfall = r'shaders\shader_templates\transparent\waterfall' + waves = r'shaders\shader_templates\transparent\waves' + z_only_active_camo = r'shaders\shader_templates\transparent\z_only_active_camo' + +def conver_real_rgba_integer_bgra(material_color): + return (round(material_color[2] * 255), round(material_color[1] * 255), round(material_color[0] * 255), 0) + +def add_animation_property(SHADER, TAG, property_list, animation_type=AnimationTypeEnum.bitmap_index, input_name="", input_type=0, range_name="", range_type=0, time=0, + output_modifier=0, output_modifier_input=0, function_header=None, function_type=FunctionTypeEnum.constant, output_value=0, + material_colors=((0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)), lower_bound=0.0, upper_bound=1.0, + input_function=(0.0, 0.0, 0, 0.0, 0.0, []), range_function=(0.0, 0.0, 0, 0.0, 0.0, []), range_lower_bound=1.0, range_upper_bound=1.0): + animation_property = ShaderAsset.AnimationProperty() + + animation_property.type = animation_type.value + animation_property.input_name = input_name + animation_property.input_name_length = len(input_name) + animation_property.input_type = input_type + animation_property.range_name = range_name + animation_property.range_name_length = len(range_name) + animation_property.range_type = range_type + animation_property.time_period = time + animation_property.output_modifier = output_modifier + animation_property.output_modifier_input = output_modifier_input + animation_property.map_property_header = TAG.TagBlockHeader("MAPP", 1, 1, 12) + animation_property.function_header = function_header + animation_property.function_type = function_type.value + animation_property.range_check = output_value + animation_property.input_function_data = ShaderAsset.FunctionData() + animation_property.range_function_data = ShaderAsset.FunctionData() + animation_property.upper_bound = upper_bound + animation_property.lower_bound = lower_bound + animation_property.range_upper_bound = range_lower_bound + animation_property.range_lower_bound = range_upper_bound + animation_property.color_a = conver_real_rgba_integer_bgra(material_colors[0]) + animation_property.color_b = conver_real_rgba_integer_bgra(material_colors[1]) + animation_property.color_c = conver_real_rgba_integer_bgra(material_colors[2]) + animation_property.color_d = conver_real_rgba_integer_bgra(material_colors[3]) + + animation_property.input_function_data.points = [] + animation_property.range_function_data.points = [] + if FunctionTypeEnum.transition == function_type: + animation_property.input_function_data.min = input_function[0] + animation_property.input_function_data.max = input_function[1] + animation_property.input_function_data.exponent = input_function[2] + animation_property.input_function_data.frequency = input_function[3] + animation_property.input_function_data.phase = input_function[4] + + animation_property.range_function_data.min = range_function[0] + animation_property.range_function_data.max = range_function[1] + animation_property.range_function_data.exponent = range_function[2] + animation_property.range_function_data.frequency = range_function[3] + animation_property.range_function_data.phase = range_function[4] + + elif FunctionTypeEnum.periodic == function_type: + animation_property.input_function_data.min = input_function[0] + animation_property.input_function_data.max = input_function[1] + animation_property.input_function_data.exponent = input_function[2] + animation_property.input_function_data.frequency = input_function[3] + animation_property.input_function_data.phase = input_function[4] + + animation_property.range_function_data.min = range_function[0] + animation_property.range_function_data.max = range_function[1] + animation_property.range_function_data.exponent = range_function[2] + animation_property.range_function_data.frequency = range_function[3] + animation_property.range_function_data.phase = range_function[4] + + elif FunctionTypeEnum.linear == function_type: + animation_property.input_function_data.points = input_function[5] + animation_property.range_function_data.points = range_function[5] + elif FunctionTypeEnum.linear_key == function_type: + animation_property.input_function_data.points = input_function[5] + animation_property.range_function_data.points = range_function[5] + elif FunctionTypeEnum.multi_linear_key == function_type: + animation_property.input_function_data.points = input_function[5] + animation_property.range_function_data.points = range_function[5] + elif FunctionTypeEnum.spline == function_type: + animation_property.input_function_data.points = input_function[5] + animation_property.range_function_data.points = range_function[5] + elif FunctionTypeEnum.multi_spline == function_type: + animation_property.input_function_data.points = input_function[5] + animation_property.range_function_data.points = range_function[5] + + property_list.append(animation_property) + +def add_parameter(SHADER, TAG, parameter_name="", enum=TypeEnum.bitmap, bitmap_name="", float_value=1.0, rgba=(0.0, 0.0, 0.0, 1.0), replace_directory=False): + parameter = ShaderAsset.Parameter() + + path = bitmap_name + new_directory = r"scenarios\bitmaps\solo\a10" + if len(bitmap_name) > 0 and replace_directory: + base_name = os.path.basename(bitmap_name).replace(" ", "_") + path = os.path.join(new_directory, base_name) + + parameter.name = parameter_name + parameter.type = enum.value + parameter.bitmap = TAG.TagRef("bitm", path, len(path)) + parameter.const_value = float_value + parameter.const_color = rgba + + return parameter + +def get_percentage(channel): + value = 0.0 + if not channel == 0: + value = channel / 255 + + return value + +def write_identity(output_stream, TAG, animation_properties): + output_stream.write(struct.pack('<4s3I', TAG.string_to_bytes("tbfd", True), 0, 20, 1)) + output_stream.write(struct.pack(' 1: + output_stream.write(struct.pack(' 1: + output_stream.write(struct.pack(' 1: + output_stream.write(struct.pack(' 1: + output_stream.write(struct.pack(' 1: + output_stream.write(struct.pack(' 1: + output_stream.write(struct.pack(' 1: + output_stream.write(struct.pack(' 1: + output_stream.write(struct.pack(' 1: + output_stream.write(struct.pack(' 0: + parameters_header.write(output_stream, TAG, True) + for parameter_element in parameters: + output_stream.write(struct.pack('>I', len(parameter_element.name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % name_length, TAG.string_to_bytes(parameter_element.name, False))) + if bitmap_name_length > 0: + output_stream.write(struct.pack('<%ssx' % bitmap_name_length, TAG.string_to_bytes(parameter_element.bitmap.name, False))) + + if len(parameter_element.animation_properties) > 0: + parameter_element.animation_properties_header.write(output_stream, TAG, True) + for animation_properties in parameter_element.animation_properties: + output_stream.write(struct.pack('I', len(animation_properties.input_name))) + output_stream.write(struct.pack('>I', len(animation_properties.range_name))) + output_stream.write(struct.pack(' 0: + output_stream.write(struct.pack('<%ss' % input_name_length, TAG.string_to_bytes(animation_properties.input_name, False))) + if range_name_length > 0: + output_stream.write(struct.pack('<%ss' % range_name_length, TAG.string_to_bytes(animation_properties.range_name, False))) + + write_function(output_stream, TAG, animation_properties) + +def write_function_size(output_stream, function_property): + function_type = FunctionTypeEnum(function_property.function_type) + if FunctionTypeEnum.identity == function_type: + output_stream.write(struct.pack('