4242import com .jme3 .gde .core .assets .SpatialAssetDataObject ;
4343import com .jme3 .scene .Node ;
4444import com .jme3 .scene .Spatial ;
45- import com .jme3 .scene . plugins . bvh . SkeletonMapping ;
45+ import com .jme3 .util . SafeArrayList ;
4646import java .awt .event .ActionEvent ;
4747import java .awt .event .ActionListener ;
4848import java .io .IOException ;
4949import java .util .Collection ;
5050import java .util .Iterator ;
5151import java .util .List ;
52- import jme3utilities .MyAnimation ;
53- import jme3utilities .wes .AnimationEdit ;
54- import org .bushe .swing .event .Logger ;
52+ import java .util .logging .Level ;
53+ import java .util .logging .Logger ;
5554import org .openide .DialogDisplayer ;
5655import org .openide .NotifyDescriptor ;
5756import org .openide .NotifyDescriptor .Confirmation ;
5857import org .openide .util .Exceptions ;
5958
6059/**
61- * Action for merging one or more spatials' animation to another.
62- * Same rig required.
60+ * Action for merging one or more spatials' animation to another. Same rig
61+ * required.
62+ *
6363 * @author rickard
6464 */
6565public class MergeAnimationsAction implements ActionListener {
66-
66+
6767 private static final String CANCEL = "Cancel" ;
6868 private final List <SpatialAssetDataObject > spatials ;
6969 private final Logger logger ;
@@ -77,84 +77,63 @@ public MergeAnimationsAction(List<SpatialAssetDataObject> context) {
7777 public void actionPerformed (ActionEvent e ) {
7878 if (spatials .size () == 1 ) {
7979 DialogDisplayer .getDefault ().notify (new NotifyDescriptor .Message (
80- "Must select more than one spatial" ,
81- NotifyDescriptor .ERROR_MESSAGE ));
80+ "Must select more than one spatial" ,
81+ NotifyDescriptor .ERROR_MESSAGE ));
8282 return ;
8383 }
84-
84+
8585 final Object selectedSpatial = createSelector ().getValue ();
86-
86+
8787 if (selectedSpatial == null || selectedSpatial .equals (CANCEL )) {
88- logger .log (Logger . Level .INFO , "Operation cancelled by user." );
88+ logger .log (Level .INFO , "Operation cancelled by user." );
8989 return ;
9090 }
91-
91+
9292 final SpatialAssetDataObject targetAsset = findSpatial (selectedSpatial .toString ());
93-
93+
9494 if (targetAsset == null ) {
95- logger .log (Logger . Level .INFO , "Operation failed. No spatial." );
95+ logger .log (Level .INFO , "Operation failed. No spatial." );
9696 return ;
9797 }
98-
98+
9999 final Spatial targetSpatial = targetAsset .loadAsset ();
100100 final AnimComposer targetAnimComposer = findAnimComposer (targetSpatial , null );
101-
101+
102102 if (targetAnimComposer == null ) {
103103 DialogDisplayer .getDefault ().notify (new NotifyDescriptor .Message (
104- String .format ("%s has no AnimComposer." , targetSpatial ),
105- NotifyDescriptor .ERROR_MESSAGE ));
104+ String .format ("%s has no AnimComposer." , targetSpatial ),
105+ NotifyDescriptor .ERROR_MESSAGE ));
106106 return ;
107107 }
108108 final Spatial targetAnimComposerSpatial = targetAnimComposer .getSpatial ();
109109 for (final Iterator <SpatialAssetDataObject > it = spatials .iterator (); it .hasNext ();) {
110110 final SpatialAssetDataObject spatialAssetDataObject = it .next ();
111- if (spatialAssetDataObject .getName ().equals (selectedSpatial )) {
111+ if (spatialAssetDataObject .getName ().equals (selectedSpatial )) {
112112 continue ;
113113 }
114114 Spatial sourceSpatial = spatialAssetDataObject .loadAsset ();
115115 final AnimComposer sourceAnimComposer = findAnimComposer (sourceSpatial , null );
116116 if (sourceAnimComposer == null ) {
117117 DialogDisplayer .getDefault ().notify (new NotifyDescriptor .Message (
118- String .format ("%s has no AnimComposer." , sourceSpatial ),
119- NotifyDescriptor .ERROR_MESSAGE ));
118+ String .format ("%s has no AnimComposer." , sourceSpatial ),
119+ NotifyDescriptor .ERROR_MESSAGE ));
120120 return ;
121121 }
122122 copyClips (sourceAnimComposer , targetAnimComposer , targetAnimComposerSpatial .getControl (SkinningControl .class ).getArmature ());
123123 }
124- logger .log (Logger . Level .INFO , "Merging animations done. Saving." );
124+ logger .log (Level .INFO , "Merging animations done. Saving." );
125125 try {
126126 targetAsset .saveAsset ();
127127 } catch (IOException ex ) {
128128 Exceptions .printStackTrace (ex );
129129 }
130130 }
131-
132- private static AnimClip retargetClip (
133- AnimClip clip , Armature armature , String clipName ) {
134- AnimTrack [] animTracks = clip .getTracks ();
135- AnimClip result = new AnimClip (clipName );
136- for (AnimTrack animTrack : animTracks ) {
137- if (MyAnimation .isJointTrack (animTrack )) {
138- TransformTrack transformTrack = (TransformTrack ) animTrack ;
139- HasLocalTransform animTarget = transformTrack .getTarget ();
140- Joint animJoint = (Joint ) animTarget ;
141- String jointName = animJoint .getName ();
142- Joint charaJoint = armature .getJoint (jointName );
143- if (charaJoint != null ) {
144- transformTrack .setTarget (charaJoint );
145- AnimationEdit .addTrack (result , transformTrack );
146- }
147- }
148- }
149- return result ;
150- }
151131
152-
153132 private void copyClips (final AnimComposer from , final AnimComposer to , Armature toArmature ) {
154133 final Collection <AnimClip > animClips = from .getAnimClips ();
155- for (AnimClip animClip : animClips ) {
134+ for (AnimClip animClip : animClips ) {
156135 to .addAnimClip (retargetClip (animClip , toArmature , animClip .getName ()));
157- logger .log (Logger . Level .DEBUG , String .format ("Added anim clip %s" , animClip .getName ()));
136+ logger .log (Level .FINE , String .format ("Added anim clip %s" , animClip .getName ()));
158137 }
159138 }
160139
@@ -166,22 +145,22 @@ private Confirmation createSelector() {
166145 spatials [index ++] = spatialAssetDataObject .getName ();
167146 }
168147 spatials [index ++] = CANCEL ;
169- final NotifyDescriptor .Confirmation message =
170- new NotifyDescriptor .Confirmation (
148+ final NotifyDescriptor .Confirmation message
149+ = new NotifyDescriptor .Confirmation (
171150 "Select spatial to copy animations to." );
172151 message .setOptions (spatials );
173-
152+
174153 DialogDisplayer .getDefault ().notify (message );
175-
154+
176155 return message ;
177156 }
178-
157+
179158 private AnimComposer findAnimComposer (Spatial spatial , AnimComposer animComposer ) {
180159 if (spatial .getControl (AnimComposer .class ) != null ) {
181- animComposer = spatial .getControl (AnimComposer .class );
160+ return spatial .getControl (AnimComposer .class );
182161 }
183162 if (animComposer == null && spatial instanceof Node node ) {
184- for (Spatial child : node .getChildren ()) {
163+ for (Spatial child : node .getChildren ()) {
185164 animComposer = findAnimComposer (child , animComposer );
186165 if (animComposer != null ) {
187166 return animComposer ;
@@ -190,19 +169,65 @@ private AnimComposer findAnimComposer(Spatial spatial, AnimComposer animComposer
190169 }
191170 return animComposer ;
192171 }
193-
172+
194173 private SpatialAssetDataObject findSpatial (String selected ) {
195174 for (Iterator <SpatialAssetDataObject > it = spatials .iterator (); it .hasNext ();) {
196175 final SpatialAssetDataObject spatialAssetDataObject = it .next ();
197- if (spatialAssetDataObject .getName ().equals (selected )) {
176+ if (spatialAssetDataObject .getName ().equals (selected )) {
198177 return spatialAssetDataObject ;
199178 }
200179 }
201180 NotifyDescriptor .Message msg = new NotifyDescriptor .Message (
202- "Main asset to copy to not found. This is likely an issue with the tool itself." ,
203- NotifyDescriptor .ERROR_MESSAGE );
181+ "Main asset to copy to not found. This is likely an issue with the tool itself." ,
182+ NotifyDescriptor .ERROR_MESSAGE );
204183 DialogDisplayer .getDefault ().notify (msg );
205184 return null ;
206185 }
207-
186+
187+ private AnimClip retargetClip (AnimClip sourceClip , Armature targetArmature , String clipName ) {
188+
189+ // Create a list to hold the new tracks
190+ SafeArrayList <AnimTrack > tracks = new SafeArrayList <>(AnimTrack .class );
191+
192+ // Iterate through each track in the source clip
193+ for (AnimTrack animTrack : sourceClip .getTracks ()) {
194+
195+ TransformTrack sourceTrack = (TransformTrack ) animTrack ;
196+ String targetName = getTargetName (sourceTrack .getTarget ());
197+ if (targetName == null ) {
198+ logger .log (Level .SEVERE , String .format ("Unsupported target for: %s. Skipping." , animTrack ));
199+ continue ;
200+ }
201+ Joint target = targetArmature .getJoint (targetName );
202+
203+ if (target != null ) {
204+ // Clone the source track and set the new target joint
205+ TransformTrack newTrack = sourceTrack .jmeClone ();
206+ newTrack .setTarget (target );
207+ tracks .add (newTrack );
208+
209+ } else {
210+ logger .log (Level .WARNING , "Joint not found in the target Armature: {0}" , targetName );
211+ }
212+ }
213+
214+ // Create a new animation clip with the specified name and set its tracks
215+ AnimClip newClip = new AnimClip (clipName );
216+ newClip .setTracks (tracks .getArray ());
217+
218+ logger .log (Level .INFO , "Created new AnimClip {0} with {1} tracks out of {2} from the source clip" ,
219+ new Object []{clipName , tracks .size (), sourceClip .getTracks ().length });
220+ return newClip ;
221+ }
222+
223+ private String getTargetName (final HasLocalTransform target ) {
224+ if (target instanceof Node node ) {
225+ return node .getName ();
226+ }
227+ if (target instanceof Joint joint ) {
228+ return joint .getName ();
229+ }
230+ return null ;
231+ }
232+
208233}
0 commit comments