Skip to content

Create AnimLayers in the SceneComposer and play animations #612

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 12 additions & 24 deletions jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/JmeControl.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2016 jMonkeyEngine
* Copyright (c) 2009-2024jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -37,7 +37,6 @@
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import javax.swing.Action;
import org.openide.actions.DeleteAction;
Expand Down Expand Up @@ -97,17 +96,12 @@ public void destroy() throws IOException {
final Spatial spat = getParentNode().getLookup().lookup(Spatial.class);
try {
fireSave(true);
SceneApplication.getApplication().enqueue(new Callable<Void>() {

public Void call() throws Exception {
spat.removeControl(control);
return null;
}
SceneApplication.getApplication().enqueue(() -> {
spat.removeControl(control);
return null;
}).get();
((AbstractSceneExplorerNode) getParentNode()).refresh(true);
} catch (InterruptedException ex) {
Exceptions.printStackTrace(ex);
} catch (ExecutionException ex) {
} catch (InterruptedException | ExecutionException ex) {
Exceptions.printStackTrace(ex);
}
}
Expand All @@ -116,9 +110,8 @@ public Void call() throws Exception {
@Override
public void fireSave(boolean modified) {
super.fireSave(true);
Node parent = getParentNode();
if (parent instanceof AbstractSceneExplorerNode) {
AbstractSceneExplorerNode par=(AbstractSceneExplorerNode)parent;
final Node parent = getParentNode();
if (parent instanceof AbstractSceneExplorerNode par) {
par.fireSave(modified);
}
}
Expand All @@ -128,23 +121,18 @@ public void fireSave(boolean modified) {
* This only works for extended AbstractControls!!
* Also see: {@link #isEnabled() }
* @param enabled Whether the Control should be enabled or disabled
* @return If we had success (false when an Exception occured or no {@link Control} assigned or not of type {@link AbstractControl} )
* @return If we had success (false when an Exception occurred or no {@link Control} assigned or not of type {@link AbstractControl} )
*/
public boolean setEnabled(final boolean enabled) {
if (!isEnableable())
return false;
try {
SceneApplication.getApplication().enqueue(new Callable<Void>() {
public Void call() throws Exception {
((AbstractControl)control).setEnabled(enabled);
return null;
}
SceneApplication.getApplication().enqueue(() -> {
((AbstractControl)control).setEnabled(enabled);
return null;
}).get();

} catch (InterruptedException ex) {
Exceptions.printStackTrace(ex);
return false;
} catch (ExecutionException ex) {
} catch (InterruptedException | ExecutionException ex) {
Exceptions.printStackTrace(ex);
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2020 jMonkeyEngine
* Copyright (c) 2009-2024 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -43,6 +43,8 @@
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.Action;
import javax.swing.SwingUtilities;
Expand All @@ -58,7 +60,7 @@

/**
* Visual representation of the AnimClip Class in the Scene Explorer
* @author MeFisto94
* @author MeFisto94, neph1
*/
@org.openide.util.lookup.ServiceProvider(service = SceneExplorerNode.class)
@SuppressWarnings({"unchecked", "rawtypes"})
Expand All @@ -67,7 +69,6 @@ public class JmeAnimClip extends AbstractSceneExplorerNode {
private AnimClip animClip;
private Image icon;
private JmeAnimComposer jmeControl;
private boolean playing = false;

public JmeAnimClip() {
}
Expand Down Expand Up @@ -103,7 +104,7 @@ public Image getOpenedIcon(int type) {
}

public void toggleIcon(boolean enabled) {
if (!playing) {
if (!enabled) {
icon = IconList.animation.getImage();
} else {
icon = IconList.animationPlay.getImage();
Expand All @@ -113,13 +114,12 @@ public void toggleIcon(boolean enabled) {

@Override
public Action getPreferredAction() {
return Actions.alwaysEnabled(new PlayAction(), "Play", "", false);
return Actions.alwaysEnabled(new PlayAction(AnimComposer.DEFAULT_LAYER), "Play", "", false);
}

private void play() {
playing = !playing;
toggleIcon(playing);
jmeControl.setAnimClip(this);
private void play(String layer) {
toggleIcon(true);
jmeControl.setAnimClip(layer, this);
}

@Override
Expand All @@ -142,13 +142,25 @@ public void setChanged() {

@Override
public Action[] getActions(boolean context) {
return new Action[]{Actions.alwaysEnabled(new PlayAction(), playing ? "Stop" : "Play", "", false),
SystemAction.get(RenameAction.class),
SystemAction.get(DeleteAction.class),
//Actions.alwaysEnabled(new EffectTrackWizardAction(jmeControl.getLookup().lookup(AnimComposer.class).getSpatial(), this), "Add Effect Track", "", false),
//Actions.alwaysEnabled(new AudioTrackWizardAction(jmeControl.getLookup().lookup(AnimComposer.class).getSpatial(), this), "Add Audio Track", "", false),
// @TODO: not working yet, Actions.alwaysEnabled(new ExtractAnimationAction(), "Extract sub-animation", "", true)
};
final AnimComposer control = jmeControl.getLookup().lookup(AnimComposer.class);
if(control == null) {
return new Action[]{
Actions.alwaysEnabled(new PlayAction(AnimComposer.DEFAULT_LAYER), jmeControl.getPlaying(AnimComposer.DEFAULT_LAYER) == this ? "Stop" : "Play", "", false),
SystemAction.get(RenameAction.class),
SystemAction.get(DeleteAction.class),
};
}
final String[] layers = control.getLayerNames().stream().toArray(String[] ::new);

List<Action> playActions = new ArrayList<>();

for(String layer: layers) {
playActions.add(Actions.alwaysEnabled(new PlayAction(layer), jmeControl.getPlaying(layer) == this ? "Stop " + layer : "Play " + layer, "", false));
}
playActions.add(SystemAction.get(RenameAction.class));
playActions.add(SystemAction.get(DeleteAction.class));
final Action[] actions = new Action[playActions.size()];
return playActions.toArray(actions);
}

@Override
Expand All @@ -157,19 +169,19 @@ public boolean canDestroy() {
}

public void stop() {
playing = false;
toggleIcon(playing);
toggleIcon(false);
}

@Override
public void destroy() throws IOException {
super.destroy();
final AnimComposer control = jmeControl.getLookup().lookup(AnimComposer.class);
if (playing) {

if (jmeControl.getPlaying(AnimComposer.DEFAULT_LAYER) == this) {
control.removeCurrentAction(AnimComposer.DEFAULT_LAYER);
jmeControl.setAnimClip(null);

jmeControl.setAnimClip(AnimComposer.DEFAULT_LAYER, null);
}

lookupContents.remove(JmeAnimClip.this.animClip);
lookupContents.remove(this);
SceneApplication.getApplication().enqueue( () -> {
Expand Down Expand Up @@ -202,6 +214,13 @@ public Node[] createNodes(Object key, DataObject key2, boolean cookie) {
}

class PlayAction implements ActionListener {

private final String layer;

public PlayAction(String layer) {
this.layer = layer;
}

@Override
public void actionPerformed(ActionEvent e) {
final AnimComposer control = jmeControl.getLookup().lookup(AnimComposer.class);
Expand All @@ -211,14 +230,14 @@ public void actionPerformed(ActionEvent e) {

try {
SceneApplication.getApplication().enqueue(() -> {
if (playing) { // Stop Playing
control.removeCurrentAction(AnimComposer.DEFAULT_LAYER);
jmeControl.setAnimClip(null);
if (jmeControl.getPlaying(layer) == JmeAnimClip.this) { // Stop Playing
control.removeCurrentAction(layer);
jmeControl.setAnimClip(layer, null);
return null;
} else {
control.setCurrentAction(animClip.getName());
control.setCurrentAction(animClip.getName(), layer);
java.awt.EventQueue.invokeLater(() -> {
play();
play(layer);
});
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,29 @@
import com.jme3.anim.AnimComposer;
import com.jme3.gde.core.icons.IconList;
import com.jme3.gde.core.scene.SceneApplication;
import com.jme3.gde.core.sceneexplorer.SceneExplorerTopComponent;
import com.jme3.gde.core.sceneexplorer.nodes.JmeControl;
import com.jme3.gde.core.sceneexplorer.nodes.SceneExplorerNode;
import com.jme3.gde.core.sceneexplorer.nodes.actions.ControlsPopup;
import com.jme3.gde.core.sceneexplorer.nodes.actions.animation.AnimClipProperty;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.swing.AbstractAction;
import javax.swing.Action;
import org.openide.actions.DeleteAction;
import org.openide.awt.Actions;
import org.openide.explorer.ExplorerManager;
import org.openide.loaders.DataObject;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.Exceptions;
import org.openide.util.actions.SystemAction;
import org.openide.windows.TopComponent;

/**
* Visual representation of the AnimComposer Class in the Scene Explorer
Expand All @@ -56,13 +66,13 @@
@SuppressWarnings({"unchecked", "rawtypes"})
public class JmeAnimComposer extends JmeControl {
private AnimComposer animComposer;
private JmeAnimClip playingAnimation = null;
private final Map<String, JmeAnimClip> playingAnimation = new HashMap<>();
private static Image smallImage = IconList.animControl.getImage();

public JmeAnimComposer() {
}

public JmeAnimComposer(AnimComposer animComposer, JmeAnimClipChildren children, DataObject obj) {
public JmeAnimComposer(AnimComposer animComposer, JmeAnimComposerChildren children, DataObject obj) {
super(children);
dataObject = obj;
children.setDataObject(dataObject);
Expand Down Expand Up @@ -100,15 +110,15 @@ protected Sheet createSheet() {
return sheet;
}

public boolean isPlaying() {
return playingAnimation != null;
public JmeAnimClip getPlaying(String layer) {
return playingAnimation.get(layer);
}

public void setAnimClip(JmeAnimClip anim) {
if (playingAnimation != null) {
playingAnimation.stop();
public void setAnimClip(String layer, JmeAnimClip anim) {
if (playingAnimation.get(layer) != null) {
playingAnimation.get(layer).stop();
}
playingAnimation = anim;
playingAnimation.put(layer, anim);
}

public float getGlobalSpeed() {
Expand All @@ -130,7 +140,8 @@ public void setGlobalSpeed(final float speed) {
public Action[] getActions(boolean context) {
return new Action[]{
new ControlsPopup(this),
SystemAction.get(DeleteAction.class)
new StopAllAction(),
SystemAction.get(DeleteAction.class),
};
}

Expand All @@ -146,13 +157,28 @@ public Class getExplorerNodeClass() {

@Override
public Node[] createNodes(Object key, DataObject key2, boolean cookie) {
JmeAnimClipChildren children = new JmeAnimClipChildren(this);
JmeAnimComposerChildren children = new JmeAnimComposerChildren(this);
return new Node[]{ new JmeAnimComposer((AnimComposer)key, children, key2)};
}

@Override
public void refresh(boolean immediate) {
((JmeAnimClipChildren) jmeChildren).refreshChildren(immediate);
((JmeAnimComposerChildren) jmeChildren).refreshChildren(immediate);
super.refresh(immediate);
}

private class StopAllAction extends AbstractAction {

public StopAllAction() {
super("Stop animations");
}

@Override
public void actionPerformed(ActionEvent e) {
for(JmeAnimClip layer: JmeAnimComposer.this.playingAnimation.values()) {
layer.stop();
}
}
}

}
Loading
Loading