Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ class Fast64_ObjectProperties(bpy.types.PropertyGroup):
"""

sm64: bpy.props.PointerProperty(type=SM64_ObjectProperties, name="SM64 Object Properties")
oot: bpy.props.PointerProperty(type=OOT_ObjectProperties, name="OOT Object Properties")
oot: bpy.props.PointerProperty(type=OOT_ObjectProperties, name="Z64 Object Properties") # TODO: rename oot to z64


class UpgradeF3DMaterialsDialog(bpy.types.Operator):
Expand Down
2 changes: 1 addition & 1 deletion fast64_internal/data/z64/enum_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def __init__(self, game: str):
item.attrib["ID"],
item.attrib["Key"],
# note: the name sets automatically after the init if None
item.attrib["Name"] if enum.attrib["Key"] == "seqId" else None,
item.attrib.get("Name"),
int(item.attrib["Index"]),
enum.attrib["Key"],
game,
Expand Down
3 changes: 3 additions & 0 deletions fast64_internal/data/z64/xml/oot_enum_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,9 @@
<Item Key="sdc_fishing_pond" ID="SDC_FISHING_POND" Name="Fishing Pond (Turibori)" Index="50"/>
<Item Key="sdc_ganons_tower_collapse_interior" ID="SDC_GANONS_TOWER_COLLAPSE_INTERIOR" Name="Ganon's Tower (Collapsing) (Ganon Sonogo)" Index="51"/>
<Item Key="sdc_inside_ganons_castle_collapse" ID="SDC_INSIDE_GANONS_CASTLE_COLLAPSE" Name="Inside Ganon's Castle (Collapsing) (Ganontika Sonogo)" Index="52"/>
<!-- For mods -->
<Item Key="sdc_mat_anim" ID="SDC_MAT_ANIM" Name="Material Animated" Index="53"/>
<Item Key="sdc_mat_anim_manual_step" ID="SDC_MAT_ANIM_MANUAL_STEP" Name="Material Animated (manual step)" Index="54"/>
</Enum>
<Enum Key="global_object" ID="GlobalObjects">
<Item Key="object_gameplay_field_keep" ID="OBJECT_GAMEPLAY_FIELD_KEEP" Name="Overworld" Index="1"/>
Expand Down
10 changes: 9 additions & 1 deletion fast64_internal/f3d/f3d_gbi.py
Original file line number Diff line number Diff line change
Expand Up @@ -2203,7 +2203,10 @@ def to_binary(self, f3d, segments):
def to_c_static(self, name: str):
data = f"Gfx {name}[] = {{\n"
for command in self.commands:
data += f"\t{command.to_c(True)},\n"
if command.default_formatting:
data += f"\t{command.to_c(True)},\n"
else:
data += command.to_c(True)
data += "};\n\n"
return data

Expand Down Expand Up @@ -3415,6 +3418,11 @@ class GbiMacro:
This is unannotated and will not be considered when calculating the hash.
"""

default_formatting = True
"""
Type: bool. Used to allow an overriden `to_c` function customize the formatting (identation, newlines, etc).
"""

def get_ptr_offsets(self, f3d):
return [4]

Expand Down
67 changes: 43 additions & 24 deletions fast64_internal/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,11 @@ def getGroupNameFromIndex(obj, index):
return None


def copyPropertyCollection(oldProp, newProp):
newProp.clear()
for item in oldProp:
newItem = newProp.add()
def copyPropertyCollection(from_prop, to_prop, do_clear: bool = True):
if do_clear:
to_prop.clear()
for item in from_prop:
newItem = to_prop.add()
if isinstance(item, bpy.types.PropertyGroup):
copyPropertyGroup(item, newItem)
elif type(item).__name__ == "bpy_prop_collection_idprop":
Expand All @@ -273,18 +274,18 @@ def copyPropertyCollection(oldProp, newProp):
newItem = item


