4444
4545from io_scene_niftools .modules .nif_export .animation import Animation
4646from io_scene_niftools .modules .nif_export .block_registry import block_store
47- from io_scene_niftools .utils import math
48- from io_scene_niftools .utils .singleton import NifOp
47+ from io_scene_niftools .utils import math , consts
4948from io_scene_niftools .utils .logging import NifError , NifLog
5049
5150
@@ -67,77 +66,57 @@ def iter_frame_key(fcurves, mathutilclass):
6766 yield frame , mathutilclass (key )
6867
6968 def export_kf_root (self , b_armature = None ):
70-
69+ """Creates and returns a KF root block and exports controllers for objects and bones"""
7170 scene = bpy .context .scene
72- # morrowind
73- if scene .niftools_scene .game in ('MORROWIND' , 'FREEDOM_FORCE' ):
74- # create kf root header
71+ game = scene .niftools_scene .game
72+ if game in ('MORROWIND' , 'FREEDOM_FORCE' ):
7573 kf_root = block_store .create_block ("NiSequenceStreamHelper" )
76- # kf_root.add_extra_data(anim_textextra)
77- # # reparent controller tree
78- # for node, ctrls in node_kfctrls.items():
79- # for ctrl in ctrls:
80- # # create node reference by name
81- # nodename_extra = block_store.create_block("NiStringExtraData")
82- # nodename_extra.bytes_remaining = len(node.name) + 4
83- # nodename_extra.string_data = node.name
84-
85- # # break the controller chain
86- # ctrl.next_controller = None
87-
88- # # add node reference and controller
89- # kf_root.add_extra_data(nodename_extra)
90- # kf_root.add_controller(ctrl)
91- # # wipe controller target
92- # ctrl.target = None
93-
94- elif scene .niftools_scene .game in (
95- 'SKYRIM' , 'OBLIVION' , 'FALLOUT_3' , 'CIVILIZATION_IV' , 'ZOO_TYCOON_2' , 'FREEDOM_FORCE_VS_THE_3RD_REICH' ):
96-
97- # create kf root header
74+ elif game in (
75+ 'SKYRIM' , 'OBLIVION' , 'FALLOUT_3' , 'CIVILIZATION_IV' , 'ZOO_TYCOON_2' , 'FREEDOM_FORCE_VS_THE_3RD_REICH' ,
76+ 'MEGAMI_TENSEI_IMAGINE' ):
9877 kf_root = block_store .create_block ("NiControllerSequence" )
99- targetname = "Scene Root"
100-
101- # per-node animation
102- if b_armature :
103- b_action = self .get_active_action (b_armature )
104- for b_bone in b_armature .data .bones :
105- self .export_transforms (kf_root , b_armature , b_action , b_bone )
106- if scene .niftools_scene .game in ('SKYRIM' , ):
107- targetname = "NPC Root [Root]"
108- else :
109- # quick hack to set correct target name
110- if "Bip01" in b_armature .data .bones :
111- targetname = "Bip01"
112- elif "Bip02" in b_armature .data .bones :
113- targetname = "Bip02"
114-
115- # per-object animation
116- else :
117- for b_obj in bpy .data .objects :
118- b_action = self .get_active_action (b_obj )
119- self .export_transforms (kf_root , b_obj , b_action )
120-
121- anim_textextra = self .export_text_keys (b_action )
122-
123- kf_root .name = b_action .name
124- kf_root .unknown_int_1 = 1
125- kf_root .weight = 1.0
126- kf_root .text_keys = anim_textextra
127- kf_root .cycle_type = NifFormat .CycleType .CYCLE_CLAMP
128- kf_root .frequency = 1.0
129-
130- if anim_textextra .num_text_keys > 0 :
131- kf_root .start_time = anim_textextra .text_keys [0 ].time
132- kf_root .stop_time = anim_textextra .text_keys [anim_textextra .num_text_keys - 1 ].time
78+ else :
79+ raise NifError (f"Keyframe export for '{ game } ' is not supported." )
80+
81+ anim_textextra = self .create_text_keys (kf_root )
82+ targetname = "Scene Root"
83+
84+ # per-node animation
85+ if b_armature :
86+ b_action = self .get_active_action (b_armature )
87+ for b_bone in b_armature .data .bones :
88+ self .export_transforms (kf_root , b_armature , b_action , b_bone )
89+ if game in ('SKYRIM' ,):
90+ targetname = "NPC Root [Root]"
13391 else :
134- kf_root .start_time = scene .frame_start / self .fps
135- kf_root .stop_time = scene .frame_end / self .fps
92+ # quick hack to set correct target name
93+ if "Bip01" in b_armature .data .bones :
94+ targetname = "Bip01"
95+ elif "Bip02" in b_armature .data .bones :
96+ targetname = "Bip02"
13697
137- kf_root . target_name = targetname
98+ # per-object animation
13899 else :
139- raise NifError (
140- f"Keyframe export for '{ bpy .context .scene .niftools_scene .game } ' is not supported." )
100+ for b_obj in bpy .data .objects :
101+ b_action = self .get_active_action (b_obj )
102+ self .export_transforms (kf_root , b_obj , b_action )
103+
104+ self .export_text_keys (b_action , anim_textextra )
105+
106+ kf_root .name = b_action .name
107+ kf_root .unknown_int_1 = 1
108+ kf_root .weight = 1.0
109+ kf_root .cycle_type = NifFormat .CycleType .CYCLE_CLAMP
110+ kf_root .frequency = 1.0
111+
112+ if anim_textextra .num_text_keys > 0 :
113+ kf_root .start_time = anim_textextra .text_keys [0 ].time
114+ kf_root .stop_time = anim_textextra .text_keys [anim_textextra .num_text_keys - 1 ].time
115+ else :
116+ kf_root .start_time = scene .frame_start / self .fps
117+ kf_root .stop_time = scene .frame_end / self .fps
118+
119+ kf_root .target_name = targetname
141120 return kf_root
142121
143122 def export_transforms (self , parent_block , b_obj , b_action , bone = None ):
@@ -161,11 +140,11 @@ def export_transforms(self, parent_block, b_obj, b_action, bone=None):
161140
162141 # skeletal animation - with bone correction & coordinate corrections
163142 if bone and bone .name in b_action .groups :
164- # get bind matrix for bone or object
143+ # get bind matrix for bone
165144 bind_matrix = math .get_object_bind (bone )
166145 exp_fcurves = b_action .groups [bone .name ].channels
167146 # just for more detailed error reporting later on
168- bonestr = " in bone " + bone .name
147+ bonestr = f " in bone { bone .name } "
169148 target_name = block_store .get_full_name (bone )
170149 priority = bone .niftools .priority
171150
@@ -295,23 +274,20 @@ def export_transforms(self, parent_block, b_obj, b_action, bone=None):
295274 key .time = frame / self .fps
296275 key .value = scale
297276
298- def export_text_keys (self , b_action ):
299- """Process b_action's pose markers and return an extra string data block. """
300- try :
301- if NifOp . props . animation == 'GEOM_NIF' :
302- # animation group extra data is not present in geometry only files
303- return
304- except AttributeError :
305- # kf export has no animation mode
306- pass
277+ def create_text_keys (self , kf_root ):
278+ """Create the text keys before filling in the data so that the extra data hierarchy is correct """
279+ # add a NiTextKeyExtraData block
280+ n_text_extra = block_store . create_block ( "NiTextKeyExtraData" , None )
281+ if isinstance ( kf_root , NifFormat . NiControllerSequence ):
282+ kf_root . text_keys = n_text_extra
283+ elif isinstance ( kf_root , NifFormat . NiSequenceStreamHelper ) :
284+ kf_root . add_extra_data ( n_text_extra )
285+ return n_text_extra
307286
287+ def export_text_keys (self , b_action , n_text_extra ):
288+ """Process b_action's pose markers and populate the extra string data block."""
308289 NifLog .info ("Exporting animation groups" )
309-
310290 self .add_dummy_markers (b_action )
311-
312- # add a NiTextKeyExtraData block
313- n_text_extra = block_store .create_block ("NiTextKeyExtraData" , b_action .pose_markers )
314-
315291 # create a text key for each frame descriptor
316292 n_text_extra .num_text_keys = len (b_action .pose_markers )
317293 n_text_extra .text_keys .update_size ()
@@ -320,8 +296,19 @@ def export_text_keys(self, b_action):
320296 f = marker .frame
321297 if (f < f0 ) or (f > f1 ):
322298 NifLog .warn (f"Marker out of animated range ({ f } not between [{ f0 } , { f1 } ])" )
323-
324299 key .time = f / self .fps
325300 key .value = marker .name .replace ('/' , '\r \n ' )
326301
327- return n_text_extra
302+ def add_dummy_controllers (self ):
303+ NifLog .info ("Adding controllers and interpolators for skeleton" )
304+ # note: block_store.block_to_obj changes during iteration, so need list copy
305+ for n_block in list (block_store .block_to_obj .keys ()):
306+ if isinstance (n_block , NifFormat .NiNode ) and n_block .name .decode () == "Bip01" :
307+ for n_bone in n_block .tree (block_type = NifFormat .NiNode ):
308+ n_kfc , n_kfi = self .transform_anim .create_controller (n_bone , n_bone .name .decode ())
309+ # todo [anim] use self.nif_export.animationhelper.set_flags_and_timing
310+ n_kfc .flags = 12
311+ n_kfc .frequency = 1.0
312+ n_kfc .phase = 0.0
313+ n_kfc .start_time = consts .FLOAT_MAX
314+ n_kfc .stop_time = consts .FLOAT_MIN
0 commit comments