Skip to content

Commit

Permalink
Merge pull request #262 from kurethedead/blender-4.0
Browse files Browse the repository at this point in the history
Add support for Blender 4.0
  • Loading branch information
sauraen authored Nov 26, 2023
2 parents ced31a4 + b5397f3 commit 81d7a43
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 59 deletions.
73 changes: 53 additions & 20 deletions fast64_internal/f3d/f3d_material.py
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,7 @@ def checkDrawLayersWarnings(self, f3dMat: "F3DMaterialProperty", useDict: Dict[s
lightFxPrereq = isF3DEX3 and settings.g_lighting

blendUse = all_blender_uses(settings)
anyUseShadeAlpha = useDict["Shade Alpha"] or blendUse["Shade Alpha"]
anyUseShadeAlpha = useDict["Shade Alpha"] or (blendUse is not None and blendUse["Shade Alpha"])

g_lighting = settings.g_lighting
g_fog = settings.g_fog
Expand Down Expand Up @@ -1953,17 +1953,14 @@ def update_preset_manual(material, context):


def update_preset_manual_v4(material, preset):
override = bpy.context.copy()
override["material"] = material
if preset == "Shaded Solid":
preset = "sm64_shaded_solid"
if preset == "Shaded Texture":
preset = "sm64_shaded_texture"
if preset.lower() != "custom":
material.f3d_update_flag = True
bpy.ops.script.execute_preset(
override, filepath=findF3DPresetPath(preset), menu_idname="MATERIAL_MT_f3d_presets"
)
with bpy.context.temp_override(material=material):
bpy.ops.script.execute_preset(filepath=findF3DPresetPath(preset), menu_idname="MATERIAL_MT_f3d_presets")
material.f3d_update_flag = False


Expand Down Expand Up @@ -2011,8 +2008,13 @@ def createOrUpdateSceneProperties():

if upgrade_group and group:
# Need to upgrade; remove old outputs
for out in group.outputs:
group.outputs.remove(out)
if bpy.app.version >= (4,0,0):
for item in group.interface.items_tree:
if item.item_type == 'SOCKET' and item.in_out == 'OUTPUT':
group.interface.remove(item)
else:
for out in group.outputs:
group.outputs.remove(out)
new_group = group
else:
logger.info("Creating Scene Properties")
Expand All @@ -2024,18 +2026,49 @@ def createOrUpdateSceneProperties():
new_group["version"] = SCENE_PROPERTIES_VERSION

# Create outputs
_nodeFogEnable: NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogEnable")
_nodeFogColor: NodeSocketColor = new_group.outputs.new("NodeSocketColor", "FogColor")
_nodeF3D_NearClip: NodeSocketFloat = new_group.outputs.new("NodeSocketFloat", "F3D_NearClip")
_nodeF3D_FarClip: NodeSocketFloat = new_group.outputs.new("NodeSocketFloat", "F3D_FarClip")
_nodeBlender_Game_Scale: NodeSocketFloat = new_group.outputs.new("NodeSocketFloat", "Blender_Game_Scale")
_nodeFogNear: NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogNear")
_nodeFogFar: NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogFar")
_nodeShadeColor: NodeSocketColor = new_group.outputs.new("NodeSocketColor", "ShadeColor")
_nodeAmbientColor: NodeSocketColor = new_group.outputs.new("NodeSocketColor", "AmbientColor")
_nodeLightDirection: NodeSocketVectorDirection = new_group.outputs.new(
"NodeSocketVectorDirection", "LightDirection"
)
if bpy.app.version >= (4, 0, 0):
tree_interface = new_group.interface

_nodeFogEnable: NodeSocketFloat = tree_interface.new_socket(
"FogEnable", socket_type="NodeSocketFloat", in_out="OUTPUT"
)
_nodeFogColor: NodeSocketColor = tree_interface.new_socket(
"FogColor", socket_type="NodeSocketColor", in_out="OUTPUT"
)
_nodeF3D_NearClip: NodeSocketFloat = tree_interface.new_socket(
"F3D_NearClip", socket_type="NodeSocketFloat", in_out="OUTPUT"
)
_nodeF3D_FarClip: NodeSocketFloat = tree_interface.new_socket(
"F3D_FarClip", socket_type="NodeSocketFloat", in_out="OUTPUT"
)
_nodeBlender_Game_Scale: NodeSocketFloat = tree_interface.new_socket(
"Blender_Game_Scale", socket_type="NodeSocketFloat", in_out="OUTPUT"
)
_nodeFogNear: NodeSocketFloat = tree_interface.new_socket("FogNear", socket_type="NodeSocketFloat", in_out="OUTPUT")
_nodeFogFar: NodeSocketFloat = tree_interface.new_socket("FogFar", socket_type="NodeSocketFloat", in_out="OUTPUT")
_nodeShadeColor: NodeSocketColor = tree_interface.new_socket(
"ShadeColor", socket_type="NodeSocketColor", in_out="OUTPUT"
)
_nodeAmbientColor: NodeSocketColor = tree_interface.new_socket(
"AmbientColor", socket_type="NodeSocketColor", in_out="OUTPUT"
)
_nodeLightDirection: NodeSocketVector = tree_interface.new_socket(
"LightDirection", socket_type="NodeSocketVector", in_out="OUTPUT"
)

else:
_nodeFogEnable: NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogEnable")
_nodeFogColor: NodeSocketColor = new_group.outputs.new("NodeSocketColor", "FogColor")
_nodeF3D_NearClip: NodeSocketFloat = new_group.outputs.new("NodeSocketFloat", "F3D_NearClip")
_nodeF3D_FarClip: NodeSocketFloat = new_group.outputs.new("NodeSocketFloat", "F3D_FarClip")
_nodeBlender_Game_Scale: NodeSocketFloat = new_group.outputs.new("NodeSocketFloat", "Blender_Game_Scale")
_nodeFogNear: NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogNear")
_nodeFogFar: NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogFar")
_nodeShadeColor: NodeSocketColor = new_group.outputs.new("NodeSocketColor", "ShadeColor")
_nodeAmbientColor: NodeSocketColor = new_group.outputs.new("NodeSocketColor", "AmbientColor")
_nodeLightDirection: NodeSocketVectorDirection = new_group.outputs.new(
"NodeSocketVectorDirection", "LightDirection"
)

# Set outputs from render settings
sceneOutputs: NodeGroupOutput = new_group.nodes["Group Output"]
Expand Down
24 changes: 13 additions & 11 deletions fast64_internal/sm64/sm64_geolayout_bone.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import bpy
from bpy.ops import object
from bpy.types import Bone, Object, Panel, Operator, Armature, Mesh, Material, PropertyGroup
from bpy.utils import register_class, unregister_class
Expand Down Expand Up @@ -81,7 +82,6 @@


def drawGeoInfo(panel: Panel, bone: Bone):

panel.layout.box().label(text="Geolayout Inspector")
if bone is None:
panel.layout.label(text="Edit geolayout properties in Pose mode.")
Expand Down Expand Up @@ -427,9 +427,14 @@ def draw(self, context):

def getSwitchOptionBone(switchArmature):
optionBones = []
for poseBone in switchArmature.pose.bones:
if poseBone.bone_group is not None and poseBone.bone_group.name == "SwitchOption":
optionBones.append(poseBone.name)
if bpy.app.version >= (4, 0, 0):
for bone in switchArmature.data.bones:
if "SwitchOption" in bone.collections:
optionBones.append(bone.name)
else:
for poseBone in switchArmature.pose.bones:
if poseBone.bone_group is not None and poseBone.bone_group.name == "SwitchOption":
optionBones.append(poseBone.name)
if len(optionBones) > 1:
raise PluginError("There should only be one switch option bone in " + switchArmature.name + ".")
elif len(optionBones) < 1:
Expand All @@ -441,18 +446,15 @@ def getSwitchOptionBone(switchArmature):
return optionBones[0]


def updateBone(self, context):
if not hasattr(context, "bone"):
print("No bone in context.")
return
def updateBone(bone, context):
armatureObj = context.object

createBoneGroups(armatureObj)
if context.bone.geo_cmd not in animatableBoneTypes:
addBoneToGroup(armatureObj, context.bone.name, context.bone.geo_cmd)
if bone.geo_cmd not in animatableBoneTypes:
addBoneToGroup(armatureObj, bone.name, bone.geo_cmd)
object.mode_set(mode="POSE")
else:
addBoneToGroup(armatureObj, context.bone.name, None)
addBoneToGroup(armatureObj, bone.name, None)
object.mode_set(mode="POSE")


Expand Down
87 changes: 69 additions & 18 deletions fast64_internal/sm64/sm64_geolayout_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from ..panels import SM64_Panel, sm64GoalImport
from .sm64_level_parser import parseLevelAtPointer
from .sm64_constants import level_pointers, level_enums
from .sm64_geolayout_bone import enumShadowType
from .sm64_geolayout_bone import enumShadowType, animatableBoneTypes, enumBoneType
from .sm64_geolayout_constants import getGeoLayoutCmdLength, nodeGroupCmds, GEO_BRANCH_STORE

from ..utility import (
Expand Down Expand Up @@ -165,7 +165,7 @@ def parseGeoLayout(
# bpy.ops.transform.rotate(value = math.radians(-90), orient_axis = 'X')
# bpy.ops.object.transform_apply()

if useArmature:
if bpy.app.version < (4, 0, 0) and useArmature:
armatureObj.data.layers[1] = True

"""
Expand Down Expand Up @@ -508,34 +508,59 @@ def parseNode(


def generateMetarig(armatureObj):
armature = armatureObj.data
startBones = findStartBones(armatureObj)
createBoneGroups(armatureObj)
for boneName in startBones:
traverseArmatureForMetarig(armatureObj, boneName, None)
armatureObj.data.layers = createBoneLayerMask([boneLayers["visual"]])
if bpy.app.version >= (4, 0, 0):
if not "visual" in armature.collections:
armature.collections.new(name="visual")
armature.collections["visual"].assign(armature.bones[boneName])
else:
armatureObj.data.layers = createBoneLayerMask([boneLayers["visual"]])

if bpy.context.mode != "OBJECT":
bpy.ops.object.mode_set(mode="OBJECT")


def traverseArmatureForMetarig(armatureObj, boneName, parentName):
if bpy.context.mode != "OBJECT":
bpy.ops.object.mode_set(mode="OBJECT")
poseBone = armatureObj.pose.bones[boneName]
if poseBone.bone_group is None:
processBoneMeta(armatureObj, boneName, parentName)
elif poseBone.bone_group.name == "Ignore":
return

armature = armatureObj.data
bone = armature.bones[boneName]
poseBone = armatureObj.pose.bones[boneName]
nextParentName = boneName if poseBone.bone_group is None else parentName
childrenNames = [child.name for child in poseBone.children]

if bpy.app.version >= (4, 0, 0):
if "Ignore" in bone.collections:
return
nonAnimatableBoneTypes = set([item[0] for item in enumBoneType]) - animatableBoneTypes
isAnimatableBone = not any([item in bone.collections for item in nonAnimatableBoneTypes])
if isAnimatableBone:
processBoneMeta(armatureObj, boneName, parentName)
nextParentName = boneName if isAnimatableBone else parentName
bone = armature.bones[boneName] # re-obtain reference after edit mode changes
childrenNames = [child.name for child in bone.children]

else:
if poseBone.bone_group is None:
processBoneMeta(armatureObj, boneName, parentName)
elif poseBone.bone_group.name == "Ignore":
return

poseBone = armatureObj.pose.bones[boneName] # re-obtain reference after edit mode changes
nextParentName = boneName if poseBone.bone_group is None else parentName
childrenNames = [child.name for child in poseBone.children]

for childName in childrenNames:
traverseArmatureForMetarig(armatureObj, childName, nextParentName)


def processBoneMeta(armatureObj, boneName, parentName):
bpy.ops.object.mode_set(mode="EDIT")
bone = armatureObj.data.edit_bones[boneName]
armature = armatureObj.data

# create meta bone, which the actual bone copies the rotation of
metabone = armatureObj.data.edit_bones.new("meta_" + boneName)
Expand Down Expand Up @@ -574,16 +599,33 @@ def processBoneMeta(armatureObj, boneName, parentName):
translateConstraint.target = armatureObj
translateConstraint.subtarget = metaboneName

metabone.layers = createBoneLayerMask([boneLayers["meta"]])
if bpy.app.version >= (4, 0, 0):
if "meta" not in armature.collections:
armature.collections.new(name="meta")
armature.collections["meta"].assign(metabone)

if not "visual" in armature.collections:
armature.collections.new(name="visual")
armature.collections["visual"].assign(visualBone)

# Ignore collection should always be created, but check just in case
# (generateMetarig() calls createBoneGroups() before traverseArmatureForMetarig())
if not "Ignore" in armature.collections:
armature.collections.new(name="Ignore")
armature.collections["Ignore"].assign(visualBone)
armature.collections["Ignore"].assign(metabone)

else:
metabone.layers = createBoneLayerMask([boneLayers["meta"]])
visualBone.layers = createBoneLayerMask([boneLayers["visual"]])

metabonePose.bone_group_index = getBoneGroupIndex(armatureObj, "Ignore")
visualBonePose.bone_group_index = getBoneGroupIndex(armatureObj, "Ignore")

metabone.use_deform = False
metabonePose.lock_rotation = (True, True, True)

visualBone.layers = createBoneLayerMask([boneLayers["visual"]])
visualBone.use_deform = False

metabonePose.bone_group_index = getBoneGroupIndex(armatureObj, "Ignore")
visualBonePose.bone_group_index = getBoneGroupIndex(armatureObj, "Ignore")

bpy.ops.object.mode_set(mode="EDIT")
metabone = armatureObj.data.edit_bones[metaboneName]
visualBone = armatureObj.data.edit_bones[visualBoneName]
Expand Down Expand Up @@ -622,6 +664,7 @@ def processBoneMeta(armatureObj, boneName, parentName):


def createConnectBone(armatureObj, childName, parentName):
armature = armatureObj.data
child = armatureObj.data.edit_bones[childName]
parent = armatureObj.data.edit_bones[parentName]

Expand All @@ -643,8 +686,16 @@ def createConnectBone(armatureObj, childName, parentName):
bpy.ops.object.mode_set(mode="OBJECT")
connectPoseBone = armatureObj.pose.bones[connectBoneName]
connectBone = armatureObj.data.bones[connectBoneName]
connectBone.layers = createBoneLayerMask([boneLayers["visual"]])
connectPoseBone.bone_group_index = getBoneGroupIndex(armatureObj, "Ignore")

if bpy.app.version > (4, 0, 0):
if not "visual" in armature.collections:
armature.collections.new(name="visual")
armature.collections["visual"].assign(connectBone)
armature.collections["Ignore"].assign(connectBone)
else:
connectBone.layers = createBoneLayerMask([boneLayers["visual"]])
connectPoseBone.bone_group_index = getBoneGroupIndex(armatureObj, "Ignore")

connectPoseBone.lock_rotation = (True, True, True)
bpy.ops.object.mode_set(mode="EDIT")

Expand Down
41 changes: 32 additions & 9 deletions fast64_internal/sm64/sm64_geolayout_utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,33 @@ def createBoneLayerMask(values):


def createBoneGroups(armatureObj):
for (groupName, properties) in boneNodeProperties.items():
if getBoneGroupByName(armatureObj, groupName) is None:
boneGroup = armatureObj.pose.bone_groups.new(name=groupName)
boneGroup.color_set = properties.theme
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(armatureObj, boneName, groupName):
armature = armatureObj.data
if groupName is None:
if bpy.context.mode != "OBJECT":
bpy.ops.object.mode_set(mode="OBJECT")
posebone = armatureObj.pose.bones[boneName]
bone = armatureObj.data.bones[boneName]
posebone.bone_group = None
bone = armature.bones[boneName]
bone.use_deform = True
bone.layers = createBoneLayerMask([boneLayers["anim"]])
if bpy.app.version >= (4, 0, 0):
if not "anim" in armature.collections:
armature.collections.new(name="anim")
armature.collections["anim"].assign(bone)
else:
posebone.bone_group = None
bone.layers = createBoneLayerMask([boneLayers["anim"]])

posebone.lock_location = (False, False, False)
posebone.lock_rotation = (False, False, False)
posebone.lock_scale = (False, False, False)
Expand All @@ -80,13 +92,24 @@ def addBoneToGroup(armatureObj, boneName, groupName):

if bpy.context.mode != "OBJECT":
bpy.ops.object.mode_set(mode="OBJECT")

posebone = armatureObj.pose.bones[boneName]
bone = armatureObj.data.bones[boneName]
posebone.bone_group_index = getBoneGroupIndex(armatureObj, groupName)
if bpy.app.version >= (4, 0, 0):
armature.collections[groupName].assign(bone)
else:
posebone.bone_group_index = getBoneGroupIndex(armatureObj, groupName)

if groupName != "Ignore":
bone.use_deform = boneNodeProperties[groupName].deform
if groupName != "DisplayList":
bone.layers = createBoneLayerMask([boneLayers["other"]])
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"]])

if groupName != "SwitchOption":
posebone.lock_location = (True, True, True)
posebone.lock_rotation = (True, True, True)
Expand Down
1 change: 0 additions & 1 deletion fast64_internal/sm64/sm64_geolayout_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1603,7 +1603,6 @@ def processBone(
):
bone = armatureObj.data.bones[boneName]
poseBone = armatureObj.pose.bones[boneName]
boneGroup = poseBone.bone_group
finalTransform = copy.deepcopy(transformMatrix)
materialOverrides = copy.copy(materialOverrides)

Expand Down

0 comments on commit 81d7a43

Please sign in to comment.