def copyPropertyGroup(oldProp, newProp):
for sub_value_attr in oldProp.bl_rna.properties.keys():
def copyPropertyGroup(from_prop, to_prop):
for sub_value_attr in from_prop.bl_rna.properties.keys():
if sub_value_attr == "rna_type":
continue
sub_value = getattr(oldProp, sub_value_attr)
sub_value = getattr(from_prop, sub_value_attr)
if isinstance(sub_value, bpy.types.PropertyGroup):
copyPropertyGroup(sub_value, getattr(newProp, sub_value_attr))
copyPropertyGroup(sub_value, getattr(to_prop, sub_value_attr))
elif type(sub_value).__name__ == "bpy_prop_collection_idprop":
newCollection = getattr(newProp, sub_value_attr)
newCollection = getattr(to_prop, sub_value_attr)
copyPropertyCollection(sub_value, newCollection)
else:
setattr(newProp, sub_value_attr, sub_value)
setattr(to_prop, sub_value_attr, sub_value)


def get_attr_or_property(prop: dict | object, attr: str, newProp: dict | object):
Expand Down Expand Up @@ -1331,7 +1332,7 @@ def filepath_ui_warnings(
return run_and_draw_errors(layout, filepath_checks, path, empty, doesnt_exist, not_a_file, False)


def toAlnum(name, exceptions=[]):
def toAlnum(name: str, exceptions=[]):
if name is None or name == "":
return None
for i in range(len(name)):
Expand Down Expand Up @@ -2061,17 +2062,35 @@ def get_include_data(include: str, strip: bool = False):


def get_new_object(
name: str, data: Optional[Any], selectObject: bool, parentObj: Optional[bpy.types.Object]
name: str,
data: Optional[Any],
do_select: bool,
location=[0.0, 0.0, 0.0],
rotation_euler=[0.0, 0.0, 0.0],
scale=[1.0, 1.0, 1.0],
parent: Optional[bpy.types.Object] = None,
) -> bpy.types.Object:
newObj = bpy.data.objects.new(name=name, object_data=data)
bpy.context.view_layer.active_layer_collection.collection.objects.link(newObj)

if selectObject:
newObj.select_set(True)
bpy.context.view_layer.objects.active = newObj

newObj.parent = parentObj
newObj.location = [0.0, 0.0, 0.0]
newObj.rotation_euler = [0.0, 0.0, 0.0]
newObj.scale = [1.0, 1.0, 1.0]
return newObj
new_obj = bpy.data.objects.new(name=name, object_data=data)
bpy.context.view_layer.active_layer_collection.collection.objects.link(new_obj)

if do_select:
new_obj.select_set(True)
bpy.context.view_layer.objects.active = new_obj

new_obj.parent = parent
new_obj.location = location
new_obj.rotation_euler = rotation_euler
new_obj.scale = scale
return new_obj


def get_new_empty_object(
name: str,
do_select: bool = False,
location=[0.0, 0.0, 0.0],
rotation_euler=[0.0, 0.0, 0.0],
scale=[1.0, 1.0, 1.0],
parent: Optional[bpy.types.Object] = None,
):
"""Creates and returns a new empty object"""
return get_new_object(name, None, do_select, location, rotation_euler, scale, parent)
41 changes: 41 additions & 0 deletions fast64_internal/z64/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
9. [Custom Link Process](#custom-link-process)
10. [Custom Skeleton Mesh Process](#custom-skeleton-mesh-process)
11. [Cutscenes](#cutscenes)
12. [Animated Materials](#animated-materials)

### Getting Started
1. In the 3D view properties sidebar (default hotkey to show this is `n` in the viewport), go to the ``Fast64`` tab, then ``Fast64 Global Settings`` and set ``Game`` to ``OOT``.
Expand Down Expand Up @@ -194,3 +195,43 @@ If the camera preview in Blender isn't following where you have the bones or if
2. If you moved / rotated / etc. one of the camera shots / armatures in object mode, this transformation will be ignored. You can fix this by selecting the shot / armature in object mode and clicking Object > Apply > All Transforms. That will convert the transform to actual changed positions for each bone.

If the game crashes check the transitions if you use the transition command (check both the ones from the entrance table and your cutscene script), also it will crash if you try to use the map select without having a 5th entrance (or more depending on the number of cutscenes you have) in the group for your scene.

### Animated Materials

This is a feature you can use for Majora's Mask and OoT backports like HackerOoT (requires enabling `Enable MM Features` for non-HackerOoT OoT decomp projects). It allows you to do some animation on any material you want, on Majora's Mask it's used to animate some actor's textures, and it's used in scenes too, this is what makes the walls in Majora's Lair animated, for instance.

**Important**: this requires the scene to use a specific draw config called `Material Animated` (or `Material Animated (manual step)` for special cases).

**Getting Started**

For non-scene export you'll need to either use the `Add Animated Material` button under the `Tools` tab, or manually adding an empty object and setting the object mode to `Animated Materials`. If doing the latter make sure the object is parented to the scene object, it can be parented to a room too, or anything else as long as the scene object is in the hierarchy, but it will be exported to the scene file.

For scenes it's integrated as a tab in the scene header properties panel.

**Creating the animated materials list**

For non-scene export, click on `Add Item` to add a new animated material list.

You can pick the segment number with the `Segment Number` field (make sure to use the same number on the material you want this to be used on), for convenience the exporter will add a macro to make it more readable. `Draw Handler Type` lets you choose what kind of animated material you want, it can be one of:
- `0`: Texture Scroll
- `1`: Two-textures Scroll
- `2`: Color
- `3`: Color LERP
- `4`: Color Non-linear Interpolation
- `5`: Texture Cycle (like a GIF)

For the LERP and non-linear interpolation color types you will also have a `Keyframe Length` field, this corresponds to the length of the animation. `Draw Color` (type 2) can use environment color but it's not mandatory unlike the other ones.

Both texture scroll types will use the same elements:
- `Step X`: step value on the X axis
- `Step Y`: step value on the Y axis
- `Texture Width`: the width of the texture
- `Texture Height`: the height of the texture

All 3 color types will use the same elements:
- `Frame No.`: when to execute this entry (relative to the keyframe length), not available for `Draw Color`
- `Primitive LOD Frac`: unknown purpose, feel free to complete!
- `Primitive Color`: the primitive color to apply
- `Environment Color`: the environment color to apply, optional for `Draw Color`

The texture cycle type will show you two lists to fill, one for the texture symbols to use and another one for the indices that points to the textures list. Note that both list don't need to be the same length, also this technically uses a keyframe length too but it should always match the total number of indices that's why you can't manually choose it.
77 changes: 54 additions & 23 deletions fast64_internal/z64/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@
from .spline.properties import spline_props_register, spline_props_unregister
from .spline.panels import spline_panels_register, spline_panels_unregister

from .animated_mats.operators import animated_mats_ops_register, animated_mats_ops_unregister
from .animated_mats.panels import animated_mats_panels_register, animated_mats_panels_unregister
from .animated_mats.properties import (
Z64_AnimatedMaterialExportSettings,
Z64_AnimatedMaterialImportSettings,
animated_mats_props_register,
animated_mats_props_unregister,
)

from .hackeroot.operators import hackeroot_ops_register, hackeroot_ops_unregister
from .hackeroot.properties import HackerOoTSettings, hackeroot_props_register, hackeroot_props_unregister
from .hackeroot.panels import hackeroot_panels_register, hackeroot_panels_unregister

from .tools import (
oot_operator_panel_register,
oot_operator_panel_unregister,
Expand Down Expand Up @@ -110,6 +123,10 @@ class OOT_Properties(bpy.types.PropertyGroup):
oot_version: bpy.props.EnumProperty(name="OoT Version", items=oot_versions_items, default="gc-eu-mq-dbg")
mm_version: bpy.props.EnumProperty(name="MM Version", items=mm_versions_items, default="n64-us")
oot_version_custom: bpy.props.StringProperty(name="Custom Version")
mm_features: bpy.props.BoolProperty(name="Enable MM Features", default=False)
hackeroot_settings: bpy.props.PointerProperty(type=HackerOoTSettings)
anim_mats_export_settings: bpy.props.PointerProperty(type=Z64_AnimatedMaterialExportSettings)
anim_mats_import_settings: bpy.props.PointerProperty(type=Z64_AnimatedMaterialImportSettings)

def get_extracted_path(self):
version = self.oot_version if game_data.z64.is_oot() else self.mm_version
Expand Down Expand Up @@ -156,6 +173,7 @@ def is_z64sceneh_present(self):

def oot_panel_register():
oot_operator_panel_register()
hackeroot_panels_register()
cutscene_panels_register()
scene_panels_register()
f3d_panels_register()
Expand All @@ -164,10 +182,12 @@ def oot_panel_register():
spline_panels_register()
anim_panels_register()
skeleton_panels_register()
animated_mats_panels_register()


def oot_panel_unregister():
oot_operator_panel_unregister()
hackeroot_panels_unregister()
cutscene_panels_unregister()
collision_panels_unregister()
oot_obj_panel_unregister()
Expand All @@ -176,6 +196,7 @@ def oot_panel_unregister():
f3d_panels_unregister()
anim_panels_unregister()
skeleton_panels_unregister()
animated_mats_panels_unregister()


def oot_register(registerPanels):
Expand All @@ -184,13 +205,14 @@ def oot_register(registerPanels):
collision_ops_register() # register first, so panel goes above mat panel
collision_props_register()
cutscene_props_register()
animated_mats_ops_register()
animated_mats_props_register()
scene_ops_register()
scene_props_register()
room_ops_register()
room_props_register()
actor_ops_register()
actor_props_register()
oot_obj_register()
spline_props_register()
f3d_props_register()
anim_ops_register()
Expand All @@ -200,13 +222,17 @@ def oot_register(registerPanels):
f3d_ops_register()
file_register()
anim_props_register()
hackeroot_props_register()
hackeroot_ops_register()

csMotion_ops_register()
csMotion_props_register()
csMotion_panels_register()
csMotion_preview_register()
cutscene_preview_register()

oot_obj_register()

for cls in oot_classes:
register_class(cls)

Expand All @@ -215,36 +241,41 @@ def oot_register(registerPanels):


def oot_unregister(unregisterPanels):
if unregisterPanels:
oot_panel_unregister()

for cls in reversed(oot_classes):
unregister_class(cls)

oot_operator_unregister()
collections_unregister()
collision_ops_unregister() # register first, so panel goes above mat panel
collision_props_unregister()
oot_obj_unregister()
cutscene_props_unregister()
scene_ops_unregister()
scene_props_unregister()
room_ops_unregister()
room_props_unregister()
actor_ops_unregister()
actor_props_unregister()
spline_props_unregister()
f3d_props_unregister()
anim_ops_unregister()
skeleton_ops_unregister()
skeleton_props_unregister()
cutscene_ops_unregister()
f3d_ops_unregister()
file_unregister()
anim_props_unregister()

cutscene_preview_unregister()
csMotion_preview_unregister()
csMotion_panels_unregister()
csMotion_props_unregister()
csMotion_ops_unregister()

if unregisterPanels:
oot_panel_unregister()
hackeroot_ops_unregister()
hackeroot_props_unregister()
anim_props_unregister()
file_unregister()
f3d_ops_unregister()
cutscene_ops_unregister()
skeleton_props_unregister()
skeleton_ops_unregister()
anim_ops_unregister()
f3d_props_unregister()
spline_props_unregister()
actor_props_unregister()
actor_ops_unregister()
room_props_unregister()
room_ops_unregister()
scene_props_unregister()
scene_ops_unregister()
animated_mats_props_unregister()
animated_mats_ops_unregister()
cutscene_props_unregister()
collision_props_unregister()
collision_ops_unregister()
collections_unregister()
oot_operator_unregister()
Loading