mirror of
https://github.com/ApfelTeeSaft/lightspeed64.git
synced 2026-08-26 19:33:24 +00:00
* implement prop * first iter of custom cmd exporting * let rovert fly * Update sm64_objects.py * Update sm64_objects.py * Update sm64_objects.py * upgrade existing custom geo into new custom type * Hack for geo because * [SM64] Add depth arg and remove comma from existing cmds * [SM64] add depth arg to level and collision objects * change unused depth to _depth and add my stashed changes.. oops * some code clean up and ui improvement * Allow custom geo to have children * command preset system (WIP) allows you to create presets for your repo. missing auto updates to presets * detect changes via a hash * fix last remaining bugs * fix preset edit preview * add draw layers * basic example macro * boolean support and fixes * world's most over enginered way to handle numbers * bugs and visual fixes * make color serializable * dont use PRESET_EDIT if presumably there is no edit data * use degrees * inital support for bones * dont require transform to be inherented, fixes bone using scale arg * remove sys, last few bits of code tomorrow * move custom args into a folder * lock * typing fixes * use dict for exports * early binary support * Missing dl ext and animated * dl ext impl * make is animatable work * update updateBone to suit my new needs * finish upgrade code * dl ext now dl cmd, example macros for dl cmds * SM64_CustomArgProperties * enum property support to really hammer in the scope creep * Nice move by SpongeBob! This match is just about over. checked for mesh, removed one indent, changed Special to Collision * some fixes to the ops * make binary more flexiable by introducing eval expressions Now behavior that would require a macro can be written in an eval statement, this is peak over engineering. It's also present in c under a toggle because SCALE GEO CMD COMPATABILITY!!! * make hard defined int types for binary * one last fix * allow color quant for background node compat, fix enum ops * impl last translate/rotate name in custom * undo export color * fix area root UI * fix commas * add order, fix up a couple of nits * lila did a stupid * fix transforms * conventional rotation in macros is not scaled to s16 * let eval run on tuples * implement generators * add cast_integer * Update properties.py * round to conventional for scale * document collection operator base more, fix bug with scale rounding also add copy_on_add * undo repo settings ver change since there has been no breaking changes * remove unused * implement top level level script commands * make clean color into tuple, run eval on lists * update base displaylist node * little thing i noticed missing * unnecessary names removed * Fix transforms in level * fix parameter * show animatable toggle in presets * More control of the level script section * copy enum example * over engineered description code * update preset instead of changing to NONE * fix number updates * typo * fix crash * VERY IMPORTANT FIX * link to docs * add comment about updating existing animation pr ops * fix animation command checks
175 lines
6.2 KiB
Python
175 lines
6.2 KiB
Python
import bpy
|
|
from bpy.types import Object, Armature, Bone, PoseBone
|
|
|
|
from ..f3d.f3d_gbi import GfxList
|
|
from ..utility import PluginError
|
|
|
|
|
|
def is_bone_animatable(bone: Bone):
|
|
bone_props: "SM64_BoneProperties" = bone.fast64.sm64
|
|
geo_cmd: str = bone.geo_cmd
|
|
if geo_cmd == "DisplayListWithOffset":
|
|
return True
|
|
elif geo_cmd == "Custom" and bone_props.custom.is_animated:
|
|
return True
|
|
return False
|
|
|
|
|
|
def getBoneGroupByName(armatureObj, name):
|
|
for boneGroup in armatureObj.pose.bone_groups:
|
|
if boneGroup.name == name:
|
|
return boneGroup
|
|
return None
|
|
|
|
|
|
def getBoneGroupIndex(armatureObj, name):
|
|
index = 0
|
|
for boneGroup in armatureObj.pose.bone_groups:
|
|
if boneGroup.name == name:
|
|
return index
|
|
else:
|
|
index += 1
|
|
return -1
|
|
|
|
|
|
class BoneNodeProperties:
|
|
def __init__(self, deform, theme):
|
|
self.deform = deform
|
|
self.theme = theme
|
|
|
|
|
|
# Only 0x13 commands are keyframe animated.
|
|
# We want to ignore/prevent animations on these other nodes.
|
|
boneNodeProperties = {
|
|
"Switch": BoneNodeProperties(False, "THEME01"), # 0xE
|
|
"Start": BoneNodeProperties(True, "THEME12"), # 0x0B
|
|
"TranslateRotate": BoneNodeProperties(True, "THEME02"), # 0x10
|
|
"Translate": BoneNodeProperties(True, "THEME03"), # 0x11
|
|
"Rotate": BoneNodeProperties(True, "THEME04"), # 0x12
|
|
"Billboard": BoneNodeProperties(True, "THEME14"), # 0x14
|
|
"DisplayList": BoneNodeProperties(True, "THEME06"), # 0x15
|
|
"Shadow": BoneNodeProperties(False, "THEME07"), # 0x16
|
|
"Function": BoneNodeProperties(False, "THEME05"), # 0x18
|
|
"HeldObject": BoneNodeProperties(False, "THEME09"), # 0x1C
|
|
"Scale": BoneNodeProperties(True, "THEME10"), # 0x1D
|
|
"StartRenderArea": BoneNodeProperties(True, "THEME13"), # 0x20
|
|
"Ignore": BoneNodeProperties(False, "THEME08"), # Used for rigging
|
|
"SwitchOption": BoneNodeProperties(False, "THEME11"),
|
|
"DisplayListWithOffset": BoneNodeProperties(True, "THEME00"),
|
|
"Custom": BoneNodeProperties(True, "THEME15"),
|
|
}
|
|
|
|
boneLayers = {"anim": 0, "other": 1, "meta": 2, "visual": 3}
|
|
|
|
|
|
def createBoneLayerMask(values):
|
|
mask = [False] * 32
|
|
for value in values:
|
|
mask[value] = True
|
|
return mask
|
|
|
|
|
|
def createBoneGroups(armatureObj):
|
|
armature = armatureObj.data
|
|
for groupName, properties in boneNodeProperties.items():
|
|
if bpy.app.version >= (4, 0, 0):
|
|
if groupName not in armature.collections:
|
|
boneGroup = armature.collections.new(name=groupName)
|
|
else:
|
|
if getBoneGroupByName(armatureObj, groupName) is None:
|
|
boneGroup = armatureObj.pose.bone_groups.new(name=groupName)
|
|
boneGroup.color_set = properties.theme
|
|
|
|
|
|
def addBoneToGroup(armature_obj: Object, name: str):
|
|
armature: Armature = armature_obj.data
|
|
pose_bone: PoseBone = armature_obj.pose.bones[name]
|
|
bone: Bone = armature.bones[name]
|
|
geo_cmd: str = bone.geo_cmd
|
|
if geo_cmd not in boneNodeProperties:
|
|
raise PluginError(f"Bone group {geo_cmd} doesn't exist.")
|
|
|
|
lock_location, lock_rotation, lock_scale = False, False, False
|
|
|
|
if is_bone_animatable(bone):
|
|
if bpy.app.version >= (4, 0, 0):
|
|
if not "anim" in armature.collections:
|
|
armature.collections.new(name="anim")
|
|
armature.collections["anim"].assign(bone)
|
|
else:
|
|
pose_bone.bone_group = None
|
|
bone.layers = createBoneLayerMask([boneLayers["anim"]])
|
|
|
|
if bpy.app.version >= (4, 0, 0):
|
|
armature.collections[geo_cmd].assign(bone)
|
|
else:
|
|
pose_bone.bone_group_index = getBoneGroupIndex(armature_obj, geo_cmd)
|
|
|
|
if geo_cmd == "Custom":
|
|
custom = bone.fast64.sm64.custom
|
|
bone.use_deform = custom.dl_option != "NONE"
|
|
if not custom.is_animated:
|
|
lock_location = lock_rotation = lock_scale = True
|
|
elif geo_cmd != "Ignore":
|
|
bone.use_deform = boneNodeProperties[geo_cmd].deform
|
|
if geo_cmd != "SwitchOption":
|
|
lock_location = True
|
|
lock_rotation = lock_scale = True
|
|
if geo_cmd not in {"Ignore", "DisplayList"}:
|
|
if bpy.app.version >= (4, 0, 0):
|
|
if not "other" in armature.collections:
|
|
armature.collections.new(name="other")
|
|
armature.collections["other"].assign(bone)
|
|
else:
|
|
bone.layers = createBoneLayerMask([boneLayers["other"]])
|
|
|
|
pose_bone.lock_location = (lock_location, lock_location, lock_location)
|
|
pose_bone.lock_rotation = (lock_rotation, lock_rotation, lock_rotation)
|
|
pose_bone.lock_scale = (lock_scale, lock_scale, lock_scale)
|
|
|
|
|
|
def updateBone(bone, context):
|
|
armatureObj = context.object
|
|
|
|
createBoneGroups(armatureObj)
|
|
addBoneToGroup(armatureObj, bone.name)
|
|
|
|
|
|
class BaseDisplayListNode:
|
|
"""Base displaylist node with common helper functions dealing with displaylists"""
|
|
|
|
dl_ext = "WITH_DL" # add dl_ext to geo command if command has a displaylist
|
|
override_layer = False
|
|
dlRef: str | GfxList | None
|
|
|
|
def get_dl_address(self):
|
|
assert not isinstance(self.dlRef, str), "dlRef string not supported in binary"
|
|
if isinstance(self.dlRef, GfxList):
|
|
return self.dlRef.startAddress
|
|
if self.hasDL and self.DLmicrocode is not None:
|
|
return self.DLmicrocode.startAddress
|
|
return None
|
|
|
|
def get_dl_name(self):
|
|
if isinstance(self.dlRef, GfxList):
|
|
return self.dlRef.name
|
|
if self.hasDL and (self.dlRef or self.DLmicrocode is not None):
|
|
return self.dlRef or self.DLmicrocode.name
|
|
return "NULL"
|
|
|
|
def get_c_func_macro(self, base_cmd: str):
|
|
return f"{base_cmd}_{self.dl_ext}" if self.hasDL else base_cmd
|
|
|
|
def c_func_macro(self, base_cmd: str, *args: str):
|
|
"""
|
|
Supply base command and all arguments for command.
|
|
if self.hasDL:
|
|
this will add self.dl_ext to the command, and
|
|
adds the name of the displaylist to the end of the command
|
|
Example return: 'GEO_YOUR_COMMAND_WITH_DL(arg, arg2),'
|
|
"""
|
|
all_args = list(args)
|
|
if self.hasDL:
|
|
all_args.append(self.get_dl_name())
|
|
return f'{self.get_c_func_macro(base_cmd)}({", ".join(all_args)})'
|