Home | History | Annotate | Download | only in flame
      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;
    129 		public ControllerData (ParticleController emitter) {
    130 			controller = emitter;
    131 		}
    132 	}
    133 
    134 	private static class InfluencerWrapper<T>{
    135 		String string;
    136 		Class<Influencer> type;
    137 		public InfluencerWrapper(String string, Class<Influencer> type){
    138 			this.string = string;
    139 			this.type = type;
    140 		}
    141 
    142 		@Override
    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;
    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 
    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 
    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 
    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 
    296 	protected JPanel getPanel (Emitter emitter) {
    297 		if(emitter instanceof RegularEmitter){
    298 			return new RegularEmitterPanel(this, (RegularEmitter) emitter);
    299 		}
    300 		return null;
    301 	}
    302 
    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 
    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 
    365 	void addRow(JPanel panel, JPanel row) {
    366 		addRow(panel, row, 1, 0);
    367 	}
    368 
    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 
    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 
    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 
    394 	public ParticleController getEmitter () {
    395 		return effectPanel.editIndex >=0 ? controllersData.get(effectPanel.editIndex).controller : null;
    396 	}
    397 
    398 	public void addEmitter (ParticleController emitter) {
    399 		controllersData.add(new ControllerData(emitter));
    400 		rebuildActiveControllers();
    401 	}
    402 
    403 	public void removeEmitter (int row) {
    404 		controllersData.removeIndex(row).controller.dispose();
    405 		rebuildActiveControllers();
    406 	}
    407 
    408 	public void setEnabled (int emitterIndex, boolean enabled) {
    409 		ControllerData data = controllersData.get(emitterIndex);
    410 		data.enabled = enabled;
    411 		rebuildActiveControllers();
    412 	}
    413 
    414 	public boolean isEnabled (int emitterIndex) {
    415 		return controllersData.get(emitterIndex).enabled;
    416 	}
    417 
    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 
    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 
    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 
    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
    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 
    752 		public void render () {
    753 			//float delta = Math.max(0, Gdx.graphics.getDeltaTime() * deltaMultiplier.getValue());
    754 			update();
    755 			renderWorld();
    756 		}
    757 
    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 
    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
    800 		public void dispose () {}
    801 
    802 		@Override
    803 		public void pause () {}
    804 
    805 		@Override
    806 		public void resume () {}
    807 
    808 		public void setDrawXYZ(boolean isDraw)
    809 		{
    810 			isDrawXYZ = isDraw;
    811 		}
    812 
    813 		public boolean IsDrawXYZ()
    814 		{
    815 			return isDrawXYZ;
    816 		}
    817 
    818 		public void setDrawXZPlane(boolean isDraw)
    819 		{
    820 			isDrawXZPlane = isDraw;
    821 		}
    822 
    823 		public boolean IsDrawXZPlane()
    824 		{
    825 			return isDrawXZPlane;
    826 		}
    827 
    828 		public void setDrawXYPlane(boolean isDraw)
    829 		{
    830 			isDrawXYPlane = isDraw;
    831 		}
    832 
    833 		public boolean IsDrawXYPlane()
    834 		{
    835 			return isDrawXYPlane;
    836 		}
    837 	}
    838 
    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 
    856 	public AppRenderer getRenderer() {
    857 		return renderer;
    858 	}
    859 
    860 	String lastDir;
    861 	public File showFileLoadDialog () {
    862 		return showFileDialog("Open", FileDialog.LOAD);
    863 	}
    864 
    865 	public File showFileSaveDialog () {
    866 		return showFileDialog("Save", FileDialog.SAVE);
    867 	}
    868 
    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
    882 	public void error (AssetDescriptor asset, Throwable throwable) {
    883 		throwable.printStackTrace();
    884 	}
    885 
    886 	public PointSpriteParticleBatch getPointSpriteBatch () {
    887 		return renderer.pointSpriteBatch;
    888 	}
    889 
    890 	public BillboardParticleBatch getBillboardBatch () {
    891 		return renderer.billboardBatch;
    892 	}
    893 
    894 	public ModelInstanceParticleBatch getModelInstanceParticleBatch () {
    895 		return renderer.modelInstanceParticleBatch;
    896 	}
    897 
    898 	public void setAtlas(TextureAtlas atlas){
    899 		//currentAtlas = atlas;
    900 		setTexture(atlas.getTextures().first());
    901 	}
    902 
    903 	public void setTexture(Texture texture){
    904 		renderer.billboardBatch.setTexture(texture);
    905 		renderer.pointSpriteBatch.setTexture(texture);
    906 	}
    907 
    908 	public Texture getTexture(){
    909 		return renderer.billboardBatch.getTexture();
    910 	}
    911 
    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 
    921 	public TextureAtlas getAtlas(){
    922 		return getAtlas(renderer.billboardBatch.getTexture());
    923 	}
    924 
    925 	public boolean isUsingDefaultTexture () {
    926 		return renderer.billboardBatch.getTexture() == assetManager.get(DEFAULT_BILLBOARD_PARTICLE, Texture.class);
    927 	}
    928 
    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 
    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 
    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 
    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 
   1019 	public void restart () {
   1020 		effect.init();
   1021 		effect.start();
   1022 	}
   1023 }
   1024