com.badlogic.gdx.tools.flame.FlameMain Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.tools.flame;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.FileDialog;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.Writer;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.CompoundBorder;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.assets.AssetDescriptor;
import com.badlogic.gdx.assets.AssetErrorListener;
import com.badlogic.gdx.assets.AssetLoaderParameters;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.AssetLoader;
import com.badlogic.gdx.assets.loaders.FileHandleResolver;
import com.badlogic.gdx.assets.loaders.resolvers.AbsoluteFileHandleResolver;
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
import com.badlogic.gdx.backends.lwjgl.LwjglCanvas;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.attributes.BlendingAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.g3d.particles.ParticleController;
import com.badlogic.gdx.graphics.g3d.particles.ParticleEffect;
import com.badlogic.gdx.graphics.g3d.particles.ParticleEffectLoader;
import com.badlogic.gdx.graphics.g3d.particles.ParticleEffectLoader.ParticleEffectSaveParameter;
import com.badlogic.gdx.graphics.g3d.particles.ParticleSystem;
import com.badlogic.gdx.graphics.g3d.particles.batches.BillboardParticleBatch;
import com.badlogic.gdx.graphics.g3d.particles.batches.ModelInstanceParticleBatch;
import com.badlogic.gdx.graphics.g3d.particles.batches.ParticleBatch;
import com.badlogic.gdx.graphics.g3d.particles.batches.PointSpriteParticleBatch;
import com.badlogic.gdx.graphics.g3d.particles.emitters.Emitter;
import com.badlogic.gdx.graphics.g3d.particles.emitters.RegularEmitter;
import com.badlogic.gdx.graphics.g3d.particles.influencers.ColorInfluencer;
import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsInfluencer;
import com.badlogic.gdx.graphics.g3d.particles.influencers.Influencer;
import com.badlogic.gdx.graphics.g3d.particles.influencers.ModelInfluencer;
import com.badlogic.gdx.graphics.g3d.particles.influencers.ParticleControllerFinalizerInfluencer;
import com.badlogic.gdx.graphics.g3d.particles.influencers.ParticleControllerInfluencer;
import com.badlogic.gdx.graphics.g3d.particles.influencers.RegionInfluencer;
import com.badlogic.gdx.graphics.g3d.particles.influencers.ScaleInfluencer;
import com.badlogic.gdx.graphics.g3d.particles.influencers.SpawnInfluencer;
import com.badlogic.gdx.graphics.g3d.particles.renderers.BillboardRenderer;
import com.badlogic.gdx.graphics.g3d.particles.renderers.ModelInstanceRenderer;
import com.badlogic.gdx.graphics.g3d.particles.renderers.ParticleControllerControllerRenderer;
import com.badlogic.gdx.graphics.g3d.particles.renderers.PointSpriteRenderer;
import com.badlogic.gdx.graphics.g3d.particles.values.GradientColorValue;
import com.badlogic.gdx.graphics.g3d.particles.values.NumericValue;
import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.RandomXS128;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.JsonWriter;
import com.badlogic.gdx.utils.StreamUtils;
import com.badlogic.gdx.utils.StringBuilder;
/** @author Inferno */
public class FlameMain extends JFrame implements AssetErrorListener {
public static final String DEFAULT_FONT = "default.fnt", DEFAULT_BILLBOARD_PARTICLE = "pre_particle.png",
DEFAULT_MODEL_PARTICLE = "monkey.g3db",
// DEFAULT_PFX = "default.pfx",
DEFAULT_TEMPLATE_PFX = "defaultTemplate.pfx", DEFAULT_SKIN = "uiskin.json";
public static final int EVT_ASSET_RELOADED = 0;
static class ControllerData {
public boolean enabled = true;
public ParticleController controller;
public ControllerData (ParticleController emitter) {
controller = emitter;
}
}
private static class InfluencerWrapper {
String string;
Class type;
public InfluencerWrapper (String string, Class type) {
this.string = string;
this.type = type;
}
@Override
public String toString () {
return string;
}
}
public enum ControllerType {
Billboard("Billboard", new InfluencerWrapper[] {new InfluencerWrapper("Single Color", ColorInfluencer.Single.class),
new InfluencerWrapper("Random Color", ColorInfluencer.Random.class),
new InfluencerWrapper("Single Region", RegionInfluencer.Single.class),
new InfluencerWrapper("Random Region", RegionInfluencer.Random.class),
new InfluencerWrapper("Animated Region", RegionInfluencer.Animated.class),
new InfluencerWrapper("Scale", ScaleInfluencer.class), new InfluencerWrapper("Spawn", SpawnInfluencer.class),
new InfluencerWrapper("Dynamics", DynamicsInfluencer.class)}), PointSprite(
"Point Sprite",
new InfluencerWrapper[] {new InfluencerWrapper("Single Color", ColorInfluencer.Single.class),
new InfluencerWrapper("Random Color", ColorInfluencer.Random.class),
new InfluencerWrapper("Single Region", RegionInfluencer.Single.class),
new InfluencerWrapper("Random Region", RegionInfluencer.Random.class),
new InfluencerWrapper("Animated Region", RegionInfluencer.Animated.class),
new InfluencerWrapper("Scale", ScaleInfluencer.class), new InfluencerWrapper("Spawn", SpawnInfluencer.class),
new InfluencerWrapper("Dynamics", DynamicsInfluencer.class)}), ModelInstance(
"Model Instance",
new InfluencerWrapper[] {new InfluencerWrapper("Single Color", ColorInfluencer.Single.class),
new InfluencerWrapper("Random Color", ColorInfluencer.Random.class),
new InfluencerWrapper("Single Model", ModelInfluencer.Single.class),
new InfluencerWrapper("Random Model", ModelInfluencer.Random.class),
new InfluencerWrapper("Scale", ScaleInfluencer.class), new InfluencerWrapper("Spawn", SpawnInfluencer.class),
new InfluencerWrapper("Dynamics", DynamicsInfluencer.class)}), ParticleController(
"Particle Controller",
new InfluencerWrapper[] {
new InfluencerWrapper("Single Particle Controller", ParticleControllerInfluencer.Single.class),
new InfluencerWrapper("Random Particle Controller", ParticleControllerInfluencer.Random.class),
new InfluencerWrapper("Scale", ScaleInfluencer.class),
new InfluencerWrapper("Spawn", SpawnInfluencer.class),
new InfluencerWrapper("Dynamics", DynamicsInfluencer.class)});
public String desc;
public InfluencerWrapper[] wrappers;
private ControllerType (String desc, InfluencerWrapper[] wrappers) {
this.desc = desc;
this.wrappers = wrappers;
}
}
LwjglCanvas lwjglCanvas;
JPanel controllerPropertiesPanel;
JPanel editorPropertiesPanel;
EffectPanel effectPanel;
JSplitPane splitPane;
NumericValue fovValue;
NumericValue deltaMultiplier;
GradientColorValue backgroundColor;
AppRenderer renderer;
AssetManager assetManager;
JComboBox influencerBox;
TextureAtlas textureAtlas;
JsonWriter.OutputType jsonOutputType = JsonWriter.OutputType.minimal;
boolean jsonPrettyPrint = false;
private ParticleEffect effect;
/** READ only */
public Array controllersData;
ParticleSystem particleSystem;
public FlameMain () {
super("Flame");
MathUtils.random = new RandomXS128();
particleSystem = ParticleSystem.get();
effect = new ParticleEffect();
particleSystem.add(effect);
assetManager = new AssetManager();
assetManager.setErrorListener(this);
assetManager.setLoader(ParticleEffect.class, new ParticleEffectLoader(new InternalFileHandleResolver()));
controllersData = new Array();
lwjglCanvas = new LwjglCanvas(renderer = new AppRenderer());
addWindowListener(new WindowAdapter() {
public void windowClosed (WindowEvent event) {
// System.exit(0);
Gdx.app.exit();
}
});
initializeComponents();
setSize(1280, 950);
setLocationRelativeTo(null);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setVisible(true);
}
public ControllerType getControllerType () {
ParticleController controller = getEmitter();
ControllerType type = null;
if (controller.renderer instanceof BillboardRenderer)
type = ControllerType.Billboard;
else if (controller.renderer instanceof PointSpriteRenderer)
type = ControllerType.PointSprite;
else if (controller.renderer instanceof ModelInstanceRenderer)
type = ControllerType.ModelInstance;
else if (controller.renderer instanceof ParticleControllerControllerRenderer) type = ControllerType.ParticleController;
return type;
}
void reloadRows () {
EventQueue.invokeLater(new Runnable() {
public void run () {
// Ensure no listener is left watching for events
EventManager.get().clear();
// Clear
editorPropertiesPanel.removeAll();
influencerBox.removeAllItems();
controllerPropertiesPanel.removeAll();
// Editor props
addRow(editorPropertiesPanel, new NumericPanel(FlameMain.this, fovValue, "Field of View", ""));
addRow(editorPropertiesPanel, new NumericPanel(FlameMain.this, deltaMultiplier, "Delta multiplier", ""));
addRow(editorPropertiesPanel, new GradientPanel(FlameMain.this, backgroundColor, "Background color", "", true));
addRow(editorPropertiesPanel, new DrawPanel(FlameMain.this, "Draw", ""));
addRow(editorPropertiesPanel, new TextureLoaderPanel(FlameMain.this, "Texture", ""));
addRow(editorPropertiesPanel, new BillboardBatchPanel(FlameMain.this, renderer.billboardBatch), 1, 1);
addRow(editorPropertiesPanel, new PointSpriteBatchPanel(FlameMain.this, renderer.pointSpriteBatch), 1, 1);
addRow(editorPropertiesPanel, new SavePanel(FlameMain.this, "Save", ""));
editorPropertiesPanel.repaint();
// Controller props
ParticleController controller = getEmitter();
if (controller != null) {
// Reload available influencers
DefaultComboBoxModel model = (DefaultComboBoxModel)influencerBox.getModel();
ControllerType type = getControllerType();
if (type != null) {
for (Object value : type.wrappers)
model.addElement(value);
}
JPanel panel = null;
addRow(controllerPropertiesPanel, getPanel(controller.emitter));
for (int i = 0, c = controller.influencers.size; i < c; ++i) {
Influencer influencer = (Influencer)controller.influencers.get(i);
panel = getPanel(influencer);
if (panel != null) addRow(controllerPropertiesPanel, panel, 1, i == c - 1 ? 1 : 0);
}
for (Component component : controllerPropertiesPanel.getComponents())
if (component instanceof EditorPanel) ((EditorPanel)component).update(FlameMain.this);
}
controllerPropertiesPanel.repaint();
}
});
}
protected JPanel getPanel (Emitter emitter) {
if (emitter instanceof RegularEmitter) {
return new RegularEmitterPanel(this, (RegularEmitter)emitter);
}
return null;
}
protected JPanel getPanel (Influencer influencer) {
if (influencer instanceof ColorInfluencer.Single) {
return new ColorInfluencerPanel(this, (ColorInfluencer.Single)influencer);
}
if (influencer instanceof ColorInfluencer.Random) {
return new InfluencerPanel(this, (ColorInfluencer.Random)influencer, "Random Color Influencer",
"Assign a random color to the particles") {};
} else if (influencer instanceof ScaleInfluencer) {
return new ScaleInfluencerPanel(this, (ScaleInfluencer)influencer);
} else if (influencer instanceof SpawnInfluencer) {
return new SpawnInfluencerPanel(this, (SpawnInfluencer)influencer);
} else if (influencer instanceof DynamicsInfluencer) {
return new DynamicsInfluencerPanel(this, (DynamicsInfluencer)influencer);
} else if (influencer instanceof ModelInfluencer) {
boolean single = influencer instanceof ModelInfluencer.Single;
String name = single ? "Model Single Influencer" : "Model Random Influencer";
return new ModelInfluencerPanel(this, (ModelInfluencer)influencer, single, name,
"Defines what model will be used for the particles");
} else if (influencer instanceof ParticleControllerInfluencer) {
boolean single = influencer instanceof ParticleControllerInfluencer.Single;
String name = single ? "Particle Controller Single Influencer" : "Particle Controller Random Influencer";
return new ParticleControllerInfluencerPanel(this, (ParticleControllerInfluencer)influencer, single, name,
"Defines what controller will be used for the particles");
} else if (influencer instanceof RegionInfluencer.Single) {
return new RegionInfluencerPanel(this, "Billboard Single Region Influencer", "Assign the chosen region to the particles",
(RegionInfluencer.Single)influencer);
} else if (influencer instanceof RegionInfluencer.Animated) {
return new RegionInfluencerPanel(this, "Billboard Animated Region Influencer", "Animates the region of the particles",
(RegionInfluencer.Animated)influencer);
} else if (influencer instanceof RegionInfluencer.Random) {
return new RegionInfluencerPanel(this, "Billboard Random Region Influencer",
"Assigns a randomly picked (among those selected) region to the particles", (RegionInfluencer.Random)influencer);
} else if (influencer instanceof ParticleControllerFinalizerInfluencer) {
return new InfluencerPanel(this,
(ParticleControllerFinalizerInfluencer)influencer, "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.",
true, false) {};
}
return null;
}
protected JPanel getPanel (ParticleBatch renderer) {
if (renderer instanceof PointSpriteParticleBatch) {
return new PointSpriteBatchPanel(this, (PointSpriteParticleBatch)renderer);
}
if (renderer instanceof BillboardParticleBatch) {
return new BillboardBatchPanel(this, (BillboardParticleBatch)renderer);
} else if (renderer instanceof ModelInstanceParticleBatch) {
return new EmptyPanel(this, "Model Instance Batch", "It renders particles as model instances.");
}
return null;
}
void addRow (JPanel panel, JPanel row) {
addRow(panel, row, 1, 0);
}
void addRow (JPanel panel, JPanel row, float wx, float wy) {
row.setBorder(BorderFactory.createMatteBorder(0, 0, 2, 0, java.awt.Color.black));
panel.add(row, new GridBagConstraints(0, -1, 1, 1, wx, wy, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL,
new Insets(0, 0, 0, 0), 0, 0));
}
public void setVisible (String name, boolean visible) {
for (Component component : controllerPropertiesPanel.getComponents())
if (component instanceof EditorPanel && ((EditorPanel)component).getName().equals(name)) component.setVisible(visible);
}
private void rebuildActiveControllers () {
// rebuild list
Array effectControllers = effect.getControllers();
effectControllers.clear();
for (ControllerData controllerData : controllersData) {
if (controllerData.enabled) effectControllers.add(controllerData.controller);
}
// System.out.println("rebuilding active controllers");
effect.init();
effect.start();
}
public ParticleController getEmitter () {
return effectPanel.editIndex >= 0 ? controllersData.get(effectPanel.editIndex).controller : null;
}
public void addEmitter (ParticleController emitter) {
controllersData.add(new ControllerData(emitter));
rebuildActiveControllers();
}
public void removeEmitter (int row) {
controllersData.removeIndex(row).controller.dispose();
rebuildActiveControllers();
}
public void setEnabled (int emitterIndex, boolean enabled) {
ControllerData data = controllersData.get(emitterIndex);
data.enabled = enabled;
rebuildActiveControllers();
}
public boolean isEnabled (int emitterIndex) {
return controllersData.get(emitterIndex).enabled;
}
private void initializeComponents () {
splitPane = new JSplitPane();
splitPane.setUI(new BasicSplitPaneUI() {
public void paint (Graphics g, JComponent jc) {
}
});
splitPane.setDividerSize(4);
getContentPane().add(splitPane, BorderLayout.CENTER);
{
JSplitPane rightSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
rightSplit.setUI(new BasicSplitPaneUI() {
public void paint (Graphics g, JComponent jc) {
}
});
rightSplit.setDividerSize(4);
splitPane.add(rightSplit, JSplitPane.RIGHT);
{
JPanel propertiesPanel = new JPanel(new GridBagLayout());
rightSplit.add(propertiesPanel, JSplitPane.TOP);
propertiesPanel.setBorder(new CompoundBorder(BorderFactory.createEmptyBorder(3, 0, 6, 6),
BorderFactory.createTitledBorder("Editor Properties")));
{
JScrollPane scroll = new JScrollPane();
propertiesPanel.add(scroll, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTH,
GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
scroll.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
{
editorPropertiesPanel = new JPanel(new GridBagLayout());
scroll.setViewportView(editorPropertiesPanel);
scroll.getVerticalScrollBar().setUnitIncrement(70);
}
}
}
{
JSplitPane rightSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
rightSplitPane.setUI(new BasicSplitPaneUI() {
public void paint (Graphics g, JComponent jc) {
}
});
rightSplitPane.setDividerSize(4);
rightSplitPane.setDividerLocation(100);
rightSplit.add(rightSplitPane, JSplitPane.BOTTOM);
JPanel propertiesPanel = new JPanel(new GridBagLayout());
rightSplitPane.add(propertiesPanel, JSplitPane.TOP);
propertiesPanel.setBorder(
new CompoundBorder(BorderFactory.createEmptyBorder(3, 0, 6, 6), BorderFactory.createTitledBorder("Influencers")));
{
JScrollPane scroll = new JScrollPane();
propertiesPanel.add(scroll, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTH,
GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
scroll.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
{
JPanel influencersPanel = new JPanel(new GridBagLayout());
influencerBox = new JComboBox(new DefaultComboBoxModel());
JButton addInfluencerButton = new JButton("Add");
addInfluencerButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed (ActionEvent e) {
InfluencerWrapper wrapper = (InfluencerWrapper)influencerBox.getSelectedItem();
ParticleController controller = getEmitter();
if (controller != null) addInfluencer(wrapper.type, controller);
}
});
influencersPanel.add(influencerBox, new GridBagConstraints(0, 0, 1, 1, 0, 1, GridBagConstraints.NORTHWEST,
GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
influencersPanel.add(addInfluencerButton, new GridBagConstraints(1, 0, 1, 1, 1, 1, GridBagConstraints.NORTHWEST,
GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
scroll.setViewportView(influencersPanel);
scroll.getVerticalScrollBar().setUnitIncrement(70);
}
}
propertiesPanel = new JPanel(new GridBagLayout());
rightSplitPane.add(propertiesPanel, JSplitPane.BOTTOM);
propertiesPanel.setBorder(new CompoundBorder(BorderFactory.createEmptyBorder(3, 0, 6, 6),
BorderFactory.createTitledBorder("Particle Controller Components")));
{
JScrollPane scroll = new JScrollPane();
propertiesPanel.add(scroll, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTH,
GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
scroll.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
{
controllerPropertiesPanel = new JPanel(new GridBagLayout());
scroll.setViewportView(controllerPropertiesPanel);
scroll.getVerticalScrollBar().setUnitIncrement(70);
}
}
}
rightSplit.setDividerLocation(250);
}
{
JSplitPane leftSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
leftSplit.setUI(new BasicSplitPaneUI() {
public void paint (Graphics g, JComponent jc) {
}
});
leftSplit.setDividerSize(4);
splitPane.add(leftSplit, JSplitPane.LEFT);
{
JPanel spacer = new JPanel(new BorderLayout());
leftSplit.add(spacer, JSplitPane.TOP);
spacer.add(lwjglCanvas.getCanvas());
spacer.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 4));
}
{
JPanel emittersPanel = new JPanel(new BorderLayout());
leftSplit.add(emittersPanel, JSplitPane.BOTTOM);
emittersPanel.setBorder(new CompoundBorder(BorderFactory.createEmptyBorder(0, 6, 6, 0),
BorderFactory.createTitledBorder("Particle Controllers")));
{
effectPanel = new EffectPanel(this);
emittersPanel.add(effectPanel);
}
}
leftSplit.setDividerLocation(625);
}
splitPane.setDividerLocation(500);
}
protected void addInfluencer (Class type, ParticleController controller) {
if (controller.findInfluencer(type) != null) return;
try {
controller.end();
Influencer newInfluencer = type.newInstance();
boolean replaced = false;
if (ColorInfluencer.class.isAssignableFrom(type)) {
replaced = controller.replaceInfluencer(ColorInfluencer.class, (ColorInfluencer)newInfluencer);
} else if (RegionInfluencer.class.isAssignableFrom(type)) {
replaced = controller.replaceInfluencer(RegionInfluencer.class, (RegionInfluencer)newInfluencer);
} else if (ModelInfluencer.class.isAssignableFrom(type)) {
ModelInfluencer newModelInfluencer = (ModelInfluencer)newInfluencer;
ModelInfluencer currentInfluencer = (ModelInfluencer)controller.findInfluencer(ModelInfluencer.class);
if (currentInfluencer != null) {
newModelInfluencer.models.add(currentInfluencer.models.first());
}
replaced = controller.replaceInfluencer(ModelInfluencer.class, (ModelInfluencer)newInfluencer);
} else if (ParticleControllerInfluencer.class.isAssignableFrom(type)) {
ParticleControllerInfluencer newModelInfluencer = (ParticleControllerInfluencer)newInfluencer;
ParticleControllerInfluencer currentInfluencer = (ParticleControllerInfluencer)controller
.findInfluencer(ParticleControllerInfluencer.class);
if (currentInfluencer != null) {
newModelInfluencer.templates.add(currentInfluencer.templates.first());
}
replaced = controller.replaceInfluencer(ParticleControllerInfluencer.class,
(ParticleControllerInfluencer)newInfluencer);
}
if (!replaced) {
if (getControllerType() != ControllerType.ParticleController)
controller.influencers.add(newInfluencer);
else {
Influencer finalizer = controller.influencers.pop();
controller.influencers.add(newInfluencer);
controller.influencers.add(finalizer);
}
}
controller.init();
effect.start();
reloadRows();
} catch (Exception e1) {
e1.printStackTrace();
}
}
protected boolean canAddInfluencer (Class influencerType, ParticleController controller) {
boolean hasSameInfluencer = controller.findInfluencer(influencerType) != null;
if (!hasSameInfluencer) {
if ((ColorInfluencer.Single.class.isAssignableFrom(influencerType)
&& controller.findInfluencer(ColorInfluencer.Random.class) != null)
|| (ColorInfluencer.Random.class.isAssignableFrom(influencerType)
&& controller.findInfluencer(ColorInfluencer.Single.class) != null)) {
return false;
}
if (RegionInfluencer.class.isAssignableFrom(influencerType)) {
return controller.findInfluencer(RegionInfluencer.class) == null;
} else if (ModelInfluencer.class.isAssignableFrom(influencerType)) {
return controller.findInfluencer(ModelInfluencer.class) == null;
} else if (ParticleControllerInfluencer.class.isAssignableFrom(influencerType)) {
return controller.findInfluencer(ParticleControllerInfluencer.class) == null;
}
}
return !hasSameInfluencer;
}
class AppRenderer implements ApplicationListener {
// Stats
private float maxActiveTimer;
private int maxActive, lastMaxActive;
boolean isUpdate = true;
// Controls
private CameraInputController cameraInputController;
// UI
private Stage ui;
TextButton playPauseButton;
private Label fpsLabel, pointCountLabel, billboardCountLabel, modelInstanceCountLabel, maxLabel;
StringBuilder stringBuilder;
// Render
public PerspectiveCamera worldCamera;
private boolean isDrawXYZ, isDrawXZPlane, isDrawXYPlane;
private Array models;
private ModelInstance xyzInstance, xzPlaneInstance, xyPlaneInstance;
private Environment environment;
private ModelBatch modelBatch;
PointSpriteParticleBatch pointSpriteBatch;
BillboardParticleBatch billboardBatch;
ModelInstanceParticleBatch modelInstanceParticleBatch;
public void create () {
if (ui != null) return;
int w = Gdx.graphics.getWidth(), h = Gdx.graphics.getHeight();
modelBatch = new ModelBatch();
environment = new Environment();
environment.add(new DirectionalLight().set(Color.WHITE, 0, 0, -1));
worldCamera = new PerspectiveCamera(67, w, h);
worldCamera.position.set(10, 10, 10);
worldCamera.lookAt(0, 0, 0);
worldCamera.near = 0.1f;
worldCamera.far = 300f;
worldCamera.update();
cameraInputController = new CameraInputController(worldCamera);
// Batches
pointSpriteBatch = new PointSpriteParticleBatch();
pointSpriteBatch.setCamera(worldCamera);
billboardBatch = new BillboardParticleBatch();
billboardBatch.setCamera(worldCamera);
modelInstanceParticleBatch = new ModelInstanceParticleBatch();
particleSystem.add(billboardBatch);
particleSystem.add(pointSpriteBatch);
particleSystem.add(modelInstanceParticleBatch);
fovValue = new NumericValue();
fovValue.setValue(67);
fovValue.setActive(true);
deltaMultiplier = new NumericValue();
deltaMultiplier.setValue(1.0f);
deltaMultiplier.setActive(true);
backgroundColor = new GradientColorValue();
Color color = Color.valueOf("878787");
backgroundColor.setColors(new float[] {color.r, color.g, color.b});
models = new Array();
ModelBuilder builder = new ModelBuilder();
Model xyzModel = builder.createXYZCoordinates(10, new Material(), Usage.Position | Usage.ColorPacked),
planeModel = builder.createLineGrid(10, 10, 1, 1, new Material(ColorAttribute.createDiffuse(Color.WHITE)),
Usage.Position);
models.add(xyzModel);
models.add(planeModel);
xyzInstance = new ModelInstance(xyzModel);
xzPlaneInstance = new ModelInstance(planeModel);
xyPlaneInstance = new ModelInstance(planeModel);
xyPlaneInstance.transform.rotate(1f, 0f, 0f, 90f);
setDrawXYZ(true);
setDrawXZPlane(true);
// Load default resources
ParticleEffectLoader.ParticleEffectLoadParameter params = new ParticleEffectLoader.ParticleEffectLoadParameter(
particleSystem.getBatches());
assetManager.load(DEFAULT_BILLBOARD_PARTICLE, Texture.class);
assetManager.load(DEFAULT_MODEL_PARTICLE, Model.class);
assetManager.load(DEFAULT_SKIN, Skin.class);
assetManager.load(DEFAULT_TEMPLATE_PFX, ParticleEffect.class, params);
assetManager.finishLoading();
assetManager.setLoader(ParticleEffect.class, new ParticleEffectLoader(new AbsoluteFileHandleResolver()));
assetManager.get(DEFAULT_MODEL_PARTICLE, Model.class).materials.get(0)
.set(new BlendingAttribute(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA, 1));
// Ui
stringBuilder = new StringBuilder();
Skin skin = assetManager.get(DEFAULT_SKIN, Skin.class);
ui = new Stage();
fpsLabel = new Label("", skin);
pointCountLabel = new Label("", skin);
billboardCountLabel = new Label("", skin);
modelInstanceCountLabel = new Label("", skin);
maxLabel = new Label("", skin);
playPauseButton = new TextButton("Pause", skin);
playPauseButton.addListener(new ClickListener() {
@Override
public void clicked (InputEvent event, float x, float y) {
isUpdate = !isUpdate;
playPauseButton.setText(isUpdate ? "Pause" : "Play");
}
});
Table table = new Table(skin);
table.setFillParent(true);
table.pad(5);
table.add(fpsLabel).expandX().left().row();
table.add(pointCountLabel).expandX().left().row();
table.add(billboardCountLabel).expandX().left().row();
table.add(modelInstanceCountLabel).expandX().left().row();
table.add(maxLabel).expandX().left().row();
table.add(playPauseButton).expand().bottom().left().row();
ui.addActor(table);
setTexture((Texture)assetManager.get(DEFAULT_BILLBOARD_PARTICLE));
effectPanel.createDefaultEmitter(ControllerType.Billboard, true, true);
}
@Override
public void resize (int width, int height) {
Gdx.input.setInputProcessor(new InputMultiplexer(ui, cameraInputController));
Gdx.gl.glViewport(0, 0, width, height);
worldCamera.viewportWidth = width;
worldCamera.viewportHeight = height;
worldCamera.update();
ui.getViewport().setWorldSize(width, height);
ui.getViewport().update(width, height, true);
}
public void render () {
float delta = Math.max(0, Gdx.graphics.getDeltaTime() * deltaMultiplier.getValue());
update(delta);
renderWorld();
}
private void update (float delta) {
worldCamera.fieldOfView = fovValue.getValue();
worldCamera.update();
cameraInputController.update();
if (isUpdate) {
particleSystem.update(delta);
// Update ui
stringBuilder.delete(0, stringBuilder.length);
stringBuilder.append("Point Sprites : ").append(pointSpriteBatch.getBufferedCount());
pointCountLabel.setText(stringBuilder);
stringBuilder.delete(0, stringBuilder.length);
stringBuilder.append("Billboards : ").append(billboardBatch.getBufferedCount());
billboardCountLabel.setText(stringBuilder);
stringBuilder.delete(0, stringBuilder.length);
stringBuilder.append("Model Instances : ").append(modelInstanceParticleBatch.getBufferedCount());
modelInstanceCountLabel.setText(stringBuilder);
}
stringBuilder.delete(0, stringBuilder.length);
stringBuilder.append("FPS : ").append(Gdx.graphics.getFramesPerSecond());
fpsLabel.setText(stringBuilder);
ui.act(Gdx.graphics.getDeltaTime());
}
private void renderWorld () {
float[] colors = backgroundColor.getColors();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
Gdx.gl.glClearColor(colors[0], colors[1], colors[2], 0);
modelBatch.begin(worldCamera);
if (isDrawXYZ) modelBatch.render(xyzInstance);
if (isDrawXZPlane) modelBatch.render(xzPlaneInstance);
if (isDrawXYPlane) modelBatch.render(xyPlaneInstance);
particleSystem.begin();
particleSystem.draw();
particleSystem.end();
// Draw
modelBatch.render(particleSystem, environment);
modelBatch.end();
ui.draw();
}
@Override
public void dispose () {
}
@Override
public void pause () {
}
@Override
public void resume () {
}
public void setDrawXYZ (boolean isDraw) {
isDrawXYZ = isDraw;
}
public boolean IsDrawXYZ () {
return isDrawXYZ;
}
public void setDrawXZPlane (boolean isDraw) {
isDrawXZPlane = isDraw;
}
public boolean IsDrawXZPlane () {
return isDrawXZPlane;
}
public void setDrawXYPlane (boolean isDraw) {
isDrawXYPlane = isDraw;
}
public boolean IsDrawXYPlane () {
return isDrawXYPlane;
}
}
public static void main (String[] args) {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
try {
UIManager.setLookAndFeel(info.getClassName());
} catch (Throwable ignored) {
}
break;
}
}
EventQueue.invokeLater(new Runnable() {
public void run () {
new FlameMain();
}
});
}
public AppRenderer getRenderer () {
return renderer;
}
String lastDir;
public File showFileLoadDialog () {
return showFileDialog("Open", FileDialog.LOAD);
}
public File showFileSaveDialog () {
return showFileDialog("Save", FileDialog.SAVE);
}
private File showFileDialog (String title, int mode) {
FileDialog dialog = new FileDialog(this, title, mode);
if (lastDir != null) dialog.setDirectory(lastDir);
dialog.setVisible(true);
final String file = dialog.getFile();
final String dir = dialog.getDirectory();
if (dir == null || file == null || file.trim().length() == 0) return null;
lastDir = dir;
return new File(dir, file);
}
@Override
public void error (AssetDescriptor asset, Throwable throwable) {
throwable.printStackTrace();
}
public PointSpriteParticleBatch getPointSpriteBatch () {
return renderer.pointSpriteBatch;
}
public BillboardParticleBatch getBillboardBatch () {
return renderer.billboardBatch;
}
public ModelInstanceParticleBatch getModelInstanceParticleBatch () {
return renderer.modelInstanceParticleBatch;
}
public void setAtlas (TextureAtlas atlas) {
this.textureAtlas = atlas;
setTexture(atlas.getTextures().first());
}
public void setTexture (Texture texture) {
renderer.billboardBatch.setTexture(texture);
renderer.pointSpriteBatch.setTexture(texture);
}
public Texture getTexture () {
return renderer.billboardBatch.getTexture();
}
public TextureAtlas getAtlas (Texture texture) {
Array atlases = assetManager.getAll(TextureAtlas.class, new Array());
for (TextureAtlas atlas : atlases) {
if (atlas.getTextures().contains(texture)) return atlas;
}
return null;
}
public TextureAtlas getAtlas () {
return getAtlas(renderer.billboardBatch.getTexture());
}
public String getAtlasFilename () {
if (textureAtlas == null) {
return null;
}
return assetManager.getAssetFileName(textureAtlas);
}
public boolean isUsingDefaultTexture () {
return renderer.billboardBatch.getTexture() == assetManager.get(DEFAULT_BILLBOARD_PARTICLE, Texture.class);
}
public Array getParticleEffects (Array controllers, Array out) {
out.clear();
assetManager.getAll(ParticleEffect.class, out);
for (int i = 0; i < out.size;) {
ParticleEffect effect = out.get(i);
Array effectControllers = effect.getControllers();
boolean remove = true;
for (ParticleController controller : controllers) {
if (effectControllers.contains(controller, true)) {
remove = false;
break;
}
}
if (remove) {
out.removeIndex(i);
continue;
}
++i;
}
return out;
}
public void saveEffect (File file) {
Writer fileWriter = null;
try {
ParticleEffectLoader loader = (ParticleEffectLoader)assetManager.getLoader(ParticleEffect.class);
loader.save(effect, new ParticleEffectSaveParameter(new FileHandle(file.getAbsolutePath()), assetManager,
particleSystem.getBatches(), jsonOutputType, jsonPrettyPrint));
} catch (Exception ex) {
System.out.println("Error saving effect: " + file.getAbsolutePath());
ex.printStackTrace();
JOptionPane.showMessageDialog(this, "Error saving effect.");
} finally {
StreamUtils.closeQuietly(fileWriter);
}
}
public ParticleEffect openEffect (File file, boolean replaceCurrentWorkspace) {
try {
ParticleEffect loadedEffect = load(file.getAbsolutePath(), ParticleEffect.class, null,
new ParticleEffectLoader.ParticleEffectLoadParameter(particleSystem.getBatches()));
loadedEffect = loadedEffect.copy();
loadedEffect.init();
if (replaceCurrentWorkspace) {
effect = loadedEffect;
controllersData.clear();
particleSystem.removeAll();
particleSystem.add(effect);
for (ParticleController controller : effect.getControllers())
controllersData.add(new ControllerData(controller));
rebuildActiveControllers();
}
reloadRows();
return loadedEffect;
} catch (Exception ex) {
System.out.println("Error loading effect: " + file.getAbsolutePath());
ex.printStackTrace();
JOptionPane.showMessageDialog(this, "Error opening effect.");
}
return null;
}
public T load (String resource, Class type, AssetLoader loader, AssetLoaderParameters params) {
String resolvedPath = resource.replaceAll("\\\\", "/");
boolean exist = assetManager.isLoaded(resolvedPath, type);
T oldAsset = null;
if (exist) {
oldAsset = assetManager.get(resolvedPath, type);
for (int i = assetManager.getReferenceCount(resolvedPath); i > 0; --i)
assetManager.unload(resolvedPath);
}
AssetLoader> currentLoader = assetManager.getLoader(type);
if (loader != null) assetManager.setLoader(type, loader);
assetManager.setLoader(ParticleEffect.class, new ParticleEffectLoader(new FileHandleResolver() {
@Override
public FileHandle resolve (String fileName) {
FileHandle attempt = Gdx.files.absolute(fileName);
if (attempt.exists()) return attempt;
if (DEFAULT_BILLBOARD_PARTICLE.equals(attempt.name())) return Gdx.files.internal(DEFAULT_BILLBOARD_PARTICLE);
if (DEFAULT_MODEL_PARTICLE.equals(attempt.name())) return Gdx.files.internal(DEFAULT_MODEL_PARTICLE);
if (DEFAULT_TEMPLATE_PFX.equals(attempt.name())) return Gdx.files.internal(DEFAULT_TEMPLATE_PFX);
return attempt;
}
}));
assetManager.load(resolvedPath, type, params);
assetManager.finishLoading();
T res = assetManager.get(resolvedPath);
if (currentLoader != null) assetManager.setLoader(type, currentLoader);
if (exist) EventManager.get().fire(EVT_ASSET_RELOADED, new Object[] {oldAsset, res});
return res;
}
public void restart () {
effect.init();
effect.start();
}
}