mirror of
https://github.com/ApfelTeeSaft/lightspeed64.git
synced 2026-08-26 19:33:24 +00:00
Added F3D/skeleton/animation importing for OOT.
This commit is contained in:
+28
-6
@@ -40,9 +40,7 @@ class ArmatureApplyWithMesh(bpy.types.Operator):
|
||||
# Can also be called from operator search menu (Spacebar)
|
||||
def execute(self, context):
|
||||
try:
|
||||
if context.mode != 'OBJECT' and context.mode != 'POSE':
|
||||
raise PluginError("Operator can only be used in object or pose mode.")
|
||||
elif context.mode == 'POSE':
|
||||
if context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = "OBJECT")
|
||||
|
||||
if len(context.selected_objects) == 0:
|
||||
@@ -72,7 +70,11 @@ class ArmatureApplyWithMesh(bpy.types.Operator):
|
||||
context.view_layer.objects.active = armatureObj
|
||||
bpy.ops.object.mode_set(mode = "POSE")
|
||||
bpy.ops.pose.armature_apply()
|
||||
if context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = "OBJECT")
|
||||
except Exception as e:
|
||||
if context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = "OBJECT")
|
||||
raisePluginError(self, e)
|
||||
return {"CANCELLED"}
|
||||
|
||||
@@ -125,9 +127,7 @@ class CreateMetarig(bpy.types.Operator):
|
||||
# Can also be called from operator search menu (Spacebar)
|
||||
def execute(self, context):
|
||||
try:
|
||||
if context.mode != 'OBJECT' and context.mode != 'POSE':
|
||||
raise PluginError("Operator can only be used in object or pose mode.")
|
||||
elif context.mode == 'POSE':
|
||||
if context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = "OBJECT")
|
||||
|
||||
if len(context.selected_objects) == 0:
|
||||
@@ -231,6 +231,23 @@ class Fast64_GlobalSettingsPanel(bpy.types.Panel):
|
||||
prop_split(col, context.scene, 'gameEditorMode', "Game")
|
||||
col.prop(context.scene, 'exportHiddenGeometry')
|
||||
col.prop(context.scene, 'fullTraceback')
|
||||
|
||||
class Fast64_GlobalToolsPanel(bpy.types.Panel):
|
||||
bl_idname = "FAST64_PT_global_tools"
|
||||
bl_label = "Fast64 Tools"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = 'Fast64'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
# called every frame
|
||||
def draw(self, context):
|
||||
col = self.layout.column()
|
||||
col.operator(ArmatureApplyWithMesh.bl_idname)
|
||||
#col.operator(CreateMetarig.bl_idname)
|
||||
|
||||
|
||||
#def updateGameEditor(scene, context):
|
||||
@@ -260,6 +277,7 @@ classes = (
|
||||
F3D_GlobalSettingsPanel,
|
||||
Fast64_GlobalSettingsPanel,
|
||||
SM64_ArmatureToolsPanel,
|
||||
Fast64_GlobalToolsPanel,
|
||||
)
|
||||
|
||||
# called on add-on enabling
|
||||
@@ -278,6 +296,7 @@ def register():
|
||||
register_class(cls)
|
||||
|
||||
f3d_writer_register()
|
||||
f3d_parser_register()
|
||||
|
||||
# ROM
|
||||
|
||||
@@ -293,10 +312,12 @@ def register():
|
||||
name = 'Save Textures As PNGs (Breaks CI Textures)')
|
||||
bpy.types.Scene.generateF3DNodeGraph = bpy.props.BoolProperty(name = "Generate F3D Node Graph", default = True)
|
||||
bpy.types.Scene.exportHiddenGeometry = bpy.props.BoolProperty(name = "Export Hidden Geometry", default = True)
|
||||
bpy.types.Scene.blenderF3DScale = bpy.props.FloatProperty(name = "F3D Blender Scale", default = 100)
|
||||
|
||||
# called on add-on disabling
|
||||
def unregister():
|
||||
f3d_writer_unregister()
|
||||
f3d_parser_unregister()
|
||||
sm64_unregister(True)
|
||||
oot_unregister(True)
|
||||
mat_unregister()
|
||||
@@ -311,6 +332,7 @@ def unregister():
|
||||
del bpy.types.Scene.gameEditorMode
|
||||
del bpy.types.Scene.generateF3DNodeGraph
|
||||
del bpy.types.Scene.exportHiddenGeometry
|
||||
del bpy.types.Scene.blenderF3DScale
|
||||
|
||||
for cls in classes:
|
||||
unregister_class(cls)
|
||||
|
||||
@@ -294,6 +294,7 @@ enumRenderModesCycle1 = [
|
||||
('G_RM_ZB_XLU_SURF', "Transparent (No AA)", 'G_RM_ZB_XLU_SURF'),
|
||||
('G_RM_ZB_XLU_DECAL', "Transparent Decal (No AA)", 'G_RM_ZB_XLU_DECAL'),
|
||||
('G_RM_OPA_SURF', "Opaque (No AA, No ZBuf)", 'G_RM_OPA_SURF'),
|
||||
('G_RM_ZB_CLD_SURF', "Cloud (No AA)", 'G_RM_ZB_CLD_SURF'),
|
||||
]
|
||||
|
||||
enumRenderModesCycle2 = [
|
||||
@@ -312,6 +313,7 @@ enumRenderModesCycle2 = [
|
||||
('G_RM_ZB_OPA_DECAL2', "Opaque Decal (No AA)", 'G_RM_ZB_OPA_DECAL2'),
|
||||
('G_RM_ZB_XLU_SURF2', "Transparent (No AA)", 'G_RM_ZB_XLU_SURF2'),
|
||||
('G_RM_ZB_XLU_DECAL2', "Transparent Decal (No AA)", 'G_RM_ZB_XLU_DECAL2'),
|
||||
('G_RM_ZB_CLD_SURF2', "Cloud (No AA)", 'G_RM_ZB_CLD_SURF2'),
|
||||
]
|
||||
|
||||
enumTexFormat = [
|
||||
|
||||
+148
-73
@@ -475,81 +475,103 @@ class F3D:
|
||||
self.G_ACMUX_0 = 7
|
||||
|
||||
# typical CC cycle 1 modes
|
||||
self.G_CC_PRIMITIVE = '0', '0', '0', 'PRIMITIVE', '0', '0', '0', 'PRIMITIVE'
|
||||
self.G_CC_SHADE = '0', '0', '0', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_MODULATEI = 'TEXEL0', '0', 'SHADE', '0', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_MODULATEIA = 'TEXEL0', '0', 'SHADE', '0', 'TEXEL0', '0', 'SHADE', '0'
|
||||
self.G_CC_MODULATEIDECALA = 'TEXEL0', '0', 'SHADE', '0', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_MODULATERGB = self.G_CC_MODULATEI
|
||||
self.G_CC_MODULATERGBA = self.G_CC_MODULATEIA
|
||||
self.G_CC_MODULATERGBDECALA = self.G_CC_MODULATEIDECALA
|
||||
self.G_CC_MODULATEI_PRIM = 'TEXEL0', '0', 'PRIMITIVE', '0', '0', '0', '0', 'PRIMITIVE'
|
||||
self.G_CC_MODULATEIA_PRIM = 'TEXEL0', '0', 'PRIMITIVE', '0', 'TEXEL0', '0', 'PRIMITIVE', '0'
|
||||
self.G_CC_MODULATEIDECALA_PRIM = 'TEXEL0', '0', 'PRIMITIVE', '0', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_MODULATERGB_PRIM = self.G_CC_MODULATEI_PRIM
|
||||
self.G_CC_MODULATERGBA_PRIM = self.G_CC_MODULATEIA_PRIM
|
||||
self.G_CC_MODULATERGBDECALA_PRIM = self.G_CC_MODULATEIDECALA_PRIM
|
||||
self.G_CC_DECALRGB = '0', '0', '0', 'TEXEL0', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_DECALRGBA = '0', '0', '0', 'TEXEL0', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_BLENDI = 'ENVIRONMENT', 'SHADE', 'TEXEL0', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_BLENDIA = 'ENVIRONMENT', 'SHADE', 'TEXEL0', 'SHADE', 'TEXEL0', '0', 'SHADE', '0'
|
||||
self.G_CC_BLENDIDECALA = 'ENVIRONMENT', 'SHADE', 'TEXEL0', 'SHADE', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_BLENDRGBA = 'TEXEL0', 'SHADE', 'TEXEL0_ALPHA', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_BLENDRGBDECALA = 'TEXEL0', 'SHADE', 'TEXEL0_ALPHA', 'SHADE', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_ADDRGB = '1', '0', 'TEXEL0', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_ADDRGBDECALA = '1', '0', 'TEXEL0', 'SHADE', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_REFLECTRGB = 'ENVIRONMENT', '0', 'TEXEL0', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_REFLECTRGBDECALA = 'ENVIRONMENT', '0', 'TEXEL0', 'SHADE', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_HILITERGB = 'PRIMITIVE', 'SHADE', 'TEXEL0', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_HILITERGBA = 'PRIMITIVE', 'SHADE', 'TEXEL0', 'SHADE', 'PRIMITIVE', 'SHADE', 'TEXEL0', 'SHADE'
|
||||
self.G_CC_HILITERGBDECALA = 'PRIMITIVE', 'SHADE', 'TEXEL0', 'SHADE', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_SHADEDECALA = '0', '0', '0', 'SHADE', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_BLENDPE = 'PRIMITIVE', 'ENVIRONMENT', 'TEXEL0', 'ENVIRONMENT', 'TEXEL0', '0', 'SHADE', '0'
|
||||
self.G_CC_BLENDPEDECALA = 'PRIMITIVE', 'ENVIRONMENT', 'TEXEL0', 'ENVIRONMENT', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_PRIMITIVE = '0', '0', '0', 'PRIMITIVE', '0', '0', '0', 'PRIMITIVE'
|
||||
self.G_CC_SHADE = '0', '0', '0', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
|
||||
# oddball 'modes'
|
||||
self._G_CC_BLENDPE = 'ENVIRONMENT', 'PRIMITIVE', 'TEXEL0', 'PRIMITIVE', 'TEXEL0', '0', 'SHADE', '0'
|
||||
self._G_CC_BLENDPEDECALA = 'ENVIRONMENT', 'PRIMITIVE', 'TEXEL0', 'PRIMITIVE', '0', '0', '0', 'TEXEL0'
|
||||
self._G_CC_TWOCOLORTEX = 'PRIMITIVE', 'SHADE', 'TEXEL0', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
# used for 1-cycle sparse 'mip-maps', primitive color has color of lowest 'LOD'
|
||||
self._G_CC_SPARSEST = 'PRIMITIVE', 'TEXEL0', 'LOD_FRACTION', 'TEXEL0', 'PRIMITIVE', 'TEXEL0', 'LOD_FRACTION', 'TEXEL0'
|
||||
self.G_CC_TEMPLERP = 'TEXEL1', 'TEXEL0', 'PRIM_LOD_FRAC', 'TEXEL0', 'TEXEL1', 'TEXEL0', 'PRIM_LOD_FRAC', 'TEXEL0'
|
||||
self.G_CC_MODULATEI = 'TEXEL0', '0', 'SHADE', '0', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_MODULATEIDECALA = 'TEXEL0', '0', 'SHADE', '0', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_MODULATEIFADE = 'TEXEL0', '0', 'SHADE', '0', '0', '0', '0', 'ENVIRONMENT'
|
||||
|
||||
# typical CC cycle 1 'modes', usually followed by other cycle 2 'modes'
|
||||
self.G_CC_TRILERP = 'TEXEL1', 'TEXEL0', 'LOD_FRACTION', 'TEXEL0', 'TEXEL1', 'TEXEL0', 'LOD_FRACTION', 'TEXEL0'
|
||||
self.G_CC_INTERFERENCE = 'TEXEL0', '0', 'TEXEL1', '0', 'TEXEL0', '0', 'TEXEL1', '0'
|
||||
self.G_CC_MODULATERGB = self.G_CC_MODULATEI
|
||||
self.G_CC_MODULATERGBDECALA = self.G_CC_MODULATEIDECALA
|
||||
self.G_CC_MODULATERGBFADE = self.G_CC_MODULATEIFADE
|
||||
|
||||
# One-cycle color convert 'operation'
|
||||
self.G_CC_1CYUV2RGB = 'TEXEL0', 'K4', 'K5', 'TEXEL0', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_MODULATEIA = 'TEXEL0', '0', 'SHADE', '0', 'TEXEL0', '0', 'SHADE', '0'
|
||||
self.G_CC_MODULATEIFADEA = 'TEXEL0', '0', 'SHADE', '0', 'TEXEL0', '0', 'ENVIRONMENT', '0'
|
||||
|
||||
'''
|
||||
* NOTE: YUV2RGB expects TF step1 color conversion to occur in 2nd clock.
|
||||
* 'Therefore', CC looks for step1 results in TEXEL1
|
||||
'''
|
||||
self.G_CC_YUV2RGB = 'TEXEL1', 'K4', 'K5', 'TEXEL1', '0', '0', '0', 0
|
||||
self.G_CC_MODULATEFADE = 'TEXEL0', '0', 'SHADE', '0', 'ENVIRONMENT', '0', 'TEXEL0', '0'
|
||||
|
||||
# typical CC cycle 2 modes
|
||||
self.G_CC_PASS2 = '0', '0', '0', 'COMBINED', '0', '0', '0', 'COMBINED'
|
||||
self.G_CC_MODULATEI2 = 'COMBINED', '0', 'SHADE', '0', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_MODULATEIA2 = 'COMBINED', '0', 'SHADE', '0', 'COMBINED', '0', 'SHADE', '0'
|
||||
self.G_CC_MODULATERGB2 = self.G_CC_MODULATEI2
|
||||
self.G_CC_MODULATERGBA2 = self.G_CC_MODULATEIA2
|
||||
self.G_CC_MODULATEI_PRIM2 = 'COMBINED', '0', 'PRIMITIVE', '0', '0', '0', '0', 'PRIMITIVE'
|
||||
self.G_CC_MODULATEIA_PRIM2 = 'COMBINED', '0', 'PRIMITIVE', '0', 'COMBINED', '0', 'PRIMITIVE', '0'
|
||||
self.G_CC_MODULATERGB_PRIM2 = self.G_CC_MODULATEI_PRIM2
|
||||
self.G_CC_MODULATERGBA_PRIM2 = self.G_CC_MODULATEIA_PRIM2
|
||||
self.G_CC_DECALRGB2 = '0', '0', '0', 'COMBINED', '0', '0', '0', 'SHADE'
|
||||
'''
|
||||
* ?
|
||||
self.G_CC_DECALRGBA2 = 'COMBINED', 'SHADE', 'COMBINED_ALPHA', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
'''
|
||||
self.G_CC_BLENDI2 = 'ENVIRONMENT', 'SHADE', 'COMBINED', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_BLENDIA2 = 'ENVIRONMENT', 'SHADE', 'COMBINED', 'SHADE', 'COMBINED', '0', 'SHADE', '0'
|
||||
self.G_CC_CHROMA_KEY2 = 'TEXEL0', 'CENTER', 'SCALE', '0', '0', '0', '0', '0'
|
||||
self.G_CC_HILITERGB2 = 'ENVIRONMENT', 'COMBINED', 'TEXEL0', 'COMBINED', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_HILITERGBA2 = 'ENVIRONMENT', 'COMBINED', 'TEXEL0', 'COMBINED', 'ENVIRONMENT', 'COMBINED', 'TEXEL0', 'COMBINED'
|
||||
self.G_CC_HILITERGBDECALA2 = 'ENVIRONMENT', 'COMBINED', 'TEXEL0', 'COMBINED', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_HILITERGBPASSA2 = 'ENVIRONMENT', 'COMBINED', 'TEXEL0', 'COMBINED', '0', '0', '0', 'COMBINED'
|
||||
self.G_CC_MODULATERGBA = self.G_CC_MODULATEIA
|
||||
self.G_CC_MODULATERGBFADEA = self.G_CC_MODULATEIFADEA
|
||||
|
||||
self.G_CC_MODULATEI_PRIM = 'TEXEL0', '0', 'PRIMITIVE', '0', '0', '0', '0', 'PRIMITIVE'
|
||||
self.G_CC_MODULATEIA_PRIM = 'TEXEL0', '0', 'PRIMITIVE', '0', 'TEXEL0', '0', 'PRIMITIVE', '0'
|
||||
self.G_CC_MODULATEIDECALA_PRIM = 'TEXEL0', '0', 'PRIMITIVE', '0', '0', '0', '0', 'TEXEL0'
|
||||
|
||||
self.G_CC_MODULATERGB_PRIM = self.G_CC_MODULATEI_PRIM
|
||||
self.G_CC_MODULATERGBA_PRIM = self.G_CC_MODULATEIA_PRIM
|
||||
self.G_CC_MODULATERGBDECALA_PRIM = self.G_CC_MODULATEIDECALA_PRIM
|
||||
|
||||
self.G_CC_FADE = 'SHADE', '0', 'ENVIRONMENT', '0', 'SHADE', '0', 'ENVIRONMENT', '0'
|
||||
self.G_CC_FADEA = 'TEXEL0', '0', 'ENVIRONMENT', '0', 'TEXEL0', '0', 'ENVIRONMENT', '0'
|
||||
|
||||
self.G_CC_DECALRGB = '0', '0', '0', 'TEXEL0', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_DECALRGBA = '0', '0', '0', 'TEXEL0', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_DECALFADE = '0', '0', '0', 'TEXEL0', '0', '0', '0', 'ENVIRONMENT'
|
||||
|
||||
self.G_CC_DECALFADEA = '0', '0', '0', 'TEXEL0', 'TEXEL0', '0', 'ENVIRONMENT', '0'
|
||||
|
||||
self.G_CC_BLENDI = 'ENVIRONMENT', 'SHADE', 'TEXEL0', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_BLENDIA = 'ENVIRONMENT', 'SHADE', 'TEXEL0', 'SHADE', 'TEXEL0', '0', 'SHADE', '0'
|
||||
self.G_CC_BLENDIDECALA = 'ENVIRONMENT', 'SHADE', 'TEXEL0', 'SHADE', '0', '0', '0', 'TEXEL0'
|
||||
|
||||
self.G_CC_BLENDRGBA = 'TEXEL0', 'SHADE', 'TEXEL0_ALPHA', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_BLENDRGBDECALA = 'TEXEL0', 'SHADE', 'TEXEL0_ALPHA', 'SHADE', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_BLENDRGBFADEA = 'TEXEL0', 'SHADE', 'TEXEL0_ALPHA', 'SHADE', '0', '0', '0', 'ENVIRONMENT'
|
||||
|
||||
self.G_CC_ADDRGB = 'TEXEL0', '0', 'TEXEL0', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_ADDRGBDECALA = 'TEXEL0', '0', 'TEXEL0', 'SHADE', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_ADDRGBFADE = 'TEXEL0', '0', 'TEXEL0', 'SHADE', '0', '0', '0', 'ENVIRONMENT'
|
||||
|
||||
self.G_CC_REFLECTRGB = 'ENVIRONMENT', '0', 'TEXEL0', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_REFLECTRGBDECALA = 'ENVIRONMENT', '0', 'TEXEL0', 'SHADE', '0', '0', '0', 'TEXEL0'
|
||||
|
||||
self.G_CC_HILITERGB = 'PRIMITIVE', 'SHADE', 'TEXEL0', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_HILITERGBA = 'PRIMITIVE', 'SHADE', 'TEXEL0', 'SHADE', 'PRIMITIVE', 'SHADE', 'TEXEL0', 'SHADE'
|
||||
self.G_CC_HILITERGBDECALA = 'PRIMITIVE', 'SHADE', 'TEXEL0', 'SHADE', '0', '0', '0', 'TEXEL0'
|
||||
|
||||
self.G_CC_SHADEDECALA = '0', '0', '0', 'SHADE', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_SHADEFADEA = '0', '0', '0', 'SHADE', '0', '0', '0', 'ENVIRONMENT'
|
||||
|
||||
self.G_CC_BLENDPE = 'PRIMITIVE', 'ENVIRONMENT', 'TEXEL0', 'ENVIRONMENT', 'TEXEL0', '0', 'SHADE', '0'
|
||||
self.G_CC_BLENDPEDECALA = 'PRIMITIVE', 'ENVIRONMENT', 'TEXEL0', 'ENVIRONMENT', '0', '0', '0', 'TEXEL0'
|
||||
|
||||
# oddball modes
|
||||
self._G_CC_BLENDPE = 'ENVIRONMENT', 'PRIMITIVE', 'TEXEL0', 'PRIMITIVE', 'TEXEL0', '0', 'SHADE', '0'
|
||||
self._G_CC_BLENDPEDECALA = 'ENVIRONMENT', 'PRIMITIVE', 'TEXEL0', 'PRIMITIVE', '0', '0', '0', 'TEXEL0'
|
||||
self._G_CC_TWOCOLORTEX = 'PRIMITIVE', 'SHADE', 'TEXEL0', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
|
||||
# used for 1-cycle sparse mip-maps, primitive color has color of lowest LOD
|
||||
self._G_CC_SPARSEST = 'PRIMITIVE', 'TEXEL0', 'LOD_FRACTION', 'TEXEL0', 'PRIMITIVE', 'TEXEL0', 'LOD_FRACTION', 'TEXEL0'
|
||||
self.G_CC_TEMPLERP = 'TEXEL1', 'TEXEL0', 'PRIM_LOD_FRAC', 'TEXEL0', 'TEXEL1', 'TEXEL0', 'PRIM_LOD_FRAC', 'TEXEL0'
|
||||
|
||||
# typical CC cycle 1 modes, usually followed by other cycle 2 modes
|
||||
self.G_CC_TRILERP = 'TEXEL1', 'TEXEL0', 'LOD_FRACTION', 'TEXEL0', 'TEXEL1', 'TEXEL0', 'LOD_FRACTION', 'TEXEL0'
|
||||
self.G_CC_INTERFERENCE = 'TEXEL0', '0', 'TEXEL1', '0', 'TEXEL0', '0', 'TEXEL1', '0'
|
||||
|
||||
self.G_CC_1CYUV2RGB = 'TEXEL0', 'K4', 'K5', 'TEXEL0', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_YUV2RGB = 'TEXEL1', 'K4', 'K5', 'TEXEL1', '0', '0', '0', '0'
|
||||
|
||||
#typical CC cycle 2 modes
|
||||
self.G_CC_PASS2 = '0', '0', '0', 'COMBINED', '0', '0', '0', 'COMBINED'
|
||||
self.G_CC_MODULATEI2 = 'COMBINED', '0', 'SHADE', '0', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_MODULATEIA2 = 'COMBINED', '0', 'SHADE', '0', 'COMBINED', '0', 'SHADE', '0'
|
||||
self.G_CC_MODULATERGB2 = self.G_CC_MODULATEI2
|
||||
self.G_CC_MODULATERGBA2 = self.G_CC_MODULATEIA2
|
||||
self.G_CC_MODULATEI_PRIM2 = 'COMBINED', '0', 'PRIMITIVE', '0', '0', '0', '0', 'PRIMITIVE'
|
||||
self.G_CC_MODULATEIA_PRIM2 = 'COMBINED', '0', 'PRIMITIVE', '0', 'COMBINED', '0', 'PRIMITIVE', '0'
|
||||
self.G_CC_MODULATERGB_PRIM2 = self.G_CC_MODULATEI_PRIM2
|
||||
self.G_CC_MODULATERGBA_PRIM2 = self.G_CC_MODULATEIA_PRIM2
|
||||
self.G_CC_DECALRGB2 = '0', '0', '0', 'COMBINED', '0', '0', '0', 'SHADE'
|
||||
|
||||
self.G_CC_DECALRGBA2 = 'COMBINED', 'SHADE', 'COMBINED_ALPHA', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_BLENDI2 = 'ENVIRONMENT', 'SHADE', 'COMBINED', 'SHADE', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_BLENDIA2 = 'ENVIRONMENT', 'SHADE', 'COMBINED', 'SHADE', 'COMBINED', '0', 'SHADE', '0'
|
||||
self.G_CC_CHROMA_KEY2 = 'TEXEL0', 'CENTER', 'SCALE', '0', '0', '0', '0', '0'
|
||||
self.G_CC_HILITERGB2 = 'ENVIRONMENT', 'COMBINED', 'TEXEL0', 'COMBINED', '0', '0', '0', 'SHADE'
|
||||
self.G_CC_HILITERGBA2 = 'ENVIRONMENT', 'COMBINED', 'TEXEL0', 'COMBINED', 'ENVIRONMENT', 'COMBINED', 'TEXEL0', 'COMBINED'
|
||||
self.G_CC_HILITERGBDECALA2 = 'ENVIRONMENT', 'COMBINED', 'TEXEL0', 'COMBINED', '0', '0', '0', 'TEXEL0'
|
||||
self.G_CC_HILITERGBPASSA2 = 'ENVIRONMENT', 'COMBINED', 'TEXEL0', 'COMBINED', '0', '0', '0', 'COMBINED'
|
||||
|
||||
# G_SETOTHERMODE_L sft: shift count
|
||||
|
||||
@@ -676,6 +698,41 @@ class F3D:
|
||||
self.G_BL_1 = G_BL_1 = 2
|
||||
self.G_BL_0 = G_BL_0 = 3
|
||||
|
||||
self.cvgDstDict = {
|
||||
CVG_DST_CLAMP : "CVG_DST_CLAMP",
|
||||
CVG_DST_WRAP : "CVG_DST_WRAP",
|
||||
CVG_DST_FULL : "CVG_DST_FULL",
|
||||
CVG_DST_SAVE : "CVG_DST_SAVE",
|
||||
}
|
||||
|
||||
self.zmodeDict = {
|
||||
ZMODE_OPA : "ZMODE_OPA",
|
||||
ZMODE_INTER : "ZMODE_INTER",
|
||||
ZMODE_XLU : "ZMODE_XLU",
|
||||
ZMODE_DEC : "ZMODE_DEC",
|
||||
}
|
||||
|
||||
self.blendColorDict = {
|
||||
G_BL_CLR_IN : "G_BL_CLR_IN",
|
||||
G_BL_CLR_MEM : "G_BL_CLR_MEM",
|
||||
G_BL_CLR_BL : "G_BL_CLR_BL",
|
||||
G_BL_CLR_FOG : "G_BL_CLR_FOG",
|
||||
}
|
||||
|
||||
self.blendAlphaDict = {
|
||||
G_BL_A_IN : 'G_BL_A_IN',
|
||||
G_BL_A_FOG : 'G_BL_A_FOG',
|
||||
G_BL_A_SHADE : 'G_BL_A_SHADE',
|
||||
G_BL_0 : 'G_BL_0',
|
||||
}
|
||||
|
||||
self.blendMixDict = {
|
||||
G_BL_1MA : 'G_BL_1MA',
|
||||
G_BL_A_MEM : 'G_BL_A_MEM',
|
||||
G_BL_1 : 'G_BL_1',
|
||||
G_BL_0 : 'G_BL_0',
|
||||
}
|
||||
|
||||
def GBL_c1(m1a, m1b, m2a, m2b):
|
||||
return (m1a) << 30 | (m1b) << 26 | (m2a) << 22 | (m2b) << 18
|
||||
def GBL_c2(m1a, m1b, m2a, m2b):
|
||||
@@ -1039,7 +1096,7 @@ class F3D:
|
||||
self.G_RM_OPA_CI2 = RM_OPA_CI(2)
|
||||
|
||||
|
||||
self.G_RM_FOG_SHADE_A = GBL_c1(G_BL_CLR_FOG, G_BL_A_SHADE, G_BL_CLR_IN, G_BL_1MA)
|
||||
self.G_RM_FOG_SHADE_A = GBL_c1(G_BL_CLR_FOG, G_BL_A_SHADE, G_BL_CLR_IN, G_BL_1MA)
|
||||
self.G_RM_FOG_PRIM_A = GBL_c1(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_IN, G_BL_1MA)
|
||||
self.G_RM_PASS = GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)
|
||||
|
||||
@@ -1285,6 +1342,11 @@ class F3D:
|
||||
'NUMLIGHTS_7' : 7,
|
||||
}
|
||||
|
||||
def GBL_c1(self, m1a, m1b, m2a, m2b):
|
||||
return (m1a) << 30 | (m1b) << 26 | (m2a) << 22 | (m2b) << 18
|
||||
def GBL_c2(self, m1a, m1b, m2a, m2b):
|
||||
return (m1a) << 28 | (m1b) << 24 | (m2a) << 20 | (m2b) << 16
|
||||
|
||||
# macros for command parsing
|
||||
def GDMACMD(self, x): return (x)
|
||||
def GIMMCMD(self, x): return (self.G_IMMFIRST-(x))
|
||||
@@ -1749,11 +1811,24 @@ class FModel:
|
||||
fMaterial.usedLights.append(key)
|
||||
self.lights[key] = value
|
||||
|
||||
def addMesh(self, name, namePrefix, drawLayer, isSkinned):
|
||||
def addMesh(self, name, namePrefix, drawLayer, isSkinned, contextObj):
|
||||
meshName = getFMeshName(name, namePrefix, drawLayer, isSkinned)
|
||||
checkUniqueBoneNames(self, meshName, name)
|
||||
self.meshes[meshName] = FMesh(meshName, self.DLFormat)
|
||||
|
||||
self.onAddMesh(self.meshes[meshName], contextObj)
|
||||
|
||||
return self.meshes[meshName]
|
||||
|
||||
def onAddMesh(self, fMesh, contextObj):
|
||||
return
|
||||
|
||||
def endDraw(self, fMesh, contextObj):
|
||||
self.onEndDraw(fMesh, contextObj)
|
||||
fMesh.draw.commands.append(SPEndDisplayList())
|
||||
|
||||
def onEndDraw(self, fMesh, contextObj):
|
||||
return
|
||||
|
||||
def getTextureAndHandleShared(self, imageKey):
|
||||
# Check if texture is in self
|
||||
@@ -2022,7 +2097,7 @@ class FModel:
|
||||
oldpath = image.filepath
|
||||
try:
|
||||
image.filepath = \
|
||||
os.path.join(dirpath, imageFileName)
|
||||
bpy.path.abspath(os.path.join(dirpath, imageFileName))
|
||||
image.save()
|
||||
texturesSaved += 1
|
||||
if not isPacked:
|
||||
|
||||
@@ -85,11 +85,11 @@ drawLayerOOTtoSM64 = {
|
||||
"Overlay" : '1',
|
||||
}
|
||||
|
||||
drawLayerOOTAlpha = {
|
||||
"Opaque" : "CLIP",
|
||||
"Transparent" : "BLEND",
|
||||
"Overlay" : 'CLIP',
|
||||
}
|
||||
#drawLayerOOTAlpha = {
|
||||
# "Opaque" : "OPAQUE",
|
||||
# "Transparent" : "BLEND",
|
||||
# "Overlay" : 'CLIP',
|
||||
#}
|
||||
|
||||
drawLayerSM64Alpha = {
|
||||
'0' : "CLIP",
|
||||
@@ -166,7 +166,17 @@ def update_blend_method(material, context):
|
||||
if material.mat_ver > 3:
|
||||
drawLayer = material.f3d_mat.draw_layer
|
||||
if context.scene.gameEditorMode == "OOT":
|
||||
material.blend_method = drawLayerOOTAlpha[drawLayer.oot]
|
||||
if drawLayer.oot == "Opaque" or drawLayer.oot == "Overlay":
|
||||
f3dMat = material.f3d_mat
|
||||
if not f3dMat.rdp_settings.rendermode_advanced_enabled and\
|
||||
"TEX_EDGE" not in f3dMat.rdp_settings.rendermode_preset_cycle_1 and\
|
||||
("TEX_EDGE" not in f3dMat.rdp_settings.rendermode_preset_cycle_2 or\
|
||||
f3dMat.rdp_settings.g_mdsft_cycletype != 'G_CYC_2CYCLE'):
|
||||
material.blend_method = "OPAQUE"
|
||||
else:
|
||||
material.blend_method = "CLIP"
|
||||
else:
|
||||
material.blend_method = "BLEND"
|
||||
elif context.scene.gameEditorMode == "SM64":
|
||||
material.blend_method = drawLayerSM64Alpha[drawLayer.sm64]
|
||||
|
||||
@@ -442,7 +452,7 @@ class F3DPanel(bpy.types.Panel):
|
||||
inputGroup.prop(textureProp, 'menu', text = name + ' Properties',
|
||||
icon = 'TRIA_DOWN' if textureProp.menu else 'TRIA_RIGHT')
|
||||
if textureProp.menu:
|
||||
|
||||
tex = textureProp.tex
|
||||
prop_input_name = inputGroup.column()
|
||||
prop_input = inputGroup.column()
|
||||
|
||||
@@ -453,13 +463,22 @@ class F3DPanel(bpy.types.Panel):
|
||||
#prop_input.template_image(textureProp, 'tex',
|
||||
# nodes[name].image_user)
|
||||
texIndex = name[-1]
|
||||
prop_input.template_ID(textureProp, 'tex', new='image.new', open='image.open',
|
||||
unlink='image.tex' + texIndex + "_unlink")
|
||||
prop_input.enabled = textureProp.tex_set
|
||||
|
||||
tex = textureProp.tex
|
||||
if tex is not None:
|
||||
prop_input.label(text = "Size: " + str(tex.size[0]) + " x " + str(tex.size[1]))
|
||||
prop_input.prop(textureProp, "use_tex_reference")
|
||||
if textureProp.use_tex_reference:
|
||||
prop_split(prop_input, textureProp, "tex_reference", "Texture Reference")
|
||||
prop_split(prop_input, textureProp, "tex_reference_size", "Texture Size")
|
||||
if textureProp.tex_format[:2] == 'CI':
|
||||
prop_split(prop_input, textureProp, "pal_reference", "Palette Reference")
|
||||
prop_split(prop_input, textureProp, "pal_reference_size", "Palette Size")
|
||||
|
||||
else:
|
||||
prop_input.template_ID(textureProp, 'tex', new='image.new', open='image.open',
|
||||
unlink='image.tex' + texIndex + "_unlink")
|
||||
prop_input.enabled = textureProp.tex_set
|
||||
|
||||
if tex is not None:
|
||||
prop_input.label(text = "Size: " + str(tex.size[0]) + " x " + str(tex.size[1]))
|
||||
|
||||
if material.mat_ver > 3 and material.f3d_mat.use_large_textures:
|
||||
prop_input.label(text = "Large texture mode enabled.")
|
||||
@@ -1165,7 +1184,7 @@ def update_node_values_without_preset(self, context):
|
||||
update_node_values_of_material(material, context)
|
||||
material.f3d_update_flag = False
|
||||
else:
|
||||
print('No material in context.')
|
||||
#print('No material in context.')
|
||||
pass
|
||||
|
||||
def update_node_values_directly(material, context):
|
||||
@@ -1491,9 +1510,10 @@ def update_tex_values_field_v3(self, texProperty, tex_size,
|
||||
(texProperty.T.high + 1) / tex_size[1], 0)
|
||||
|
||||
# Clamp
|
||||
isTexGen = self.f3d_mat.rdp_settings.g_tex_gen or self.f3d_mat.rdp_settings.g_tex_gen_linear
|
||||
nodes['Get UV ' + str(texIndex) + ' F3D v3'].inputs[5].default_value = (
|
||||
1 if texProperty.S.clamp else 0,
|
||||
1 if texProperty.T.clamp else 0, 0)
|
||||
1 if texProperty.S.clamp and not isTexGen else 0,
|
||||
1 if texProperty.T.clamp and not isTexGen else 0, 0)
|
||||
|
||||
# Normalized Mask
|
||||
nodes['Get UV ' + str(texIndex) + ' F3D v3'].inputs[6].default_value = (
|
||||
@@ -1525,8 +1545,11 @@ def update_tex_values_index(self, context, texProperty, texNodeName,
|
||||
nodes = self.node_tree.nodes
|
||||
|
||||
nodes[texNodeName].image = texProperty.tex
|
||||
if nodes[texNodeName].image is not None:
|
||||
tex_size = nodes[texNodeName].image.size
|
||||
if nodes[texNodeName].image is not None or texProperty.use_tex_reference:
|
||||
if nodes[texNodeName].image is not None:
|
||||
tex_size = nodes[texNodeName].image.size
|
||||
else:
|
||||
tex_size = texProperty.tex_reference_size
|
||||
if tex_size[0] > 0 and tex_size[1] > 0:
|
||||
if self.mat_ver == 1:
|
||||
tex_x = nodes[uvNodeName].node_tree.nodes[\
|
||||
@@ -1750,14 +1773,15 @@ def hasNodeGraph(material):
|
||||
|
||||
def createF3DMat(obj, preset = 'Shaded Solid', index = None):
|
||||
material = bpy.data.materials.new('f3d_material')
|
||||
if index is None:
|
||||
obj.data.materials.append(material)
|
||||
if bpy.context.object is not None:
|
||||
bpy.context.object.active_material_index = len(obj.material_slots) - 1
|
||||
else:
|
||||
obj.material_slots[index].material = material
|
||||
if bpy.context.object is not None:
|
||||
bpy.context.object.active_material_index = index
|
||||
if obj is not None:
|
||||
if index is None:
|
||||
obj.data.materials.append(material)
|
||||
if bpy.context.object is not None:
|
||||
bpy.context.object.active_material_index = len(obj.material_slots) - 1
|
||||
else:
|
||||
obj.material_slots[index].material = material
|
||||
if bpy.context.object is not None:
|
||||
bpy.context.object.active_material_index = index
|
||||
|
||||
material.is_f3d = True
|
||||
material.mat_ver = 4
|
||||
@@ -2001,6 +2025,12 @@ class TextureProperty(bpy.types.PropertyGroup):
|
||||
S : bpy.props.PointerProperty(type = TextureFieldProperty)
|
||||
T : bpy.props.PointerProperty(type = TextureFieldProperty)
|
||||
|
||||
use_tex_reference: bpy.props.BoolProperty(name = "Use Texture Reference", default = False, update = update_tex_values)
|
||||
tex_reference: bpy.props.StringProperty(name = "Texture Reference", default = '0x08000000')
|
||||
tex_reference_size: bpy.props.IntVectorProperty(name = "Texture Reference Size", min = 1, size = 2, default = (32,32), update = update_tex_values)
|
||||
pal_reference: bpy.props.StringProperty(name = "Palette Reference", default = '0x08000000')
|
||||
pal_reference_size : bpy.props.IntProperty(name = "Texture Reference Size", min = 1, default = 16)
|
||||
|
||||
menu : bpy.props.BoolProperty()
|
||||
tex_set : bpy.props.BoolProperty(default = True, update = update_node_values)
|
||||
autoprop : bpy.props.BoolProperty(name = 'Autoprop', update = update_tex_values, default = True)
|
||||
@@ -2383,6 +2413,11 @@ class AddPresetF3D(AddPresetBase, Operator):
|
||||
"f3d_mat.tex0.tex",
|
||||
"f3d_mat.tex0.tex_format",
|
||||
"f3d_mat.tex0.ci_format",
|
||||
"f3d_mat.tex0.use_tex_reference",
|
||||
"f3d_mat.tex0.tex_reference",
|
||||
"f3d_mat.tex0.tex_reference_size",
|
||||
"f3d_mat.tex0.pal_reference",
|
||||
"f3d_mat.tex0.pal_reference_size",
|
||||
"f3d_mat.tex0.S",
|
||||
"f3d_mat.tex0.T",
|
||||
"f3d_mat.tex0.menu",
|
||||
@@ -2391,6 +2426,11 @@ class AddPresetF3D(AddPresetBase, Operator):
|
||||
"f3d_mat.tex1.tex",
|
||||
"f3d_mat.tex1.tex_format",
|
||||
"f3d_mat.tex1.ci_format",
|
||||
"f3d_mat.tex1.use_tex_reference",
|
||||
"f3d_mat.tex1.tex_reference",
|
||||
"f3d_mat.tex1.tex_reference_size",
|
||||
"f3d_mat.tex1.pal_reference",
|
||||
"f3d_mat.tex1.pal_reference_size",
|
||||
"f3d_mat.tex1.S",
|
||||
"f3d_mat.tex1.T",
|
||||
"f3d_mat.tex1.menu",
|
||||
|
||||
@@ -1156,7 +1156,7 @@ class GetAlphaFromColor(ShaderNode):
|
||||
|
||||
# Copy function to initialize a copied node from an existing one.
|
||||
def copy(self, node):
|
||||
print("Copying from node ", node)
|
||||
pass #print("Copying from node ", node)
|
||||
|
||||
# Free function to clean up on removal.
|
||||
def free(self):
|
||||
@@ -1214,7 +1214,7 @@ class F3DNodeA(ShaderNode):
|
||||
|
||||
# Copy function to initialize a copied node from an existing one.
|
||||
def copy(self, node):
|
||||
print("Copying from node ", node)
|
||||
pass #print("Copying from node ", node)
|
||||
|
||||
# Free function to clean up on removal.
|
||||
def free(self):
|
||||
@@ -1257,7 +1257,7 @@ class F3DNodeB(ShaderNode):
|
||||
|
||||
# Copy function to initialize a copied node from an existing one.
|
||||
def copy(self, node):
|
||||
print("Copying from node ", node)
|
||||
pass #print("Copying from node ", node)
|
||||
|
||||
# Free function to clean up on removal.
|
||||
def free(self):
|
||||
@@ -1300,7 +1300,7 @@ class F3DNodeC(ShaderNode):
|
||||
|
||||
# Copy function to initialize a copied node from an existing one.
|
||||
def copy(self, node):
|
||||
print("Copying from node ", node)
|
||||
pass #print("Copying from node ", node)
|
||||
|
||||
# Free function to clean up on removal.
|
||||
def free(self):
|
||||
@@ -1343,7 +1343,7 @@ class F3DNodeD(ShaderNode):
|
||||
|
||||
# Copy function to initialize a copied node from an existing one.
|
||||
def copy(self, node):
|
||||
print("Copying from node ", node)
|
||||
pass #print("Copying from node ", node)
|
||||
|
||||
# Free function to clean up on removal.
|
||||
def free(self):
|
||||
@@ -1387,7 +1387,7 @@ class F3DNodeA_alpha(ShaderNode):
|
||||
|
||||
# Copy function to initialize a copied node from an existing one.
|
||||
def copy(self, node):
|
||||
print("Copying from node ", node)
|
||||
pass #print("Copying from node ", node)
|
||||
|
||||
# Free function to clean up on removal.
|
||||
def free(self):
|
||||
@@ -1430,7 +1430,7 @@ class F3DNodeB_alpha(ShaderNode):
|
||||
|
||||
# Copy function to initialize a copied node from an existing one.
|
||||
def copy(self, node):
|
||||
print("Copying from node ", node)
|
||||
pass #print("Copying from node ", node)
|
||||
|
||||
# Free function to clean up on removal.
|
||||
def free(self):
|
||||
@@ -1473,7 +1473,7 @@ class F3DNodeC_alpha(ShaderNode):
|
||||
|
||||
# Copy function to initialize a copied node from an existing one.
|
||||
def copy(self, node):
|
||||
print("Copying from node ", node)
|
||||
pass #print("Copying from node ", node)
|
||||
|
||||
# Free function to clean up on removal.
|
||||
def free(self):
|
||||
@@ -1516,7 +1516,7 @@ class F3DNodeD_alpha(ShaderNode):
|
||||
|
||||
# Copy function to initialize a copied node from an existing one.
|
||||
def copy(self, node):
|
||||
print("Copying from node ", node)
|
||||
pass #print("Copying from node ", node)
|
||||
|
||||
# Free function to clean up on removal.
|
||||
def free(self):
|
||||
|
||||
@@ -508,7 +508,7 @@ f3d_mat.set_combiner = True
|
||||
f3d_mat.use_default_lighting = True
|
||||
f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0)
|
||||
f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0)
|
||||
f3d_mat.env_color = (0.0, 0.6405822038650513, 0.06569604575634003, 1.0)
|
||||
f3d_mat.env_color = (1.0, 1.0, 1.0, 1.0)
|
||||
f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0)
|
||||
f3d_mat.key_scale = (0.0, 0.0, 0.0)
|
||||
f3d_mat.key_width = (0.0, 0.0, 0.0)
|
||||
@@ -626,7 +626,7 @@ f3d_mat.set_combiner = True
|
||||
f3d_mat.use_default_lighting = True
|
||||
f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0)
|
||||
f3d_mat.prim_color = (1.0, 1.0, 1.0, 0.5)
|
||||
f3d_mat.env_color = (0.0, 0.6405822038650513, 0.06569604575634003, 1.0)
|
||||
f3d_mat.env_color = (1.0, 1.0, 1.0, 1.0)
|
||||
f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0)
|
||||
f3d_mat.key_scale = (0.0, 0.0, 0.0)
|
||||
f3d_mat.key_width = (0.0, 0.0, 0.0)
|
||||
|
||||
+1641
-10
File diff suppressed because it is too large
Load Diff
@@ -153,6 +153,8 @@ def fixLargeUVs(obj):
|
||||
2 if f3dMat.tex0.T.mirror or f3dMat.tex1.T.mirror else 1]
|
||||
|
||||
size = texSizeDict[material]
|
||||
if size[0] == 0 or size[1] == 0:
|
||||
continue
|
||||
cellSize = [1024 / size[0], 1024 / size[1]]
|
||||
minUV, maxUV = findUVBounds(polygon, uv_data)
|
||||
uvOffset = [0,0]
|
||||
@@ -390,7 +392,7 @@ def saveStaticModel(triConverterInfo, fModel, obj, transformMatrix, ownerName, c
|
||||
drawLayerName = None
|
||||
|
||||
if drawLayer not in fMeshes:
|
||||
fMesh = fModel.addMesh(obj.original_name, ownerName, drawLayerName, False)
|
||||
fMesh = fModel.addMesh(obj.original_name, ownerName, drawLayerName, False, obj)
|
||||
fMeshes[drawLayer] = fMesh
|
||||
|
||||
if obj.use_f3d_culling and (fModel.f3d.F3DEX_GBI or fModel.f3d.F3DEX_GBI_2):
|
||||
@@ -409,9 +411,10 @@ def saveStaticModel(triConverterInfo, fModel, obj, transformMatrix, ownerName, c
|
||||
|
||||
for drawLayer, fMesh in fMeshes.items():
|
||||
if revertMatAtEnd:
|
||||
fModel.onEndDraw(fMesh, obj)
|
||||
revertMatAndEndDraw(fMesh.draw, [])
|
||||
else:
|
||||
fMesh.draw.commands.append(SPEndDisplayList())
|
||||
fModel.endDraw(fMesh, obj)
|
||||
return fMeshes
|
||||
|
||||
def addCullCommand(obj, fMesh, transformMatrix, matWriteMethod):
|
||||
@@ -1106,13 +1109,19 @@ def getTexDimensions(material):
|
||||
texDimensions1 = None
|
||||
useDict = all_combiner_uses(f3dMat)
|
||||
if useDict['Texture 0'] and f3dMat.tex0.tex_set:
|
||||
if f3dMat.tex0.tex is None:
|
||||
raise PluginError('In material \"' + material.name + '\", a texture has not been set.')
|
||||
texDimensions0 = f3dMat.tex0.tex.size[0], f3dMat.tex0.tex.size[1]
|
||||
if f3dMat.tex0.use_tex_reference:
|
||||
texDimensions0 = f3dMat.tex0.tex_reference_size
|
||||
else:
|
||||
if f3dMat.tex0.tex is None:
|
||||
raise PluginError('In material \"' + material.name + '\", a texture has not been set.')
|
||||
texDimensions0 = f3dMat.tex0.tex.size[0], f3dMat.tex0.tex.size[1]
|
||||
if useDict['Texture 1'] and f3dMat.tex1.tex_set:
|
||||
if f3dMat.tex1.tex is None:
|
||||
raise PluginError('In material \"' + material.name + '\", a texture has not been set.')
|
||||
texDimensions1 = f3dMat.tex1.tex.size[0], f3dMat.tex1.tex.size[1]
|
||||
if f3dMat.tex1.use_tex_reference:
|
||||
texDimensions1 = f3dMat.tex1.tex_reference_size
|
||||
else:
|
||||
if f3dMat.tex1.tex is None:
|
||||
raise PluginError('In material \"' + material.name + '\", a texture has not been set.')
|
||||
texDimensions1 = f3dMat.tex1.tex.size[0], f3dMat.tex1.tex.size[1]
|
||||
|
||||
if texDimensions0 is not None and texDimensions1 is not None:
|
||||
texDimensions = texDimensions0 if f3dMat.uv_basis == 'TEXEL0' \
|
||||
@@ -1243,7 +1252,7 @@ def saveOrGetF3DMaterial(material, fModel, obj, drawLayer, convertTextureData):
|
||||
nextTmem = 0
|
||||
loadTextures = not (material.mat_ver > 3 and f3dMat.use_large_textures)
|
||||
if useDict['Texture 0'] and f3dMat.tex0.tex_set:
|
||||
if f3dMat.tex0.tex is None:
|
||||
if f3dMat.tex0.tex is None and not f3dMat.tex0.use_tex_reference:
|
||||
raise PluginError('In material \"' + material.name + '\", a texture has not been set.')
|
||||
|
||||
fMaterial.useLargeTextures = not loadTextures
|
||||
@@ -1253,7 +1262,7 @@ def saveOrGetF3DMaterial(material, fModel, obj, drawLayer, convertTextureData):
|
||||
fMaterial, fMaterial.material, fMaterial.revert, f3dMat.tex0, 0, nextTmem, None, convertTextureData,
|
||||
None, loadTextures, True)
|
||||
if useDict['Texture 1'] and f3dMat.tex1.tex_set:
|
||||
if f3dMat.tex1.tex is None:
|
||||
if f3dMat.tex1.tex is None and not f3dMat.tex1.use_tex_reference:
|
||||
raise PluginError('In material \"' + material.name + '\", a texture has not been set.')
|
||||
|
||||
fMaterial.useLargeTextures = not loadTextures
|
||||
@@ -1377,18 +1386,27 @@ def saveOrGetF3DMaterial(material, fModel, obj, drawLayer, convertTextureData):
|
||||
def saveTextureIndex(propName, fModel, fMaterial, loadTexGfx, revertTexGfx, texProp, index, tmem,
|
||||
overrideName, convertTextureData, tileSettingsOverride, loadTextures, loadPalettes):
|
||||
tex = texProp.tex
|
||||
if tex is None:
|
||||
raise PluginError('In ' + propName + ", no texture is selected.")
|
||||
elif len(tex.pixels) == 0:
|
||||
raise PluginError("Could not load missing texture: " + tex.name + ". Make sure this texture has not been deleted or moved on disk.")
|
||||
|
||||
if tex is not None and (tex.size[0] == 0 or tex.size[1] == 0):
|
||||
raise PluginError("Image " + tex.name + " has either a 0 width or height; image may have been removed from original location.")
|
||||
|
||||
if not texProp.use_tex_reference:
|
||||
if tex is None:
|
||||
raise PluginError('In ' + propName + ", no texture is selected.")
|
||||
elif len(tex.pixels) == 0:
|
||||
raise PluginError("Could not load missing texture: " + tex.name + ". Make sure this texture has not been deleted or moved on disk.")
|
||||
|
||||
texFormat = texProp.tex_format
|
||||
isCITexture = texFormat[:2] == 'CI'
|
||||
palFormat = texProp.ci_format if isCITexture else ''
|
||||
if tex.filepath == "":
|
||||
name = tex.name
|
||||
|
||||
if not texProp.use_tex_reference:
|
||||
if tex.filepath == "":
|
||||
name = tex.name
|
||||
else:
|
||||
name = tex.filepath
|
||||
else:
|
||||
name = tex.filepath
|
||||
name = texProp.tex_reference
|
||||
texName = fModel.name + '_' + \
|
||||
(getNameFromPath(name, True) + '_' + texFormat.lower() if overrideName is None else overrideName)
|
||||
|
||||
@@ -1399,8 +1417,10 @@ def saveTextureIndex(propName, fModel, fMaterial, loadTexGfx, revertTexGfx, texP
|
||||
setTLUTMode = False
|
||||
else:
|
||||
tileSettings = None
|
||||
width = tex.size[0]
|
||||
height = tex.size[1]
|
||||
if texProp.use_tex_reference:
|
||||
width, height = texProp.tex_reference_size
|
||||
else:
|
||||
width, height = tex.size
|
||||
setTLUTMode = fModel.matWriteMethod == GfxMatWriteMethod.WriteAll
|
||||
|
||||
nextTmem = tmem + getTmemWordUsage(texFormat, width, height)
|
||||
@@ -1409,7 +1429,7 @@ def saveTextureIndex(propName, fModel, fMaterial, loadTexGfx, revertTexGfx, texP
|
||||
if nextTmem > (512 if texFormat[:2] != 'CI' else 256):
|
||||
raise PluginError("Error in \"" + propName + "\": Textures are too big. Max TMEM size is 4k " + \
|
||||
"bytes, ex. 2 32x32 RGBA 16 bit textures.\nNote that texture width will be internally padded to 64 bit boundaries.")
|
||||
if tex.size[0] > 1024 or tex.size[1] > 1024:
|
||||
if width > 1024 or height > 1024:
|
||||
raise PluginError("Error in \"" + propName + "\": Any side of an image cannot be greater " +\
|
||||
"than 1024.")
|
||||
|
||||
@@ -1445,14 +1465,22 @@ def saveTextureIndex(propName, fModel, fMaterial, loadTexGfx, revertTexGfx, texP
|
||||
|
||||
convertTextureData = convertTextureData and not (fMaterial.useLargeTextures and fMaterial.saveLargeTextures[index])
|
||||
if isCITexture:
|
||||
fImage, fPalette = saveOrGetPaletteDefinition(
|
||||
fMaterial, fModel, tex, texName, texFormat, palFormat, convertTextureData)
|
||||
if texProp.use_tex_reference:
|
||||
fImage = FImage(texProp.tex_reference, None, None, width, height, None, False)
|
||||
fPalette = FImage(texProp.pal_reference, None, None, 1, texProp.pal_reference_size, None, False)
|
||||
else:
|
||||
fImage, fPalette = saveOrGetPaletteDefinition(
|
||||
fMaterial, fModel, tex, texName, texFormat, palFormat, convertTextureData)
|
||||
|
||||
if loadPalettes:
|
||||
savePaletteLoading(loadTexGfx, revertTexGfx, fPalette,
|
||||
palFormat, 0, fPalette.height, fModel.f3d, fModel.matWriteMethod)
|
||||
else:
|
||||
fImage = saveOrGetTextureDefinition(fMaterial, fModel, tex, texName,
|
||||
texFormat, convertTextureData)
|
||||
if texProp.use_tex_reference:
|
||||
fImage = FImage(texProp.tex_reference, None, None, width, height, None, False)
|
||||
else:
|
||||
fImage = saveOrGetTextureDefinition(fMaterial, fModel, tex, texName,
|
||||
texFormat, convertTextureData)
|
||||
|
||||
if setTLUTMode and not isCITexture:
|
||||
loadTexGfx.commands.append(DPSetTextureLUT('G_TT_NONE'))
|
||||
@@ -1716,7 +1744,7 @@ def saveOrGetTextureDefinition(fMaterial, fModel, image, imageName, texFormat, c
|
||||
((((int(round(image.pixels[(j * image.size[0] + i) * image.channels + 0] * 0x1F)) & 0x1F) << 3) |
|
||||
((int(round(image.pixels[(j * image.size[0] + i) * image.channels + 1] * 0x1F)) & 0x1F) >> 2)),
|
||||
|
||||
(((int(round(image.pixels[(j * image.size[0] + i) * image.channels + 1] * 0x1F)) & 0x02) << 6) | \
|
||||
(((int(round(image.pixels[(j * image.size[0] + i) * image.channels + 1] * 0x1F)) & 0x03) << 6) | \
|
||||
((int(round(image.pixels[(j * image.size[0] + i) * image.channels + 2] * 0x1F)) & 0x1F) << 1) | \
|
||||
(1 if image.pixels[(j * image.size[0] + i) * image.channels + 3] > 0.5 else 0)))
|
||||
|
||||
@@ -2092,7 +2120,10 @@ def saveOtherModeLDefinitionAll(fMaterial, settings, defaults, defaultRenderMode
|
||||
flagList, blendList = getRenderModeFlagList(settings, fMaterial)
|
||||
cmd.flagList.extend(flagList)
|
||||
if blendList is not None:
|
||||
cmd.flagList.extend(blendList)
|
||||
cmd.flagList.extend([
|
||||
"GBL_c1(" + blendList[0] + ", " + blendList[1] + ", " + blendList[2] + ", " + blendList[3] + ")",
|
||||
"GBL_c2(" + blendList[4] + ", " + blendList[5] + ", " + blendList[6] + ", " + blendList[7] + ")",
|
||||
])
|
||||
else:
|
||||
cmd.flagList.extend(defaultRenderMode)
|
||||
|
||||
@@ -2128,9 +2159,11 @@ def getRenderModeFlagList(settings, fMaterial):
|
||||
settings.rendermode_preset_cycle_1,
|
||||
settings.rendermode_preset_cycle_2]
|
||||
else:
|
||||
cycle2 = settings.rendermode_preset_cycle_1 + '2'
|
||||
if cycle2 not in [value[0] for value in enumRenderModesCycle2]:
|
||||
cycle2 = "G_RM_NOOP"
|
||||
flagList = [
|
||||
settings.rendermode_preset_cycle_1,
|
||||
settings.rendermode_preset_cycle_1 + '2']
|
||||
settings.rendermode_preset_cycle_1, cycle2]
|
||||
else:
|
||||
if settings.g_mdsft_cycletype == 'G_CYC_2CYCLE':
|
||||
blendList = \
|
||||
@@ -2233,6 +2266,27 @@ def exportF3DtoC(dirPath, obj, DLFormat, transformMatrix,
|
||||
writeCData(staticData, os.path.join(modelDirPath, 'header.h'),
|
||||
os.path.join(modelDirPath, 'model.inc.c'))
|
||||
|
||||
def removeDL(sourcePath, headerPath, DLName):
|
||||
DLDataC = readFile(sourcePath)
|
||||
originalDataC = DLDataC
|
||||
|
||||
DLDataH = readFile(headerPath)
|
||||
originalDataH = DLDataH
|
||||
|
||||
matchResult = re.search("Gfx\s*" + re.escape(DLName) + "\s*\[\s*[0-9x]*\s*\]\s*=\s*\{([^}]*)}\s*;\s*", DLDataC, re.DOTALL)
|
||||
if matchResult is not None:
|
||||
DLDataC = DLDataC[:matchResult.start(0)] + DLDataC[matchResult.end(0):]
|
||||
|
||||
headerMatch = getDeclaration(DLDataH, DLName)
|
||||
if headerMatch is not None:
|
||||
DLDataH = DLDataH[:headerMatch.start(0)] + DLDataH[headerMatch.end(0):]
|
||||
|
||||
if DLDataC != originalDataC:
|
||||
writeFile(sourcePath, DLDataC)
|
||||
|
||||
if DLDataH != originalDataH:
|
||||
writeFile(headerPath, DLDataH)
|
||||
|
||||
class F3D_ExportDL(bpy.types.Operator):
|
||||
# set bl_ properties
|
||||
bl_idname = 'object.f3d_export_dl'
|
||||
@@ -2252,7 +2306,7 @@ class F3D_ExportDL(bpy.types.Operator):
|
||||
if not isinstance(obj.data, bpy.types.Mesh):
|
||||
raise PluginError("Object is not a mesh.")
|
||||
|
||||
scaleValue = bpy.context.scene.blenderToSM64Scale
|
||||
scaleValue = bpy.context.scene.blenderF3DScale
|
||||
finalTransform = mathutils.Matrix.Diagonal(mathutils.Vector((
|
||||
scaleValue, scaleValue, scaleValue))).to_4x4()
|
||||
|
||||
@@ -2305,8 +2359,9 @@ class F3D_ExportDLPanel(bpy.types.Panel):
|
||||
col = self.layout.column()
|
||||
col.operator(F3D_ExportDL.bl_idname)
|
||||
|
||||
col.prop(context.scene, 'DLExportPath')
|
||||
prop_split(col, context.scene, 'DLName', 'Name')
|
||||
prop_split(col, context.scene, 'DLExportPath', "Export Path")
|
||||
prop_split(col, context.scene, "blenderF3DScale", "Scale")
|
||||
prop_split(col, context.scene, 'matWriteMethod', "Material Write Method")
|
||||
col.prop(context.scene, 'DLExportisStatic')
|
||||
|
||||
|
||||
@@ -44,4 +44,26 @@ Additionally, for functions like Gfx_TexScroll(), the x,y inputs are pre-shifted
|
||||
Collision properties are found underneath the material properties. Water boxes and collision properties will have references the properties "Camera", "Lighting", and "Exit", which reference the indices of the scene cameras, scene light list, and scene exit list respectively. If you want separate visual/collision geometry, you can set per mesh object "Ignore Collision" or "Ignore Render" in object inspector window.
|
||||
|
||||
### Skeletons And Animations
|
||||
To export a skeletal mesh, select an armature and then click "Export" for the armature exporter. The root bone of the armature must be named "root". To export an animation, the process is the same. The active animation on the armature will be exported.
|
||||
For bones, there are 3 bone types: Default, Custom DL, and Ignore. This can be set in the bone properties window.
|
||||
Default is a regular deformation bone. Ignore will not be handled by the exporter/importer. Custom DL lets you define the name of a DL that you want a bone to draw instead of drawing geometry from the armature. There is also an option for billboarding the specific geometry associated with the bone.
|
||||
|
||||
The armature properties window also has the option to set a LOD armature. This armature must have the same bone structure as your current armature.
|
||||
|
||||
To export a skeletal mesh, select an armature and then click "Export" for the armature exporter. Make sure there is only one bone without a parent (the root bone), as the exporter will choose the first parentless bone as the start bone of the armature.
|
||||
|
||||
To import a skeletal mesh, just click "Import" for the armature importer. You may encounter a couple issues:
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
1. Eye/face textures are black: Texture pointers which are set dynamically will not be imported. Instead, the name of the pointer will be used instead of the actual data.
|
||||
2. Certain colors are white/different: Some graphical effects are achieved through dynamic Gfx commands, such as tinting white textures. These effects will not be imported.
|
||||
3. Strange imported normals: Due to the behaviour of rotating vertices on a skinned triangle that differs between Blender and the N64, normals may look strange. Note that these normals will look correct if re exported back into the game (assuming the rest pose is not changed).
|
||||
|
||||
Note that rest pose rotations are zeroed out on export, so you can modify the rest pose of imported armature while still preserving its structure. You can do this by using the "Apply As Rest Pose" operator under the Fast64 tab. Note that imported animations however still require the imported rest pose to work correctly.
|
||||
|
||||
There may also be an issue where some meshes import completely black due to the assumption that the F3D cycle mode is set to 2-Cycle, when it should really be 1-Cycle. Try changing the cycle type to 1-Cycle in cases where a dynamic texture pointer is not expected.
|
||||
|
||||
To import an animation, select the armature the animation belongs to then click "Import" on the animation importer.
|
||||
To export an animation, select an armature and click "Export", which will export the active animation of the armature.
|
||||
@@ -1,4 +1,3 @@
|
||||
from .oot_collision_c import *
|
||||
from .oot_level_c import *
|
||||
from .oot_dma_manager_c import *
|
||||
from .oot_scene_id_c import *
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
from ..oot_collision import *
|
||||
|
||||
def ootCollisionVertexToC(vertex):
|
||||
return "{ " + str(vertex.position[0]) + ", " +\
|
||||
str(vertex.position[1]) + ", " +\
|
||||
str(vertex.position[2]) + " },\n"
|
||||
|
||||
def ootCollisionPolygonToC(polygon, ignoreCamera, ignoreActor, ignoreProjectile, enableConveyor, polygonTypeIndex):
|
||||
return "{ " + format(polygonTypeIndex, "#06x") + ', ' +\
|
||||
format(polygon.convertShort02(ignoreCamera, ignoreActor, ignoreProjectile), "#06x") + ', ' +\
|
||||
format(polygon.convertShort04(enableConveyor), "#06x") + ', ' +\
|
||||
format(polygon.convertShort06(), "#06x") + ', ' +\
|
||||
format(polygon.normal[0], "#06x") + ', ' +\
|
||||
format(polygon.normal[1], "#06x") + ', ' +\
|
||||
format(polygon.normal[2], "#06x") + ', ' +\
|
||||
format(polygon.distance, "#06x") + ' },\n'
|
||||
|
||||
def ootPolygonTypeToC(polygonType):
|
||||
return " " + format(polygonType.convertHigh(), "#010x") + ', ' +\
|
||||
format(polygonType.convertLow(), "#010x") + ',\n'
|
||||
|
||||
def ootWaterBoxToC(waterBox):
|
||||
return "{ " + str(waterBox.low[0]) + ", " +\
|
||||
str(waterBox.height) + ", " +\
|
||||
str(waterBox.low[1]) + ", " +\
|
||||
str(waterBox.high[0] - waterBox.low[0]) + ", " +\
|
||||
str(waterBox.high[1] - waterBox.low[1]) + ", " +\
|
||||
format(waterBox.propertyData(), "#010x") + ' },\n'
|
||||
|
||||
def ootCameraDataToC(camData):
|
||||
posC = CData()
|
||||
camC = CData()
|
||||
if len(camData.camPosDict) > 0:
|
||||
|
||||
camDataName = "CamData " + camData.camDataName() + "[" + str(len(camData.camPosDict)) + "]"
|
||||
|
||||
camC.source = camDataName + ' = {\n'
|
||||
camC.header = "extern " + camDataName + ';\n'
|
||||
|
||||
camPosIndex = 0
|
||||
for i in range(len(camData.camPosDict)):
|
||||
camC.source += '\t' + ootCameraEntryToC(camData.camPosDict[i], camData, camPosIndex)
|
||||
if camData.camPosDict[i].hasPositionData:
|
||||
posC.source += ootCameraPosToC(camData.camPosDict[i])
|
||||
camPosIndex += 3
|
||||
posC.source += '};\n\n'
|
||||
camC.source += '};\n\n'
|
||||
|
||||
posDataName = "Vec3s " + camData.camPositionsName() + '[' + str(camPosIndex) + ']'
|
||||
posC.header = "extern " + posDataName + ';\n'
|
||||
posC.source = posDataName + " = {\n" + posC.source
|
||||
|
||||
posC.append(camC)
|
||||
return posC
|
||||
|
||||
def ootCameraPosToC(camPos):
|
||||
return "\t{ " +\
|
||||
str(camPos.position[0]) + ', ' +\
|
||||
str(camPos.position[1]) + ', ' +\
|
||||
str(camPos.position[2]) + ' },\n\t{ ' +\
|
||||
str(camPos.rotation[0]) + ', ' +\
|
||||
str(camPos.rotation[1]) + ', ' +\
|
||||
str(camPos.rotation[2]) + ' },\n\t{ ' +\
|
||||
str(camPos.fov) + ', ' +\
|
||||
str(camPos.jfifID) + ', ' +\
|
||||
str(camPos.unknown) + ' },\n'
|
||||
|
||||
def ootCameraEntryToC(camPos, camData, camPosIndex):
|
||||
return "{ " +\
|
||||
str(camPos.camSType) + ', ' +\
|
||||
('3' if camPos.hasPositionData else '0') + ', ' +\
|
||||
(("&" + camData.camPositionsName() + '[' + str(camPosIndex) + ']')
|
||||
if camPos.hasPositionData else "0") + ' },\n'
|
||||
|
||||
def ootCollisionToC(collision):
|
||||
data = CData()
|
||||
|
||||
data.append(ootCameraDataToC(collision.cameraData))
|
||||
|
||||
if len(collision.polygonGroups) > 0:
|
||||
data.header += "extern u32 " + collision.polygonTypesName() + "[];\n"
|
||||
data.header += "extern CollisionPoly " + collision.polygonsName() + "[];\n"
|
||||
polygonTypeC = "u32 " + collision.polygonTypesName() + "[] = {\n"
|
||||
polygonC = "CollisionPoly " + collision.polygonsName() + "[] = {\n"
|
||||
polygonIndex = 0
|
||||
for polygonType, polygons in collision.polygonGroups.items():
|
||||
polygonTypeC += '\t' + ootPolygonTypeToC(polygonType)
|
||||
for polygon in polygons:
|
||||
polygonC += '\t' + ootCollisionPolygonToC(polygon,
|
||||
polygonType.ignoreCameraCollision,
|
||||
polygonType.ignoreActorCollision,
|
||||
polygonType.ignoreProjectileCollision,
|
||||
polygonType.enableConveyor,
|
||||
polygonIndex)
|
||||
polygonIndex += 1
|
||||
polygonTypeC += '};\n\n'
|
||||
polygonC += '};\n\n'
|
||||
|
||||
data.source += polygonTypeC + polygonC
|
||||
polygonTypesName = collision.polygonTypesName()
|
||||
polygonsName = collision.polygonsName()
|
||||
else:
|
||||
polygonTypesName = '0'
|
||||
polygonsName = '0'
|
||||
|
||||
if len(collision.vertices) > 0:
|
||||
data.header += "extern Vec3s " + collision.verticesName() + "[" + str(len(collision.vertices)) + "];\n"
|
||||
data.source += "Vec3s " + collision.verticesName() + "[" + str(len(collision.vertices)) + "] = {\n"
|
||||
for vertex in collision.vertices:
|
||||
data.source += '\t' + ootCollisionVertexToC(vertex)
|
||||
data.source += '};\n\n'
|
||||
collisionVerticesName = collision.verticesName()
|
||||
else:
|
||||
collisionVerticesName = '0'
|
||||
|
||||
if len(collision.waterBoxes) > 0:
|
||||
data.header += "extern WaterBox " + collision.waterBoxesName() + "[];\n"
|
||||
data.source += "WaterBox " + collision.waterBoxesName() + "[] = {\n"
|
||||
for waterBox in collision.waterBoxes:
|
||||
data.source += '\t' + ootWaterBoxToC(waterBox)
|
||||
data.source += '};\n\n'
|
||||
waterBoxesName = collision.waterBoxesName()
|
||||
else:
|
||||
waterBoxesName = '0'
|
||||
|
||||
if len(collision.cameraData.camPosDict) > 0:
|
||||
camDataName = "&" + collision.camDataName()
|
||||
else:
|
||||
camDataName = '0'
|
||||
|
||||
data.header += "extern CollisionHeader " + collision.headerName() + ';\n'
|
||||
data.source += "CollisionHeader " + collision.headerName() + ' = { '
|
||||
|
||||
if len(collision.bounds) == 2:
|
||||
for bound in range(2): # min, max bound
|
||||
for field in range(3): # x, y, z
|
||||
data.source += str(collision.bounds[bound][field]) + ', '
|
||||
else:
|
||||
data.source += '0, 0, 0, 0, 0, 0, '
|
||||
|
||||
data.source += \
|
||||
str(len(collision.vertices)) + ', ' +\
|
||||
collisionVerticesName + ', ' +\
|
||||
str(collision.polygonCount()) + ", " +\
|
||||
polygonsName + ', ' +\
|
||||
polygonTypesName + ', ' +\
|
||||
camDataName + ', ' +\
|
||||
str(len(collision.waterBoxes)) + ", " +\
|
||||
waterBoxesName + ' };\n\n'
|
||||
|
||||
return data
|
||||
@@ -1,7 +1,7 @@
|
||||
import math
|
||||
from ..oot_f3d_writer import *
|
||||
from ..oot_level_writer import *
|
||||
from .oot_collision_c import *
|
||||
from ..oot_collision import *
|
||||
|
||||
def cmdName(name, header, index):
|
||||
return name + "_header" + format(header, '02') + "_cmd" + format(index, '02')
|
||||
|
||||
+173
-16
@@ -1,4 +1,4 @@
|
||||
import shutil, copy, math, mathutils, bpy, os
|
||||
import shutil, copy, math, mathutils, bpy, os, re
|
||||
|
||||
from bpy.utils import register_class, unregister_class
|
||||
from .oot_constants import *
|
||||
@@ -178,6 +178,96 @@ def exportAnimationC(armatureObj, exportPath, isCustomExport, folderName, skelet
|
||||
if not isCustomExport:
|
||||
addIncludeFiles(folderName, path, ootAnim.name)
|
||||
|
||||
def getNextBone(boneStack, armatureObj):
|
||||
if len(boneStack) == 0:
|
||||
raise PluginError("More bones in animation than on armature.")
|
||||
bone = armatureObj.data.bones[boneStack[0]]
|
||||
boneStack = boneStack[1:]
|
||||
boneStack = getSortedChildren(armatureObj, bone) + boneStack
|
||||
return bone, boneStack
|
||||
|
||||
def ootImportAnimationC(armatureObj, filepath, animName, actorScale):
|
||||
animData = readFile(filepath)
|
||||
|
||||
matchResult = re.search(re.escape(animName) + "\s*=\s*\{\s*\{\s*([^,\s]*)\s*\}*\s*,\s*([^,\s]*)\s*,\s*([^,\s]*)\s*,\s*([^,\s]*)\s*\}\s*;", animData)
|
||||
if matchResult is None:
|
||||
raise PluginError("Cannot find animation named " + animName + " in " + filepath)
|
||||
frameCount = hexOrDecInt(matchResult.group(1).strip())
|
||||
frameDataName = matchResult.group(2).strip()
|
||||
jointIndicesName = matchResult.group(3).strip()
|
||||
staticIndexMax = hexOrDecInt(matchResult.group(4).strip())
|
||||
|
||||
frameData = getFrameData(filepath, animData, frameDataName)
|
||||
jointIndices = getJointIndices(filepath, animData, jointIndicesName)
|
||||
|
||||
#print(frameDataName + " " + jointIndicesName)
|
||||
#print(str(frameData) + "\n" + str(jointIndices))
|
||||
|
||||
bpy.context.scene.frame_end = frameCount
|
||||
anim = bpy.data.actions.new(animName)
|
||||
|
||||
startBoneName = getStartBone(armatureObj)
|
||||
boneStack = [startBoneName]
|
||||
|
||||
isRootTranslation = True
|
||||
# boneFrameData = [[x keyframes], [y keyframes], [z keyframes]]
|
||||
# len(armatureFrameData) should be = number of bones
|
||||
# property index = 0,1,2 (aka x,y,z)
|
||||
for jointIndex in jointIndices:
|
||||
if isRootTranslation:
|
||||
for propertyIndex in range(3):
|
||||
fcurve = anim.fcurves.new(
|
||||
data_path = 'pose.bones["' + startBoneName + '"].location',
|
||||
index = propertyIndex,
|
||||
action_group = startBoneName)
|
||||
if jointIndex[propertyIndex] < staticIndexMax:
|
||||
value = frameData[jointIndex[propertyIndex]] / actorScale
|
||||
fcurve.keyframe_points.insert(0, value)
|
||||
else:
|
||||
for frame in range(frameCount):
|
||||
value = frameData[jointIndex[propertyIndex] + frame] / actorScale
|
||||
fcurve.keyframe_points.insert(frame, value)
|
||||
isRootTranslation = False
|
||||
else:
|
||||
# WARNING: This assumes the order bones are processed are in alphabetical order.
|
||||
# If this changes in the future, then this won't work.
|
||||
bone, boneStack = getNextBone(boneStack, armatureObj)
|
||||
for propertyIndex in range(3):
|
||||
fcurve = anim.fcurves.new(
|
||||
data_path = 'pose.bones["' + bone.name + '"].rotation_euler',
|
||||
index = propertyIndex,
|
||||
action_group = bone.name)
|
||||
if jointIndex[propertyIndex] < staticIndexMax:
|
||||
value = math.radians(frameData[jointIndex[propertyIndex]] * 360 / (2**16))
|
||||
fcurve.keyframe_points.insert(0, value)
|
||||
else:
|
||||
for frame in range(frameCount):
|
||||
value = math.radians(frameData[jointIndex[propertyIndex] + frame] * 360 / (2**16))
|
||||
fcurve.keyframe_points.insert(frame, value)
|
||||
|
||||
if armatureObj.animation_data is None:
|
||||
armatureObj.animation_data_create()
|
||||
armatureObj.animation_data.action = anim
|
||||
|
||||
def getFrameData(filepath, animData, frameDataName):
|
||||
matchResult = re.search(re.escape(frameDataName) + "\s*\[\s*[0-9]*\s*\]\s*=\s*\{([^\}]*)\}", animData, re.DOTALL)
|
||||
if matchResult is None:
|
||||
raise PluginError("Cannot find animation frame data named " + frameDataName + " in " + filepath)
|
||||
data = matchResult.group(1)
|
||||
frameData = [int.from_bytes([int(value.strip()[2:4], 16), int(value.strip()[4:6], 16)],
|
||||
'big', signed = True) for value in data.split(",") if value.strip() != ""]
|
||||
|
||||
return frameData
|
||||
|
||||
def getJointIndices(filepath, animData, jointIndicesName):
|
||||
matchResult = re.search(re.escape(jointIndicesName) + "\s*\[\s*[0-9]*\s*\]\s*=\s*\{([^;]*);", animData, re.DOTALL)
|
||||
if matchResult is None:
|
||||
raise PluginError("Cannot find animation joint indices data named " + jointIndicesName + " in " + filepath)
|
||||
data = matchResult.group(1)
|
||||
jointIndicesData = [[hexOrDecInt(match.group(i)) for i in range(1,4)] for match in re.finditer("\{([^,\}]*),([^,\}]*),([^,\}]*)\}", data, re.DOTALL)]
|
||||
|
||||
return jointIndicesData
|
||||
|
||||
class OOT_ExportAnim(bpy.types.Operator):
|
||||
bl_idname = 'object.oot_export_anim'
|
||||
bl_label = "Export Animation"
|
||||
@@ -201,10 +291,52 @@ class OOT_ExportAnim(bpy.types.Operator):
|
||||
|
||||
try:
|
||||
isCustomExport = context.scene.ootAnimIsCustomExport
|
||||
exportPath = context.scene.ootAnimCustomPath
|
||||
folderName = context.scene.ootAnimFolderName
|
||||
skeletonName = context.scene.ootAnimName
|
||||
exportAnimationC(armatureObj, exportPath, isCustomExport, folderName, skeletonName)
|
||||
exportPath = bpy.path.abspath(context.scene.ootAnimExportCustomPath)
|
||||
folderName = context.scene.ootAnimExportFolderName
|
||||
skeletonName = context.scene.ootAnimSkeletonName
|
||||
|
||||
path = ootGetObjectPath(isCustomExport, exportPath, folderName)
|
||||
|
||||
exportAnimationC(armatureObj, path, isCustomExport, folderName, skeletonName)
|
||||
self.report({'INFO'}, 'Success!')
|
||||
|
||||
except Exception as e:
|
||||
raisePluginError(self, e)
|
||||
return {'CANCELLED'} # must return a set
|
||||
|
||||
return {'FINISHED'} # must return a set
|
||||
|
||||
class OOT_ImportAnim(bpy.types.Operator):
|
||||
bl_idname = 'object.oot_import_anim'
|
||||
bl_label = "Import Animation"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
# Called on demand (i.e. button press, menu item)
|
||||
# Can also be called from operator search menu (Spacebar)
|
||||
def execute(self, context):
|
||||
try:
|
||||
if len(context.selected_objects) == 0 or not \
|
||||
isinstance(context.selected_objects[0].data, bpy.types.Armature):
|
||||
raise PluginError("Armature not selected.")
|
||||
if len(context.selected_objects) > 1 :
|
||||
raise PluginError("Multiple objects selected, make sure to select only one.")
|
||||
armatureObj = context.selected_objects[0]
|
||||
if context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = "OBJECT")
|
||||
except Exception as e:
|
||||
raisePluginError(self, e)
|
||||
return {"CANCELLED"}
|
||||
|
||||
try:
|
||||
isCustomImport = context.scene.ootAnimIsCustomImport
|
||||
folderName = context.scene.ootAnimImportFolderName
|
||||
importPath = bpy.path.abspath(context.scene.ootAnimImportCustomPath)
|
||||
animName = context.scene.ootAnimName
|
||||
actorScale = context.scene.ootActorBlenderScale
|
||||
|
||||
path = ootGetObjectPath(isCustomImport, importPath, folderName)
|
||||
|
||||
ootImportAnimationC(armatureObj, path, animName, actorScale)
|
||||
self.report({'INFO'}, 'Success!')
|
||||
|
||||
except Exception as e:
|
||||
@@ -227,18 +359,30 @@ class OOT_ExportAnimPanel(bpy.types.Panel):
|
||||
# called every frame
|
||||
def draw(self, context):
|
||||
col = self.layout.column()
|
||||
|
||||
col.operator(OOT_ExportAnim.bl_idname)
|
||||
|
||||
col.prop(context.scene, 'ootAnimIsCustomExport')
|
||||
prop_split(col, context.scene, 'ootAnimName', 'Skeleton')
|
||||
prop_split(col, context.scene, 'ootAnimSkeletonName', 'Skeleton Name')
|
||||
if context.scene.ootAnimIsCustomExport:
|
||||
col.prop(context.scene, 'ootAnimCustomPath')
|
||||
prop_split(col, context.scene, 'ootAnimExportCustomPath', "Folder")
|
||||
else:
|
||||
prop_split(col, context.scene, 'ootAnimFolderName', 'Object')
|
||||
|
||||
prop_split(col, context.scene, 'ootAnimExportFolderName', 'Object')
|
||||
col.prop(context.scene, 'ootAnimIsCustomExport')
|
||||
|
||||
|
||||
col.operator(OOT_ImportAnim.bl_idname)
|
||||
prop_split(col, context.scene, 'ootAnimName', 'Anim Name')
|
||||
|
||||
if context.scene.ootAnimIsCustomImport:
|
||||
prop_split(col, context.scene, 'ootAnimImportCustomPath', "File")
|
||||
else:
|
||||
prop_split(col, context.scene, 'ootAnimImportFolderName', 'Object')
|
||||
col.prop(context.scene, 'ootAnimIsCustomImport')
|
||||
|
||||
|
||||
|
||||
oot_anim_classes = (
|
||||
OOT_ExportAnim,
|
||||
OOT_ImportAnim,
|
||||
)
|
||||
|
||||
oot_anim_panels = (
|
||||
@@ -255,17 +399,30 @@ def oot_anim_panel_unregister():
|
||||
|
||||
def oot_anim_register():
|
||||
bpy.types.Scene.ootAnimIsCustomExport = bpy.props.BoolProperty(name = "Use Custom Path")
|
||||
bpy.types.Scene.ootAnimCustomPath = bpy.props.StringProperty(
|
||||
bpy.types.Scene.ootAnimExportCustomPath = bpy.props.StringProperty(
|
||||
name ='Folder', subtype = 'FILE_PATH')
|
||||
bpy.types.Scene.ootAnimFolderName = bpy.props.StringProperty(name = "Animation Folder", default = "gameplay_keep")
|
||||
bpy.types.Scene.ootAnimName = bpy.props.StringProperty(name = "Skeleton Name", default = "skeleton")
|
||||
bpy.types.Scene.ootAnimExportFolderName = bpy.props.StringProperty(name = "Animation Folder", default = "object_geldb")
|
||||
|
||||
bpy.types.Scene.ootAnimIsCustomImport = bpy.props.BoolProperty(name = "Use Custom Path")
|
||||
bpy.types.Scene.ootAnimImportCustomPath = bpy.props.StringProperty(
|
||||
name ='Folder', subtype = 'FILE_PATH')
|
||||
bpy.types.Scene.ootAnimImportFolderName = bpy.props.StringProperty(name = "Animation Folder", default = "object_geldb")
|
||||
|
||||
bpy.types.Scene.ootAnimSkeletonName = bpy.props.StringProperty(name = "Skeleton Name", default = "gGerudoRedSkel")
|
||||
bpy.types.Scene.ootAnimName = bpy.props.StringProperty(name = "Anim Name", default = "gGerudoRedSpinAttackAnim")
|
||||
for cls in oot_anim_classes:
|
||||
register_class(cls)
|
||||
|
||||
def oot_anim_unregister():
|
||||
del bpy.types.Scene.ootAnimIsCustomExport
|
||||
del bpy.types.Scene.ootAnimCustomPath
|
||||
del bpy.types.Scene.ootAnimFolderName
|
||||
del bpy.types.Scene.ootAnimExportCustomPath
|
||||
del bpy.types.Scene.ootAnimExportFolderName
|
||||
|
||||
del bpy.types.Scene.ootAnimIsCustomImport
|
||||
del bpy.types.Scene.ootAnimImportCustomPath
|
||||
del bpy.types.Scene.ootAnimImportFolderName
|
||||
|
||||
del bpy.types.Scene.ootAnimSkeletonName
|
||||
del bpy.types.Scene.ootAnimName
|
||||
for cls in reversed(oot_anim_classes):
|
||||
unregister_class(cls)
|
||||
@@ -466,14 +466,14 @@ class OOT_CollisionPanel(bpy.types.Panel):
|
||||
box.prop(collisionProp, "conveyorKeepMomentum", text = "Keep Momentum")
|
||||
|
||||
# water boxes handled by level writer
|
||||
def exportCollisionCommon(collision, sceneObj, allObjs, transformMatrix, includeChildren, name):
|
||||
def exportCollisionCommon(collision, obj, transformMatrix, includeChildren, name):
|
||||
bpy.ops.object.select_all(action = 'DESELECT')
|
||||
sceneObj.select_set(True)
|
||||
obj.select_set(True)
|
||||
|
||||
# dict of collisionType : faces
|
||||
collisionDict = {}
|
||||
|
||||
addCollisionTriangles(sceneObj, collisionDict, includeChildren, transformMatrix, collision.bounds)
|
||||
addCollisionTriangles(obj, collisionDict, includeChildren, transformMatrix, collision.bounds)
|
||||
for material, faces in collisionDict.items():
|
||||
polygonType = getPolygonType(material.ootCollisionProperty)
|
||||
collision.polygonGroups[polygonType] = []
|
||||
@@ -488,6 +488,30 @@ def exportCollisionCommon(collision, sceneObj, allObjs, transformMatrix, include
|
||||
indices.append(index)
|
||||
collision.polygonGroups[polygonType].append(OOTCollisionPolygon(indices, normal, distance))
|
||||
|
||||
def exportCollisionToC(obj, transformMatrix, includeChildren, name, isCustomExport, folderName,
|
||||
exportPath):
|
||||
collision = OOTCollision(name)
|
||||
collision.cameraData = OOTCameraData(name)
|
||||
exportCollisionCommon(collision, obj, transformMatrix, includeChildren, name)
|
||||
collisionC = ootCollisionToC(collision)
|
||||
|
||||
data = CData()
|
||||
data.source += '#include "ultra64.h"\n#include "z64.h"\n#include "macros.h"\n'
|
||||
if not isCustomExport:
|
||||
data.source += '#include "' + folderName + '.h"\n\n'
|
||||
else:
|
||||
data.source += '\n'
|
||||
|
||||
data.append(collisionC)
|
||||
|
||||
path = ootGetPath(exportPath, isCustomExport, 'assets/objects/', folderName, False, False)
|
||||
writeCData(data,
|
||||
os.path.join(path, name + '.h'),
|
||||
os.path.join(path, name + '.c'))
|
||||
|
||||
if not isCustomExport:
|
||||
addIncludeFiles(folderName, path, name)
|
||||
|
||||
def updateBounds(position, bounds):
|
||||
if len(bounds) == 0:
|
||||
bounds.append([position[0], position[1], position[2]])
|
||||
@@ -561,6 +585,160 @@ def collisionVertIndex(vert, vertArray):
|
||||
return i
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
def ootCollisionVertexToC(vertex):
|
||||
return "{ " + str(vertex.position[0]) + ", " +\
|
||||
str(vertex.position[1]) + ", " +\
|
||||
str(vertex.position[2]) + " },\n"
|
||||
|
||||
def ootCollisionPolygonToC(polygon, ignoreCamera, ignoreActor, ignoreProjectile, enableConveyor, polygonTypeIndex):
|
||||
return "{ " + format(polygonTypeIndex, "#06x") + ', ' +\
|
||||
format(polygon.convertShort02(ignoreCamera, ignoreActor, ignoreProjectile), "#06x") + ', ' +\
|
||||
format(polygon.convertShort04(enableConveyor), "#06x") + ', ' +\
|
||||
format(polygon.convertShort06(), "#06x") + ', ' +\
|
||||
format(polygon.normal[0], "#06x") + ', ' +\
|
||||
format(polygon.normal[1], "#06x") + ', ' +\
|
||||
format(polygon.normal[2], "#06x") + ', ' +\
|
||||
format(polygon.distance, "#06x") + ' },\n'
|
||||
|
||||
def ootPolygonTypeToC(polygonType):
|
||||
return " " + format(polygonType.convertHigh(), "#010x") + ', ' +\
|
||||
format(polygonType.convertLow(), "#010x") + ',\n'
|
||||
|
||||
def ootWaterBoxToC(waterBox):
|
||||
return "{ " + str(waterBox.low[0]) + ", " +\
|
||||
str(waterBox.height) + ", " +\
|
||||
str(waterBox.low[1]) + ", " +\
|
||||
str(waterBox.high[0] - waterBox.low[0]) + ", " +\
|
||||
str(waterBox.high[1] - waterBox.low[1]) + ", " +\
|
||||
format(waterBox.propertyData(), "#010x") + ' },\n'
|
||||
|
||||
def ootCameraDataToC(camData):
|
||||
posC = CData()
|
||||
camC = CData()
|
||||
if len(camData.camPosDict) > 0:
|
||||
|
||||
camDataName = "CamData " + camData.camDataName() + "[" + str(len(camData.camPosDict)) + "]"
|
||||
|
||||
camC.source = camDataName + ' = {\n'
|
||||
camC.header = "extern " + camDataName + ';\n'
|
||||
|
||||
camPosIndex = 0
|
||||
for i in range(len(camData.camPosDict)):
|
||||
camC.source += '\t' + ootCameraEntryToC(camData.camPosDict[i], camData, camPosIndex)
|
||||
if camData.camPosDict[i].hasPositionData:
|
||||
posC.source += ootCameraPosToC(camData.camPosDict[i])
|
||||
camPosIndex += 3
|
||||
posC.source += '};\n\n'
|
||||
camC.source += '};\n\n'
|
||||
|
||||
posDataName = "Vec3s " + camData.camPositionsName() + '[' + str(camPosIndex) + ']'
|
||||
posC.header = "extern " + posDataName + ';\n'
|
||||
posC.source = posDataName + " = {\n" + posC.source
|
||||
|
||||
posC.append(camC)
|
||||
return posC
|
||||
|
||||
def ootCameraPosToC(camPos):
|
||||
return "\t{ " +\
|
||||
str(camPos.position[0]) + ', ' +\
|
||||
str(camPos.position[1]) + ', ' +\
|
||||
str(camPos.position[2]) + ' },\n\t{ ' +\
|
||||
str(camPos.rotation[0]) + ', ' +\
|
||||
str(camPos.rotation[1]) + ', ' +\
|
||||
str(camPos.rotation[2]) + ' },\n\t{ ' +\
|
||||
str(camPos.fov) + ', ' +\
|
||||
str(camPos.jfifID) + ', ' +\
|
||||
str(camPos.unknown) + ' },\n'
|
||||
|
||||
def ootCameraEntryToC(camPos, camData, camPosIndex):
|
||||
return "{ " +\
|
||||
str(camPos.camSType) + ', ' +\
|
||||
('3' if camPos.hasPositionData else '0') + ', ' +\
|
||||
(("&" + camData.camPositionsName() + '[' + str(camPosIndex) + ']')
|
||||
if camPos.hasPositionData else "0") + ' },\n'
|
||||
|
||||
def ootCollisionToC(collision):
|
||||
data = CData()
|
||||
|
||||
data.append(ootCameraDataToC(collision.cameraData))
|
||||
|
||||
if len(collision.polygonGroups) > 0:
|
||||
data.header += "extern u32 " + collision.polygonTypesName() + "[];\n"
|
||||
data.header += "extern CollisionPoly " + collision.polygonsName() + "[];\n"
|
||||
polygonTypeC = "u32 " + collision.polygonTypesName() + "[] = {\n"
|
||||
polygonC = "CollisionPoly " + collision.polygonsName() + "[] = {\n"
|
||||
polygonIndex = 0
|
||||
for polygonType, polygons in collision.polygonGroups.items():
|
||||
polygonTypeC += '\t' + ootPolygonTypeToC(polygonType)
|
||||
for polygon in polygons:
|
||||
polygonC += '\t' + ootCollisionPolygonToC(polygon,
|
||||
polygonType.ignoreCameraCollision,
|
||||
polygonType.ignoreActorCollision,
|
||||
polygonType.ignoreProjectileCollision,
|
||||
polygonType.enableConveyor,
|
||||
polygonIndex)
|
||||
polygonIndex += 1
|
||||
polygonTypeC += '};\n\n'
|
||||
polygonC += '};\n\n'
|
||||
|
||||
data.source += polygonTypeC + polygonC
|
||||
polygonTypesName = collision.polygonTypesName()
|
||||
polygonsName = collision.polygonsName()
|
||||
else:
|
||||
polygonTypesName = '0'
|
||||
polygonsName = '0'
|
||||
|
||||
if len(collision.vertices) > 0:
|
||||
data.header += "extern Vec3s " + collision.verticesName() + "[" + str(len(collision.vertices)) + "];\n"
|
||||
data.source += "Vec3s " + collision.verticesName() + "[" + str(len(collision.vertices)) + "] = {\n"
|
||||
for vertex in collision.vertices:
|
||||
data.source += '\t' + ootCollisionVertexToC(vertex)
|
||||
data.source += '};\n\n'
|
||||
collisionVerticesName = collision.verticesName()
|
||||
else:
|
||||
collisionVerticesName = '0'
|
||||
|
||||
if len(collision.waterBoxes) > 0:
|
||||
data.header += "extern WaterBox " + collision.waterBoxesName() + "[];\n"
|
||||
data.source += "WaterBox " + collision.waterBoxesName() + "[] = {\n"
|
||||
for waterBox in collision.waterBoxes:
|
||||
data.source += '\t' + ootWaterBoxToC(waterBox)
|
||||
data.source += '};\n\n'
|
||||
waterBoxesName = collision.waterBoxesName()
|
||||
else:
|
||||
waterBoxesName = '0'
|
||||
|
||||
if len(collision.cameraData.camPosDict) > 0:
|
||||
camDataName = "&" + collision.camDataName()
|
||||
else:
|
||||
camDataName = '0'
|
||||
|
||||
data.header += "extern CollisionHeader " + collision.headerName() + ';\n'
|
||||
data.source += "CollisionHeader " + collision.headerName() + ' = { '
|
||||
|
||||
if len(collision.bounds) == 2:
|
||||
for bound in range(2): # min, max bound
|
||||
for field in range(3): # x, y, z
|
||||
data.source += str(collision.bounds[bound][field]) + ', '
|
||||
else:
|
||||
data.source += '0, 0, 0, 0, 0, 0, '
|
||||
|
||||
data.source += \
|
||||
str(len(collision.vertices)) + ', ' +\
|
||||
collisionVerticesName + ', ' +\
|
||||
str(collision.polygonCount()) + ", " +\
|
||||
polygonsName + ', ' +\
|
||||
polygonTypesName + ', ' +\
|
||||
camDataName + ', ' +\
|
||||
str(len(collision.waterBoxes)) + ", " +\
|
||||
waterBoxesName + ' };\n\n'
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class OOT_ExportCollision(bpy.types.Operator):
|
||||
# set bl_ properties
|
||||
bl_idname = 'object.oot_export_collision'
|
||||
@@ -568,109 +746,38 @@ class OOT_ExportCollision(bpy.types.Operator):
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
def execute(self, context):
|
||||
romfileOutput = None
|
||||
tempROM = None
|
||||
try:
|
||||
obj = None
|
||||
if context.mode != 'OBJECT':
|
||||
raise PluginError("Operator can only be used in object mode.")
|
||||
if len(context.selected_objects) == 0:
|
||||
raise PluginError("Object not selected.")
|
||||
obj = context.active_object
|
||||
#if type(obj.data) is not bpy.types.Mesh:
|
||||
# raise PluginError("Mesh not selected.")
|
||||
|
||||
#T, R, S = obj.matrix_world.decompose()
|
||||
#objTransform = R.to_matrix().to_4x4() @ \
|
||||
# mathutils.Matrix.Diagonal(S).to_4x4()
|
||||
#finalTransform = (blenderToSM64Rotation * \
|
||||
# (bpy.context.scene.blenderToSM64Scale)).to_4x4()
|
||||
#finalTransform = mathutils.Matrix.Identity(4)
|
||||
obj = None
|
||||
if context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = "OBJECT")
|
||||
if len(context.selected_objects) == 0:
|
||||
raise PluginError("No object selected.")
|
||||
obj = context.active_object
|
||||
if type(obj.data) is not bpy.types.Mesh:
|
||||
raise PluginError("No mesh object selected.")
|
||||
|
||||
scaleValue = bpy.context.scene.blenderToSM64Scale
|
||||
finalTransform = mathutils.Matrix.Scale(context.scene.ootActorBlenderScale, 4)
|
||||
|
||||
try:
|
||||
scaleValue = bpy.context.scene.ootBlenderScale
|
||||
finalTransform = mathutils.Matrix.Diagonal(mathutils.Vector((
|
||||
scaleValue, scaleValue, scaleValue))).to_4x4()
|
||||
except Exception as e:
|
||||
raisePluginError(self, e)
|
||||
return {"CANCELLED"}
|
||||
|
||||
try:
|
||||
applyRotation([obj], math.radians(90), 'X')
|
||||
if context.scene.colExportType == 'C':
|
||||
exportPath, levelName = getPathAndLevel(context.scene.colCustomExport,
|
||||
context.scene.colExportPath, context.scene.colLevelName,
|
||||
context.scene.colLevelOption)
|
||||
if not context.scene.colCustomExport:
|
||||
applyBasicTweaks(exportPath)
|
||||
exportCollisionC(obj, finalTransform,
|
||||
exportPath, False,
|
||||
context.scene.colIncludeChildren,
|
||||
bpy.context.scene.colName, context.scene.colCustomExport, context.scene.colExportRooms,
|
||||
context.scene.colExportHeaderType, context.scene.colGroupName, levelName)
|
||||
self.report({'INFO'}, 'Success!')
|
||||
elif context.scene.colExportType == 'Insertable Binary':
|
||||
exportCollisionInsertableBinary(obj, finalTransform,
|
||||
bpy.path.abspath(context.scene.colInsertableBinaryPath),
|
||||
False, context.scene.colIncludeChildren)
|
||||
self.report({'INFO'}, 'Success! Collision at ' + \
|
||||
context.scene.colInsertableBinaryPath)
|
||||
else:
|
||||
tempROM = tempName(context.scene.outputRom)
|
||||
checkExpanded(bpy.path.abspath(context.scene.exportRom))
|
||||
romfileExport = \
|
||||
open(bpy.path.abspath(context.scene.exportRom), 'rb')
|
||||
shutil.copy(bpy.path.abspath(context.scene.exportRom),
|
||||
bpy.path.abspath(tempROM))
|
||||
romfileExport.close()
|
||||
romfileOutput = open(bpy.path.abspath(tempROM), 'rb+')
|
||||
|
||||
levelParsed = parseLevelAtPointer(romfileOutput,
|
||||
level_pointers[context.scene.colExportLevel])
|
||||
segmentData = levelParsed.segmentData
|
||||
includeChildren = context.scene.ootColIncludeChildren
|
||||
name = context.scene.ootColName
|
||||
isCustomExport = context.scene.ootColCustomExport
|
||||
folderName = context.scene.ootColFolder
|
||||
exportPath = bpy.path.abspath(context.scene.ootColExportPath)
|
||||
|
||||
if context.scene.extendBank4:
|
||||
ExtendBank0x04(romfileOutput, segmentData,
|
||||
defaultExtendSegment4)
|
||||
filepath = ootGetObjectPath(isCustomExport, exportPath, folderName)
|
||||
exportCollisionToC(obj, finalTransform, includeChildren, name, isCustomExport, folderName,
|
||||
filepath)
|
||||
|
||||
addrRange = \
|
||||
exportCollisionBinary(obj, finalTransform, romfileOutput,
|
||||
int(context.scene.colStartAddr, 16),
|
||||
int(context.scene.colEndAddr, 16),
|
||||
False, context.scene.colIncludeChildren)
|
||||
|
||||
segAddress = encodeSegmentedAddr(addrRange[0], segmentData)
|
||||
if context.scene.set_addr_0x2A:
|
||||
romfileOutput.seek(int(context.scene.addr_0x2A, 16) + 4)
|
||||
romfileOutput.write(segAddress)
|
||||
segPointer = bytesToHex(segAddress)
|
||||
|
||||
romfileOutput.close()
|
||||
|
||||
if os.path.exists(bpy.path.abspath(context.scene.outputRom)):
|
||||
os.remove(bpy.path.abspath(context.scene.outputRom))
|
||||
os.rename(bpy.path.abspath(tempROM),
|
||||
bpy.path.abspath(context.scene.outputRom))
|
||||
|
||||
self.report({'INFO'}, 'Success! Collision at (' + \
|
||||
hex(addrRange[0]) + ', ' + hex(addrRange[1]) + \
|
||||
') (Seg. ' + segPointer + ').')
|
||||
|
||||
applyRotation([obj], math.radians(-90), 'X')
|
||||
return {'FINISHED'} # must return a set
|
||||
self.report({'INFO'}, 'Success!')
|
||||
return {'FINISHED'}
|
||||
|
||||
except Exception as e:
|
||||
if context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||
|
||||
applyRotation([obj], math.radians(-90), 'X')
|
||||
|
||||
if context.scene.colExportType == 'Binary':
|
||||
if romfileOutput is not None:
|
||||
romfileOutput.close()
|
||||
if tempROM is not None and os.path.exists(bpy.path.abspath(tempROM)):
|
||||
os.remove(bpy.path.abspath(tempROM))
|
||||
obj.select_set(True)
|
||||
context.view_layer.objects.active = obj
|
||||
raisePluginError(self, e)
|
||||
return {'CANCELLED'} # must return a set
|
||||
|
||||
@@ -688,18 +795,15 @@ class OOT_ExportCollisionPanel(bpy.types.Panel):
|
||||
# called every frame
|
||||
def draw(self, context):
|
||||
col = self.layout.column()
|
||||
propsColE = col.operator(OOT_ExportCollision.bl_idname)
|
||||
col.operator(OOT_ExportCollision.bl_idname)
|
||||
|
||||
col.prop(context.scene, 'colIncludeChildren')
|
||||
col.prop(context.scene, 'colCustomExport')
|
||||
if context.scene.colCustomExport:
|
||||
col.prop(context.scene, 'colExportPath')
|
||||
prop_split(col, context.scene, 'colName', 'Name')
|
||||
customExportWarning(col)
|
||||
prop_split(col, context.scene, 'ootColName', 'Name')
|
||||
if context.scene.ootColCustomExport:
|
||||
prop_split(col, context.scene, 'ootColExportPath', "Custom Folder")
|
||||
else:
|
||||
prop_split(col, context.scene, 'colExportLevel', 'Level')
|
||||
if context.scene.colExportLevel == 'Custom':
|
||||
prop_split(col, context.scene, 'colLevelName', 'Level Name')
|
||||
prop_split(col, context.scene, 'ootColFolder', 'Object')
|
||||
col.prop(context.scene, 'ootColCustomExport')
|
||||
col.prop(context.scene, 'ootColIncludeChildren')
|
||||
|
||||
|
||||
oot_col_classes = (
|
||||
@@ -713,7 +817,7 @@ oot_col_classes = (
|
||||
oot_col_panel_classes = (
|
||||
OOT_CollisionPanel,
|
||||
OOT_CameraPosPanel,
|
||||
#OOT_ExportCollisionPanel,
|
||||
OOT_ExportCollisionPanel,
|
||||
)
|
||||
|
||||
def oot_col_panel_register():
|
||||
@@ -736,11 +840,13 @@ def oot_col_register():
|
||||
bpy.types.Scene.ootColIncludeChildren = bpy.props.BoolProperty(
|
||||
name = 'Include child objects', default = True)
|
||||
bpy.types.Scene.ootColName = bpy.props.StringProperty(
|
||||
name = 'Name', default = 'link')
|
||||
name = 'Name', default = 'collision')
|
||||
bpy.types.Scene.ootColLevelName = bpy.props.StringProperty(
|
||||
name = 'Name', default = 'SCENE_YDAN')
|
||||
bpy.types.Scene.ootColCustomExport = bpy.props.BoolProperty(
|
||||
name = 'Custom Export Path')
|
||||
bpy.types.Scene.ootColFolder = bpy.props.StringProperty(
|
||||
name = 'Object Name', default = "gameplay_keep")
|
||||
|
||||
bpy.types.Object.ootCameraPositionProperty = bpy.props.PointerProperty(type = OOTCameraPositionProperty)
|
||||
bpy.types.Material.ootCollisionProperty = bpy.props.PointerProperty(type = OOTMaterialCollisionProperty)
|
||||
@@ -753,6 +859,7 @@ def oot_col_unregister():
|
||||
del bpy.types.Scene.ootColLevelName
|
||||
del bpy.types.Scene.ootColIncludeChildren
|
||||
del bpy.types.Scene.ootColCustomExport
|
||||
del bpy.types.Scene.ootColFolder
|
||||
|
||||
for cls in reversed(oot_col_classes):
|
||||
unregister_class(cls)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import shutil, copy, bpy
|
||||
import shutil, copy, bpy, os
|
||||
|
||||
from ..f3d.f3d_writer import *
|
||||
from ..f3d.f3d_material import *
|
||||
@@ -6,18 +6,24 @@ from bpy.utils import register_class, unregister_class
|
||||
from .oot_constants import *
|
||||
from .oot_utility import *
|
||||
from .oot_scene_room import *
|
||||
from ..f3d.f3d_parser import *
|
||||
|
||||
class OOTModel(FModel):
|
||||
def __init__(self, f3dType, isHWv1, name, DLFormat):
|
||||
def __init__(self, f3dType, isHWv1, name, DLFormat, drawLayerOverride):
|
||||
self.drawLayerOverride = drawLayerOverride
|
||||
FModel.__init__(self, f3dType, isHWv1, name, DLFormat, GfxMatWriteMethod.WriteAll)
|
||||
|
||||
def getDrawLayerV3(self, obj):
|
||||
return obj.ootDrawLayer
|
||||
|
||||
def getRenderMode(self, drawLayer):
|
||||
if self.drawLayerOverride:
|
||||
drawLayerUsed = self.drawLayerOverride
|
||||
else:
|
||||
drawLayerUsed = drawLayer
|
||||
defaultRenderModes = bpy.context.scene.world.ootDefaultRenderModes
|
||||
cycle1 = getattr(defaultRenderModes, drawLayer.lower() + "Cycle1")
|
||||
cycle2 = getattr(defaultRenderModes, drawLayer.lower() + "Cycle2")
|
||||
cycle1 = getattr(defaultRenderModes, drawLayerUsed.lower() + "Cycle1")
|
||||
cycle2 = getattr(defaultRenderModes, drawLayerUsed.lower() + "Cycle2")
|
||||
return [cycle1, cycle2]
|
||||
|
||||
def getTextureSuffixFromFormat(self, texFmt):
|
||||
@@ -34,6 +40,11 @@ class OOTModel(FModel):
|
||||
GfxList("0x" + format(i,'X') + '000000', GfxListTag.Material, DLFormat.Static)))
|
||||
return
|
||||
|
||||
def onAddMesh(self, fMesh, contextObj):
|
||||
if contextObj is not None and hasattr(contextObj, 'ootDynamicTransform'):
|
||||
if contextObj.ootDynamicTransform.billboard:
|
||||
fMesh.draw.commands.append(SPMatrix("0x01000000", "G_MTX_MODELVIEW | G_MTX_NOPUSH | G_MTX_MUL"))
|
||||
|
||||
class OOTDynamicMaterialDrawLayer:
|
||||
def __init__(self, opaque, transparent):
|
||||
self.opaque = opaque
|
||||
@@ -89,6 +100,176 @@ class OOTVertexGroupInfo(VertexGroupInfo):
|
||||
self.vertexGroupToMatrixIndex = {}
|
||||
VertexGroupInfo.__init__(self)
|
||||
|
||||
#class OOTBox:
|
||||
# def __init__(self):
|
||||
# self.minBounds = [-2**8, -2**8]
|
||||
# self.maxBounds = [2**8 - 1, 2**8 - 1]
|
||||
|
||||
|
||||
class OOTObjectCategorizer:
|
||||
def __init__(self):
|
||||
self.sceneObj = None
|
||||
self.roomObjs = []
|
||||
self.actors = []
|
||||
self.transitionActors = []
|
||||
self.meshes = []
|
||||
self.entrances = []
|
||||
self.waterBoxes = []
|
||||
|
||||
def sortObjects(self, allObjs):
|
||||
for obj in allObjs:
|
||||
if obj.data is None:
|
||||
if obj.ootEmptyType == "Actor":
|
||||
self.actors.append(obj)
|
||||
elif obj.ootEmptyType == "Transition Actor":
|
||||
self.transitionActors.append(obj)
|
||||
elif obj.ootEmptyType == "Entrance":
|
||||
self.entrances.append(obj)
|
||||
elif obj.ootEmptyType == "Water Box":
|
||||
self.waterBoxes.append(obj)
|
||||
elif obj.ootEmptyType == "Room":
|
||||
self.roomObjs.append(obj)
|
||||
elif obj.ootEmptyType == "Scene":
|
||||
self.sceneObj = obj
|
||||
elif isinstance(obj.data, bpy.types.Mesh):
|
||||
self.meshes.append(obj)
|
||||
|
||||
# This also sets all origins relative to the scene object.
|
||||
def ootDuplicateHierarchy(obj, ignoreAttr, includeEmpties, objectCategorizer):
|
||||
# Duplicate objects to apply scale / modifiers / linked data
|
||||
bpy.ops.object.select_all(action = 'DESELECT')
|
||||
ootSelectMeshChildrenOnly(obj, includeEmpties)
|
||||
obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.duplicate()
|
||||
try:
|
||||
tempObj = bpy.context.view_layer.objects.active
|
||||
allObjs = bpy.context.selected_objects
|
||||
bpy.ops.object.make_single_user(obdata = True)
|
||||
|
||||
objectCategorizer.sortObjects(allObjs)
|
||||
meshObjs = objectCategorizer.meshes
|
||||
bpy.ops.object.select_all(action = 'DESELECT')
|
||||
for selectedObj in meshObjs:
|
||||
selectedObj.select_set(True)
|
||||
bpy.ops.object.transform_apply(location = False,
|
||||
rotation = True, scale = True, properties = False)
|
||||
|
||||
for selectedObj in meshObjs:
|
||||
bpy.ops.object.select_all(action = 'DESELECT')
|
||||
selectedObj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = selectedObj
|
||||
for modifier in selectedObj.modifiers:
|
||||
attemptModifierApply(modifier)
|
||||
for selectedObj in meshObjs:
|
||||
setOrigin(obj, selectedObj)
|
||||
if ignoreAttr is not None:
|
||||
for selectedObj in meshObjs:
|
||||
if getattr(selectedObj, ignoreAttr):
|
||||
for child in selectedObj.children:
|
||||
bpy.ops.object.select_all(action = 'DESELECT')
|
||||
child.select_set(True)
|
||||
bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')
|
||||
selectedObj.parent.select_set(True)
|
||||
bpy.ops.object.parent_set(keep_transform = True)
|
||||
selectedObj.parent = None
|
||||
applyRotation([tempObj], math.radians(90), 'X')
|
||||
return tempObj, allObjs
|
||||
except Exception as e:
|
||||
cleanupDuplicatedObjects(allObjs)
|
||||
obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
raise Exception(str(e))
|
||||
|
||||
def ootSelectMeshChildrenOnly(obj, includeEmpties):
|
||||
isMesh = isinstance(obj.data, bpy.types.Mesh)
|
||||
isEmpty = (obj.data is None or \
|
||||
isinstance(obj.data, bpy.types.Camera) or\
|
||||
isinstance(obj.data, bpy.types.Curve)) and includeEmpties
|
||||
if (isMesh or isEmpty):
|
||||
obj.select_set(True)
|
||||
obj.original_name = obj.name
|
||||
for child in obj.children:
|
||||
ootSelectMeshChildrenOnly(child, includeEmpties)
|
||||
|
||||
def ootCleanupScene(originalSceneObj, allObjs):
|
||||
cleanupDuplicatedObjects(allObjs)
|
||||
originalSceneObj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = originalSceneObj
|
||||
|
||||
class OOTF3DContext(F3DContext):
|
||||
def __init__(self, f3d, limbList, basePath):
|
||||
self.limbList = limbList
|
||||
self.dlList = [] # in the order they are rendered
|
||||
self.isBillboard = False
|
||||
materialContext = createF3DMat(None, preset = 'oot_shaded_solid')
|
||||
#materialContext.f3d_mat.rdp_settings.g_mdsft_cycletype = "G_CYC_1CYCLE"
|
||||
F3DContext.__init__(self, f3d, basePath, materialContext)
|
||||
|
||||
def getLimbName(self, index):
|
||||
return self.limbList[index]
|
||||
|
||||
def getBoneName(self, index):
|
||||
return "bone" + format(index, "03") + "_" + self.getLimbName(index)
|
||||
|
||||
def vertexFormatPatterns(self, data):
|
||||
# position, uv, color/normal
|
||||
if "VTX" in data:
|
||||
return ["VTX\s*\(([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*)\)"]
|
||||
else:
|
||||
return F3DContext.vertexFormatPatterns(self, data)
|
||||
|
||||
# For game specific instance, override this to be able to identify which verts belong to which bone.
|
||||
def setCurrentTransform(self, name):
|
||||
if name[:4].lower() == "0x0d":
|
||||
self.currentTransformName = self.getLimbName(self.dlList[int(int(name[4:], 16) / MTX_SIZE)].limbIndex)
|
||||
else:
|
||||
try:
|
||||
pointer = hexOrDecInt(name)
|
||||
except:
|
||||
self.currentTransformName = name
|
||||
else:
|
||||
if pointer >> 24 == 0x01:
|
||||
self.isBillboard = True
|
||||
else:
|
||||
print("Unhandled matrix: " + name)
|
||||
|
||||
def processDLName(self, name):
|
||||
# Commands loaded to 0x0C are material related only.
|
||||
try:
|
||||
pointer = hexOrDecInt(name)
|
||||
except:
|
||||
return name
|
||||
else:
|
||||
segment = pointer >> 24
|
||||
print("POINTER")
|
||||
if segment >= 0x08 and segment <= 0x0D:
|
||||
print("SETTING " + str(segment))
|
||||
setattr(self.materialContext.ootMaterial.opaque, "segment" + format(segment, "1X"), True)
|
||||
setattr(self.materialContext.ootMaterial.transparent, "segment" + format(segment, "1X"), True)
|
||||
return None
|
||||
return name
|
||||
|
||||
def processTextureName(self, textureName):
|
||||
try:
|
||||
pointer = hexOrDecInt(textureName)
|
||||
except:
|
||||
return textureName
|
||||
else:
|
||||
return textureName
|
||||
#if (pointer >> 24) == 0x08:
|
||||
# print("Unhandled OOT pointer: " + textureName)
|
||||
|
||||
def clearMaterial(self):
|
||||
self.isBillboard = False
|
||||
clearOOTMaterialDrawLayerProperty(self.materialContext.ootMaterial.opaque)
|
||||
clearOOTMaterialDrawLayerProperty(self.materialContext.ootMaterial.transparent)
|
||||
F3DContext.clearMaterial(self)
|
||||
|
||||
def postMaterialChanged(self):
|
||||
clearOOTMaterialDrawLayerProperty(self.materialContext.ootMaterial.opaque)
|
||||
clearOOTMaterialDrawLayerProperty(self.materialContext.ootMaterial.transparent)
|
||||
|
||||
# returns:
|
||||
# mesh,
|
||||
# anySkinnedFaces (to determine if skeleton should be flex)
|
||||
@@ -164,8 +345,8 @@ def ootProcessVertexGroup(fModel, meshObj, vertexGroup, convertTransformMatrix,
|
||||
# This doesn't handle case where vertices belong to a limb, but not triangles.
|
||||
# Therefore we create a dummy DL
|
||||
if anyConnectedToUnhandledBone:
|
||||
fMesh = fModel.addMesh(vertexGroup, namePrefix, drawLayerOverride, False)
|
||||
fMesh.draw.commands.append(SPEndDisplayList())
|
||||
fMesh = fModel.addMesh(vertexGroup, namePrefix, drawLayerOverride, False, bone)
|
||||
fModel.endDraw(fMesh, bone)
|
||||
meshInfo.vertexGroupInfo.vertexGroupToMatrixIndex[currentGroupIndex] = nextDLIndex
|
||||
return fMesh, False
|
||||
else:
|
||||
@@ -189,7 +370,7 @@ def ootProcessVertexGroup(fModel, meshObj, vertexGroup, convertTransformMatrix,
|
||||
drawLayer = drawLayerOverride
|
||||
|
||||
if drawLayerKey not in fMeshes:
|
||||
fMesh = fModel.addMesh(vertexGroup, namePrefix, drawLayer, False)
|
||||
fMesh = fModel.addMesh(vertexGroup, namePrefix, drawLayer, False, bone)
|
||||
fMeshes[drawLayerKey] = fMesh
|
||||
|
||||
checkForF3dMaterialInFaces(meshObj, material)
|
||||
@@ -214,7 +395,7 @@ def ootProcessVertexGroup(fModel, meshObj, vertexGroup, convertTransformMatrix,
|
||||
|
||||
if drawLayerKey not in fMeshes:
|
||||
# technically skinned, but for oot we don't have separate skinned/unskinned meshes.
|
||||
fMesh = fModel.addMesh(vertexGroup, namePrefix, drawLayer, False)
|
||||
fMesh = fModel.addMesh(vertexGroup, namePrefix, drawLayer, False, bone)
|
||||
fMeshes[drawLayerKey] = fMesh
|
||||
|
||||
checkForF3dMaterialInFaces(meshObj, material)
|
||||
@@ -228,7 +409,7 @@ def ootProcessVertexGroup(fModel, meshObj, vertexGroup, convertTransformMatrix,
|
||||
meshObj, drawLayer, convertTextureData, currentGroupIndex, triConverterInfo, None, None)
|
||||
|
||||
for drawLayer, fMesh in fMeshes.items():
|
||||
fMesh.draw.commands.append(SPEndDisplayList())
|
||||
fModel.endDraw(fMesh, bone)
|
||||
|
||||
return fMeshes[drawLayerKey], len(skinnedFaces) > 0
|
||||
|
||||
@@ -237,6 +418,54 @@ ootEnumObjectMenu = [
|
||||
("Room", "Parent Room Settings", "Room"),
|
||||
]
|
||||
|
||||
def ootConvertMeshToC(originalObj, finalTransform, f3dType, isHWv1, name, folderName, DLFormat, saveTextures,
|
||||
exportPath, isCustomExport, drawLayer, removeVanillaData):
|
||||
name = toAlnum(name)
|
||||
|
||||
try:
|
||||
obj, allObjs = ootDuplicateHierarchy(originalObj, None, False, OOTObjectCategorizer())
|
||||
|
||||
fModel = OOTModel(f3dType, isHWv1, name, DLFormat, drawLayer)
|
||||
triConverterInfo = TriangleConverterInfo(obj, None, fModel.f3d, finalTransform, getInfoDict(obj))
|
||||
fMeshes = saveStaticModel(triConverterInfo, fModel, obj, finalTransform, fModel.name,
|
||||
not saveTextures, False, 'oot')
|
||||
|
||||
# Since we provide a draw layer override, there should only be one fMesh.
|
||||
for drawLayer, fMesh in fMeshes.items():
|
||||
fMesh.draw.name = name
|
||||
|
||||
ootCleanupScene(originalObj, allObjs)
|
||||
|
||||
except Exception as e:
|
||||
ootCleanupScene(originalObj, allObjs)
|
||||
raise Exception(str(e))
|
||||
|
||||
data = CData()
|
||||
data.source += '#include "ultra64.h"\n#include "global.h"\n'
|
||||
if not isCustomExport:
|
||||
data.source += '#include "' + folderName + '.h"\n\n'
|
||||
else:
|
||||
data.source += '\n'
|
||||
|
||||
exportData = fModel.to_c(
|
||||
TextureExportSettings(False, saveTextures, "test"), OOTGfxFormatter(ScrollMethod.Vertex))
|
||||
|
||||
data.append(exportData.all())
|
||||
|
||||
path = ootGetPath(exportPath, isCustomExport, 'assets/objects/', folderName, False, False)
|
||||
writeCData(data,
|
||||
os.path.join(path, name + '.h'),
|
||||
os.path.join(path, name + '.c'))
|
||||
|
||||
if not isCustomExport:
|
||||
addIncludeFiles(folderName, path, name)
|
||||
if removeVanillaData:
|
||||
headerPath = os.path.join(path, folderName + '.h')
|
||||
sourcePath = os.path.join(path, folderName + '.c')
|
||||
removeDL(sourcePath, headerPath, name)
|
||||
|
||||
|
||||
|
||||
class OOT_DisplayListPanel(bpy.types.Panel):
|
||||
bl_label = "Display List Inspector"
|
||||
bl_idname = "OBJECT_PT_OOT_DL_Inspector"
|
||||
@@ -259,165 +488,97 @@ class OOT_DisplayListPanel(bpy.types.Panel):
|
||||
box.prop(obj, "ignore_render")
|
||||
box.prop(obj, "ignore_collision")
|
||||
|
||||
# Doesn't work since all static meshes are pre-transformed
|
||||
#box.prop(obj.ootDynamicTransform, "billboard")
|
||||
|
||||
#drawParentSceneRoom(box, obj)
|
||||
|
||||
def ootExportF3DtoC(basePath, obj, DLFormat, transformMatrix,
|
||||
f3dType, isHWv1, texDir, savePNG, texSeparate, includeChildren, name, levelName, groupName, customExport, headerType):
|
||||
dirPath, texDir = getExportDir(customExport, basePath, headerType,
|
||||
levelName, texDir, name)
|
||||
|
||||
fModel = OOTModel(f3dType, isHWv1, name, DLFormat, 0)
|
||||
fMesh = exportF3DCommon(obj, fModel, transformMatrix,
|
||||
includeChildren, name, DLFormat, not savePNG)
|
||||
|
||||
modelDirPath = os.path.join(dirPath, toAlnum(name))
|
||||
|
||||
if not os.path.exists(modelDirPath):
|
||||
os.mkdir(modelDirPath)
|
||||
|
||||
if headerType == 'Actor':
|
||||
scrollName = 'actor_dl_' + name
|
||||
elif headerType == 'Level':
|
||||
scrollName = levelName + '_level_dl_' + name
|
||||
|
||||
gfxFormatter = SM64GfxFormatter(ScrollMethod.Vertex)
|
||||
exportData = fModel.to_c(
|
||||
TextureExportSettings(texSeparate, savePNG, texDir), gfxFormatter)
|
||||
staticData = exportData.staticData
|
||||
dynamicData = exportData.dynamicData
|
||||
texC = exportData.textureData
|
||||
|
||||
scrollData, hasScrolling = fModel.to_c_vertex_scroll(scrollName, gfxFormatter)
|
||||
scroll_data = scrollData.source
|
||||
cDefineScroll = scrollData.header
|
||||
|
||||
modifyTexScrollFiles(basePath, modelDirPath, cDefineScroll, scroll_data, hasScrolling)
|
||||
|
||||
if DLFormat == DLFormat.Static:
|
||||
staticData.append(dynamicData)
|
||||
else:
|
||||
geoString = writeMaterialFiles(basePath, modelDirPath,
|
||||
'#include "actors/' + toAlnum(name) + '/header.h"',
|
||||
'#include "actors/' + toAlnum(name) + '/material.inc.h"',
|
||||
dynamicData.header, dynamicData.source, '', customExport)
|
||||
|
||||
#fModel.save_textures(modelDirPath, not savePNG)
|
||||
|
||||
#fModel.freePalettes()
|
||||
|
||||
if texSeparate:
|
||||
texCFile = open(os.path.join(modelDirPath, 'texture.inc.c'), 'w', newline='\n')
|
||||
texCFile.write(texC.source)
|
||||
texCFile.close()
|
||||
|
||||
modelPath = os.path.join(modelDirPath, 'model.inc.c')
|
||||
outFile = open(modelPath, 'w', newline='\n')
|
||||
outFile.write(staticData.source)
|
||||
outFile.close()
|
||||
|
||||
headerPath = os.path.join(modelDirPath, 'header.h')
|
||||
cDefFile = open(headerPath, 'w', newline='\n')
|
||||
cDefFile.write(staticData.header)
|
||||
cDefFile.close()
|
||||
|
||||
if not customExport:
|
||||
if headerType == 'Actor':
|
||||
# Write to group files
|
||||
if groupName == '' or groupName is None:
|
||||
raise PluginError("Actor header type chosen but group name not provided.")
|
||||
|
||||
groupPathC = os.path.join(dirPath, groupName + ".c")
|
||||
groupPathH = os.path.join(dirPath, groupName + ".h")
|
||||
|
||||
writeIfNotFound(groupPathC, '\n#include "' + toAlnum(name) + '/model.inc.c"', '')
|
||||
writeIfNotFound(groupPathH, '\n#include "' + toAlnum(name) + '/header.h"', '\n#endif')
|
||||
|
||||
if DLFormat != DLFormat.Static: # Change this
|
||||
writeMaterialHeaders(basePath,
|
||||
'#include "actors/' + toAlnum(name) + '/material.inc.c"',
|
||||
'#include "actors/' + toAlnum(name) + '/material.inc.h"')
|
||||
|
||||
texscrollIncludeC = '#include "actors/' + name + '/texscroll.inc.c"'
|
||||
texscrollIncludeH = '#include "actors/' + name + '/texscroll.inc.h"'
|
||||
texscrollGroup = groupName
|
||||
texscrollGroupInclude = '#include "actors/' + groupName + '.h"'
|
||||
|
||||
elif headerType == 'Level':
|
||||
groupPathC = os.path.join(dirPath, "leveldata.c")
|
||||
groupPathH = os.path.join(dirPath, "header.h")
|
||||
|
||||
writeIfNotFound(groupPathC, '\n#include "levels/' + levelName + '/' + \
|
||||
toAlnum(name) + '/model.inc.c"', '')
|
||||
writeIfNotFound(groupPathH, '\n#include "levels/' + levelName + '/' + \
|
||||
toAlnum(name) + '/header.h"', '\n#endif')
|
||||
|
||||
if DLFormat != DLFormat.Static: # Change this
|
||||
writeMaterialHeaders(basePath,
|
||||
'#include "levels/' + levelName + '/' + toAlnum(name) + '/material.inc.c"',
|
||||
'#include "levels/' + levelName + '/' + toAlnum(name) + '/material.inc.h"')
|
||||
|
||||
texscrollIncludeC = '#include "levels/' + levelName + '/' + name + '/texscroll.inc.c"'
|
||||
texscrollIncludeH = '#include "levels/' + levelName + '/' + name + '/texscroll.inc.h"'
|
||||
texscrollGroup = levelName
|
||||
texscrollGroupInclude = '#include "levels/' + levelName + '/header.h"'
|
||||
|
||||
modifyTexScrollHeadersGroup(basePath, texscrollIncludeC, texscrollIncludeH,
|
||||
texscrollGroup, cDefineScroll, texscrollGroupInclude, hasScrolling)
|
||||
|
||||
if bpy.context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||
|
||||
class OOT_ExportDL(bpy.types.Operator):
|
||||
class OOT_ImportDL(bpy.types.Operator):
|
||||
# set bl_ properties
|
||||
bl_idname = 'object.oot_export_dl'
|
||||
bl_label = "Export Display List"
|
||||
bl_idname = 'object.oot_import_dl'
|
||||
bl_label = "Import DL"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
# Called on demand (i.e. button press, menu item)
|
||||
# Can also be called from operator search menu (Spacebar)
|
||||
def execute(self, context):
|
||||
obj = None
|
||||
if context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = "OBJECT")
|
||||
|
||||
try:
|
||||
if context.mode != 'OBJECT':
|
||||
raise PluginError("Operator can only be used in object mode.")
|
||||
allObjs = context.selected_objects
|
||||
if len(allObjs) == 0:
|
||||
raise PluginError("No objects selected.")
|
||||
obj = context.selected_objects[0]
|
||||
if not isinstance(obj.data, bpy.types.Mesh):
|
||||
raise PluginError("Object is not a mesh.")
|
||||
name = context.scene.ootDLImportName
|
||||
folderName = context.scene.ootDLImportFolderName
|
||||
importPath = bpy.path.abspath(context.scene.ootDLImportCustomPath)
|
||||
isCustomImport = context.scene.ootDLImportUseCustomPath
|
||||
scale = context.scene.ootActorBlenderScale
|
||||
basePath = bpy.path.abspath(context.scene.ootDecompPath)
|
||||
removeDoubles = context.scene.ootDLRemoveDoubles
|
||||
importNormals = context.scene.ootDLImportNormals
|
||||
drawLayer = bpy.context.scene.ootDLImportDrawLayer
|
||||
|
||||
scaleValue = bpy.context.scene.ootBlenderScale
|
||||
finalTransform = mathutils.Matrix.Diagonal(mathutils.Vector((
|
||||
scaleValue, scaleValue, scaleValue))).to_4x4()
|
||||
filepaths = [ootGetObjectPath(isCustomImport, importPath, folderName)]
|
||||
if not isCustomImport:
|
||||
filepaths.append(os.path.join(bpy.context.scene.ootDecompPath, "assets/objects/gameplay_keep/gameplay_keep.c"))
|
||||
|
||||
except Exception as e:
|
||||
raisePluginError(self, e)
|
||||
return {"CANCELLED"}
|
||||
|
||||
try:
|
||||
applyRotation([obj], math.radians(90), 'X')
|
||||
importMeshC(filepaths, name, scale, removeDoubles, importNormals, drawLayer,
|
||||
OOTF3DContext(F3D("F3DEX2/LX2", False), [name], basePath))
|
||||
|
||||
exportPath, levelName = getPathAndLevel(context.scene.ootDLCustomExport,
|
||||
context.scene.ootExportPath, context.scene.ootDLLevelName,
|
||||
context.scene.ootDLLevelOption)
|
||||
#if not context.scene.ootDLCustomExport:
|
||||
# applyBasicTweaks(exportPath)
|
||||
ootExportF3DtoC(exportPath, obj,
|
||||
DLFormat.Static if context.scene.ootDLExportisStatic else DLFormat.Dynamic, finalTransform,
|
||||
context.scene.f3d_type, context.scene.isHWv1,
|
||||
bpy.context.scene.ootDLTexDir,
|
||||
bpy.context.scene.ootDLSaveTextures or bpy.context.scene.ignoreTextureRestrictions,
|
||||
bpy.context.scene.ootDLSeparateTextureDef,
|
||||
bpy.context.scene.ootDLincludeChildren, bpy.context.scene.ootDLName, levelName, context.scene.ootDLGroupName,
|
||||
context.scene.ootDLCustomExport,
|
||||
context.scene.ootDLExportHeaderType)
|
||||
self.report({'INFO'}, 'Success!')
|
||||
|
||||
applyRotation([obj], math.radians(-90), 'X')
|
||||
return {'FINISHED'} # must return a set
|
||||
self.report({'INFO'}, 'Success!')
|
||||
return {'FINISHED'}
|
||||
|
||||
except Exception as e:
|
||||
if context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||
raisePluginError(self, e)
|
||||
return {'CANCELLED'} # must return a set
|
||||
|
||||
class OOT_ExportDL(bpy.types.Operator):
|
||||
# set bl_ properties
|
||||
bl_idname = 'object.oot_export_dl'
|
||||
bl_label = "Export DL"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
# Called on demand (i.e. button press, menu item)
|
||||
# Can also be called from operator search menu (Spacebar)
|
||||
def execute(self, context):
|
||||
obj = None
|
||||
if context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = "OBJECT")
|
||||
if len(context.selected_objects) == 0:
|
||||
raise PluginError("Mesh not selected.")
|
||||
obj = context.active_object
|
||||
if type(obj.data) is not bpy.types.Mesh:
|
||||
raise PluginError("Mesh not selected.")
|
||||
|
||||
finalTransform = mathutils.Matrix.Scale(context.scene.ootActorBlenderScale, 4)
|
||||
|
||||
try:
|
||||
#exportPath, levelName = getPathAndLevel(context.scene.geoCustomExport,
|
||||
# context.scene.geoExportPath, context.scene.geoLevelName,
|
||||
# context.scene.geoLevelOption)
|
||||
|
||||
saveTextures = bpy.context.scene.saveTextures or bpy.context.scene.ignoreTextureRestrictions
|
||||
isHWv1 = context.scene.isHWv1
|
||||
f3dType = context.scene.f3d_type
|
||||
|
||||
name = context.scene.ootDLExportName
|
||||
folderName = context.scene.ootDLExportFolderName
|
||||
exportPath = bpy.path.abspath(context.scene.ootDLExportCustomPath)
|
||||
isCustomExport = context.scene.ootDLExportUseCustomPath
|
||||
drawLayer = context.scene.ootDLExportDrawLayer
|
||||
removeVanillaData = context.scene.ootDLRemoveVanillaData
|
||||
|
||||
ootConvertMeshToC(obj, finalTransform,
|
||||
f3dType, isHWv1, name, folderName, DLFormat.Static, saveTextures,
|
||||
exportPath, isCustomExport, drawLayer, removeVanillaData)
|
||||
|
||||
self.report({'INFO'}, 'Success!')
|
||||
return {'FINISHED'}
|
||||
|
||||
except Exception as e:
|
||||
if context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||
applyRotation([obj], math.radians(-90), 'X')
|
||||
raisePluginError(self, e)
|
||||
return {'CANCELLED'} # must return a set
|
||||
|
||||
@@ -435,41 +596,29 @@ class OOT_ExportDLPanel(bpy.types.Panel):
|
||||
# called every frame
|
||||
def draw(self, context):
|
||||
col = self.layout.column()
|
||||
propsDLE = col.operator(OOT_ExportDL.bl_idname)
|
||||
|
||||
col.prop(context.scene, 'ootDLExportisStatic')
|
||||
col.operator(OOT_ExportDL.bl_idname)
|
||||
|
||||
col.prop(context.scene, 'ootDLCustomExport')
|
||||
if context.scene.ootDLCustomExport:
|
||||
col.prop(context.scene, 'ootDLExportPath')
|
||||
prop_split(col, context.scene, 'ootDLName', 'Name')
|
||||
if not bpy.context.scene.ignoreTextureRestrictions:
|
||||
col.prop(context.scene, 'saveTextures')
|
||||
if context.scene.saveTextures:
|
||||
prop_split(col, context.scene, 'ootDLTexDir',
|
||||
'Texture Include Path')
|
||||
col.prop(context.scene, 'ootDLSeparateTextureDef')
|
||||
customExportWarning(col)
|
||||
else:
|
||||
prop_split(col, context.scene, 'ootDLExportHeaderType', 'Export Type')
|
||||
prop_split(col, context.scene, 'ootDLName', 'Name')
|
||||
if context.scene.ootDLExportHeaderType == 'Actor':
|
||||
prop_split(col, context.scene, 'ootDLGroupName', 'Group Name')
|
||||
elif context.scene.ootDLExportHeaderType == 'Level':
|
||||
prop_split(col, context.scene, 'ootDLLevelOption', 'Level')
|
||||
if context.scene.ootDLLevelOption == 'custom':
|
||||
prop_split(col, context.scene, 'ootDLLevelName', 'Level Name')
|
||||
if not bpy.context.scene.ignoreTextureRestrictions:
|
||||
col.prop(context.scene, 'saveTextures')
|
||||
if context.scene.saveTextures:
|
||||
col.prop(context.scene, 'ootDLSeparateTextureDef')
|
||||
|
||||
#decompFolderMessage(col)
|
||||
#writeBox = makeWriteInfoBox(col)
|
||||
#writeBoxExportType(writeBox, context.scene.ootDLExportHeaderType,
|
||||
# context.scene.ootDLName, context.scene.ootDLLevelName, context.scene.ootDLLevelOption)
|
||||
|
||||
col.prop(context.scene, 'ootDLincludeChildren')
|
||||
prop_split(col, context.scene, 'ootDLExportName', "DL")
|
||||
if context.scene.ootDLExportUseCustomPath:
|
||||
prop_split(col, context.scene, 'ootDLExportCustomPath', "Folder")
|
||||
else:
|
||||
prop_split(col, context.scene, 'ootDLExportFolderName', "Object")
|
||||
prop_split(col, context.scene, "ootDLExportDrawLayer", "Export Draw Layer")
|
||||
col.prop(context.scene, "ootDLExportUseCustomPath")
|
||||
col.prop(context.scene, "ootDLRemoveVanillaData")
|
||||
|
||||
col.operator(OOT_ImportDL.bl_idname)
|
||||
|
||||
prop_split(col, context.scene, 'ootDLImportName', "DL")
|
||||
if context.scene.ootDLImportUseCustomPath:
|
||||
prop_split(col, context.scene, 'ootDLImportCustomPath', "File")
|
||||
else:
|
||||
prop_split(col, context.scene, 'ootDLImportFolderName', "Object")
|
||||
prop_split(col, context.scene, "ootDLImportDrawLayer", "Import Draw Layer")
|
||||
|
||||
col.prop(context.scene, "ootDLImportUseCustomPath")
|
||||
col.prop(context.scene, "ootDLRemoveDoubles")
|
||||
col.prop(context.scene, "ootDLImportNormals")
|
||||
|
||||
class OOTDefaultRenderModesProperty(bpy.types.PropertyGroup):
|
||||
expandTab : bpy.props.BoolProperty()
|
||||
@@ -524,7 +673,16 @@ class OOT_MaterialPanel(bpy.types.Panel):
|
||||
layout = self.layout
|
||||
mat = context.material
|
||||
col = layout.column()
|
||||
drawOOTMaterialProperty(col.box().column(), mat.ootMaterial, mat.f3d_mat.draw_layer.oot)
|
||||
|
||||
if hasattr(context, "object") and context.object is not None and \
|
||||
context.object.parent is not None and isinstance(context.object.parent.data, bpy.types.Armature):
|
||||
drawLayer = context.object.parent.ootDrawLayer
|
||||
if drawLayer != mat.f3d_mat.draw_layer.oot:
|
||||
col.label(text = "Draw layer is being overriden by skeleton.", icon = "ERROR")
|
||||
else:
|
||||
drawLayer = mat.f3d_mat.draw_layer.oot
|
||||
|
||||
drawOOTMaterialProperty(col.box().column(), mat.ootMaterial, drawLayer)
|
||||
|
||||
def drawOOTMaterialDrawLayerProperty(layout, matDrawLayerProp, suffix):
|
||||
#layout.box().row().label(text = title)
|
||||
@@ -536,6 +694,11 @@ def drawOOTMaterialDrawLayerProperty(layout, matDrawLayerProp, suffix):
|
||||
name = "Segment " + format(i, 'X') + " " + suffix
|
||||
col.prop(matDrawLayerProp, "segment" + format(i, 'X'), text = name)
|
||||
|
||||
def clearOOTMaterialDrawLayerProperty(matDrawLayerProp):
|
||||
for i in range(0x08, 0x0E):
|
||||
setattr(matDrawLayerProp, "segment" + format(i, 'X'), False)
|
||||
|
||||
|
||||
drawLayerSuffix = {
|
||||
"Opaque" : "OPA",
|
||||
"Transparent" : 'XLU',
|
||||
@@ -543,6 +706,8 @@ drawLayerSuffix = {
|
||||
}
|
||||
|
||||
def drawOOTMaterialProperty(layout, matProp, drawLayer):
|
||||
if drawLayer == "Overlay":
|
||||
return
|
||||
suffix = "(" + drawLayerSuffix[drawLayer] + ")"
|
||||
layout.box().column().label(text = "OOT Dynamic Material Properties " + suffix)
|
||||
layout.label(text = "See gSPSegment calls in z_scene_table.c.")
|
||||
@@ -562,11 +727,16 @@ class OOTDynamicMaterialProperty(bpy.types.PropertyGroup):
|
||||
opaque : bpy.props.PointerProperty(type = OOTDynamicMaterialDrawLayerProperty)
|
||||
transparent : bpy.props.PointerProperty(type = OOTDynamicMaterialDrawLayerProperty)
|
||||
|
||||
class OOTDynamicTransformProperty(bpy.types.PropertyGroup):
|
||||
billboard : bpy.props.BoolProperty(name = "Billboard")
|
||||
|
||||
oot_dl_writer_classes = (
|
||||
OOTDefaultRenderModesProperty,
|
||||
OOTDynamicMaterialDrawLayerProperty,
|
||||
OOTDynamicMaterialProperty,
|
||||
#OOT_ExportDL,
|
||||
OOTDynamicTransformProperty,
|
||||
OOT_ExportDL,
|
||||
OOT_ImportDL,
|
||||
)
|
||||
|
||||
oot_dl_writer_panel_classes = (
|
||||
@@ -574,6 +744,7 @@ oot_dl_writer_panel_classes = (
|
||||
OOT_DisplayListPanel,
|
||||
OOT_DrawLayersPanel,
|
||||
OOT_MaterialPanel,
|
||||
OOT_ExportDLPanel,
|
||||
)
|
||||
|
||||
def oot_dl_writer_panel_register():
|
||||
@@ -589,34 +760,36 @@ def oot_dl_writer_register():
|
||||
register_class(cls)
|
||||
|
||||
bpy.types.Object.ootDrawLayer = bpy.props.EnumProperty(items = ootEnumDrawLayers, default = 'Opaque')
|
||||
|
||||
# Doesn't work since all static meshes are pre-transformed
|
||||
#bpy.types.Object.ootDynamicTransform = bpy.props.PointerProperty(type = OOTDynamicTransformProperty)
|
||||
bpy.types.World.ootDefaultRenderModes = bpy.props.PointerProperty(type = OOTDefaultRenderModesProperty)
|
||||
|
||||
bpy.types.Scene.ootLevelDLExport = bpy.props.EnumProperty(items = ootEnumSceneID,
|
||||
name = 'Level', default = 'SCENE_YDAN')
|
||||
bpy.types.Scene.ootDLExportPath = bpy.props.StringProperty(
|
||||
name = 'Directory', subtype = 'FILE_PATH')
|
||||
bpy.types.Scene.ootDLExportisStatic = bpy.props.BoolProperty(
|
||||
name = 'Static DL', default = True)
|
||||
bpy.types.Scene.ootDLDefinePath = bpy.props.StringProperty(
|
||||
name = 'Definitions Filepath', subtype = 'FILE_PATH')
|
||||
bpy.types.Scene.ootDLTexDir = bpy.props.StringProperty(
|
||||
name ='Include Path', default = 'levels/bob')
|
||||
bpy.types.Scene.ootDLSeparateTextureDef = bpy.props.BoolProperty(
|
||||
name = 'Save texture.inc.c separately')
|
||||
bpy.types.Scene.ootDLincludeChildren = bpy.props.BoolProperty(
|
||||
name = 'Include Children')
|
||||
bpy.types.Scene.ootDLName = bpy.props.StringProperty(
|
||||
name = 'Name', default = 'mario')
|
||||
bpy.types.Scene.ootDLCustomExport = bpy.props.BoolProperty(
|
||||
name = 'Custom Export Path')
|
||||
bpy.types.Scene.ootDLExportHeaderType = bpy.props.EnumProperty(
|
||||
items = enumExportHeaderType, name = 'Header Export', default = 'Actor')
|
||||
bpy.types.Scene.ootDLGroupName = bpy.props.StringProperty(name = 'Group Name',
|
||||
default = 'group0')
|
||||
bpy.types.Scene.ootDLLevelName = bpy.props.StringProperty(name = 'Level',
|
||||
default = 'bob')
|
||||
bpy.types.Scene.ootDLLevelOption = bpy.props.EnumProperty(
|
||||
items = ootEnumSceneID, name = 'Level', default = 'SCENE_YDAN')
|
||||
bpy.types.Scene.ootDLExportName = bpy.props.StringProperty(
|
||||
name = "DL Name", default = "gBoulderFragmentsDL")
|
||||
bpy.types.Scene.ootDLExportFolderName = bpy.props.StringProperty(
|
||||
name = "DL Folder", default = "gameplay_keep")
|
||||
bpy.types.Scene.ootDLExportCustomPath = bpy.props.StringProperty(
|
||||
name ='Custom DL Path', subtype = 'FILE_PATH')
|
||||
bpy.types.Scene.ootDLExportUseCustomPath = bpy.props.BoolProperty(
|
||||
name = "Use Custom Path")
|
||||
bpy.types.Scene.ootDLRemoveVanillaData = bpy.props.BoolProperty(
|
||||
name = "Replace Vanilla DLs")
|
||||
|
||||
bpy.types.Scene.ootDLImportName = bpy.props.StringProperty(
|
||||
name = "DL Name", default = "gBoulderFragmentsDL")
|
||||
bpy.types.Scene.ootDLImportFolderName = bpy.props.StringProperty(
|
||||
name = "DL Folder", default = "gameplay_keep")
|
||||
bpy.types.Scene.ootDLImportCustomPath = bpy.props.StringProperty(
|
||||
name ='Custom DL Path', subtype = 'FILE_PATH')
|
||||
bpy.types.Scene.ootDLImportUseCustomPath = bpy.props.BoolProperty(
|
||||
name = "Use Custom Path")
|
||||
|
||||
bpy.types.Scene.ootDLRemoveDoubles = bpy.props.BoolProperty(name = "Remove Doubles", default = True)
|
||||
bpy.types.Scene.ootDLImportNormals = bpy.props.BoolProperty(name = "Import Normals", default = True)
|
||||
bpy.types.Scene.ootDLImportDrawLayer = bpy.props.EnumProperty(name = "Draw Layer", items = ootEnumDrawLayers)
|
||||
|
||||
bpy.types.Scene.ootDLExportDrawLayer = bpy.props.EnumProperty(name = "Draw Layer", items = ootEnumDrawLayers)
|
||||
|
||||
bpy.types.Material.ootMaterial = bpy.props.PointerProperty(type = OOTDynamicMaterialProperty)
|
||||
bpy.types.Object.ootObjectMenu = bpy.props.EnumProperty(items = ootEnumObjectMenu)
|
||||
@@ -625,17 +798,22 @@ def oot_dl_writer_unregister():
|
||||
for cls in reversed(oot_dl_writer_classes):
|
||||
unregister_class(cls)
|
||||
|
||||
del bpy.types.Scene.ootLevelDLExport
|
||||
del bpy.types.Scene.ootDLExportPath
|
||||
del bpy.types.Scene.ootDLExportisStatic
|
||||
del bpy.types.Scene.ootDLDefinePath
|
||||
del bpy.types.Scene.ootDLTexDir
|
||||
del bpy.types.Scene.ootDLSeparateTextureDef
|
||||
del bpy.types.Scene.ootDLincludeChildren
|
||||
del bpy.types.Scene.ootDLName
|
||||
del bpy.types.Scene.ootDLCustomExport
|
||||
del bpy.types.Scene.ootDLExportHeaderType
|
||||
del bpy.types.Scene.ootDLGroupName
|
||||
del bpy.types.Scene.ootDLLevelName
|
||||
del bpy.types.Scene.ootDLLevelOption
|
||||
del bpy.types.Material.ootMaterial
|
||||
del bpy.types.Scene.ootDLExportName
|
||||
del bpy.types.Scene.ootDLExportFolderName
|
||||
del bpy.types.Scene.ootDLExportCustomPath
|
||||
del bpy.types.Scene.ootDLExportUseCustomPath
|
||||
del bpy.types.Scene.ootDLRemoveVanillaData
|
||||
|
||||
del bpy.types.Scene.ootDLImportName
|
||||
del bpy.types.Scene.ootDLImportFolderName
|
||||
del bpy.types.Scene.ootDLImportCustomPath
|
||||
del bpy.types.Scene.ootDLImportUseCustomPath
|
||||
|
||||
del bpy.types.Scene.ootDLRemoveDoubles
|
||||
del bpy.types.Scene.ootDLImportNormals
|
||||
del bpy.types.Scene.ootDLImportDrawLayer
|
||||
|
||||
del bpy.types.Scene.ootDLExportDrawLayer
|
||||
|
||||
del bpy.types.Material.ootMaterial
|
||||
del bpy.types.Object.ootObjectMenu
|
||||
@@ -167,7 +167,7 @@ class OOTScene:
|
||||
|
||||
def addRoom(self, roomIndex, roomName, meshType):
|
||||
roomModel = self.model.addSubModel(
|
||||
OOTModel(self.model.f3d.F3D_VER, self.model.f3d._HW_VERSION_1, roomName + '_dl', self.model.DLFormat))
|
||||
OOTModel(self.model.f3d.F3D_VER, self.model.f3d._HW_VERSION_1, roomName + '_dl', self.model.DLFormat, None))
|
||||
room = OOTRoom(roomIndex, roomName, roomModel, meshType)
|
||||
if roomIndex in self.rooms:
|
||||
raise PluginError("Repeat room index " + str(roomIndex) + " for " + str(roomName))
|
||||
|
||||
@@ -17,103 +17,6 @@ from io import BytesIO
|
||||
import bpy, bmesh, os, math, re, shutil, mathutils
|
||||
|
||||
|
||||
#class OOTBox:
|
||||
# def __init__(self):
|
||||
# self.minBounds = [-2**8, -2**8]
|
||||
# self.maxBounds = [2**8 - 1, 2**8 - 1]
|
||||
|
||||
|
||||
class OOTObjectCategorizer:
|
||||
def __init__(self):
|
||||
self.sceneObj = None
|
||||
self.roomObjs = []
|
||||
self.actors = []
|
||||
self.transitionActors = []
|
||||
self.meshes = []
|
||||
self.entrances = []
|
||||
self.waterBoxes = []
|
||||
|
||||
def sortObjects(self, allObjs):
|
||||
for obj in allObjs:
|
||||
if obj.data is None:
|
||||
if obj.ootEmptyType == "Actor":
|
||||
self.actors.append(obj)
|
||||
elif obj.ootEmptyType == "Transition Actor":
|
||||
self.transitionActors.append(obj)
|
||||
elif obj.ootEmptyType == "Entrance":
|
||||
self.entrances.append(obj)
|
||||
elif obj.ootEmptyType == "Water Box":
|
||||
self.waterBoxes.append(obj)
|
||||
elif obj.ootEmptyType == "Room":
|
||||
self.roomObjs.append(obj)
|
||||
elif obj.ootEmptyType == "Scene":
|
||||
self.sceneObj = obj
|
||||
elif isinstance(obj.data, bpy.types.Mesh):
|
||||
self.meshes.append(obj)
|
||||
|
||||
# This also sets all origins relative to the scene object.
|
||||
def ootDuplicateHierarchy(obj, ignoreAttr, includeEmpties, objectCategorizer):
|
||||
# Duplicate objects to apply scale / modifiers / linked data
|
||||
bpy.ops.object.select_all(action = 'DESELECT')
|
||||
ootSelectMeshChildrenOnly(obj, includeEmpties)
|
||||
obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.duplicate()
|
||||
try:
|
||||
tempObj = bpy.context.view_layer.objects.active
|
||||
allObjs = bpy.context.selected_objects
|
||||
bpy.ops.object.make_single_user(obdata = True)
|
||||
|
||||
objectCategorizer.sortObjects(allObjs)
|
||||
meshObjs = objectCategorizer.meshes
|
||||
bpy.ops.object.select_all(action = 'DESELECT')
|
||||
for selectedObj in meshObjs:
|
||||
selectedObj.select_set(True)
|
||||
bpy.ops.object.transform_apply(location = False,
|
||||
rotation = True, scale = True, properties = False)
|
||||
|
||||
for selectedObj in meshObjs:
|
||||
bpy.ops.object.select_all(action = 'DESELECT')
|
||||
selectedObj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = selectedObj
|
||||
for modifier in selectedObj.modifiers:
|
||||
attemptModifierApply(modifier)
|
||||
for selectedObj in meshObjs:
|
||||
setOrigin(obj, selectedObj)
|
||||
if ignoreAttr is not None:
|
||||
for selectedObj in meshObjs:
|
||||
if getattr(selectedObj, ignoreAttr):
|
||||
for child in selectedObj.children:
|
||||
bpy.ops.object.select_all(action = 'DESELECT')
|
||||
child.select_set(True)
|
||||
bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')
|
||||
selectedObj.parent.select_set(True)
|
||||
bpy.ops.object.parent_set(keep_transform = True)
|
||||
selectedObj.parent = None
|
||||
applyRotation([tempObj], math.radians(90), 'X')
|
||||
return tempObj, allObjs
|
||||
except Exception as e:
|
||||
cleanupDuplicatedObjects(allObjs)
|
||||
obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
raise Exception(str(e))
|
||||
|
||||
def ootSelectMeshChildrenOnly(obj, includeEmpties):
|
||||
isMesh = isinstance(obj.data, bpy.types.Mesh)
|
||||
isEmpty = (obj.data is None or \
|
||||
isinstance(obj.data, bpy.types.Camera) or\
|
||||
isinstance(obj.data, bpy.types.Curve)) and includeEmpties
|
||||
if (isMesh or isEmpty):
|
||||
obj.select_set(True)
|
||||
obj.original_name = obj.name
|
||||
for child in obj.children:
|
||||
ootSelectMeshChildrenOnly(child, includeEmpties)
|
||||
|
||||
def ootCleanupScene(originalSceneObj, allObjs):
|
||||
cleanupDuplicatedObjects(allObjs)
|
||||
originalSceneObj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = originalSceneObj
|
||||
|
||||
def sceneNameFromID(sceneID):
|
||||
if sceneID in ootSceneIDToName:
|
||||
return ootSceneIDToName[sceneID]
|
||||
@@ -339,7 +242,7 @@ def ootConvertScene(originalSceneObj, transformMatrix,
|
||||
raise PluginError("The scene has no child empties with the 'Room' empty type.")
|
||||
|
||||
try:
|
||||
scene = OOTScene(sceneName, OOTModel(f3dType, isHWv1, sceneName + '_dl', DLFormat))
|
||||
scene = OOTScene(sceneName, OOTModel(f3dType, isHWv1, sceneName + '_dl', DLFormat, None))
|
||||
readSceneData(scene, sceneObj.ootSceneHeader, sceneObj.ootAlternateSceneHeaders)
|
||||
processedRooms = set()
|
||||
|
||||
@@ -372,7 +275,7 @@ def ootConvertScene(originalSceneObj, transformMatrix,
|
||||
scene.pathList.append(ootConvertPath(sceneName, len(scene.pathList), obj, relativeTransform))
|
||||
|
||||
scene.validateIndices()
|
||||
exportCollisionCommon(scene.collision, sceneObj, allObjs, transformMatrix, True, sceneName)
|
||||
exportCollisionCommon(scene.collision, sceneObj, transformMatrix, True, sceneName)
|
||||
|
||||
ootCleanupScene(originalSceneObj, allObjs)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import shutil, copy, mathutils, bpy, math, os
|
||||
import shutil, copy, mathutils, bpy, math, os, re
|
||||
|
||||
from ..f3d.f3d_parser import *
|
||||
from ..f3d.f3d_writer import *
|
||||
from ..f3d.f3d_material import TextureProperty, tmemUsageUI
|
||||
from bpy.utils import register_class, unregister_class
|
||||
@@ -8,11 +9,18 @@ from .oot_utility import *
|
||||
from .oot_f3d_writer import *
|
||||
from ..utility import *
|
||||
|
||||
ootEnumBoneType = [
|
||||
("Default", "Default", "Default"),
|
||||
("Custom DL", "Custom DL", "Custom DL"),
|
||||
("Ignore", "Ignore", "Ignore"),
|
||||
]
|
||||
|
||||
class OOTSkeleton():
|
||||
def __init__(self, name):
|
||||
self.name = name + "Skel"
|
||||
self.name = name
|
||||
self.segmentID = None
|
||||
self.limbRoot = None
|
||||
self.hasLOD = False
|
||||
|
||||
def createLimbList(self):
|
||||
if self.limbRoot is None:
|
||||
@@ -56,7 +64,7 @@ class OOTSkeleton():
|
||||
|
||||
data.source += "void* " + self.limbsName() + "[" + str(self.getNumLimbs()) + "] = {\n"
|
||||
for limb in limbList:
|
||||
limbData.source += limb.toC()
|
||||
limbData.source += limb.toC(self.hasLOD)
|
||||
data.source += '\t&' + limb.name() + ',\n'
|
||||
limbData.source += '\n'
|
||||
data.source += "};\n\n"
|
||||
@@ -88,17 +96,31 @@ class OOTLimb():
|
||||
self.children = []
|
||||
self.inverseRotation = None
|
||||
|
||||
def toC(self):
|
||||
return "StandardLimb " + self.name() + " = { " +\
|
||||
def toC(self, isLOD):
|
||||
if not isLOD:
|
||||
data = "StandardLimb "
|
||||
else:
|
||||
data = "LodLimb "
|
||||
|
||||
data += self.name() + " = { " +\
|
||||
"{ " + str(int(round(self.translation[0]))) + ", " + \
|
||||
str(int(round(self.translation[1]))) + ", " + \
|
||||
str(int(round(self.translation[2]))) + " }, " + \
|
||||
str(self.firstChildIndex) + ", " +\
|
||||
str(self.nextSiblingIndex) + ", " + \
|
||||
(self.DL.name if self.DL is not None else "NULL") + " };\n"
|
||||
str(self.nextSiblingIndex) + ", "
|
||||
|
||||
if not isLOD:
|
||||
data += (self.DL.name if self.DL is not None else "NULL")
|
||||
else:
|
||||
data += "{ " + (self.DL.name if self.DL is not None else "NULL") + ", " +\
|
||||
(self.lodDL.name if self.lodDL is not None else "NULL") + " }"
|
||||
|
||||
data += " };\n"
|
||||
|
||||
return data
|
||||
|
||||
def name(self):
|
||||
return self.skeletonName + "Limb_" + str(self.index)
|
||||
return self.skeletonName + "Limb_" + format(self.index, '03')
|
||||
|
||||
def getNumLimbs(self):
|
||||
numLimbs = 1
|
||||
@@ -108,9 +130,7 @@ class OOTLimb():
|
||||
|
||||
def getNumDLs(self):
|
||||
numDLs = 0
|
||||
if self.DL is not None:
|
||||
numDLs += 1
|
||||
if self.lodDL is not None:
|
||||
if self.DL is not None or self.lodDL is not None:
|
||||
numDLs += 1
|
||||
|
||||
for child in self.children:
|
||||
@@ -156,6 +176,7 @@ def setBoneNonRotated(armatureObj, boneName, restPoseRotations):
|
||||
poseBone.constraints.remove(poseBone.constraints[0])
|
||||
|
||||
rotation = bone.matrix_local.inverted().decompose()[1]
|
||||
armatureObj.pose.bones[boneName].rotation_mode = "QUATERNION"
|
||||
armatureObj.pose.bones[boneName].rotation_quaternion = rotation
|
||||
|
||||
restPoseRotations[boneName] = rotation
|
||||
@@ -259,9 +280,8 @@ def ootConvertArmatureToSkeletonWithoutMesh(originalArmatureObj, convertTransfor
|
||||
None, name, False, True, "Opaque")
|
||||
return skeleton, restPoseRotations
|
||||
|
||||
def ootConvertArmatureToSkeletonWithMesh(originalArmatureObj, convertTransformMatrix,
|
||||
f3dType, isHWv1, name, DLFormat, convertTextureData, drawLayer):
|
||||
fModel = OOTModel(f3dType, isHWv1, name, DLFormat)
|
||||
def ootConvertArmatureToSkeletonWithMesh(originalArmatureObj, convertTransformMatrix, fModel, name, convertTextureData, drawLayer):
|
||||
|
||||
skeleton, fModel, restPoseRotations = ootConvertArmatureToSkeleton(originalArmatureObj, convertTransformMatrix,
|
||||
fModel, name, convertTextureData, False, drawLayer)
|
||||
return skeleton, fModel
|
||||
@@ -328,6 +348,13 @@ def ootProcessBone(fModel, boneName, parentLimb, nextIndex, meshObj, armatureObj
|
||||
convertTransformMatrix, armatureObj, namePrefix,
|
||||
meshInfo, drawLayer, convertTextureData)
|
||||
|
||||
if bone.ootBoneType == "Custom DL":
|
||||
if mesh is not None:
|
||||
raise PluginError(bone.name + " is set to use a custom DL but still has geometry assigned to it. Remove this geometry from this bone.")
|
||||
else:
|
||||
# Dummy data, only used so that name is set correctly
|
||||
mesh = FMesh(bone.ootCustomDLName, DLFormat.Static)
|
||||
|
||||
DL = None
|
||||
if mesh is not None:
|
||||
if not bone.use_deform:
|
||||
@@ -353,14 +380,40 @@ def ootProcessBone(fModel, boneName, parentLimb, nextIndex, meshObj, armatureObj
|
||||
return nextIndex
|
||||
|
||||
def ootConvertArmatureToC(originalArmatureObj, convertTransformMatrix,
|
||||
f3dType, isHWv1, skeletonName, folderName, DLFormat, savePNG, exportPath, isCustomExport, drawLayer):
|
||||
f3dType, isHWv1, skeletonName, folderName, DLFormat, savePNG, exportPath, isCustomExport, drawLayer, removeVanillaData):
|
||||
skeletonName = toAlnum(skeletonName)
|
||||
|
||||
fModel = OOTModel(f3dType, isHWv1, skeletonName, DLFormat, drawLayer)
|
||||
skeleton, fModel = ootConvertArmatureToSkeletonWithMesh(originalArmatureObj, convertTransformMatrix,
|
||||
f3dType, isHWv1, skeletonName, DLFormat, not savePNG, drawLayer)
|
||||
fModel, skeletonName, not savePNG, drawLayer)
|
||||
|
||||
if originalArmatureObj.ootFarLOD is not None:
|
||||
lodSkeleton, fModel = ootConvertArmatureToSkeletonWithMesh(originalArmatureObj.ootFarLOD, convertTransformMatrix,
|
||||
fModel, skeletonName + "_lod", not savePNG, drawLayer)
|
||||
else:
|
||||
lodSkeleton = None
|
||||
|
||||
if lodSkeleton is not None:
|
||||
skeleton.hasLOD = True
|
||||
limbList = skeleton.createLimbList()
|
||||
lodLimbList = lodSkeleton.createLimbList()
|
||||
|
||||
if len(limbList) != len(lodLimbList):
|
||||
raise PluginError(originalArmatureObj.name + " cannot use " + originalArmatureObj.ootFarLOD.name + \
|
||||
"as LOD because they do not have the same bone structure.")
|
||||
|
||||
for i in range(len(limbList)):
|
||||
limbList[i].lodDL = lodLimbList[i].DL
|
||||
limbList[i].isFlex |= lodLimbList[i].isFlex
|
||||
|
||||
|
||||
data = CData()
|
||||
data.source += '#include "ultra64.h"\n#include "global.h"\n\n'
|
||||
data.source += '#include "ultra64.h"\n#include "global.h"\n'
|
||||
if not isCustomExport:
|
||||
data.source += '#include "' + folderName + '.h"\n\n'
|
||||
else:
|
||||
data.source += '\n'
|
||||
|
||||
exportData = fModel.to_c(
|
||||
TextureExportSettings(False, savePNG, "test"), OOTGfxFormatter(ScrollMethod.Vertex))
|
||||
skeletonC = skeleton.toC()
|
||||
@@ -375,6 +428,295 @@ def ootConvertArmatureToC(originalArmatureObj, convertTransformMatrix,
|
||||
|
||||
if not isCustomExport:
|
||||
addIncludeFiles(folderName, path, skeletonName)
|
||||
if removeVanillaData:
|
||||
ootRemoveSkeleton(path, folderName, skeletonName)
|
||||
|
||||
class OOTDLEntry:
|
||||
def __init__(self, dlName, limbIndex):
|
||||
self.dlName = dlName
|
||||
self.limbIndex = limbIndex
|
||||
|
||||
def ootGetSkeleton(skeletonData, skeletonName, continueOnError):
|
||||
# TODO: Does this handle non flex skeleton?
|
||||
matchResult = re.search("(Flex)?SkeletonHeader\s*" + re.escape(skeletonName) + \
|
||||
"\s*=\s*\{\s*([^,\s]*)\s*,\s*([^,\s]*)\s*(,\s*([^,\s]*))?\s*\}\s*;\s*", skeletonData)
|
||||
if matchResult is None:
|
||||
if continueOnError:
|
||||
return None
|
||||
else:
|
||||
raise PluginError("Cannot find skeleton named " + skeletonName)
|
||||
return matchResult
|
||||
|
||||
def ootGetLimbs(skeletonData, limbsName, continueOnError):
|
||||
matchResult = re.search("(static\s*)?void\s*\*\s*" + re.escape(limbsName) + \
|
||||
"\s*\[\s*[0-9]*\s*\]\s*=\s*\{([^\}]*)\}\s*;\s*", skeletonData, re.DOTALL)
|
||||
if matchResult is None:
|
||||
if continueOnError:
|
||||
return None
|
||||
else:
|
||||
raise PluginError("Cannot find skeleton limbs named " + limbsName)
|
||||
return matchResult
|
||||
|
||||
def ootGetLimb(skeletonData, limbName, continueOnError):
|
||||
matchResult = re.search("([A-Za-z0-9\_]*)Limb\s*" + re.escape(limbName), skeletonData)
|
||||
|
||||
if matchResult is None:
|
||||
if continueOnError:
|
||||
return None
|
||||
else:
|
||||
raise PluginError("Cannot find skeleton limb named " + limbName)
|
||||
|
||||
limbType = matchResult.group(1)
|
||||
if limbType == "Lod":
|
||||
dlRegex = "\{\s*([^,\s]*)\s*,\s*([^,\s]*)\s*\}"
|
||||
else:
|
||||
dlRegex = "([^,\s]*)"
|
||||
|
||||
matchResult = re.search("[A-Za-z0-9\_]*Limb\s*" + re.escape(limbName) + \
|
||||
"\s*=\s*\{\s*\{\s*([^,\s]*)\s*,\s*([^,\s]*)\s*,\s*([^,\s]*)\s*\},\s*([^, ]*)\s*,\s*([^, ]*)\s*,\s*" +\
|
||||
dlRegex + "\s*\}\s*;\s*", skeletonData, re.DOTALL)
|
||||
|
||||
if matchResult is None:
|
||||
if continueOnError:
|
||||
return None
|
||||
else:
|
||||
raise PluginError("Cannot handle skeleton limb named " + limbName + " of type " + limbType)
|
||||
return matchResult
|
||||
|
||||
def ootImportSkeletonC(filepaths, skeletonName, actorScale, removeDoubles, importNormals, basePath, drawLayer):
|
||||
skeletonData = getImportData(filepaths)
|
||||
|
||||
matchResult = ootGetSkeleton(skeletonData, skeletonName, False)
|
||||
limbsName = matchResult.group(2)
|
||||
|
||||
matchResult = ootGetLimbs(skeletonData, limbsName, False)
|
||||
limbsData = matchResult.group(2)
|
||||
limbList = [entry.strip()[1:] for entry in limbsData.split(',')]
|
||||
|
||||
#print(limbList)
|
||||
isLOD, armatureObj = ootBuildSkeleton(skeletonName, skeletonData, limbList,
|
||||
actorScale, removeDoubles, importNormals, False, basePath, drawLayer)
|
||||
if isLOD:
|
||||
isLOD, LODArmatureObj = ootBuildSkeleton(skeletonName, skeletonData, limbList,
|
||||
actorScale, removeDoubles, importNormals, True, basePath, drawLayer)
|
||||
armatureObj.ootFarLOD = LODArmatureObj
|
||||
|
||||
|
||||
def ootBuildSkeleton(skeletonName, skeletonData, limbList, actorScale, removeDoubles,
|
||||
importNormals, useFarLOD, basePath, drawLayer):
|
||||
lodString = "_lod" if useFarLOD else ""
|
||||
|
||||
# Create new skinned mesh
|
||||
mesh = bpy.data.meshes.new(skeletonName + '_mesh' + lodString)
|
||||
obj = bpy.data.objects.new(skeletonName + '_mesh' + lodString, mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
|
||||
# Create new armature
|
||||
armature = bpy.data.armatures.new(skeletonName + lodString)
|
||||
armatureObj = bpy.data.objects.new(skeletonName + lodString, armature)
|
||||
armatureObj.show_in_front = True
|
||||
armatureObj.ootDrawLayer = drawLayer
|
||||
#armature.show_names = True
|
||||
|
||||
bpy.context.scene.collection.objects.link(armatureObj)
|
||||
bpy.context.view_layer.objects.active = armatureObj
|
||||
#bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
f3dContext = OOTF3DContext(F3D("F3DEX2/LX2", False), limbList, basePath)
|
||||
f3dContext.mat().draw_layer.oot = armatureObj.ootDrawLayer
|
||||
transformMatrix = mathutils.Matrix.Scale(1 / actorScale, 4)
|
||||
isLOD = ootAddLimbRecursively(0, skeletonData, obj, armatureObj, transformMatrix, None, f3dContext, useFarLOD)
|
||||
for dlEntry in f3dContext.dlList:
|
||||
limbName = f3dContext.getLimbName(dlEntry.limbIndex)
|
||||
boneName = f3dContext.getBoneName(dlEntry.limbIndex)
|
||||
parseF3D(skeletonData, dlEntry.dlName, obj, f3dContext.matrixData[limbName],
|
||||
limbName, boneName, "oot", drawLayer, f3dContext)
|
||||
if f3dContext.isBillboard:
|
||||
armatureObj.data.bones[boneName].ootDynamicTransform.billboard = True
|
||||
f3dContext.clearMaterial() # THIS IS IMPORTANT
|
||||
f3dContext.createMesh(obj, removeDoubles, importNormals)
|
||||
armatureObj.location = bpy.context.scene.cursor.location
|
||||
|
||||
# Set bone rotation mode.
|
||||
bpy.ops.object.select_all(action = "DESELECT")
|
||||
armatureObj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = armatureObj
|
||||
bpy.ops.object.mode_set(mode = 'POSE')
|
||||
for bone in armatureObj.pose.bones:
|
||||
bone.rotation_mode = 'XYZ'
|
||||
|
||||
# Apply mesh to armature.
|
||||
if bpy.context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||
bpy.ops.object.select_all(action = 'DESELECT')
|
||||
obj.select_set(True)
|
||||
armatureObj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = armatureObj
|
||||
bpy.ops.object.parent_set(type = "ARMATURE")
|
||||
|
||||
applyRotation([armatureObj], math.radians(-90), 'X')
|
||||
|
||||
return isLOD, armatureObj
|
||||
|
||||
def ootAddBone(armatureObj, boneName, parentBoneName, currentTransform, loadDL):
|
||||
if bpy.context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.object.select_all(action = 'DESELECT')
|
||||
bpy.context.view_layer.objects.active = armatureObj
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
bone = armatureObj.data.edit_bones.new(boneName)
|
||||
bone.use_connect = False
|
||||
if parentBoneName is not None:
|
||||
bone.parent = armatureObj.data.edit_bones[parentBoneName]
|
||||
bone.head = currentTransform @ mathutils.Vector((0,0,0))
|
||||
bone.tail = bone.head + (currentTransform.to_quaternion() @ \
|
||||
mathutils.Vector((0,0.3,0)))
|
||||
|
||||
# Connect bone to parent if it is possible without changing parent direction.
|
||||
|
||||
if parentBoneName is not None:
|
||||
nodeOffsetVector = mathutils.Vector(bone.head - bone.parent.head)
|
||||
# set fallback to nonzero to avoid creating zero length bones
|
||||
if(nodeOffsetVector.angle(bone.parent.tail - bone.parent.head, 1) \
|
||||
< 0.0001 and loadDL):
|
||||
for child in bone.parent.children:
|
||||
if child != bone:
|
||||
child.use_connect = False
|
||||
bone.parent.tail = bone.head
|
||||
bone.use_connect = True
|
||||
elif bone.head == bone.parent.head and bone.tail == bone.parent.tail:
|
||||
bone.tail += currentTransform.to_quaternion() @ mathutils.Vector((0,0.2,0))
|
||||
|
||||
if bpy.context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
|
||||
def ootAddLimbRecursively(limbIndex, skeletonData, obj, armatureObj, parentTransform, parentBoneName, f3dContext, useFarLOD):
|
||||
|
||||
limbName = f3dContext.getLimbName(limbIndex)
|
||||
boneName = f3dContext.getBoneName(limbIndex)
|
||||
matchResult = ootGetLimb(skeletonData, limbName, False)
|
||||
|
||||
isLOD = matchResult.lastindex > 6
|
||||
|
||||
if isLOD and useFarLOD:
|
||||
dlName = matchResult.group(7)
|
||||
else:
|
||||
dlName = matchResult.group(6)
|
||||
|
||||
# Animations override the root translation, so we just ignore importing them as well.
|
||||
if limbIndex == 0:
|
||||
translation = [0,0,0]
|
||||
else:
|
||||
translation = [int(matchResult.group(1)), int(matchResult.group(2)), int(matchResult.group(3))]
|
||||
nextChildIndex = int(matchResult.group(4))
|
||||
nextSiblingIndex = int(matchResult.group(5))
|
||||
|
||||
#str(limbIndex) + " " + str(translation) + " " + str(nextChildIndex) + " " + \
|
||||
# str(nextSiblingIndex) + " " + str(dlName))
|
||||
|
||||
currentTransform = parentTransform @ mathutils.Matrix.Translation(mathutils.Vector(translation))
|
||||
f3dContext.matrixData[limbName] = currentTransform
|
||||
loadDL = dlName != "NULL"
|
||||
|
||||
ootAddBone(armatureObj, boneName, parentBoneName, currentTransform, loadDL)
|
||||
|
||||
# DLs can access bone transforms not yet processed.
|
||||
# Therefore were delay F3D parsing until after skeleton is processed.
|
||||
if loadDL:
|
||||
f3dContext.dlList.append(OOTDLEntry(dlName, limbIndex))
|
||||
#parseF3D(skeletonData, dlName, obj, transformMatrix, boneName, f3dContext)
|
||||
|
||||
if nextChildIndex != 255:
|
||||
isLOD |= ootAddLimbRecursively(nextChildIndex, skeletonData, obj, armatureObj, currentTransform, boneName, f3dContext, useFarLOD)
|
||||
|
||||
if nextSiblingIndex != 255:
|
||||
isLOD |= ootAddLimbRecursively(nextSiblingIndex, skeletonData, obj, armatureObj, parentTransform, parentBoneName, f3dContext, useFarLOD)
|
||||
|
||||
return isLOD
|
||||
|
||||
def ootRemoveSkeleton(filepath, objectName, skeletonName):
|
||||
headerPath = os.path.join(filepath, objectName + '.h')
|
||||
sourcePath = os.path.join(filepath, objectName + '.c')
|
||||
|
||||
skeletonDataC = readFile(sourcePath)
|
||||
originalDataC = skeletonDataC
|
||||
|
||||
skeletonDataH = readFile(headerPath)
|
||||
originalDataH = skeletonDataH
|
||||
|
||||
matchResult = ootGetSkeleton(skeletonDataC, skeletonName, True)
|
||||
if matchResult is None:
|
||||
return
|
||||
skeletonDataC = skeletonDataC[:matchResult.start(0)] + skeletonDataC[matchResult.end(0):]
|
||||
limbsName = matchResult.group(2)
|
||||
|
||||
headerMatch = getDeclaration(skeletonDataH, skeletonName)
|
||||
if headerMatch is not None:
|
||||
skeletonDataH = skeletonDataH[:headerMatch.start(0)] + skeletonDataH[headerMatch.end(0):]
|
||||
|
||||
matchResult = ootGetLimbs(skeletonDataC, limbsName, True)
|
||||
if matchResult is None:
|
||||
return
|
||||
skeletonDataC = skeletonDataC[:matchResult.start(0)] + skeletonDataC[matchResult.end(0):]
|
||||
limbsData = matchResult.group(2)
|
||||
limbList = [entry.strip()[1:] for entry in limbsData.split(',')]
|
||||
|
||||
headerMatch = getDeclaration(skeletonDataH, limbsName)
|
||||
if headerMatch is not None:
|
||||
skeletonDataH = skeletonDataH[:headerMatch.start(0)] + skeletonDataH[headerMatch.end(0):]
|
||||
|
||||
for limb in limbList:
|
||||
matchResult = ootGetLimb(skeletonDataC, limb, True)
|
||||
if matchResult is not None:
|
||||
skeletonDataC = skeletonDataC[:matchResult.start(0)] + skeletonDataC[matchResult.end(0):]
|
||||
headerMatch = getDeclaration(skeletonDataH, limb)
|
||||
if headerMatch is not None:
|
||||
skeletonDataH = skeletonDataH[:headerMatch.start(0)] + skeletonDataH[headerMatch.end(0):]
|
||||
|
||||
if skeletonDataC != originalDataC:
|
||||
writeFile(sourcePath, skeletonDataC)
|
||||
|
||||
if skeletonDataH != originalDataH:
|
||||
writeFile(headerPath, skeletonDataH)
|
||||
|
||||
class OOT_ImportSkeleton(bpy.types.Operator):
|
||||
# set bl_ properties
|
||||
bl_idname = 'object.oot_import_skeleton'
|
||||
bl_label = "Import Skeleton"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
# Called on demand (i.e. button press, menu item)
|
||||
# Can also be called from operator search menu (Spacebar)
|
||||
def execute(self, context):
|
||||
armatureObj = None
|
||||
if context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = "OBJECT")
|
||||
|
||||
try:
|
||||
importPath = bpy.path.abspath(context.scene.ootSkeletonImportCustomPath)
|
||||
isCustomImport = context.scene.ootSkeletonImportUseCustomPath
|
||||
folderName = context.scene.ootSkeletonImportFolderName
|
||||
skeletonName = context.scene.ootSkeletonImportName
|
||||
scale = context.scene.ootActorBlenderScale
|
||||
removeDoubles = context.scene.ootActorRemoveDoubles
|
||||
importNormals = context.scene.ootActorImportNormals
|
||||
decompPath = bpy.path.abspath(bpy.context.scene.ootDecompPath)
|
||||
drawLayer = bpy.context.scene.ootActorImportDrawLayer
|
||||
|
||||
filepaths = [ootGetObjectPath(isCustomImport, importPath, folderName)]
|
||||
if not isCustomImport:
|
||||
filepaths.append(os.path.join(bpy.context.scene.ootDecompPath, "assets/objects/gameplay_keep/gameplay_keep.c"))
|
||||
|
||||
ootImportSkeletonC(filepaths, skeletonName, scale, removeDoubles, importNormals, decompPath, drawLayer)
|
||||
|
||||
self.report({'INFO'}, 'Success!')
|
||||
return {'FINISHED'}
|
||||
|
||||
except Exception as e:
|
||||
if context.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||
raisePluginError(self, e)
|
||||
return {'CANCELLED'} # must return a set
|
||||
|
||||
class OOT_ExportSkeleton(bpy.types.Operator):
|
||||
# set bl_ properties
|
||||
@@ -410,15 +752,16 @@ class OOT_ExportSkeleton(bpy.types.Operator):
|
||||
saveTextures = bpy.context.scene.saveTextures or bpy.context.scene.ignoreTextureRestrictions
|
||||
isHWv1 = context.scene.isHWv1
|
||||
f3dType = context.scene.f3d_type
|
||||
skeletonName = context.scene.ootSkeletonName
|
||||
folderName = context.scene.ootSkeletonFolderName
|
||||
exportPath = context.scene.ootSkeletonCustomPath
|
||||
isCustomExport = context.scene.ootSkeletonUseCustomPath
|
||||
drawLayer = context.scene.ootSkeletonDrawLayer
|
||||
skeletonName = context.scene.ootSkeletonExportName
|
||||
folderName = context.scene.ootSkeletonExportFolderName
|
||||
exportPath = bpy.path.abspath(context.scene.ootSkeletonExportCustomPath)
|
||||
isCustomExport = context.scene.ootSkeletonExportUseCustomPath
|
||||
drawLayer = armatureObj.ootDrawLayer
|
||||
removeVanillaData = context.scene.ootSkeletonRemoveVanillaData
|
||||
|
||||
ootConvertArmatureToC(armatureObj, finalTransform,
|
||||
f3dType, isHWv1, skeletonName, folderName, DLFormat.Static, saveTextures,
|
||||
exportPath, isCustomExport, drawLayer)
|
||||
exportPath, isCustomExport, drawLayer, removeVanillaData)
|
||||
|
||||
self.report({'INFO'}, 'Success!')
|
||||
return {'FINISHED'}
|
||||
@@ -445,20 +788,88 @@ class OOT_ExportSkeletonPanel(bpy.types.Panel):
|
||||
col = self.layout.column()
|
||||
col.operator(OOT_ExportSkeleton.bl_idname)
|
||||
|
||||
col.prop(context.scene, "ootSkeletonUseCustomPath")
|
||||
prop_split(col, context.scene, 'ootSkeletonName', "Skeleton")
|
||||
if context.scene.ootSkeletonUseCustomPath:
|
||||
prop_split(col, context.scene, 'ootSkeletonCustomPath', "Folder")
|
||||
prop_split(col, context.scene, 'ootSkeletonExportName', "Skeleton")
|
||||
if context.scene.ootSkeletonExportUseCustomPath:
|
||||
prop_split(col, context.scene, 'ootSkeletonExportCustomPath', "Folder")
|
||||
else:
|
||||
prop_split(col, context.scene, 'ootSkeletonFolderName', "Object")
|
||||
prop_split(col, context.scene, "ootSkeletonDrawLayer", "Draw Layer")
|
||||
prop_split(col, context.scene, 'ootSkeletonExportFolderName', "Object")
|
||||
col.prop(context.scene, "ootSkeletonExportUseCustomPath")
|
||||
|
||||
col.operator(OOT_ImportSkeleton.bl_idname)
|
||||
|
||||
prop_split(col, context.scene, 'ootSkeletonImportName', "Skeleton")
|
||||
if context.scene.ootSkeletonImportUseCustomPath:
|
||||
prop_split(col, context.scene, 'ootSkeletonImportCustomPath', "File")
|
||||
else:
|
||||
prop_split(col, context.scene, 'ootSkeletonImportFolderName', "Object")
|
||||
prop_split(col, context.scene, "ootActorImportDrawLayer", "Import Draw Layer")
|
||||
|
||||
col.prop(context.scene, "ootSkeletonImportUseCustomPath")
|
||||
col.prop(context.scene, "ootActorRemoveDoubles")
|
||||
col.prop(context.scene, "ootActorImportNormals")
|
||||
col.prop(context.scene, "ootSkeletonRemoveVanillaData")
|
||||
|
||||
|
||||
class OOT_SkeletonPanel(bpy.types.Panel):
|
||||
bl_idname = "OOT_PT_skeleton"
|
||||
bl_label = "OOT Skeleton Properties"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "object"
|
||||
bl_options = {'HIDE_HEADER'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene.gameEditorMode == "OOT" and \
|
||||
hasattr(context, "object") and context.object is not None and isinstance(context.object.data, bpy.types.Armature)
|
||||
|
||||
# called every frame
|
||||
def draw(self, context):
|
||||
col = self.layout.box().column()
|
||||
col.box().label(text = "OOT Skeleton Inspector")
|
||||
prop_split(col, context.object, "ootDrawLayer", "Draw Layer")
|
||||
prop_split(col, context.object, "ootFarLOD", "LOD Skeleton")
|
||||
if context.object.ootFarLOD is not None:
|
||||
col.label(text = "Make sure LOD has same bone structure.", icon = "ERROR")
|
||||
|
||||
class OOT_BonePanel(bpy.types.Panel):
|
||||
bl_idname = "OOT_PT_bone"
|
||||
bl_label = "OOT Bone Properties"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "bone"
|
||||
bl_options = {'HIDE_HEADER'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene.gameEditorMode == "OOT" and context.bone is not None
|
||||
|
||||
# called every frame
|
||||
def draw(self, context):
|
||||
col = self.layout.box().column()
|
||||
col.box().label(text = "OOT Bone Inspector")
|
||||
prop_split(col, context.bone, "ootBoneType", "Bone Type")
|
||||
if context.bone.ootBoneType == "Custom DL":
|
||||
prop_split(col, context.bone, "ootCustomDLName", "DL Name")
|
||||
if context.bone.ootBoneType == "Custom DL" or\
|
||||
context.bone.ootBoneType == "Ignore":
|
||||
col.label(text = "Make sure no geometry is skinned to this bone.", icon = "ERROR")
|
||||
|
||||
if context.bone.ootBoneType != "Ignore":
|
||||
col.prop(context.bone.ootDynamicTransform, 'billboard')
|
||||
|
||||
def pollArmature(self, obj):
|
||||
return isinstance(obj.data, bpy.types.Armature)
|
||||
|
||||
oot_skeleton_classes = (
|
||||
OOT_ExportSkeleton,
|
||||
OOT_ImportSkeleton,
|
||||
)
|
||||
|
||||
oot_skeleton_panels = (
|
||||
OOT_ExportSkeletonPanel,
|
||||
OOT_SkeletonPanel,
|
||||
OOT_BonePanel,
|
||||
)
|
||||
|
||||
def oot_skeleton_panel_register():
|
||||
@@ -470,25 +881,59 @@ def oot_skeleton_panel_unregister():
|
||||
unregister_class(cls)
|
||||
|
||||
def oot_skeleton_register():
|
||||
bpy.types.Scene.ootSkeletonName = bpy.props.StringProperty(
|
||||
name = "Skeleton Name", default = "skeleton")
|
||||
bpy.types.Scene.ootSkeletonFolderName = bpy.props.StringProperty(
|
||||
name = "Skeleton Folder", default = "gameplay_keep")
|
||||
bpy.types.Scene.ootSkeletonCustomPath = bpy.props.StringProperty(
|
||||
bpy.types.Scene.ootSkeletonExportName = bpy.props.StringProperty(
|
||||
name = "Skeleton Name", default = "gGerudoRedSkel")
|
||||
bpy.types.Scene.ootSkeletonExportFolderName = bpy.props.StringProperty(
|
||||
name = "Skeleton Folder", default = "object_geldb")
|
||||
bpy.types.Scene.ootSkeletonExportCustomPath = bpy.props.StringProperty(
|
||||
name ='Custom Skeleton Path', subtype = 'FILE_PATH')
|
||||
bpy.types.Scene.ootSkeletonUseCustomPath = bpy.props.BoolProperty(
|
||||
bpy.types.Scene.ootSkeletonExportUseCustomPath = bpy.props.BoolProperty(
|
||||
name = "Use Custom Path")
|
||||
bpy.types.Scene.ootSkeletonDrawLayer = bpy.props.EnumProperty(items = ootEnumDrawLayers)
|
||||
|
||||
bpy.types.Scene.ootSkeletonImportName = bpy.props.StringProperty(
|
||||
name = "Skeleton Name", default = "gGerudoRedSkel")
|
||||
bpy.types.Scene.ootSkeletonImportFolderName = bpy.props.StringProperty(
|
||||
name = "Skeleton Folder", default = "object_geldb")
|
||||
bpy.types.Scene.ootSkeletonImportCustomPath = bpy.props.StringProperty(
|
||||
name ='Custom Skeleton Path', subtype = 'FILE_PATH')
|
||||
bpy.types.Scene.ootSkeletonImportUseCustomPath = bpy.props.BoolProperty(
|
||||
name = "Use Custom Path")
|
||||
|
||||
bpy.types.Scene.ootActorRemoveDoubles = bpy.props.BoolProperty(name = "Remove Doubles On Import", default = True)
|
||||
bpy.types.Scene.ootActorImportNormals = bpy.props.BoolProperty(name = "Import Normals", default = True)
|
||||
bpy.types.Scene.ootSkeletonRemoveVanillaData = bpy.props.BoolProperty(name = "Replace Vanilla Headers On Export", default = True)
|
||||
bpy.types.Scene.ootActorImportDrawLayer = bpy.props.EnumProperty(name = "Import Draw Layer", items = ootEnumDrawLayers)
|
||||
|
||||
bpy.types.Object.ootFarLOD = bpy.props.PointerProperty(type = bpy.types.Object, poll = pollArmature)
|
||||
|
||||
bpy.types.Bone.ootBoneType = bpy.props.EnumProperty(name = 'Bone Type', items = ootEnumBoneType)
|
||||
bpy.types.Bone.ootDynamicTransform = bpy.props.PointerProperty(type = OOTDynamicTransformProperty)
|
||||
bpy.types.Bone.ootCustomDLName = bpy.props.StringProperty(name = 'Custom DL', default = "gEmptyDL")
|
||||
|
||||
for cls in oot_skeleton_classes:
|
||||
register_class(cls)
|
||||
|
||||
def oot_skeleton_unregister():
|
||||
del bpy.types.Scene.ootSkeletonCustomPath
|
||||
del bpy.types.Scene.ootSkeletonFolderName
|
||||
del bpy.types.Scene.ootSkeletonUseCustomPath
|
||||
del bpy.types.Scene.ootSkeletonName
|
||||
del bpy.types.Scene.ootSkeletonDrawLayer
|
||||
del bpy.types.Scene.ootSkeletonExportName
|
||||
del bpy.types.Scene.ootSkeletonExportFolderName
|
||||
del bpy.types.Scene.ootSkeletonExportCustomPath
|
||||
del bpy.types.Scene.ootSkeletonExportUseCustomPath
|
||||
|
||||
del bpy.types.Scene.ootSkeletonImportName
|
||||
del bpy.types.Scene.ootSkeletonImportFolderName
|
||||
del bpy.types.Scene.ootSkeletonImportCustomPath
|
||||
del bpy.types.Scene.ootSkeletonImportUseCustomPath
|
||||
|
||||
del bpy.types.Scene.ootActorRemoveDoubles
|
||||
del bpy.types.Scene.ootActorImportNormals
|
||||
del bpy.types.Scene.ootSkeletonRemoveVanillaData
|
||||
del bpy.types.Scene.ootActorImportDrawLayer
|
||||
|
||||
del bpy.types.Object.ootFarLOD
|
||||
|
||||
del bpy.types.Bone.ootBoneType
|
||||
del bpy.types.Bone.ootDynamicTransform
|
||||
del bpy.types.Bone.ootCustomDLName
|
||||
|
||||
for cls in reversed(oot_skeleton_classes):
|
||||
unregister_class(cls)
|
||||
@@ -1,5 +1,5 @@
|
||||
from ..utility import *
|
||||
import bpy, math, mathutils, os
|
||||
import bpy, math, mathutils, os, re
|
||||
from bpy.utils import register_class, unregister_class
|
||||
|
||||
ootSceneDungeons = [
|
||||
@@ -190,6 +190,15 @@ def checkEmptyName(name):
|
||||
if name == "":
|
||||
raise PluginError("No name entered for the exporter.")
|
||||
|
||||
def ootGetObjectPath(isCustomExport, exportPath, folderName):
|
||||
if isCustomExport:
|
||||
filepath = exportPath
|
||||
else:
|
||||
filepath = os.path.join(ootGetPath(exportPath, isCustomExport, 'assets/objects/',
|
||||
folderName, False, False), folderName + '.c')
|
||||
return filepath
|
||||
|
||||
|
||||
def ootGetPath(exportPath, isCustomExport, subPath, folderName, makeIfNotExists, useFolderForCustom):
|
||||
if isCustomExport:
|
||||
path = bpy.path.abspath(os.path.join(exportPath, (folderName if useFolderForCustom else '')))
|
||||
@@ -207,14 +216,21 @@ def ootGetPath(exportPath, isCustomExport, subPath, folderName, makeIfNotExists,
|
||||
return path
|
||||
|
||||
def getSortedChildren(armatureObj, bone):
|
||||
return sorted([child.name for child in bone.children])
|
||||
return sorted([child.name for child in bone.children if child.ootBoneType != "Ignore"], key = lambda childName : childName.lower())
|
||||
|
||||
def getStartBone(armatureObj):
|
||||
return 'root'
|
||||
startBoneNames = [bone.name for bone in armatureObj.data.bones if \
|
||||
bone.parent is None and bone.ootBoneType != "Ignore"]
|
||||
if len(startBoneNames) == 0:
|
||||
raise PluginError(armatureObj.name + " does not have any root bones that are not of the \"Ignore\" type.")
|
||||
startBoneName = startBoneNames[0]
|
||||
return startBoneName
|
||||
#return 'root'
|
||||
|
||||
def checkForStartBone(armatureObj):
|
||||
if "root" not in armatureObj.data.bones:
|
||||
raise PluginError("Skeleton must have a bone named 'root' where the skeleton starts from.")
|
||||
pass
|
||||
#if "root" not in armatureObj.data.bones:
|
||||
# raise PluginError("Skeleton must have a bone named 'root' where the skeleton starts from.")
|
||||
|
||||
class BoxEmpty:
|
||||
def __init__(self, position, scale, emptyScale):
|
||||
|
||||
@@ -660,7 +660,7 @@ def parseDLWithOffset(romfile, currentAddress,
|
||||
displayListStartAddress = decodeSegmentedAddr(segmentedAddr,
|
||||
segmentData = segmentData)
|
||||
#print(displayListStartAddress)
|
||||
parseF3D(romfile, displayListStartAddress, bpy.context.scene,
|
||||
parseF3DBinary(romfile, displayListStartAddress, bpy.context.scene,
|
||||
bMesh, obj, finalTransform, boneName, segmentData, vertexBuffer)
|
||||
|
||||
# Handle child objects
|
||||
@@ -721,7 +721,7 @@ def handleNodeCommon(romfile, armatureObj, parentBoneName, finalTransform,
|
||||
hasMeshData = int.from_bytes(segmentedAddr, 'big') != 0
|
||||
if hasMeshData:
|
||||
startAddress = decodeSegmentedAddr(segmentedAddr, segmentData)
|
||||
parseF3D(romfile, startAddress, bpy.context.scene, bMesh, obj,
|
||||
parseF3DBinary(romfile, startAddress, bpy.context.scene, bMesh, obj,
|
||||
finalTransform, boneName, segmentData, vertexBuffer)
|
||||
elif armatureObj is not None:
|
||||
armatureObj.data.bones[boneName].use_deform = False
|
||||
|
||||
@@ -1664,7 +1664,7 @@ def saveModelGivenVertexGroup(fModel, obj, vertexGroup,
|
||||
# Save unskinned mesh
|
||||
for drawLayer, materialFaces in groupFaces.items():
|
||||
if drawLayer not in fMeshes:
|
||||
fMesh = fModel.addMesh(vertexGroup, namePrefix, drawLayer, False)
|
||||
fMesh = fModel.addMesh(vertexGroup, namePrefix, drawLayer, False, None)
|
||||
fMeshes[drawLayer] = fMesh
|
||||
|
||||
for material_index, bFaces in materialFaces.items():
|
||||
|
||||
@@ -36,6 +36,31 @@ enumCompressionFormat = [
|
||||
('yay0', 'YAY0', 'YAY0'),
|
||||
]
|
||||
|
||||
def getDeclaration(data, name):
|
||||
matchResult = re.search("extern\s*[A-Za-z0-9\_]*\s*" + re.escape(name) + \
|
||||
"\s*(\[[^;\]]*\])?;\s*", data, re.DOTALL)
|
||||
return matchResult
|
||||
|
||||
def hexOrDecInt(value):
|
||||
if isinstance(value, int):
|
||||
return value
|
||||
elif "<<" in value:
|
||||
i = value.index("<<")
|
||||
return hexOrDecInt(value[:i]) << hexOrDecInt(value[i + 2:])
|
||||
elif ">>" in value:
|
||||
i = value.index(">>")
|
||||
return hexOrDecInt(value[:i]) >> hexOrDecInt(value[i + 2:])
|
||||
elif 'x' in value:
|
||||
return int(value, 16)
|
||||
else:
|
||||
return int(value)
|
||||
|
||||
def getOrMakeVertexGroup(obj, groupName):
|
||||
for group in obj.vertex_groups:
|
||||
if group.name == groupName:
|
||||
return group
|
||||
return obj.vertex_groups.new(name = groupName)
|
||||
|
||||
def unhideAllAndGetHiddenList(scene):
|
||||
hiddenObjs = []
|
||||
for obj in scene.objects:
|
||||
@@ -133,6 +158,58 @@ def copyPropertyGroup(oldProp, newProp):
|
||||
else:
|
||||
setattr(newProp, sub_value_attr, sub_value)
|
||||
|
||||
def propertyCollectionEquals(oldProp, newProp):
|
||||
if len(oldProp) != len(newProp):
|
||||
print("Unequal size: " + str(oldProp) + " " + str(len(oldProp)) + ", " + str(newProp) + str(len(newProp)))
|
||||
return False
|
||||
|
||||
equivalent = True
|
||||
for i in range(len(oldProp)):
|
||||
item = oldProp[i]
|
||||
newItem = newProp[i]
|
||||
if isinstance(item, bpy.types.PropertyGroup):
|
||||
equivalent &= propertyGroupEquals(item, newItem)
|
||||
elif type(item).__name__ == "bpy_prop_collection_idprop":
|
||||
equivalent &= propertyCollectionEquals(item, newItem)
|
||||
else:
|
||||
try:
|
||||
iterator = iter(item)
|
||||
except TypeError:
|
||||
isEquivalent = newItem == item
|
||||
else:
|
||||
isEquivalent = tuple([i for i in newItem]) == tuple([ i for i in item])
|
||||
if not isEquivalent:
|
||||
pass #print("Not equivalent: " + str(item) + " " + str(newItem))
|
||||
equivalent &= isEquivalent
|
||||
|
||||
return equivalent
|
||||
|
||||
def propertyGroupEquals(oldProp, newProp):
|
||||
equivalent = True
|
||||
for sub_value_attr in oldProp.bl_rna.properties.keys():
|
||||
if sub_value_attr == "rna_type":
|
||||
continue
|
||||
sub_value = getattr(oldProp, sub_value_attr)
|
||||
if isinstance(sub_value, bpy.types.PropertyGroup):
|
||||
equivalent &= propertyGroupEquals(sub_value, getattr(newProp, sub_value_attr))
|
||||
elif type(sub_value).__name__ == "bpy_prop_collection_idprop":
|
||||
newCollection = getattr(newProp, sub_value_attr)
|
||||
copyPropertyCollection(sub_value, newCollection)
|
||||
else:
|
||||
newValue = getattr(newProp, sub_value_attr)
|
||||
try:
|
||||
iterator = iter(newValue)
|
||||
except TypeError:
|
||||
isEquivalent = newValue == sub_value
|
||||
else:
|
||||
isEquivalent = tuple([i for i in newValue]) == tuple([i for i in sub_value])
|
||||
|
||||
if not isEquivalent:
|
||||
pass #print("Not equivalent: " + str(sub_value) + " " + str(newValue) + " " + str(sub_value_attr))
|
||||
equivalent &= isEquivalent
|
||||
|
||||
return equivalent
|
||||
|
||||
def writeCData(data, headerPath, sourcePath):
|
||||
sourceFile = open(sourcePath, 'w', newline = '\n', encoding = 'utf-8')
|
||||
sourceFile.write(data.source)
|
||||
@@ -285,9 +362,9 @@ def writeMaterialBase(baseDir):
|
||||
matCFile = open(matCPath, 'w', newline = '\n')
|
||||
matCFile.write(
|
||||
'#include "types.h"\n' +\
|
||||
'#include "rendering_graph_node.h"\n' +\
|
||||
'#include "object_fields.h"\n' +\
|
||||
'#include "materials.h"')
|
||||
'#include "rendering_graph_node.h"\n' +\
|
||||
'#include "object_fields.h"\n' +\
|
||||
'#include "materials.h"')
|
||||
|
||||
# Write global texture load function here
|
||||
# Write material.inc.c
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 146 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 295 KiB |
Reference in New Issue
Block a user