• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 package com.badlogic.gdx.tools.flame;
17 
18 import java.awt.BorderLayout;
19 import java.awt.Component;
20 import java.awt.EventQueue;
21 import java.awt.FileDialog;
22 import java.awt.Graphics;
23 import java.awt.GridBagConstraints;
24 import java.awt.GridBagLayout;
25 import java.awt.Insets;
26 import java.awt.event.ActionEvent;
27 import java.awt.event.ActionListener;
28 import java.awt.event.WindowAdapter;
29 import java.awt.event.WindowEvent;
30 import java.io.File;
31 import java.io.Writer;
32 
33 import javax.swing.BorderFactory;
34 import javax.swing.DefaultComboBoxModel;
35 import javax.swing.JButton;
36 import javax.swing.JComboBox;
37 import javax.swing.JComponent;
38 import javax.swing.JFrame;
39 import javax.swing.JOptionPane;
40 import javax.swing.JPanel;
41 import javax.swing.JScrollPane;
42 import javax.swing.JSplitPane;
43 import javax.swing.UIManager;
44 import javax.swing.UIManager.LookAndFeelInfo;
45 import javax.swing.border.CompoundBorder;
46 import javax.swing.plaf.basic.BasicSplitPaneUI;
47 
48 import com.badlogic.gdx.ApplicationListener;
49 import com.badlogic.gdx.Gdx;
50 import com.badlogic.gdx.InputMultiplexer;
51 import com.badlogic.gdx.assets.AssetDescriptor;
52 import com.badlogic.gdx.assets.AssetErrorListener;
53 import com.badlogic.gdx.assets.AssetLoaderParameters;
54 import com.badlogic.gdx.assets.AssetManager;
55 import com.badlogic.gdx.assets.loaders.AssetLoader;
56 import com.badlogic.gdx.assets.loaders.resolvers.AbsoluteFileHandleResolver;
57 import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
58 import com.badlogic.gdx.backends.lwjgl.LwjglCanvas;
59 import com.badlogic.gdx.files.FileHandle;
60 import com.badlogic.gdx.graphics.Color;
61 import com.badlogic.gdx.graphics.GL20;
62 import com.badlogic.gdx.graphics.PerspectiveCamera;
63 import com.badlogic.gdx.graphics.Texture;
64 import com.badlogic.gdx.graphics.VertexAttributes.Usage;
65 import com.badlogic.gdx.graphics.g2d.TextureAtlas;
66 import com.badlogic.gdx.graphics.g3d.Environment;
67 import com.badlogic.gdx.graphics.g3d.Material;
68 import com.badlogic.gdx.graphics.g3d.Model;
69 import com.badlogic.gdx.graphics.g3d.ModelBatch;
70 import com.badlogic.gdx.graphics.g3d.ModelInstance;
71 import com.badlogic.gdx.graphics.g3d.attributes.BlendingAttribute;
72 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
73 import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
74 import com.badlogic.gdx.graphics.g3d.particles.ParticleController;
75 import com.badlogic.gdx.graphics.g3d.particles.ParticleEffect;
76 import com.badlogic.gdx.graphics.g3d.particles.ParticleEffectLoader;
77 import com.badlogic.gdx.graphics.g3d.particles.ParticleEffectLoader.ParticleEffectSaveParameter;
78 import com.badlogic.gdx.graphics.g3d.particles.ParticleSystem;
79 import com.badlogic.gdx.graphics.g3d.particles.batches.BillboardParticleBatch;
80 import com.badlogic.gdx.graphics.g3d.particles.batches.ModelInstanceParticleBatch;
81 import com.badlogic.gdx.graphics.g3d.particles.batches.ParticleBatch;
82 import com.badlogic.gdx.graphics.g3d.particles.batches.PointSpriteParticleBatch;
83 import com.badlogic.gdx.graphics.g3d.particles.emitters.Emitter;
84 import com.badlogic.gdx.graphics.g3d.particles.emitters.RegularEmitter;
85 import com.badlogic.gdx.graphics.g3d.particles.influencers.ColorInfluencer;
86 import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsInfluencer;
87 import com.badlogic.gdx.graphics.g3d.particles.influencers.Influencer;
88 import com.badlogic.gdx.graphics.g3d.particles.influencers.ModelInfluencer;
89 import com.badlogic.gdx.graphics.g3d.particles.influencers.ParticleControllerFinalizerInfluencer;
90 import com.badlogic.gdx.graphics.g3d.particles.influencers.ParticleControllerInfluencer;
91 import com.badlogic.gdx.graphics.g3d.particles.influencers.RegionInfluencer;
92 import com.badlogic.gdx.graphics.g3d.particles.influencers.ScaleInfluencer;
93 import com.badlogic.gdx.graphics.g3d.particles.influencers.SpawnInfluencer;
94 import com.badlogic.gdx.graphics.g3d.particles.renderers.BillboardRenderer;
95 import com.badlogic.gdx.graphics.g3d.particles.renderers.ModelInstanceRenderer;
96 import com.badlogic.gdx.graphics.g3d.particles.renderers.ParticleControllerControllerRenderer;
97 import com.badlogic.gdx.graphics.g3d.particles.renderers.PointSpriteRenderer;
98 import com.badlogic.gdx.graphics.g3d.particles.values.GradientColorValue;
99 import com.badlogic.gdx.graphics.g3d.particles.values.NumericValue;
100 import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
101 import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
102 import com.badlogic.gdx.math.MathUtils;
103 import com.badlogic.gdx.math.RandomXS128;
104 import com.badlogic.gdx.scenes.scene2d.InputEvent;
105 import com.badlogic.gdx.scenes.scene2d.Stage;
106 import com.badlogic.gdx.scenes.scene2d.ui.Label;
107 import com.badlogic.gdx.scenes.scene2d.ui.Skin;
108 import com.badlogic.gdx.scenes.scene2d.ui.Table;
109 import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
110 import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
111 import com.badlogic.gdx.utils.Array;
112 import com.badlogic.gdx.utils.StreamUtils;
113 import com.badlogic.gdx.utils.StringBuilder;
114 
115 /** @author Inferno */
116 public class FlameMain extends JFrame implements AssetErrorListener {
117 	public static final String 	DEFAULT_FONT = "default.fnt",
118 											DEFAULT_BILLBOARD_PARTICLE = "pre_particle.png",
119 											DEFAULT_MODEL_PARTICLE = "monkey.g3db",
120 											//DEFAULT_PFX = "default.pfx",
121 											DEFAULT_TEMPLATE_PFX = "defaultTemplate.pfx",
122 											DEFAULT_SKIN = "uiskin.json";
123 
124 	public static final int EVT_ASSET_RELOADED = 0;
125 
126 	static class ControllerData {
127 		public boolean enabled = true;
128 		public ParticleController controller;
ControllerData(ParticleController emitter)129 		public ControllerData (ParticleController emitter) {
130 			controller = emitter;
131 		}
132 	}
133 
134 	private static class InfluencerWrapper<T>{
135 		String string;
136 		Class<Influencer> type;
InfluencerWrapper(String string, Class<Influencer> type)137 		public InfluencerWrapper(String string, Class<Influencer> type){
138 			this.string = string;
139 			this.type = type;
140 		}
141 
142 		@Override
toString()143 		public String toString () {
144 			return string;
145 		}
146 	}
147 
148 	public enum ControllerType{
149 		Billboard( "Billboard", new InfluencerWrapper[]{
150 			new InfluencerWrapper("Single Color", ColorInfluencer.Single.class),
151 			new InfluencerWrapper("Random Color", ColorInfluencer.Random.class),
152 			new InfluencerWrapper("Single Region", RegionInfluencer.Single.class),
153 			new InfluencerWrapper("Random Region", RegionInfluencer.Random.class),
154 			new InfluencerWrapper("Animated Region", RegionInfluencer.Animated.class),
155 			new InfluencerWrapper("Scale", ScaleInfluencer.class),
156 			new InfluencerWrapper("Spawn", SpawnInfluencer.class),
157 			new InfluencerWrapper("Dynamics", DynamicsInfluencer.class)}),
158 		PointSprite("Point Sprite", new InfluencerWrapper[]{
159 				new InfluencerWrapper("Single Color", ColorInfluencer.Single.class),
160 				new InfluencerWrapper("Random Color", ColorInfluencer.Random.class),
161 				new InfluencerWrapper("Single Region", RegionInfluencer.Single.class),
162 				new InfluencerWrapper("Random Region", RegionInfluencer.Random.class),
163 				new InfluencerWrapper("Animated Region", RegionInfluencer.Animated.class),
164 				new InfluencerWrapper("Scale", ScaleInfluencer.class),
165 				new InfluencerWrapper("Spawn", SpawnInfluencer.class),
166 				new InfluencerWrapper("Dynamics", DynamicsInfluencer.class)}),
167 		ModelInstance( "Model Instance", new InfluencerWrapper[]{
168 					new InfluencerWrapper("Single Color", ColorInfluencer.Single.class),
169 					new InfluencerWrapper("Random Color", ColorInfluencer.Random.class),
170 					new InfluencerWrapper("Single Model", ModelInfluencer.Single.class),
171 					new InfluencerWrapper("Random Model", ModelInfluencer.Random.class),
172 					new InfluencerWrapper("Scale", ScaleInfluencer.class),
173 					new InfluencerWrapper("Spawn", SpawnInfluencer.class),
174 					new InfluencerWrapper("Dynamics", DynamicsInfluencer.class)}),
175 		ParticleController("Particle Controller", new InfluencerWrapper[]{
176 						new InfluencerWrapper("Single Particle Controller", ParticleControllerInfluencer.Single.class),
177 						new InfluencerWrapper("Random Particle Controller", ParticleControllerInfluencer.Random.class),
178 						new InfluencerWrapper("Scale", ScaleInfluencer.class),
179 						new InfluencerWrapper("Spawn", SpawnInfluencer.class),
180 						new InfluencerWrapper("Dynamics", DynamicsInfluencer.class)});
181 
182 		public String desc;
183 		public InfluencerWrapper[] wrappers;
ControllerType(String desc, InfluencerWrapper[] wrappers)184 		private ControllerType(String desc, InfluencerWrapper[] wrappers){
185 			this.desc = desc;
186 			this.wrappers = wrappers;
187 		}
188 	}
189 
190 	LwjglCanvas lwjglCanvas;
191 	JPanel controllerPropertiesPanel;
192 	JPanel editorPropertiesPanel;
193 	EffectPanel effectPanel;
194 	JSplitPane splitPane;
195 	NumericValue fovValue;
196 	NumericValue deltaMultiplier;
197 	GradientColorValue backgroundColor;
198 	AppRenderer renderer;
199 	AssetManager assetManager;
200 	JComboBox influencerBox;
201 
202 	private ParticleEffect effect;
203 	/** READ only */
204 	public Array<ControllerData> controllersData;
205 	ParticleSystem particleSystem;
206 
FlameMain()207 	public FlameMain () {
208 		super("Flame");
209 		MathUtils.random = new RandomXS128();
210 		particleSystem = ParticleSystem.get();
211 		effect = new ParticleEffect();
212 		particleSystem.add(effect);
213 		assetManager = new AssetManager();
214 		assetManager.setErrorListener(this);
215 		assetManager.setLoader(ParticleEffect.class, new ParticleEffectLoader(new InternalFileHandleResolver()));
216 		controllersData = new Array<ControllerData>();
217 
218 		lwjglCanvas = new LwjglCanvas(renderer = new AppRenderer());
219 		addWindowListener(new WindowAdapter() {
220 			public void windowClosed (WindowEvent event) {
221 				//System.exit(0);
222 				Gdx.app.exit();
223 			}
224 		});
225 
226 		initializeComponents();
227 
228 		setSize(1280, 950);
229 		setLocationRelativeTo(null);
230 		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
231 		setVisible(true);
232 	}
233 
getControllerType()234 	public ControllerType getControllerType () {
235 		ParticleController controller = getEmitter();
236 		ControllerType type = null;
237 		if(controller.renderer instanceof BillboardRenderer)
238 			type = ControllerType.Billboard;
239 		else if(controller.renderer instanceof PointSpriteRenderer)
240 			type = ControllerType.PointSprite;
241 		else if(controller.renderer instanceof ModelInstanceRenderer)
242 			type = ControllerType.ModelInstance;
243 		else if(controller.renderer instanceof ParticleControllerControllerRenderer)
244 			type = ControllerType.ParticleController;
245 		return type;
246 	}
247 
reloadRows()248 	void reloadRows () {
249 		EventQueue.invokeLater(new Runnable() {
250 			public void run () {
251 
252 				//Ensure no listener is left watching for events
253 				EventManager.get().clear();
254 
255 				//Clear
256 				editorPropertiesPanel.removeAll();
257 				influencerBox.removeAllItems();
258 				controllerPropertiesPanel.removeAll();
259 
260 				//Editor props
261 				addRow(editorPropertiesPanel, new NumericPanel(FlameMain.this, fovValue, "Field of View", ""));
262 				addRow(editorPropertiesPanel, new NumericPanel(FlameMain.this, deltaMultiplier, "Delta multiplier", ""));
263 				addRow(editorPropertiesPanel, new GradientPanel(FlameMain.this,backgroundColor, "Background color", "", true));
264 				addRow(editorPropertiesPanel, new DrawPanel(FlameMain.this, "Draw", ""));
265 				addRow(editorPropertiesPanel, new TextureLoaderPanel(FlameMain.this, "Texture", ""));
266 				addRow(editorPropertiesPanel, new BillboardBatchPanel(FlameMain.this, renderer.billboardBatch), 1, 1);
267 				editorPropertiesPanel.repaint();
268 
269 				//Controller props
270 				ParticleController controller = getEmitter();
271 				if(controller != null){
272 					//Reload available influencers
273 					DefaultComboBoxModel model = (DefaultComboBoxModel)influencerBox.getModel();
274 					ControllerType type = getControllerType();
275 					if(type != null){
276 						for(Object value : type.wrappers)
277 							model.addElement(value);
278 					}
279 					JPanel panel = null;
280 					addRow(controllerPropertiesPanel, getPanel(controller.emitter));
281 					for(int i=0, c = controller.influencers.size; i < c; ++i){
282 						Influencer influencer = (Influencer)controller.influencers.get(i);
283 						panel = getPanel(influencer);
284 						if(panel != null)
285 							addRow(controllerPropertiesPanel, panel, 1, i == c-1 ? 1 : 0);
286 					}
287 					for (Component component : controllerPropertiesPanel.getComponents())
288 						if (component instanceof EditorPanel)
289 							((EditorPanel)component).update(FlameMain.this);
290 				}
291 				controllerPropertiesPanel.repaint();
292 			}
293 		});
294 	}
295 
getPanel(Emitter emitter)296 	protected JPanel getPanel (Emitter emitter) {
297 		if(emitter instanceof RegularEmitter){
298 			return new RegularEmitterPanel(this, (RegularEmitter) emitter);
299 		}
300 		return null;
301 	}
302 
getPanel(Influencer influencer)303 	protected JPanel getPanel (Influencer influencer) {
304 		if(influencer instanceof ColorInfluencer.Single){
305 			return new ColorInfluencerPanel(this, (ColorInfluencer.Single) influencer);
306 		}
307 		if(influencer instanceof ColorInfluencer.Random){
308 			return new InfluencerPanel<ColorInfluencer.Random>(this, (ColorInfluencer.Random) influencer,
309 				"Random Color Influencer", "Assign a random color to the particles") {};
310 		}
311 		else if(influencer instanceof ScaleInfluencer){
312 			return  new ScaleInfluencerPanel(this, (ScaleInfluencer)influencer);
313 		}
314 		else if(influencer instanceof SpawnInfluencer){
315 			return  new SpawnInfluencerPanel(this, (SpawnInfluencer)influencer);
316 		}
317 		else if(influencer instanceof DynamicsInfluencer){
318 			return  new DynamicsInfluencerPanel(this, (DynamicsInfluencer)influencer);
319 		}
320 		else if(influencer instanceof ModelInfluencer){
321 			boolean single = influencer instanceof ModelInfluencer.Single;
322 			String name = single ? "Model Single Influencer" : "Model Random Influencer";
323 			return  new ModelInfluencerPanel(this, (ModelInfluencer)influencer, single, name, "Defines what model will be used for the particles");
324 		}
325 		else if(influencer instanceof ParticleControllerInfluencer){
326 			boolean single = influencer instanceof ParticleControllerInfluencer.Single;
327 			String name = single ? "Particle Controller Single Influencer" : "Particle Controller Random Influencer";
328 			return  new ParticleControllerInfluencerPanel(this, (ParticleControllerInfluencer)influencer, single, name, "Defines what controller will be used for the particles");
329 		}
330 		else if(influencer instanceof RegionInfluencer.Single){
331 			return  new RegionInfluencerPanel(this, "Billboard Single Region Influencer",
332 				"Assign the chosen region to the particles", (RegionInfluencer.Single)influencer);
333 		}
334 		else if(influencer instanceof RegionInfluencer.Animated){
335 			return  new RegionInfluencerPanel(this, "Billboard Animated Region Influencer",
336 				"Animates the region of the particles", (RegionInfluencer.Animated)influencer);
337 		}
338 		else if(influencer instanceof RegionInfluencer.Random){
339 			return  new RegionInfluencerPanel(this, "Billboard Random Region Influencer",
340 				"Assigns a randomly picked (among those selected) region to the particles", (RegionInfluencer.Random)influencer);
341 		}
342 		else if(influencer instanceof ParticleControllerFinalizerInfluencer){
343 			return new InfluencerPanel<ParticleControllerFinalizerInfluencer>(this, (ParticleControllerFinalizerInfluencer) influencer,
344 				"ParticleControllerFinalizer Influencer", "This is required when dealing with a controller of controllers, it will update the controller assigned to each particle, it MUST be the last influencer always.",
345 				true, false) {};
346 		}
347 
348 		return null;
349 	}
350 
getPanel(ParticleBatch renderer)351 	protected JPanel getPanel (ParticleBatch renderer) {
352 		if(renderer instanceof PointSpriteParticleBatch){
353 			return new EmptyPanel(this, "Point Sprite Batch", "It renders particles as point sprites.");
354 		}
355 		if(renderer instanceof BillboardParticleBatch){
356 			return new BillboardBatchPanel(this, (BillboardParticleBatch) renderer);
357 		}
358 		else if(renderer instanceof ModelInstanceParticleBatch){
359 			return new EmptyPanel(this, "Model Instance Batch", "It renders particles as model instances.");
360 		}
361 
362 		return null;
363 	}
364 
addRow(JPanel panel, JPanel row)365 	void addRow(JPanel panel, JPanel row) {
366 		addRow(panel, row, 1, 0);
367 	}
368 
addRow(JPanel panel, JPanel row, float wx, float wy)369 	void addRow(JPanel panel, JPanel row, float wx, float wy) {
370 		row.setBorder(BorderFactory.createMatteBorder(0, 0, 2, 0, java.awt.Color.black));
371 		panel.add(row, new GridBagConstraints(0, -1, 1, 1, wx, wy, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL,
372 			new Insets(0, 0, 0, 0), 0, 0));
373 	}
374 
setVisible(String name, boolean visible)375 	public void setVisible (String name, boolean visible) {
376 		for (Component component : controllerPropertiesPanel.getComponents())
377 			if (component instanceof EditorPanel && ((EditorPanel)component).getName().equals(name)) component.setVisible(visible);
378 	}
379 
rebuildActiveControllers()380 	private void rebuildActiveControllers () {
381 		//rebuild list
382 		Array<ParticleController> effectControllers = effect.getControllers();
383 		effectControllers.clear();
384 		for(ControllerData controllerData : controllersData){
385 			if(controllerData.enabled)
386 				effectControllers.add(controllerData.controller);
387 		}
388 		//System.out.println("rebuilding active controllers");
389 
390 		effect.init();
391 		effect.start();
392 	}
393 
getEmitter()394 	public ParticleController getEmitter () {
395 		return effectPanel.editIndex >=0 ? controllersData.get(effectPanel.editIndex).controller : null;
396 	}
397 
addEmitter(ParticleController emitter)398 	public void addEmitter (ParticleController emitter) {
399 		controllersData.add(new ControllerData(emitter));
400 		rebuildActiveControllers();
401 	}
402 
removeEmitter(int row)403 	public void removeEmitter (int row) {
404 		controllersData.removeIndex(row).controller.dispose();
405 		rebuildActiveControllers();
406 	}
407 
setEnabled(int emitterIndex, boolean enabled)408 	public void setEnabled (int emitterIndex, boolean enabled) {
409 		ControllerData data = controllersData.get(emitterIndex);
410 		data.enabled = enabled;
411 		rebuildActiveControllers();
412 	}
413 
isEnabled(int emitterIndex)414 	public boolean isEnabled (int emitterIndex) {
415 		return controllersData.get(emitterIndex).enabled;
416 	}
417 
initializeComponents()418 	private void initializeComponents () {
419 		splitPane = new JSplitPane();
420 		splitPane.setUI(new BasicSplitPaneUI() {
421 			public void paint (Graphics g, JComponent jc) {
422 			}
423 		});
424 		splitPane.setDividerSize(4);
425 		getContentPane().add(splitPane, BorderLayout.CENTER);
426 		{
427 			JSplitPane rightSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
428 			rightSplit.setUI(new BasicSplitPaneUI() {
429 				public void paint (Graphics g, JComponent jc) {
430 				}
431 			});
432 			rightSplit.setDividerSize(4);
433 			splitPane.add(rightSplit, JSplitPane.RIGHT);
434 
435 			{
436 				JPanel propertiesPanel = new JPanel(new GridBagLayout());
437 				rightSplit.add(propertiesPanel, JSplitPane.TOP);
438 				propertiesPanel.setBorder(new CompoundBorder(BorderFactory.createEmptyBorder(3, 0, 6, 6), BorderFactory
439 					.createTitledBorder("Editor Properties")));
440 				{
441 					JScrollPane scroll = new JScrollPane();
442 					propertiesPanel.add(scroll, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTH,
443 						GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
444 					scroll.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
445 					{
446 						editorPropertiesPanel = new JPanel(new GridBagLayout());
447 						scroll.setViewportView(editorPropertiesPanel);
448 						scroll.getVerticalScrollBar().setUnitIncrement(70);
449 					}
450 				}
451 			}
452 
453 			{
454 				JSplitPane rightSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
455 				rightSplitPane.setUI(new BasicSplitPaneUI() {
456 					public void paint (Graphics g, JComponent jc) {
457 					}
458 				});
459 				rightSplitPane.setDividerSize(4);
460 				rightSplitPane.setDividerLocation(100);
461 				rightSplit.add(rightSplitPane, JSplitPane.BOTTOM);
462 
463 				JPanel propertiesPanel = new JPanel(new GridBagLayout());
464 				rightSplitPane.add(propertiesPanel, JSplitPane.TOP);
465 				propertiesPanel.setBorder(new CompoundBorder(BorderFactory.createEmptyBorder(3, 0, 6, 6), BorderFactory
466 					.createTitledBorder("Influencers")));
467 				{
468 					JScrollPane scroll = new JScrollPane();
469 					propertiesPanel.add(scroll, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTH,
470 						GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
471 					scroll.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
472 					{
473 						JPanel influencersPanel = new JPanel(new GridBagLayout());
474 						influencerBox = new JComboBox(new DefaultComboBoxModel());
475 						JButton addInfluencerButton = new JButton("Add");
476 						addInfluencerButton.addActionListener(new ActionListener() {
477 							@Override
478 							public void actionPerformed (ActionEvent e) {
479 								InfluencerWrapper wrapper = (InfluencerWrapper)influencerBox.getSelectedItem();
480 								ParticleController controller = getEmitter();
481 								if(controller != null)
482 									addInfluencer(wrapper.type, controller);
483 
484 							}
485 						});
486 						influencersPanel.add(influencerBox, new GridBagConstraints(0, 0, 1, 1, 0, 1, GridBagConstraints.NORTHWEST,
487 							GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
488 						influencersPanel.add(addInfluencerButton, new GridBagConstraints(1, 0, 1, 1, 1, 1, GridBagConstraints.NORTHWEST,
489 							GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
490 						scroll.setViewportView(influencersPanel);
491 						scroll.getVerticalScrollBar().setUnitIncrement(70);
492 					}
493 				}
494 
495 
496 				propertiesPanel = new JPanel(new GridBagLayout());
497 				rightSplitPane.add(propertiesPanel, JSplitPane.BOTTOM);
498 				propertiesPanel.setBorder(new CompoundBorder(BorderFactory.createEmptyBorder(3, 0, 6, 6), BorderFactory
499 					.createTitledBorder("Particle Controller Components")));
500 				{
501 					JScrollPane scroll = new JScrollPane();
502 					propertiesPanel.add(scroll, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTH,
503 						GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
504 					scroll.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
505 					{
506 						controllerPropertiesPanel = new JPanel(new GridBagLayout());
507 						scroll.setViewportView(controllerPropertiesPanel);
508 						scroll.getVerticalScrollBar().setUnitIncrement(70);
509 					}
510 				}
511 			}
512 
513 			rightSplit.setDividerLocation(250);
514 
515 		}
516 		{
517 			JSplitPane leftSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
518 			leftSplit.setUI(new BasicSplitPaneUI() {
519 				public void paint (Graphics g, JComponent jc) {
520 				}
521 			});
522 			leftSplit.setDividerSize(4);
523 			splitPane.add(leftSplit, JSplitPane.LEFT);
524 			{
525 				JPanel spacer = new JPanel(new BorderLayout());
526 				leftSplit.add(spacer, JSplitPane.TOP);
527 				spacer.add(lwjglCanvas.getCanvas());
528 				spacer.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 4));
529 			}
530 			{
531 				JPanel emittersPanel = new JPanel(new BorderLayout());
532 				leftSplit.add(emittersPanel, JSplitPane.BOTTOM);
533 				emittersPanel.setBorder(new CompoundBorder(BorderFactory.createEmptyBorder(0, 6, 6, 0), BorderFactory
534 					.createTitledBorder("Particle Controllers")));
535 				{
536 					effectPanel = new EffectPanel(this);
537 					emittersPanel.add(effectPanel);
538 				}
539 			}
540 			leftSplit.setDividerLocation(625);
541 		}
542 		splitPane.setDividerLocation(500);
543 	}
544 
addInfluencer(Class<Influencer> type, ParticleController controller)545 	protected void addInfluencer (Class<Influencer> type, ParticleController controller) {
546 		if(controller.findInfluencer(type) != null) return;
547 
548 		try {
549 			controller.end();
550 
551 			Influencer newInfluencer = type.newInstance();
552 			boolean replaced = false;
553 			if(ColorInfluencer.class.isAssignableFrom(type)){
554 				 replaced = controller.replaceInfluencer(ColorInfluencer.class, (ColorInfluencer)newInfluencer);
555 			}
556 			else if(RegionInfluencer.class.isAssignableFrom(type)){
557 				 replaced = controller.replaceInfluencer(RegionInfluencer.class, (RegionInfluencer)newInfluencer);
558 			}
559 			else if(ModelInfluencer.class.isAssignableFrom(type)){
560 				ModelInfluencer newModelInfluencer = (ModelInfluencer) newInfluencer;
561 				ModelInfluencer currentInfluencer = (ModelInfluencer)controller.findInfluencer(ModelInfluencer.class);
562 				if(currentInfluencer != null){
563 						newModelInfluencer.models.add(currentInfluencer.models.first());
564 				}
565 				replaced = controller.replaceInfluencer(ModelInfluencer.class, (ModelInfluencer)newInfluencer);
566 			}
567 			else if(ParticleControllerInfluencer.class.isAssignableFrom(type)){
568 				ParticleControllerInfluencer newModelInfluencer = (ParticleControllerInfluencer) newInfluencer;
569 				ParticleControllerInfluencer currentInfluencer = (ParticleControllerInfluencer)controller.findInfluencer(ParticleControllerInfluencer.class);
570 				if(currentInfluencer != null){
571 						newModelInfluencer.templates.add(currentInfluencer.templates.first());
572 				}
573 				replaced = controller.replaceInfluencer(ParticleControllerInfluencer.class, (ParticleControllerInfluencer)newInfluencer);
574 			}
575 
576 			if(!replaced){
577 				if(getControllerType() != ControllerType.ParticleController)
578 					controller.influencers.add(newInfluencer);
579 				else{
580 					Influencer finalizer = controller.influencers.pop();
581 					controller.influencers.add(newInfluencer);
582 					controller.influencers.add(finalizer);
583 				}
584 			}
585 
586 			controller.init();
587 			effect.start();
588 			reloadRows();
589 		} catch (Exception e1) {
590 			e1.printStackTrace();
591 		}
592 	}
593 
canAddInfluencer(Class influencerType, ParticleController controller)594 	protected boolean canAddInfluencer (Class influencerType, ParticleController controller) {
595 		boolean hasSameInfluencer = controller.findInfluencer(influencerType) != null;
596 		if(!hasSameInfluencer){
597 			if( 	(ColorInfluencer.Single.class.isAssignableFrom(influencerType) && controller.findInfluencer(ColorInfluencer.Random.class) != null) ||
598 					(ColorInfluencer.Random.class.isAssignableFrom(influencerType) && controller.findInfluencer(ColorInfluencer.Single.class) != null) ){
599 				return false;
600 			}
601 
602 			if(RegionInfluencer.class.isAssignableFrom(influencerType)){
603 				return controller.findInfluencer(RegionInfluencer.class) == null;
604 			}
605 			else if(ModelInfluencer.class.isAssignableFrom(influencerType)){
606 				return controller.findInfluencer(ModelInfluencer.class) == null;
607 			}
608 			else if(ParticleControllerInfluencer.class.isAssignableFrom(influencerType)){
609 				return controller.findInfluencer(ParticleControllerInfluencer.class) == null;
610 			}
611 		}
612 		return !hasSameInfluencer;
613 	}
614 
615 	class AppRenderer implements ApplicationListener {
616 		//Stats
617 		private float maxActiveTimer;
618 		private int maxActive, lastMaxActive;
619 		boolean isUpdate = true;
620 
621 		//Controls
622 		private CameraInputController cameraInputController;
623 
624 		//UI
625 		private Stage ui;
626 		TextButton playPauseButton;
627 		private Label fpsLabel, pointCountLabel, billboardCountLabel, modelInstanceCountLabel, maxLabel;
628 		StringBuilder stringBuilder;
629 
630 		//Render
631 		public PerspectiveCamera worldCamera;
632 		private boolean isDrawXYZ, isDrawXZPlane, isDrawXYPlane;
633 		private Array<Model> models;
634 		private ModelInstance xyzInstance, xzPlaneInstance, xyPlaneInstance;
635 		private Environment environment;
636 		private ModelBatch modelBatch;
637 		PointSpriteParticleBatch pointSpriteBatch;
638 		BillboardParticleBatch billboardBatch;
639 		ModelInstanceParticleBatch modelInstanceParticleBatch;
640 
create()641 		public void create () {
642 			if (ui != null) return;
643 			int w = Gdx.graphics.getWidth(), h = Gdx.graphics.getHeight();
644 			modelBatch = new ModelBatch();
645 			environment = new Environment();
646 			environment.add(new DirectionalLight().set(Color.WHITE, 0,0,-1));
647 
648 			worldCamera = new PerspectiveCamera(67, w, h);
649 			worldCamera.position.set(10, 10, 10);
650 			worldCamera.lookAt(0,0,0);
651 			worldCamera.near = 0.1f;
652 			worldCamera.far = 300f;
653 			worldCamera.update();
654 
655 			cameraInputController = new CameraInputController(worldCamera);
656 
657 			//Batches
658 			pointSpriteBatch = new PointSpriteParticleBatch();
659 			pointSpriteBatch.setCamera(worldCamera);
660 
661 			billboardBatch = new BillboardParticleBatch();
662 			billboardBatch.setCamera(worldCamera);
663 			modelInstanceParticleBatch = new ModelInstanceParticleBatch();
664 
665 			particleSystem.add(billboardBatch);
666 			particleSystem.add(pointSpriteBatch);
667 			particleSystem.add(modelInstanceParticleBatch);
668 
669 			fovValue = new NumericValue();
670 			fovValue.setValue(67);
671 			fovValue.setActive(true);
672 
673 			deltaMultiplier = new NumericValue();
674 			deltaMultiplier.setValue(1.0f);
675 			deltaMultiplier.setActive(true);
676 
677 			backgroundColor = new GradientColorValue();
678 			Color color = Color.valueOf("878787");
679 			backgroundColor.setColors(new float[] { color.r, color.g, color.b});
680 
681 			models = new Array<Model>();
682 			ModelBuilder builder = new ModelBuilder();
683 			Model 	xyzModel = builder.createXYZCoordinates(10, new Material(), Usage.Position|Usage.ColorPacked),
684 				planeModel = builder.createLineGrid(10, 10, 1, 1, new Material(ColorAttribute.createDiffuse(Color.WHITE)), Usage.Position);
685 			models.add(xyzModel);
686 			models.add(planeModel);
687 			xyzInstance = new ModelInstance(xyzModel);
688 			xzPlaneInstance = new ModelInstance(planeModel);
689 			xyPlaneInstance = new ModelInstance(planeModel);
690 			xyPlaneInstance.transform.rotate(1f, 0f, 0f, 90f);
691 
692 			setDrawXYZ(true);
693 			setDrawXZPlane(true);
694 
695 
696 			//Load default resources
697 			ParticleEffectLoader.ParticleEffectLoadParameter params = new ParticleEffectLoader.ParticleEffectLoadParameter(particleSystem.getBatches());
698 			assetManager.load(DEFAULT_BILLBOARD_PARTICLE, Texture.class);
699 			assetManager.load(DEFAULT_MODEL_PARTICLE, Model.class);
700 			assetManager.load(DEFAULT_SKIN, Skin.class);
701 			assetManager.load(DEFAULT_TEMPLATE_PFX, ParticleEffect.class, params);
702 
703 			assetManager.finishLoading();
704 			assetManager.setLoader(ParticleEffect.class, new ParticleEffectLoader(new AbsoluteFileHandleResolver()));
705 			assetManager.get(DEFAULT_MODEL_PARTICLE, Model.class).materials.get(0).set(new BlendingAttribute(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA, 1));
706 
707 			//Ui
708 			stringBuilder = new StringBuilder();
709 			Skin skin = assetManager.get(DEFAULT_SKIN, Skin.class);
710 			ui = new Stage();
711 			fpsLabel = new Label("", skin);
712 			pointCountLabel = new Label("", skin);
713 			billboardCountLabel = new Label("", skin);
714 			modelInstanceCountLabel = new Label("", skin);
715 
716 			maxLabel = new Label("", skin);
717 			playPauseButton = new TextButton("Pause", skin);
718 			playPauseButton.addListener(new ClickListener(){
719 				@Override
720 				public void clicked (InputEvent event, float x, float y) {
721 					isUpdate = !isUpdate;
722 					playPauseButton.setText(isUpdate ? "Pause" : "Play");
723 				}
724 			});
725 			Table table = new Table(skin);
726 			table.setFillParent(true);
727 			table.pad(5);
728 			table.add(fpsLabel).expandX().left().row();
729 			table.add(pointCountLabel).expandX().left().row();
730 			table.add(billboardCountLabel).expandX().left().row();
731 			table.add(modelInstanceCountLabel).expandX().left().row();
732 			table.add(maxLabel).expandX().left().row();
733 			table.add(playPauseButton).expand().bottom().left().row();
734 			ui.addActor(table);
735 
736 			setTexture((Texture)assetManager.get(DEFAULT_BILLBOARD_PARTICLE));
737 			effectPanel.createDefaultEmitter(ControllerType.Billboard, true, true);
738 		}
739 
740 		@Override
resize(int width, int height)741 		public void resize (int width, int height) {
742 			Gdx.input.setInputProcessor(new InputMultiplexer(ui, cameraInputController));
743 			Gdx.gl.glViewport(0, 0, width, height);
744 
745 			worldCamera.viewportWidth = width;
746 			worldCamera.viewportHeight = height;
747 			worldCamera.update();
748 			ui.getViewport().setWorldSize(width, height);
749 			ui.getViewport().update(width, height, true);
750 		}
751 
render()752 		public void render () {
753 			//float delta = Math.max(0, Gdx.graphics.getDeltaTime() * deltaMultiplier.getValue());
754 			update();
755 			renderWorld();
756 		}
757 
update()758 		private void update () {
759 			worldCamera.fieldOfView = fovValue.getValue();
760 			worldCamera.update();
761 			cameraInputController.update();
762 			if(isUpdate){
763 				particleSystem.update();
764 				//Update ui
765 				stringBuilder.delete(0, stringBuilder.length);
766 				stringBuilder.append("Point Sprites : ").append(pointSpriteBatch.getBufferedCount());
767 				pointCountLabel.setText(stringBuilder);
768 				stringBuilder.delete(0, stringBuilder.length);
769 				stringBuilder.append("Billboards : ").append(billboardBatch.getBufferedCount());
770 				billboardCountLabel.setText(stringBuilder);
771 				stringBuilder.delete(0, stringBuilder.length);
772 				stringBuilder.append("Model Instances : ").append(modelInstanceParticleBatch.getBufferedCount());
773 				modelInstanceCountLabel.setText(stringBuilder);
774 			}
775 			stringBuilder.delete(0, stringBuilder.length);
776 			stringBuilder.append("FPS : ").append(Gdx.graphics.getFramesPerSecond());
777 			fpsLabel.setText(stringBuilder);
778 			ui.act(Gdx.graphics.getDeltaTime());
779 		}
780 
renderWorld()781 		private void renderWorld () {
782 			float[] colors = backgroundColor.getColors();
783 			Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
784 			Gdx.gl.glClearColor(colors[0], colors[1], colors[2], 0);
785 			modelBatch.begin(worldCamera);
786 			if(isDrawXYZ) modelBatch.render(xyzInstance);
787 			if(isDrawXZPlane) modelBatch.render(xzPlaneInstance);
788 			if(isDrawXYPlane) modelBatch.render(xyPlaneInstance);
789 			particleSystem.begin();
790 			particleSystem.draw();
791 			particleSystem.end();
792 
793 			//Draw
794 			modelBatch.render(particleSystem, environment);
795 			modelBatch.end();
796 			ui.draw();
797 		}
798 
799 		@Override
dispose()800 		public void dispose () {}
801 
802 		@Override
pause()803 		public void pause () {}
804 
805 		@Override
resume()806 		public void resume () {}
807 
setDrawXYZ(boolean isDraw)808 		public void setDrawXYZ(boolean isDraw)
809 		{
810 			isDrawXYZ = isDraw;
811 		}
812 
IsDrawXYZ()813 		public boolean IsDrawXYZ()
814 		{
815 			return isDrawXYZ;
816 		}
817 
setDrawXZPlane(boolean isDraw)818 		public void setDrawXZPlane(boolean isDraw)
819 		{
820 			isDrawXZPlane = isDraw;
821 		}
822 
IsDrawXZPlane()823 		public boolean IsDrawXZPlane()
824 		{
825 			return isDrawXZPlane;
826 		}
827 
setDrawXYPlane(boolean isDraw)828 		public void setDrawXYPlane(boolean isDraw)
829 		{
830 			isDrawXYPlane = isDraw;
831 		}
832 
IsDrawXYPlane()833 		public boolean IsDrawXYPlane()
834 		{
835 			return isDrawXYPlane;
836 		}
837 	}
838 
main(String[] args)839 	public static void main (String[] args) {
840 		for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
841 			if ("Nimbus".equals(info.getName())) {
842 				try {
843 					UIManager.setLookAndFeel(info.getClassName());
844 				} catch (Throwable ignored) {
845 				}
846 				break;
847 			}
848 		}
849 		EventQueue.invokeLater(new Runnable() {
850 			public void run () {
851 				new FlameMain();
852 			}
853 		});
854 	}
855 
getRenderer()856 	public AppRenderer getRenderer() {
857 		return renderer;
858 	}
859 
860 	String lastDir;
showFileLoadDialog()861 	public File showFileLoadDialog () {
862 		return showFileDialog("Open", FileDialog.LOAD);
863 	}
864 
showFileSaveDialog()865 	public File showFileSaveDialog () {
866 		return showFileDialog("Save", FileDialog.SAVE);
867 	}
868 
showFileDialog(String title, int mode )869 	private File showFileDialog (String title, int mode ) {
870 		FileDialog dialog = new FileDialog(this, title, mode);
871 		if (lastDir != null) dialog.setDirectory(lastDir);
872 		dialog.setVisible(true);
873 		final String file = dialog.getFile();
874 		final String dir = dialog.getDirectory();
875 		if (dir == null || file == null || file.trim().length() == 0)
876 			return null;
877 		lastDir = dir;
878 		return new File(dir, file);
879 	}
880 
881 	@Override
error(AssetDescriptor asset, Throwable throwable)882 	public void error (AssetDescriptor asset, Throwable throwable) {
883 		throwable.printStackTrace();
884 	}
885 
getPointSpriteBatch()886 	public PointSpriteParticleBatch getPointSpriteBatch () {
887 		return renderer.pointSpriteBatch;
888 	}
889 
getBillboardBatch()890 	public BillboardParticleBatch getBillboardBatch () {
891 		return renderer.billboardBatch;
892 	}
893 
getModelInstanceParticleBatch()894 	public ModelInstanceParticleBatch getModelInstanceParticleBatch () {
895 		return renderer.modelInstanceParticleBatch;
896 	}
897 
setAtlas(TextureAtlas atlas)898 	public void setAtlas(TextureAtlas atlas){
899 		//currentAtlas = atlas;
900 		setTexture(atlas.getTextures().first());
901 	}
902 
setTexture(Texture texture)903 	public void setTexture(Texture texture){
904 		renderer.billboardBatch.setTexture(texture);
905 		renderer.pointSpriteBatch.setTexture(texture);
906 	}
907 
getTexture()908 	public Texture getTexture(){
909 		return renderer.billboardBatch.getTexture();
910 	}
911 
getAtlas(Texture texture)912 	public TextureAtlas getAtlas(Texture texture){
913 		Array<TextureAtlas> atlases = assetManager.getAll(TextureAtlas.class, new Array<TextureAtlas>());
914 		for(TextureAtlas atlas : atlases){
915 			if(atlas.getTextures().contains(texture))
916 				return atlas;
917 		}
918 		return null;
919 	}
920 
getAtlas()921 	public TextureAtlas getAtlas(){
922 		return getAtlas(renderer.billboardBatch.getTexture());
923 	}
924 
isUsingDefaultTexture()925 	public boolean isUsingDefaultTexture () {
926 		return renderer.billboardBatch.getTexture() == assetManager.get(DEFAULT_BILLBOARD_PARTICLE, Texture.class);
927 	}
928 
getParticleEffects(Array<ParticleController> controllers, Array<ParticleEffect> out)929 	public Array<ParticleEffect> getParticleEffects (Array<ParticleController> controllers, Array<ParticleEffect> out) {
930 		out.clear();
931 		assetManager.getAll(ParticleEffect.class, out);
932 		for(int i=0; i < out.size;){
933 			ParticleEffect effect = out.get(i);
934 			Array<ParticleController> effectControllers = effect.getControllers();
935 			boolean remove = true;
936 			for(ParticleController controller : controllers){
937 				if(effectControllers.contains(controller, true)){
938 					remove = false;
939 					break;
940 				}
941 			}
942 
943 			if(remove){
944 				out.removeIndex(i);
945 				continue;
946 			}
947 
948 			++i;
949 		}
950 
951 		return out;
952 	}
953 
saveEffect(File file)954 	public void saveEffect (File file) {
955 		Writer fileWriter = null;
956 		try {
957 			ParticleEffectLoader loader = (ParticleEffectLoader)assetManager.getLoader(ParticleEffect.class);
958 			loader.save(effect, new ParticleEffectSaveParameter(new FileHandle(file.getAbsolutePath()), assetManager, particleSystem.getBatches()));
959 		} catch (Exception ex) {
960 			System.out.println("Error saving effect: " + file.getAbsolutePath());
961 			ex.printStackTrace();
962 			JOptionPane.showMessageDialog(this, "Error saving effect.");
963 		} finally {
964 			StreamUtils.closeQuietly(fileWriter);
965 		}
966 	}
967 
openEffect(File file, boolean replaceCurrentWorkspace)968 	public ParticleEffect openEffect (File file, boolean replaceCurrentWorkspace) {
969 		try {
970 			ParticleEffect loadedEffect = load(file.getAbsolutePath(), ParticleEffect.class, null,
971 				new ParticleEffectLoader.ParticleEffectLoadParameter(particleSystem.getBatches()));
972 			loadedEffect = loadedEffect.copy();
973 			loadedEffect.init();
974 			if(replaceCurrentWorkspace){
975 				effect = loadedEffect;
976 				controllersData.clear();
977 				particleSystem.removeAll();
978 				particleSystem.add(effect);
979 				for(ParticleController controller : effect.getControllers())
980 					controllersData.add(new ControllerData(controller));
981 				rebuildActiveControllers();
982 			}
983 			reloadRows();
984 			return loadedEffect;
985 		} catch (Exception ex) {
986 			System.out.println("Error loading effect: " + file.getAbsolutePath());
987 			ex.printStackTrace();
988 			JOptionPane.showMessageDialog(this, "Error opening effect.");
989 		}
990 		return null;
991 	}
992 
load(String resource, Class<T> type, AssetLoader loader, AssetLoaderParameters<T> params)993 	public <T> T load (String resource, Class<T> type, AssetLoader loader, AssetLoaderParameters<T> params) {
994 		String resolvedPath = new String(resource).replaceAll("\\\\", "/");
995 		boolean exist = assetManager.isLoaded(resolvedPath, type);
996 		T oldAsset = null;
997 		if(exist){
998 			oldAsset = assetManager.get(resolvedPath, type);
999 			for(int i=assetManager.getReferenceCount(resolvedPath); i > 0; --i)
1000 				assetManager.unload(resolvedPath);
1001 		}
1002 
1003 		AssetLoader<T, AssetLoaderParameters<T>> currentLoader = assetManager.getLoader(type);
1004 		if(loader != null)
1005 			assetManager.setLoader(type, loader);
1006 
1007 		assetManager.load(resource, type, params);
1008 		assetManager.finishLoading();
1009 		T res = assetManager.get(resolvedPath);
1010 		if(currentLoader != null)
1011 			assetManager.setLoader(type, currentLoader);
1012 
1013 		if(exist)
1014 			EventManager.get().fire(EVT_ASSET_RELOADED, new Object[]{oldAsset, res});
1015 
1016 		return res;
1017 	}
1018 
restart()1019 	public void restart () {
1020 		effect.init();
1021 		effect.start();
1022 	}
1023 }
1024