diff --git a/engine/src/test/jme3test/blender/ManualBlenderTester.java b/engine/src/test/jme3test/blender/ManualBlenderTester.java index 776d26688..81f9103b2 100644 --- a/engine/src/test/jme3test/blender/ManualBlenderTester.java +++ b/engine/src/test/jme3test/blender/ManualBlenderTester.java @@ -40,8 +40,8 @@ import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; +import jme3test.blender.config.BlenderKeyConfiguration; import jme3test.blender.config.ConfigDialog; -import jme3test.blender.config.ConfigDialog.BlenderKeyConfiguration; import jme3test.blender.scene.Pivot; import com.jme3.animation.AnimControl; @@ -92,7 +92,9 @@ public class ManualBlenderTester extends SimpleApplication { //running the application ConfigDialog configDialog = new ConfigDialog("./src/test-data/Blender"); BlenderKeyConfiguration bkk = configDialog.getBlenderKeyConfiguration(); - new ManualBlenderTester(bkk, debugMode).start(); + if(bkk != null) { + new ManualBlenderTester(bkk, debugMode).start(); + } } /** diff --git a/engine/src/test/jme3test/blender/config/AbstractConfigDialog.java b/engine/src/test/jme3test/blender/config/AbstractConfigDialog.java index 27d7975aa..39a507e87 100644 --- a/engine/src/test/jme3test/blender/config/AbstractConfigDialog.java +++ b/engine/src/test/jme3test/blender/config/AbstractConfigDialog.java @@ -84,7 +84,7 @@ public abstract class AbstractConfigDialog extends JDialog { } this.setLayout(new BorderLayout()); this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - this.setLocationRelativeTo(null); + this.setLocationByPlatform(true); this.add(this.prepareBlenderFilesAndLogLevelPanel(), BorderLayout.WEST); this.add(this.prepareFilePropertiesPanel(), BorderLayout.CENTER); @@ -157,12 +157,7 @@ public abstract class AbstractConfigDialog extends JDialog { jPanelAnimations.setLayout(new BorderLayout()); jPanelAnimations.add(new JLabel("Animations"), BorderLayout.NORTH); - jTableAnimations = new JTable(); - jTableAnimations.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION); - jTableAnimations.setModel(new DefaultTableModel(new Object[]{"Object", "Name", "Start frame", "Stop frame"}, 0)); - for (int i = 0; i < jTableAnimations.getColumnModel().getColumnCount(); ++i) { - jTableAnimations.getColumnModel().getColumn(i).setCellEditor(new BlenderTableCellEditor()); - } + jTableAnimations = new AnimationsTable(); JScrollPane jScrollPaneAnimations = new JScrollPane(jTableAnimations); jTableAnimations.setFillsViewportHeight(true); diff --git a/engine/src/test/jme3test/blender/config/AnimationsTable.java b/engine/src/test/jme3test/blender/config/AnimationsTable.java new file mode 100644 index 000000000..4022b0495 --- /dev/null +++ b/engine/src/test/jme3test/blender/config/AnimationsTable.java @@ -0,0 +1,185 @@ +package jme3test.blender.config; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.Vector; + +import javax.swing.DefaultCellEditor; +import javax.swing.DefaultListSelectionModel; +import javax.swing.JFrame; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; + +/** + * Table for displaying and managing animations to import. + * @author Marcin Roguski (Kaelthas) + */ +public class AnimationsTable extends JTable { + private static final long serialVersionUID = 1978778634957586330L; + + /** + * Constructor. Creates default model. Applies basic settings. + */ + public AnimationsTable() { + super(new AnimationsTableModel(new Object[] {null, "Object name", "Animation name", "Start", "Stop"})); + this.getTableHeader().setReorderingAllowed(false); + this.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION); + CellRenderer cellRenderer = new CellRenderer(); + this.setDefaultRenderer(Object.class, cellRenderer); + this.getModel().addTableModelListener(cellRenderer); + this.getColumnModel().getColumn(0).setCellEditor(new RadioButtonCellEditor()); + } + + /** + * This class represents the model where all cells are editable and animations are always grouped + * by object. + * @author Marcin Roguski (Kaelthas) + */ + private static final class AnimationsTableModel extends DefaultTableModel { + private static final long serialVersionUID = 8285912542455513806L; + + /** + * Constructor. Creates table with given columns and no rows. + * @param columnNames the names of the columns + */ + public AnimationsTableModel(Object[] columnNames) { + super(columnNames, 0); + } + + @Override + public boolean isCellEditable(int row, int column) { + return true; + } + + @Override + @SuppressWarnings("rawtypes") + public void addRow(Vector rowData) { + String objectName = (String) rowData.get(1); + int index = 0; + boolean objectFound = false; + for(int i=0;i objectIndex = new HashMap(); + /** The other color for the group (the first one is WHITE. */ + private Color color = new Color(240, 240, 240); + /** Radio button to display row selection. */ + private JRadioButton jRadioButton = new JRadioButton(); + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, + int column) { + Object objectName = table.getModel().getValueAt(row, 1); + Component component; + if(column == 0) { + jRadioButton.setSelected((Boolean)value); + component = jRadioButton; + } else { + component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + } + + Integer index = objectIndex.get(objectName); + if(index != null) { + if(index.intValue() % 2 == 1) { + component.setBackground(color); + } else { + component.setBackground(Color.WHITE); + } + } + return component; + } + + @Override + public void tableChanged(TableModelEvent evt) { + if(evt.getType() == TableModelEvent.INSERT) { + DefaultTableModel model = (DefaultTableModel)evt.getSource(); + for(int i=evt.getFirstRow();i<=evt.getLastRow();++i) { + String objectName = (String) model.getValueAt(i, 1); + if(!objectIndex.containsKey(objectName)) { + objectIndex.put(objectName, Integer.valueOf(objectIndex.size())); + } + } + } + } + } + + /** + * This editor is used for the first column to allow the edition of row selection. + * @author Marcin Roguski (Kaelthas) + */ + private static final class RadioButtonCellEditor extends DefaultCellEditor { + private static final long serialVersionUID = 7697027333456874718L; + /** Component that allows editing. */ + private JRadioButton jRadioButton = new JRadioButton(); + + /** + * Constructor. + */ + public RadioButtonCellEditor() { + super(new JTextField()); + } + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + jRadioButton.setSelected((Boolean)value); + return jRadioButton; + } + + @Override + public Object getCellEditorValue() { + return Boolean.valueOf(jRadioButton.isSelected()); + } + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + frame.setLayout(new BorderLayout()); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + + Random random = new Random(System.currentTimeMillis()); + AnimationsTable table = new AnimationsTable(); + int objectsCount = random.nextInt(5) + 1; + for(int i=1;i<=objectsCount;++i) { + int animsCount = random.nextInt(7) + 1; + for(int j=1;j<=animsCount;++j) { + ((DefaultTableModel)table.getModel()).addRow(new Object[] {Boolean.FALSE, "Obiekt" + i, "Animacja" + j, "Start" + j, "Stop" + j}); + } + } + ((DefaultTableModel)table.getModel()).addRow(new Object[] {Boolean.FALSE, "Obiekt1", "xxx", "xxx", "xxx"}); + JScrollPane jScrollPane = new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + frame.add(jScrollPane, BorderLayout.CENTER); + frame.pack(); + frame.setVisible(true); + } +} diff --git a/engine/src/test/jme3test/blender/config/BlenderKeyConfiguration.java b/engine/src/test/jme3test/blender/config/BlenderKeyConfiguration.java new file mode 100644 index 000000000..ba75170de --- /dev/null +++ b/engine/src/test/jme3test/blender/config/BlenderKeyConfiguration.java @@ -0,0 +1,127 @@ +package jme3test.blender.config; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.logging.Level; + +import com.jme3.asset.BlenderKey; +import com.jme3.asset.ModelKey; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.export.Savable; + +/** + * This class holds the configuration for all the files. + * It can be saved and loaded using jme mechanisms. + * @author Marcin Roguski (Kaelthas) + */ +public class BlenderKeyConfiguration implements Savable { + /** + * The key is a directory of blender_version_folder. + * The value is the map between the blender file name and its blender key. + */ + /*package*/ Map> blenderKeys; + /** List of selected animations for each object. An object can have one active animations.*/ + /*package*/ Map> selectedAnimations; + /** The last version of blender opened. */ + /*package*/ String lastVersionUsed; + /** The last used blender key for each blender version. */ + /*package*/ Map lastUsedKey; + /** Last used log level. */ + /*package*/ Level logLevel; + /** This variable tells if the model or blender loader is used. */ + /*package*/ boolean useModelKey; + + /** + * Constructor that creates new empty configuration for every blender file. + * Also used for jme serialization. + */ + public BlenderKeyConfiguration() { + blenderKeys = new HashMap>(); + selectedAnimations = new HashMap>(); + lastUsedKey = new HashMap(); + logLevel = Level.INFO; + } + + /** + * This method returns the name of the last used asset folder. + * @return the name of the last used asset folder + */ + public String getLastVersionUsed() { + return lastVersionUsed; + } + + /** + * This method returns the log level of jme app. + * @return the log level of jme app + */ + public Level getLogLevel() { + return logLevel; + } + + /** + * This method returns the key that will be used during the test. + * @return the key that will be used during the test + */ + public ModelKey getKeyToUse() { + return useModelKey ? new ModelKey(lastUsedKey.get(lastVersionUsed).getName()) : lastUsedKey.get(lastVersionUsed); + } + + @Override + public void write(JmeExporter ex) throws IOException { + OutputCapsule oc = ex.getCapsule(this); + oc.write(blenderKeys.size(), "versions-count", 0); + int i=0; + for(Entry> entry : blenderKeys.entrySet()) { + oc.write(entry.getKey(), "key" + i, null); + oc.writeStringSavableMap(entry.getValue(), "value" + i++, null); + } + oc.writeStringSavableMap(lastUsedKey, "last-key", null); + if(selectedAnimations==null) { + oc.write(0, "selected-animations-count", 0); + } else { + i = 0; + oc.write(selectedAnimations.size(), "selected-animations-count", 0); + for(Entry> entry : selectedAnimations.entrySet()) { + oc.write(entry.getKey(), "animKey" + i, null); + oc.write(entry.getValue().toArray(new String[selectedAnimations.size()][]), "animVal" + i++, null); + } + } + oc.write(useModelKey, "use-model-key", false); + oc.write(logLevel == null ? null : logLevel.getName(), "log-level", Level.INFO.getName()); + oc.write(lastVersionUsed, "versionUsed", null); + } + + @Override + @SuppressWarnings("unchecked") + public void read(JmeImporter im) throws IOException { + InputCapsule ic = im.getCapsule(this); + int versionsCount = ic.readInt("versions-count", 0); + for(int i=0;i versionBlenderFiles = (Map) ic.readStringSavableMap("value" + i, null); + blenderKeys.put(versionName, versionBlenderFiles); + } + + int selectedAnimCount = ic.readInt("selected-animations-count", 0); + if(selectedAnimCount > 0) { + for(int i=0;i anims = Arrays.asList(selectedAnimations); + this.selectedAnimations.put(blenderKeyName, anims); + } + } + lastUsedKey = (Map) ic.readStringSavableMap("last-key", null); + useModelKey = ic.readBoolean("use-model-key", false); + String logLevelName = ic.readString("log-level", Level.INFO.getName()); + logLevel = logLevelName == null ? Level.INFO : Level.parse(logLevelName); + lastVersionUsed = ic.readString("versionUsed", null); + } +} \ No newline at end of file diff --git a/engine/src/test/jme3test/blender/config/ConfigDialog.java b/engine/src/test/jme3test/blender/config/ConfigDialog.java index 5aa2a705a..7358e9d8c 100644 --- a/engine/src/test/jme3test/blender/config/ConfigDialog.java +++ b/engine/src/test/jme3test/blender/config/ConfigDialog.java @@ -10,7 +10,9 @@ import java.io.FileFilter; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; @@ -26,11 +28,7 @@ import javax.swing.event.TableModelListener; import javax.swing.table.DefaultTableModel; import com.jme3.asset.BlenderKey; -import com.jme3.asset.ModelKey; -import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; import com.jme3.export.Savable; import com.jme3.export.binary.BinaryExporter; import com.jme3.export.binary.BinaryImporter; @@ -159,6 +157,26 @@ public class ConfigDialog extends AbstractConfigDialog { //enlisting the files in the list this.reloadFilesList(); + + //apply animations selection + DefaultTableModel model = (DefaultTableModel) jTableAnimations.getModel(); + BlenderKey blenderKey = blenderKeyConfiguration.lastUsedKey.get(blenderKeyConfiguration.lastVersionUsed); + if(blenderKey != null) { + String blenderKeyName = blenderKey.getName(); + List selectedAnimations = blenderKeyConfiguration.selectedAnimations.get(blenderKeyName); + if(selectedAnimations != null) { + for(String[] selectedAnimation : selectedAnimations) { + for(int i=0;i> animationEntry : animations.entrySet()) { for (Entry animDataEntry : animationEntry.getValue().entrySet()) { int[] frames = animDataEntry.getValue(); - animationsModel.addRow(new Object[]{animationEntry.getKey(), animDataEntry.getKey(), + animationsModel.addRow(new Object[]{Boolean.FALSE, animationEntry.getKey(), animDataEntry.getKey(), Integer.valueOf(frames[0]), Integer.valueOf(frames[1])}); } } @@ -243,6 +261,7 @@ public class ConfigDialog extends AbstractConfigDialog { * @param configuration the blender config to store */ private void storeConfig(BlenderKeyConfiguration configuration) { + configuration.selectedAnimations.clear(); BlenderKey blenderKey = configuration.lastUsedKey.get(configuration.lastVersionUsed); if (blenderKey != null) {//reading animations DefaultTableModel animationsTableModel = (DefaultTableModel) jTableAnimations.getModel(); @@ -250,15 +269,23 @@ public class ConfigDialog extends AbstractConfigDialog { blenderKey.getAnimations().clear(); } int animCounter = 0; + List selectedAnimations = new ArrayList(); for (int i = 0; i < animationsTableModel.getRowCount(); ++i) { - String objectName = (String) animationsTableModel.getValueAt(i, 0); - String animName = (String) animationsTableModel.getValueAt(i, 1); - Number startFrame = (Number) animationsTableModel.getValueAt(i, 2); - Number stopFrame = (Number) animationsTableModel.getValueAt(i, 3); + Boolean isSelected = (Boolean) animationsTableModel.getValueAt(i, 0); + String objectName = (String) animationsTableModel.getValueAt(i, 1); + String animName = (String) animationsTableModel.getValueAt(i, 2); + Number startFrame = (Number) animationsTableModel.getValueAt(i, 3); + Number stopFrame = (Number) animationsTableModel.getValueAt(i, 4); if (objectName != null && animName != null && startFrame.intValue() <= stopFrame.intValue()) { blenderKey.addAnimation(objectName, animName, startFrame.intValue(), stopFrame.intValue()); ++animCounter; } + if(isSelected) { + selectedAnimations.add(new String[] {objectName, animName}); + } + } + if(selectedAnimations.size() > 0) { + configuration.selectedAnimations.put(blenderKey.getName(), selectedAnimations); } if (animCounter < animationsTableModel.getRowCount()) { JOptionPane.showMessageDialog(ConfigDialog.this, "Some animations had errors!\nThey had not been added!", @@ -365,7 +392,7 @@ public class ConfigDialog extends AbstractConfigDialog { @Override public void actionPerformed(ActionEvent evt) { - ((DefaultTableModel) jTableAnimations.getModel()).addRow(new Object[]{"", "", Integer.valueOf(-1), Integer.valueOf(-1)}); + ((DefaultTableModel) jTableAnimations.getModel()).addRow(new Object[]{Boolean.FALSE, "", "", Integer.valueOf(1), Integer.valueOf(25)}); } }); jButtonRemoveAnimation.addActionListener(new ActionListener() { @@ -390,95 +417,10 @@ public class ConfigDialog extends AbstractConfigDialog { @Override public void actionPerformed(ActionEvent evt) { + ConfigDialog.this.storeConfig(blenderKeyConfiguration); + blenderKeyConfiguration = null; ConfigDialog.this.dispose(); } }); } - - /** - * This class holds the configuration for all the files. - * It can be saved and loaded using jme mechanisms. - * @author Marcin Roguski (Kaelthas) - */ - public static class BlenderKeyConfiguration implements Savable { - /** - * The key is a directory of blender_version_folder. - * The value is the map between the blender file name and its blender key. - */ - private Map> blenderKeys; - /** The last version of blender opened. */ - private String lastVersionUsed; - /** The last used blender key for each blender version. */ - private Map lastUsedKey; - /** Last used log level. */ - private Level logLevel; - /** This variable tells if the model or blender loader is used. */ - private boolean useModelKey; - - /** - * Constructor that creates new empty configuration for every blender file. - * Also used for jme serialization. - */ - public BlenderKeyConfiguration() { - blenderKeys = new HashMap>(); - lastUsedKey = new HashMap(); - logLevel = Level.INFO; - } - - /** - * This method returns the name of the last used asset folder. - * @return the name of the last used asset folder - */ - public String getLastVersionUsed() { - return lastVersionUsed; - } - - /** - * This method returns the log level of jme app. - * @return the log level of jme app - */ - public Level getLogLevel() { - return logLevel; - } - - /** - * This method returns the key that will be used during the test. - * @return the key that will be used during the test - */ - public ModelKey getKeyToUse() { - return useModelKey ? new ModelKey(lastUsedKey.get(lastVersionUsed).getName()) : lastUsedKey.get(lastVersionUsed); - } - - @Override - public void write(JmeExporter ex) throws IOException { - OutputCapsule oc = ex.getCapsule(this); - oc.write(blenderKeys.size(), "versions-count", 0); - int i=0; - for(Entry> entry : blenderKeys.entrySet()) { - oc.write(entry.getKey(), "key" + i, null); - oc.writeStringSavableMap(entry.getValue(), "value" + i++, null); - } - oc.writeStringSavableMap(lastUsedKey, "last-key", null); - oc.write(useModelKey, "use-model-key", false); - oc.write(logLevel == null ? null : logLevel.getName(), "log-level", Level.INFO.getName()); - oc.write(lastVersionUsed, "versionUsed", null); - } - - @Override - @SuppressWarnings("unchecked") - public void read(JmeImporter im) throws IOException { - InputCapsule ic = im.getCapsule(this); - int versionsCount = ic.readInt("versions-count", 0); - for(int i=0;i versionBlenderFiles = (Map) ic.readStringSavableMap("value" + i, null); - blenderKeys.put(versionName, versionBlenderFiles); - } - lastUsedKey = (Map) ic.readStringSavableMap("last-key", null); - useModelKey = ic.readBoolean("use-model-key", false); - String logLevelName = ic.readString("log-level", Level.INFO.getName()); - logLevel = logLevelName == null ? Level.INFO : Level.parse(logLevelName); - lastVersionUsed = ic.readString("versionUsed", null); - } - } }