1 package com.badlogic.gdx.tools.flame; 2 3 import java.awt.Dimension; 4 import java.awt.GridBagConstraints; 5 import java.awt.GridBagLayout; 6 import java.awt.Insets; 7 import java.awt.event.ActionEvent; 8 import java.awt.event.ActionListener; 9 10 import javax.swing.DefaultComboBoxModel; 11 import javax.swing.JButton; 12 import javax.swing.JComboBox; 13 import javax.swing.JPanel; 14 import javax.swing.JScrollPane; 15 import javax.swing.JTable; 16 import javax.swing.ListSelectionModel; 17 import javax.swing.event.ListSelectionEvent; 18 import javax.swing.event.ListSelectionListener; 19 import javax.swing.event.TableModelEvent; 20 import javax.swing.event.TableModelListener; 21 import javax.swing.table.DefaultTableModel; 22 23 import com.badlogic.gdx.graphics.g3d.particles.ParticleController; 24 import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsInfluencer; 25 import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier; 26 import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier.BrownianAcceleration; 27 import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier.CentripetalAcceleration; 28 import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier.FaceDirection; 29 import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier.PolarAcceleration; 30 import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier.Rotational3D; 31 import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier.TangentialAcceleration; 32 import com.badlogic.gdx.tools.flame.FlameMain.ControllerType; 33 import com.badlogic.gdx.utils.Array; 34 35 /** @author Inferno */ 36 public class DynamicsInfluencerPanel extends InfluencerPanel<DynamicsInfluencer> { 37 38 private static final String VEL_TYPE_ROTATIONAL_2D = "Angular Velocity 2D", 39 VEL_TYPE_ROTATIONAL_3D = "Angular Velocity 3D", 40 VEL_TYPE_CENTRIPETAL = "Centripetal", 41 VEL_TYPE_TANGENTIAL = "Tangential", 42 VEL_TYPE_POLAR = "Polar", 43 VEL_TYPE_BROWNIAN = "Brownian", 44 VEL_TYPE_FACE = "Face"; 45 46 protected class VelocityWrapper{ 47 public DynamicsModifier velocityValue; 48 public boolean isActive; 49 50 public VelocityWrapper(DynamicsModifier value, boolean isActive){ 51 this.velocityValue = value; 52 this.isActive = isActive; 53 } 54 } 55 56 JComboBox velocityBox; 57 JTable velocityTable; 58 DefaultTableModel velocityTableModel; 59 JPanel selectedVelocityPanel; 60 AngularVelocityPanel angularVelocityPanel; 61 StrengthVelocityPanel strengthVelocityPanel; 62 ParticleValuePanel emptyPanel; 63 Array<VelocityWrapper> velocities; 64 65 public DynamicsInfluencerPanel (FlameMain editor, DynamicsInfluencer influencer) { 66 super(editor, influencer, "Dynamics Influencer", 67 "Defines how the particles dynamics (acceleration, angular velocity)."); 68 velocities = new Array<VelocityWrapper>(); 69 setValue(value); 70 set(influencer); 71 } 72 73 private void set (DynamicsInfluencer influencer) { 74 //Clear 75 for (int i = velocityTableModel.getRowCount() - 1; i >= 0; i--) { 76 velocityTableModel.removeRow(i); 77 } 78 velocities.clear(); 79 80 //Add 81 for(int i=0, c = influencer.velocities.size; i < c; ++i){ 82 velocities.add(new VelocityWrapper((DynamicsModifier)influencer.velocities.items[i], true)); 83 velocityTableModel.addRow(new Object[] {"Velocity "+i, true}); 84 } 85 86 DefaultComboBoxModel model = (DefaultComboBoxModel) velocityBox.getModel(); 87 model.removeAllElements(); 88 for(Object velocityObject : getAvailableVelocities(editor.getControllerType())){ 89 model.addElement(velocityObject); 90 } 91 } 92 93 private Object[] getAvailableVelocities (ControllerType type) { 94 if(type == ControllerType.Billboard || type == ControllerType.PointSprite) { 95 return new String[]{ VEL_TYPE_ROTATIONAL_2D, VEL_TYPE_CENTRIPETAL, VEL_TYPE_TANGENTIAL, 96 VEL_TYPE_POLAR, VEL_TYPE_BROWNIAN}; 97 } 98 else if(type == ControllerType.ModelInstance|| type == ControllerType.ParticleController) { 99 return new String[]{ VEL_TYPE_ROTATIONAL_3D, VEL_TYPE_CENTRIPETAL, VEL_TYPE_TANGENTIAL, 100 VEL_TYPE_POLAR, VEL_TYPE_BROWNIAN, VEL_TYPE_FACE}; 101 } 102 return null; 103 } 104 105 protected void initializeComponents () { 106 super.initializeComponents(); 107 JPanel velocitiesPanel = new JPanel(); 108 velocitiesPanel.setLayout(new GridBagLayout()); 109 { 110 JPanel sideButtons = new JPanel(new GridBagLayout()); 111 velocitiesPanel.add(sideButtons, new GridBagConstraints(1, 0, 1, 1, 1, 1, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, 112 new Insets(0, 0, 0, 0), 0, 0)); 113 { 114 sideButtons.add(velocityBox = new JComboBox(new DefaultComboBoxModel()), 115 new GridBagConstraints(0, -1, 1, 1, 0, 0, GridBagConstraints.CENTER, 116 GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0)); 117 } 118 { 119 JButton newButton = new JButton("New"); 120 sideButtons.add(newButton, new GridBagConstraints(0, -1, 1, 1, 0, 0, GridBagConstraints.CENTER, 121 GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0)); 122 newButton.addActionListener(new ActionListener() { 123 public void actionPerformed (ActionEvent event) { 124 createVelocity(velocityBox.getSelectedItem()); 125 } 126 }); 127 } 128 { 129 JButton deleteButton = new JButton("Delete"); 130 sideButtons.add(deleteButton, new GridBagConstraints(0, -1, 1, 1, 0, 0, GridBagConstraints.CENTER, 131 GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0)); 132 deleteButton.addActionListener(new ActionListener() { 133 public void actionPerformed (ActionEvent event) { 134 deleteVelocity(); 135 } 136 }); 137 } 138 } 139 JScrollPane scroll = new JScrollPane(); 140 velocitiesPanel.add(scroll, new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 141 0, 0, 6), 0, 0)); 142 velocityTable = new JTable() { 143 public Class getColumnClass (int column) { 144 return column == 1 ? Boolean.class : super.getColumnClass(column); 145 } 146 147 @Override 148 public Dimension getPreferredScrollableViewportSize () { 149 Dimension dim = super.getPreferredScrollableViewportSize(); 150 dim.height = getPreferredSize().height; 151 return dim; 152 } 153 }; 154 velocityTable.getTableHeader().setReorderingAllowed(false); 155 velocityTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 156 scroll.setViewportView(velocityTable); 157 velocityTableModel = new DefaultTableModel(new String[0][0], new String[] {"Velocity", "Active"}); 158 velocityTable.setModel(velocityTableModel); 159 velocityTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { 160 public void valueChanged (ListSelectionEvent event) { 161 if (event.getValueIsAdjusting()) return; 162 velocitySelected(); 163 } 164 }); 165 velocityTableModel.addTableModelListener(new TableModelListener() { 166 public void tableChanged (TableModelEvent event) { 167 if (event.getColumn() != 1) return; 168 velocityChecked(event.getFirstRow(), (Boolean)velocityTable.getValueAt(event.getFirstRow(), 1)); 169 } 170 }); 171 172 //Velocity values 173 emptyPanel = new ParticleValuePanel(editor, "", "",true, false); 174 strengthVelocityPanel = new StrengthVelocityPanel(editor, null, "Life", "", ""); 175 angularVelocityPanel = new AngularVelocityPanel(editor, null, "Life", "", ""); 176 strengthVelocityPanel.setVisible(false); 177 angularVelocityPanel.setVisible(false); 178 emptyPanel.setVisible(false); 179 strengthVelocityPanel.setIsAlwayShown(true); 180 angularVelocityPanel.setIsAlwayShown(true); 181 emptyPanel.setIsAlwayShown(true); 182 emptyPanel.setValue(null); 183 184 //Assemble 185 int i=0; 186 addContent(i++, 0, velocitiesPanel); 187 addContent(i++, 0, strengthVelocityPanel); 188 addContent(i++, 0, angularVelocityPanel); 189 addContent(i++, 0, emptyPanel); 190 } 191 192 protected void velocityChecked (int index, boolean isChecked) { 193 ParticleController controller = editor.getEmitter(); 194 DynamicsInfluencer influencer = (DynamicsInfluencer)controller.findInfluencer(DynamicsInfluencer.class); 195 influencer.velocities.clear(); 196 velocities.get(index).isActive = isChecked; 197 for(VelocityWrapper wrapper : velocities){ 198 if(wrapper.isActive) 199 influencer.velocities.add(wrapper.velocityValue); 200 } 201 //Restart the effect and reinit the controller 202 editor.restart(); 203 } 204 205 protected void velocitySelected () { 206 //Show the velocity value panel 207 int index = velocityTable.getSelectedRow(); 208 if(index == -1) return; 209 210 DynamicsModifier velocityValue = velocities.get(index).velocityValue; 211 EditorPanel velocityPanel = getVelocityPanel(velocityValue); 212 213 //Show the selected velocity 214 if(selectedVelocityPanel != null && selectedVelocityPanel != velocityPanel) 215 selectedVelocityPanel.setVisible(false); 216 velocityPanel.setVisible(true); 217 velocityPanel.showContent(true); 218 selectedVelocityPanel = velocityPanel; 219 } 220 221 private EditorPanel getVelocityPanel (DynamicsModifier velocityValue) { 222 EditorPanel panel = null; 223 //Billboards 224 if(velocityValue instanceof DynamicsModifier.Rotational2D ){ 225 strengthVelocityPanel.setValue((DynamicsModifier.Strength) velocityValue); 226 strengthVelocityPanel.setName("Angular Velocity"); 227 strengthVelocityPanel.setDescription("The angular speed around the billboard facing direction, in degrees/sec ."); 228 panel = strengthVelocityPanel; 229 } 230 else if( velocityValue instanceof CentripetalAcceleration){ 231 strengthVelocityPanel.setValue((DynamicsModifier.CentripetalAcceleration) velocityValue); 232 strengthVelocityPanel.setName("Centripetal Acceleration"); 233 strengthVelocityPanel.setDescription("A directional acceleration, the direction is towards the origin (global), or towards the emitter position (local), in world units/sec2 ."); 234 panel = strengthVelocityPanel; 235 } 236 else if( velocityValue instanceof TangentialAcceleration){ 237 angularVelocityPanel.setValue((DynamicsModifier.Angular) velocityValue); 238 angularVelocityPanel.setName("Tangetial Velocity"); 239 angularVelocityPanel.setDescription("A directional acceleration (axis and magnitude), the final direction is the cross product between particle position and the axis, in world units/sec2 ."); 240 panel = angularVelocityPanel; 241 } 242 else if( velocityValue instanceof PolarAcceleration){ 243 angularVelocityPanel.setValue((DynamicsModifier.Angular) velocityValue); 244 angularVelocityPanel.setName("Polar Velocity"); 245 angularVelocityPanel.setDescription("A directional acceleration (axis and magnitude), in world units/sec2 ."); 246 panel = angularVelocityPanel; 247 } 248 else if( velocityValue instanceof BrownianAcceleration){ 249 strengthVelocityPanel.setValue((DynamicsModifier.Strength) velocityValue); 250 strengthVelocityPanel.setName("Brownian Velocity"); 251 strengthVelocityPanel.setDescription("A directional acceleration which has random direction at each update, in world units/sec2."); 252 panel = strengthVelocityPanel; 253 } 254 else if(velocityValue instanceof Rotational3D ){ 255 angularVelocityPanel.setValue((DynamicsModifier.Angular) velocityValue); 256 angularVelocityPanel.setName("Angular Velocity"); 257 angularVelocityPanel.setDescription("An angular velocity (axis and magnitude), in degree/sec2."); 258 panel = angularVelocityPanel; 259 } 260 else if( velocityValue instanceof FaceDirection){ 261 emptyPanel.setName("Face"); 262 emptyPanel.setDescription("Rotates the model to face its current velocity (Do not add any other angular velocity when using this)."); 263 panel = emptyPanel; 264 } 265 266 return panel; 267 } 268 269 private DynamicsModifier createVelocityValue (Object selectedItem) { 270 DynamicsModifier velocityValue = null; 271 if(selectedItem == VEL_TYPE_ROTATIONAL_2D) velocityValue = new DynamicsModifier.Rotational2D(); 272 else if(selectedItem == VEL_TYPE_ROTATIONAL_3D) velocityValue = new DynamicsModifier.Rotational3D(); 273 else if(selectedItem == VEL_TYPE_CENTRIPETAL) velocityValue = new DynamicsModifier.CentripetalAcceleration(); 274 else if(selectedItem == VEL_TYPE_TANGENTIAL) velocityValue = new DynamicsModifier.TangentialAcceleration(); 275 else if(selectedItem == VEL_TYPE_POLAR) velocityValue = new DynamicsModifier.PolarAcceleration(); 276 else if(selectedItem == VEL_TYPE_BROWNIAN) velocityValue = new DynamicsModifier.BrownianAcceleration(); 277 else if(selectedItem == VEL_TYPE_FACE) velocityValue = new DynamicsModifier.FaceDirection(); 278 return velocityValue; 279 } 280 281 282 protected void deleteVelocity () { 283 int row = velocityTable.getSelectedRow(); 284 if (row == -1) return; 285 286 //Remove the velocity from the table 287 ParticleController controller = editor.getEmitter(); 288 DynamicsInfluencer influencer = (DynamicsInfluencer)controller.findInfluencer(DynamicsInfluencer.class); 289 influencer.velocities.removeValue(velocities.removeIndex(row).velocityValue, true); 290 velocityTableModel.removeRow(row); 291 292 //Restart the effect and reinit the controller 293 editor.restart(); 294 295 selectedVelocityPanel.setVisible(false); 296 selectedVelocityPanel = null; 297 } 298 299 protected void createVelocity (Object selectedItem) { 300 //Add the velocity to the table and to the influencer 301 ParticleController controller = editor.getEmitter(); 302 DynamicsInfluencer influencer = (DynamicsInfluencer)controller.findInfluencer(DynamicsInfluencer.class); 303 VelocityWrapper wrapper = new VelocityWrapper(createVelocityValue(selectedItem), true); 304 velocities.add(wrapper); 305 influencer.velocities.add(wrapper.velocityValue); 306 int index = velocities.size-1; 307 velocityTableModel.addRow(new Object[] {"Velocity "+index, true}); 308 309 //Reinit 310 editor.restart(); 311 312 //Select new velocity 313 velocityTable.getSelectionModel().setSelectionInterval(index, index); 314 revalidate(); 315 repaint(); 316 } 317 318 } 319