OSDN Git Service

[changed] FileType.Absolut to Absolute.
authornathan.sweet <nathan.sweet@6c4fd544-2939-11df-bb46-9574ba5d0bfa>
Mon, 25 Oct 2010 10:07:44 +0000 (10:07 +0000)
committernathan.sweet <nathan.sweet@6c4fd544-2939-11df-bb46-9574ba5d0bfa>
Mon, 25 Oct 2010 10:07:44 +0000 (10:07 +0000)
[added] LwjglApplication#setSize.
[changed] Named LWJGL audio and music threads.
[changed] SpriteBatch#renderMesh made private.
[added] SpriteBatch#flush (public).
[added] Sprite#setTexture.
[added] Particle classes and particle editor tool (in extensions). 99,999 particles on the desktop at 44fps (default 32x32 image). Haven't tried performance on a device yet, that will get me motivated to work on it again.

31 files changed:
backends/gdx-backend-applet/src/com/badlogic/gdx/backends/applet/AppletFiles.java
backends/gdx-backend-desktop/src/com/badlogic/gdx/backends/desktop/JoglFiles.java
backends/gdx-backend-lwjgl/src/com/badlogic/gdx/backends/desktop/LwjglApplication.java
backends/gdx-backend-lwjgl/src/com/badlogic/gdx/backends/desktop/LwjglAudio.java
backends/gdx-backend-lwjgl/src/com/badlogic/gdx/backends/desktop/LwjglFiles.java
backends/gdx-backend-lwjgl/src/com/badlogic/gdx/backends/desktop/LwjglMusic.java
extensions/particle-editor/.classpath [new file with mode: 0644]
extensions/particle-editor/.project [new file with mode: 0644]
extensions/particle-editor/data/default.fnt [new file with mode: 0644]
extensions/particle-editor/data/default.png [new file with mode: 0644]
extensions/particle-editor/data/particle.png [new file with mode: 0644]
extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/Chart.java [new file with mode: 0644]
extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/CountPanel.java [new file with mode: 0644]
extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/EditorPanel.java [new file with mode: 0644]
extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/EffectPanel.java [new file with mode: 0644]
extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/GradientPanel.java [new file with mode: 0644]
extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/ImagePanel.java [new file with mode: 0644]
extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/NumericPanel.java [new file with mode: 0644]
extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/OptionsPanel.java [new file with mode: 0644]
extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/ParticleEditor.java [new file with mode: 0644]
extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/PercentagePanel.java [new file with mode: 0644]
extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/RangedNumericPanel.java [new file with mode: 0644]
extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/ScaledNumericPanel.java [new file with mode: 0644]
extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/SpawnPanel.java [new file with mode: 0644]
extensions/twl/gdx-twl/src/com/badlogic/gdx/twl/renderer/TwlRenderer.java
gdx/src/com/badlogic/gdx/Files.java
gdx/src/com/badlogic/gdx/graphics/Sprite.java
gdx/src/com/badlogic/gdx/graphics/SpriteBatch.java
gdx/src/com/badlogic/gdx/graphics/particles/ParticleEffect.java [new file with mode: 0644]
gdx/src/com/badlogic/gdx/graphics/particles/ParticleEmitter.java [new file with mode: 0644]
gdx/src/com/badlogic/gdx/utils/MathUtils.java

index e6827c1..ab0a489 100644 (file)
@@ -31,7 +31,7 @@ final class AppletFiles implements Files {
         * {@inheritDoc}\r
         */\r
        @Override public FileHandle getFileHandle (String filename, FileType type) {\r
-               if (type == FileType.Absolut || type == FileType.External)\r
+               if (type == FileType.Absolute || type == FileType.External)\r
                        return null;\r
                else\r
                        return new AppletFileHandle(filename);\r
@@ -55,7 +55,7 @@ final class AppletFiles implements Files {
         * {@inheritDoc}\r
         */\r
        @Override public InputStream readFile (String fileName, FileType type) {\r
-               if (type == FileType.Absolut || type == FileType.External)\r
+               if (type == FileType.Absolute || type == FileType.External)\r
                        return null;\r
                else\r
                        return new AppletFileHandle(fileName).getInputStream();\r
index 4979395..916050f 100644 (file)
@@ -39,7 +39,7 @@ final class JoglFiles implements Files {
         */\r
        @Override public FileHandle getFileHandle (String filename, FileType type) {\r
                File file = null;\r
-               if (type == FileType.Absolut || type == FileType.Internal)\r
+               if (type == FileType.Absolute || type == FileType.Internal)\r
                        file = new File(filename);\r
                else\r
                        file = new File(this.externalPath + filename);\r
@@ -55,7 +55,7 @@ final class JoglFiles implements Files {
         */\r
        @Override public String[] listDirectory (String directory, FileType type) {\r
                File file = null;\r
-               if (type == FileType.Absolut || type == FileType.Internal)\r
+               if (type == FileType.Absolute || type == FileType.Internal)\r
                        file = new File(directory);\r
                else\r
                        file = new File(this.externalPath + directory);\r
@@ -73,7 +73,7 @@ final class JoglFiles implements Files {
 \r
                if (type == FileType.Internal) return false;\r
 \r
-               if (type == FileType.Absolut)\r
+               if (type == FileType.Absolute)\r
                        file = new File(directory);\r
                else\r
                        file = new File(this.externalPath + directory);\r
@@ -86,7 +86,7 @@ final class JoglFiles implements Files {
        @Override public InputStream readFile (String fileName, FileType type) {\r
                File file = null;\r
                InputStream in = null;\r
-               if (type == FileType.Absolut || type == FileType.Internal)\r
+               if (type == FileType.Absolute || type == FileType.Internal)\r
                        file = new File(fileName);\r
                else\r
                        file = new File(this.externalPath + fileName);\r
@@ -109,7 +109,7 @@ final class JoglFiles implements Files {
 \r
                if (type == FileType.Internal) return null;\r
 \r
-               if (type == FileType.Absolut)\r
+               if (type == FileType.Absolute)\r
                        file = new File(filename);\r
                else\r
                        file = new File(this.externalPath + filename);\r
index 748fa09..613f4cf 100644 (file)
@@ -1,6 +1,7 @@
 \r
 package com.badlogic.gdx.backends.desktop;\r
 \r
+import java.awt.Canvas;\r
 import java.io.BufferedOutputStream;\r
 import java.io.File;\r
 import java.io.FileOutputStream;\r
@@ -132,7 +133,7 @@ import com.badlogic.gdx.Version;
                Gdx.audio = this.getAudio();\r
                Gdx.files = this.getFiles();\r
 \r
-               new Thread("LWJGL") {\r
+               new Thread("LWJGL Application") {\r
                        public void run () {\r
                                try {\r
                                        LwjglApplication.this.start();\r
@@ -161,15 +162,21 @@ import com.badlogic.gdx.Version;
                }\r
        }\r
 \r
+       public void setSize (int width, int height) {\r
+               this.width = width;\r
+               this.height = height;\r
+               for (RenderListener listener : listeners)\r
+                       listener.surfaceChanged(getWidth(), getHeight());\r
+       }\r
+\r
        void start () throws LWJGLException {\r
                gameThread = Thread.currentThread();\r
 \r
                setupDisplay();\r
 \r
-               for (RenderListener listener : listeners) {\r
+               for (RenderListener listener : listeners)\r
                        listener.surfaceCreated();\r
-                       listener.surfaceChanged(getWidth(), getHeight());\r
-               }\r
+               setSize(width, height);\r
                for (RenderListener listener : listeners)\r
                        listener.render();\r
 \r
index a6762ed..0a68294 100644 (file)
@@ -97,7 +97,7 @@ final class LwjglAudio implements Audio, Runnable {
                        line = AudioSystem.getSourceDataLine(format);\r
                        line.open(format, 4410);\r
                        line.start();\r
-                       thread = new Thread(this);\r
+                       thread = new Thread(this, "LWJGL Audio");\r
                        thread.setDaemon(true);\r
                        thread.start();\r
                } catch (Exception e) {\r
index 1db4a83..2f289b0 100644 (file)
@@ -36,7 +36,7 @@ final class LwjglFiles implements Files {
 \r
        public FileHandle getFileHandle (String filename, FileType type) {\r
                File file = null;\r
-               if (type == FileType.Absolut || type == FileType.Internal)\r
+               if (type == FileType.Absolute || type == FileType.Internal)\r
                        file = new File(filename);\r
                else\r
                        file = new File(this.externalPath + filename);\r
@@ -49,7 +49,7 @@ final class LwjglFiles implements Files {
 \r
        public String[] listDirectory (String directory, FileType type) {\r
                File file = null;\r
-               if (type == FileType.Absolut || type == FileType.Internal)\r
+               if (type == FileType.Absolute || type == FileType.Internal)\r
                        file = new File(directory);\r
                else\r
                        file = new File(this.externalPath + directory);\r
@@ -64,7 +64,7 @@ final class LwjglFiles implements Files {
 \r
                if (type == FileType.Internal) return false;\r
 \r
-               if (type == FileType.Absolut)\r
+               if (type == FileType.Absolute)\r
                        file = new File(directory);\r
                else\r
                        file = new File(this.externalPath + directory);\r
@@ -74,7 +74,7 @@ final class LwjglFiles implements Files {
        public InputStream readFile (String fileName, FileType type) {\r
                File file = null;\r
                InputStream in = null;\r
-               if (type == FileType.Absolut || type == FileType.Internal)\r
+               if (type == FileType.Absolute || type == FileType.Internal)\r
                        file = new File(fileName);\r
                else\r
                        file = new File(this.externalPath + fileName);\r
@@ -94,7 +94,7 @@ final class LwjglFiles implements Files {
 \r
                if (type == FileType.Internal) return null;\r
 \r
-               if (type == FileType.Absolut)\r
+               if (type == FileType.Absolute)\r
                        file = new File(filename);\r
                else\r
                        file = new File(this.externalPath + filename);\r
index 3e5ee9f..ce22215 100644 (file)
@@ -52,7 +52,7 @@ public class LwjglMusic implements Music, Runnable {
                ain.close();\r
                ain = null;\r
 \r
-               thread = new Thread(this);\r
+               thread = new Thread(this, "LWJGL Music");\r
                thread.setDaemon(true);\r
                thread.start();\r
        }\r
diff --git a/extensions/particle-editor/.classpath b/extensions/particle-editor/.classpath
new file mode 100644 (file)
index 0000000..3488d82
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+       <classpathentry excluding="**/.svn/*" kind="src" path="src"/>\r
+       <classpathentry excluding="**/.svn/*" kind="src" path="data"/>\r
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>\r
+       <classpathentry combineaccessrules="false" kind="src" path="/gdx"/>\r
+       <classpathentry combineaccessrules="false" kind="src" path="/gdx-backend-lwjgl"/>\r
+       <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
diff --git a/extensions/particle-editor/.project b/extensions/particle-editor/.project
new file mode 100644 (file)
index 0000000..3f221a9
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+       <name>particle-editor</name>\r
+       <comment></comment>\r
+       <projects>\r
+       </projects>\r
+       <buildSpec>\r
+               <buildCommand>\r
+                       <name>org.eclipse.jdt.core.javabuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+       </buildSpec>\r
+       <natures>\r
+               <nature>org.eclipse.jdt.core.javanature</nature>\r
+       </natures>\r
+</projectDescription>\r
diff --git a/extensions/particle-editor/data/default.fnt b/extensions/particle-editor/data/default.fnt
new file mode 100644 (file)
index 0000000..64e46f1
--- /dev/null
@@ -0,0 +1,101 @@
+info face="Droid Sans" size=17 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1\r
+common lineHeight=20 base=26 scaleW=256 scaleH=128 pages=1 packed=0\r
+page id=0 file="default.png"\r
+chars count=95\r
+char id=32   x=0     y=0     width=0     height=0     xoffset=0     yoffset=16    xadvance=4     page=0  chnl=0 \r
+char id=124   x=0     y=0     width=6     height=20     xoffset=1     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=106   x=6     y=0     width=9     height=20     xoffset=-4     yoffset=3    xadvance=4     page=0  chnl=0 \r
+char id=81   x=15     y=0     width=15     height=19     xoffset=-2     yoffset=3    xadvance=12     page=0  chnl=0 \r
+char id=74   x=30     y=0     width=11     height=19     xoffset=-5     yoffset=3    xadvance=4     page=0  chnl=0 \r
+char id=125   x=41     y=0     width=10     height=18     xoffset=-3     yoffset=3    xadvance=6     page=0  chnl=0 \r
+char id=123   x=51     y=0     width=10     height=18     xoffset=-3     yoffset=3    xadvance=6     page=0  chnl=0 \r
+char id=93   x=61     y=0     width=8     height=18     xoffset=-3     yoffset=3    xadvance=5     page=0  chnl=0 \r
+char id=91   x=69     y=0     width=8     height=18     xoffset=-2     yoffset=3    xadvance=5     page=0  chnl=0 \r
+char id=41   x=77     y=0     width=9     height=18     xoffset=-3     yoffset=3    xadvance=5     page=0  chnl=0 \r
+char id=40   x=86     y=0     width=9     height=18     xoffset=-3     yoffset=3    xadvance=5     page=0  chnl=0 \r
+char id=64   x=95     y=0     width=18     height=17     xoffset=-3     yoffset=3    xadvance=14     page=0  chnl=0 \r
+char id=121   x=113     y=0     width=13     height=17     xoffset=-3     yoffset=6    xadvance=8     page=0  chnl=0 \r
+char id=113   x=126     y=0     width=13     height=17     xoffset=-3     yoffset=6    xadvance=9     page=0  chnl=0 \r
+char id=112   x=139     y=0     width=13     height=17     xoffset=-2     yoffset=6    xadvance=9     page=0  chnl=0 \r
+char id=103   x=152     y=0     width=13     height=17     xoffset=-3     yoffset=6    xadvance=8     page=0  chnl=0 \r
+char id=38   x=165     y=0     width=16     height=16     xoffset=-3     yoffset=3    xadvance=11     page=0  chnl=0 \r
+char id=37   x=181     y=0     width=18     height=16     xoffset=-3     yoffset=3    xadvance=14     page=0  chnl=0 \r
+char id=36   x=199     y=0     width=12     height=16     xoffset=-2     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=63   x=211     y=0     width=11     height=16     xoffset=-3     yoffset=3    xadvance=7     page=0  chnl=0 \r
+char id=33   x=222     y=0     width=7     height=16     xoffset=-2     yoffset=3    xadvance=4     page=0  chnl=0 \r
+char id=48   x=229     y=0     width=13     height=16     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=57   x=242     y=0     width=13     height=16     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=56   x=0     y=20     width=13     height=16     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=54   x=13     y=20     width=13     height=16     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=53   x=26     y=20     width=12     height=16     xoffset=-2     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=51   x=38     y=20     width=13     height=16     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=100   x=51     y=20     width=13     height=16     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=98   x=64     y=20     width=13     height=16     xoffset=-2     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=85   x=77     y=20     width=14     height=16     xoffset=-2     yoffset=3    xadvance=11     page=0  chnl=0 \r
+char id=83   x=91     y=20     width=13     height=16     xoffset=-3     yoffset=3    xadvance=8     page=0  chnl=0 \r
+char id=79   x=104     y=20     width=15     height=16     xoffset=-2     yoffset=3    xadvance=12     page=0  chnl=0 \r
+char id=71   x=119     y=20     width=14     height=16     xoffset=-2     yoffset=3    xadvance=11     page=0  chnl=0 \r
+char id=67   x=133     y=20     width=13     height=16     xoffset=-2     yoffset=3    xadvance=10     page=0  chnl=0 \r
+char id=127   x=146     y=20     width=12     height=15     xoffset=-2     yoffset=3    xadvance=10     page=0  chnl=0 \r
+char id=35   x=158     y=20     width=15     height=15     xoffset=-3     yoffset=3    xadvance=10     page=0  chnl=0 \r
+char id=92   x=173     y=20     width=11     height=15     xoffset=-3     yoffset=3    xadvance=6     page=0  chnl=0 \r
+char id=47   x=184     y=20     width=11     height=15     xoffset=-3     yoffset=3    xadvance=6     page=0  chnl=0 \r
+char id=59   x=195     y=20     width=8     height=15     xoffset=-3     yoffset=6    xadvance=4     page=0  chnl=0 \r
+char id=55   x=203     y=20     width=13     height=15     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=52   x=216     y=20     width=14     height=15     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=50   x=230     y=20     width=13     height=15     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=49   x=243     y=20     width=9     height=15     xoffset=-2     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=116   x=0     y=36     width=10     height=15     xoffset=-3     yoffset=4    xadvance=5     page=0  chnl=0 \r
+char id=108   x=10     y=36     width=6     height=15     xoffset=-2     yoffset=3    xadvance=4     page=0  chnl=0 \r
+char id=107   x=16     y=36     width=12     height=15     xoffset=-2     yoffset=3    xadvance=8     page=0  chnl=0 \r
+char id=105   x=28     y=36     width=7     height=15     xoffset=-2     yoffset=3    xadvance=4     page=0  chnl=0 \r
+char id=104   x=35     y=36     width=12     height=15     xoffset=-2     yoffset=3    xadvance=10     page=0  chnl=0 \r
+char id=102   x=47     y=36     width=11     height=15     xoffset=-3     yoffset=3    xadvance=5     page=0  chnl=0 \r
+char id=90   x=58     y=36     width=13     height=15     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=89   x=71     y=36     width=13     height=15     xoffset=-3     yoffset=3    xadvance=8     page=0  chnl=0 \r
+char id=88   x=84     y=36     width=14     height=15     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=87   x=98     y=36     width=19     height=15     xoffset=-3     yoffset=3    xadvance=15     page=0  chnl=0 \r
+char id=86   x=117     y=36     width=14     height=15     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=84   x=131     y=36     width=13     height=15     xoffset=-3     yoffset=3    xadvance=8     page=0  chnl=0 \r
+char id=82   x=144     y=36     width=13     height=15     xoffset=-2     yoffset=3    xadvance=10     page=0  chnl=0 \r
+char id=80   x=157     y=36     width=12     height=15     xoffset=-2     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=78   x=169     y=36     width=14     height=15     xoffset=-2     yoffset=3    xadvance=12     page=0  chnl=0 \r
+char id=77   x=183     y=36     width=17     height=15     xoffset=-2     yoffset=3    xadvance=14     page=0  chnl=0 \r
+char id=76   x=200     y=36     width=11     height=15     xoffset=-2     yoffset=3    xadvance=8     page=0  chnl=0 \r
+char id=75   x=211     y=36     width=13     height=15     xoffset=-2     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=73   x=224     y=36     width=10     height=15     xoffset=-3     yoffset=3    xadvance=5     page=0  chnl=0 \r
+char id=72   x=234     y=36     width=14     height=15     xoffset=-2     yoffset=3    xadvance=11     page=0  chnl=0 \r
+char id=70   x=0     y=51     width=11     height=15     xoffset=-2     yoffset=3    xadvance=8     page=0  chnl=0 \r
+char id=69   x=11     y=51     width=11     height=15     xoffset=-2     yoffset=3    xadvance=8     page=0  chnl=0 \r
+char id=68   x=22     y=51     width=14     height=15     xoffset=-2     yoffset=3    xadvance=11     page=0  chnl=0 \r
+char id=66   x=36     y=51     width=13     height=15     xoffset=-2     yoffset=3    xadvance=10     page=0  chnl=0 \r
+char id=65   x=49     y=51     width=15     height=15     xoffset=-3     yoffset=3    xadvance=10     page=0  chnl=0 \r
+char id=58   x=64     y=51     width=7     height=13     xoffset=-2     yoffset=6    xadvance=4     page=0  chnl=0 \r
+char id=117   x=71     y=51     width=12     height=13     xoffset=-2     yoffset=6    xadvance=10     page=0  chnl=0 \r
+char id=115   x=83     y=51     width=11     height=13     xoffset=-3     yoffset=6    xadvance=7     page=0  chnl=0 \r
+char id=111   x=94     y=51     width=13     height=13     xoffset=-3     yoffset=6    xadvance=9     page=0  chnl=0 \r
+char id=101   x=107     y=51     width=13     height=13     xoffset=-3     yoffset=6    xadvance=9     page=0  chnl=0 \r
+char id=99   x=120     y=51     width=12     height=13     xoffset=-3     yoffset=6    xadvance=7     page=0  chnl=0 \r
+char id=97   x=132     y=51     width=12     height=13     xoffset=-3     yoffset=6    xadvance=9     page=0  chnl=0 \r
+char id=60   x=144     y=51     width=13     height=12     xoffset=-3     yoffset=5    xadvance=9     page=0  chnl=0 \r
+char id=122   x=157     y=51     width=11     height=12     xoffset=-3     yoffset=6    xadvance=7     page=0  chnl=0 \r
+char id=120   x=168     y=51     width=13     height=12     xoffset=-3     yoffset=6    xadvance=8     page=0  chnl=0 \r
+char id=119   x=181     y=51     width=17     height=12     xoffset=-3     yoffset=6    xadvance=12     page=0  chnl=0 \r
+char id=118   x=198     y=51     width=13     height=12     xoffset=-3     yoffset=6    xadvance=8     page=0  chnl=0 \r
+char id=114   x=211     y=51     width=10     height=12     xoffset=-2     yoffset=6    xadvance=6     page=0  chnl=0 \r
+char id=110   x=221     y=51     width=12     height=12     xoffset=-2     yoffset=6    xadvance=10     page=0  chnl=0 \r
+char id=109   x=233     y=51     width=17     height=12     xoffset=-2     yoffset=6    xadvance=15     page=0  chnl=0 \r
+char id=94   x=0     y=66     width=13     height=11     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=62   x=13     y=66     width=13     height=11     xoffset=-3     yoffset=5    xadvance=9     page=0  chnl=0 \r
+char id=42   x=26     y=66     width=13     height=10     xoffset=-3     yoffset=3    xadvance=9     page=0  chnl=0 \r
+char id=43   x=39     y=66     width=13     height=10     xoffset=-3     yoffset=6    xadvance=9     page=0  chnl=0 \r
+char id=61   x=52     y=66     width=13     height=8     xoffset=-3     yoffset=7    xadvance=9     page=0  chnl=0 \r
+char id=39   x=65     y=66     width=6     height=8     xoffset=-2     yoffset=3    xadvance=3     page=0  chnl=0 \r
+char id=34   x=71     y=66     width=9     height=8     xoffset=-2     yoffset=3    xadvance=6     page=0  chnl=0 \r
+char id=44   x=80     y=66     width=8     height=7     xoffset=-3     yoffset=14    xadvance=4     page=0  chnl=0 \r
+char id=126   x=88     y=66     width=13     height=6     xoffset=-3     yoffset=8    xadvance=9     page=0  chnl=0 \r
+char id=46   x=101     y=66     width=7     height=6     xoffset=-2     yoffset=13    xadvance=4     page=0  chnl=0 \r
+char id=96   x=108     y=66     width=8     height=6     xoffset=0     yoffset=2    xadvance=9     page=0  chnl=0 \r
+char id=45   x=116     y=66     width=9     height=5     xoffset=-3     yoffset=10    xadvance=5     page=0  chnl=0 \r
+char id=95   x=125     y=66     width=13     height=4     xoffset=-4     yoffset=17    xadvance=6     page=0  chnl=0 \r
+kernings count=-1\r
diff --git a/extensions/particle-editor/data/default.png b/extensions/particle-editor/data/default.png
new file mode 100644 (file)
index 0000000..1a541b8
Binary files /dev/null and b/extensions/particle-editor/data/default.png differ
diff --git a/extensions/particle-editor/data/particle.png b/extensions/particle-editor/data/particle.png
new file mode 100644 (file)
index 0000000..632fcc1
Binary files /dev/null and b/extensions/particle-editor/data/particle.png differ
diff --git a/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/Chart.java b/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/Chart.java
new file mode 100644 (file)
index 0000000..149d78c
--- /dev/null
@@ -0,0 +1,327 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.awt.BasicStroke;\r
+import java.awt.Color;\r
+import java.awt.Font;\r
+import java.awt.FontMetrics;\r
+import java.awt.Graphics;\r
+import java.awt.Graphics2D;\r
+import java.awt.GridBagLayout;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.MouseMotionListener;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.JPanel;\r
+\r
+class Chart extends JPanel {\r
+       static private final int POINT_SIZE = 6;\r
+       static private final int POINT_SIZE_EXPANDED = 10;\r
+\r
+       ArrayList<Point> points = new ArrayList();\r
+       private int numberHeight;\r
+       int chartX, chartY;\r
+       int chartWidth, chartHeight;\r
+       int maxX, maxY;\r
+       int overIndex = -1;\r
+       int movingIndex = -1;\r
+       boolean isExpanded;\r
+       String title;\r
+\r
+       public Chart (String title) {\r
+               this.title = title;\r
+\r
+               setLayout(new GridBagLayout());\r
+\r
+               addMouseListener(new MouseAdapter() {\r
+                       public void mousePressed (MouseEvent event) {\r
+                               movingIndex = overIndex;\r
+                       }\r
+\r
+                       public void mouseReleased (MouseEvent event) {\r
+                               movingIndex = -1;\r
+                       }\r
+\r
+                       public void mouseClicked (MouseEvent event) {\r
+                               if (event.getClickCount() == 2) {\r
+                                       if (overIndex <= 0) return;\r
+                                       points.remove(overIndex);\r
+                                       pointsChanged();\r
+                                       repaint();\r
+                                       return;\r
+                               }\r
+                               if (movingIndex != -1) return;\r
+                               if (overIndex != -1) return;\r
+                               int mouseX = event.getX();\r
+                               int mouseY = event.getY();\r
+                               if (mouseX < chartX || mouseX > chartX + chartWidth) return;\r
+                               if (mouseY < chartY || mouseY > chartY + chartHeight) return;\r
+                               Point newPoint = pixelToPoint(mouseX, mouseY);\r
+                               int i = 0;\r
+                               Point lastPoint = null;\r
+                               for (Point point : points) {\r
+                                       if (point.x > newPoint.x) {\r
+                                               if (Math.abs(point.x - newPoint.x) < 0.001f) return;\r
+                                               if (lastPoint != null && Math.abs(lastPoint.x - newPoint.x) < 0.001f) return;\r
+                                               points.add(i, newPoint);\r
+                                               overIndex = i;\r
+                                               pointsChanged();\r
+                                               repaint();\r
+                                               return;\r
+                                       }\r
+                                       lastPoint = point;\r
+                                       i++;\r
+                               }\r
+                               overIndex = points.size();\r
+                               points.add(newPoint);\r
+                               pointsChanged();\r
+                               repaint();\r
+                       }\r
+               });\r
+               addMouseMotionListener(new MouseMotionListener() {\r
+                       public void mouseDragged (MouseEvent event) {\r
+                               if (movingIndex == -1) return;\r
+                               float nextX = movingIndex == points.size() - 1 ? maxX : points.get(movingIndex + 1).x - 0.001f;\r
+                               if (movingIndex == 0) nextX = 0;\r
+                               float prevX = movingIndex == 0 ? 0 : points.get(movingIndex - 1).x + 0.001f;\r
+                               Point point = points.get(movingIndex);\r
+                               point.x = Math.min(nextX, Math.max(prevX, (event.getX() - chartX) / (float)chartWidth * maxX));\r
+                               point.y = Math.min(maxY, Math.max(0, chartHeight - (event.getY() - chartY)) / (float)chartHeight * maxY);\r
+                               pointsChanged();\r
+                               repaint();\r
+                       }\r
+\r
+                       public void mouseMoved (MouseEvent event) {\r
+                               int mouseX = event.getX();\r
+                               int mouseY = event.getY();\r
+                               int oldIndex = overIndex;\r
+                               overIndex = -1;\r
+                               int pointSize = isExpanded ? POINT_SIZE_EXPANDED : POINT_SIZE;\r
+                               int i = 0;\r
+                               for (Point point : points) {\r
+                                       int x = chartX + (int)(chartWidth * (point.x / (float)maxX));\r
+                                       int y = chartY + chartHeight - (int)(chartHeight * (point.y / (float)maxY));\r
+                                       if (Math.abs(x - mouseX) <= pointSize && Math.abs(y - mouseY) <= pointSize) {\r
+                                               overIndex = i;\r
+                                               break;\r
+                                       }\r
+                                       i++;\r
+                               }\r
+                               if (overIndex != oldIndex) repaint();\r
+                       }\r
+               });\r
+       }\r
+\r
+       public void addPoint (float x, float y) {\r
+               points.add(new Point(x, y));\r
+       }\r
+\r
+       public void pointsChanged () {\r
+       }\r
+\r
+       public float[] getValuesX () {\r
+               float[] values = new float[points.size()];\r
+               int i = 0;\r
+               for (Point point : points)\r
+                       values[i++] = point.x;\r
+               return values;\r
+       }\r
+\r
+       public float[] getValuesY () {\r
+               float[] values = new float[points.size()];\r
+               int i = 0;\r
+               for (Point point : points)\r
+                       values[i++] = point.y;\r
+               return values;\r
+       }\r
+\r
+       public void setValues (float[] x, float[] y) {\r
+               points.clear();\r
+               for (int i = 0; i < x.length; i++)\r
+                       points.add(new Point(x[i], y[i]));\r
+       }\r
+\r
+       Point pixelToPoint (float x, float y) {\r
+               Point point = new Point();\r
+               point.x = Math.min(maxX, Math.max(0, x - chartX) / (float)chartWidth * maxX);\r
+               point.y = Math.min(maxY, Math.max(0, chartHeight - (y - chartY)) / (float)chartHeight * maxY);\r
+               return point;\r
+       }\r
+\r
+       Point pointToPixel (Point point) {\r
+               Point pixel = new Point();\r
+               pixel.x = chartX + (int)(chartWidth * (point.x / (float)maxX));\r
+               pixel.y = chartY + chartHeight - (int)(chartHeight * (point.y / (float)maxY));\r
+               return pixel;\r
+       }\r
+\r
+       protected void paintComponent (Graphics graphics) {\r
+               // setOpaque(true);\r
+               // setBackground(Color.red);\r
+               super.paintComponent(graphics);\r
+\r
+               Graphics2D g = (Graphics2D)graphics;\r
+               FontMetrics metrics = g.getFontMetrics();\r
+               if (numberHeight == 0) {\r
+                       numberHeight = getFont().layoutGlyphVector(g.getFontRenderContext(), new char[] {'0'}, 0, 1, Font.LAYOUT_LEFT_TO_RIGHT)\r
+                               .getGlyphPixelBounds(0, g.getFontRenderContext(), 0, 0).height;\r
+               }\r
+\r
+               int width = getWidth();\r
+               if (!isExpanded) width = Math.min(150, width);\r
+               width = Math.max(100, width);\r
+               int height = getHeight();\r
+               int maxAxisLabelWidth;\r
+               int yAxisWidth;\r
+               if (isExpanded) {\r
+                       maxAxisLabelWidth = metrics.stringWidth("100%");\r
+                       yAxisWidth = maxAxisLabelWidth + 8;\r
+                       chartX = yAxisWidth;\r
+                       chartY = numberHeight / 2 + 1;\r
+                       chartWidth = width - yAxisWidth - 2;\r
+                       chartHeight = height - chartY - numberHeight - 8;\r
+               } else {\r
+                       maxAxisLabelWidth = 0;\r
+                       yAxisWidth = 2;\r
+                       chartX = yAxisWidth;\r
+                       chartY = 2;\r
+                       chartWidth = width - yAxisWidth - 2;\r
+                       chartHeight = height - chartY - 3;\r
+               }\r
+\r
+               g.setColor(Color.white);\r
+               g.fillRect(chartX, chartY, chartWidth, chartHeight);\r
+               g.setColor(Color.black);\r
+               g.drawRect(chartX, chartY, chartWidth, chartHeight);\r
+\r
+               maxX = 1;\r
+               {\r
+                       int y = height;\r
+                       if (isExpanded)\r
+                               y -= numberHeight;\r
+                       else\r
+                               y += 5;\r
+                       int xSplit = (int)Math.min(10, chartWidth / (maxAxisLabelWidth * 1.5f));\r
+                       for (int i = 0; i <= xSplit; i++) {\r
+                               float percent = i / (float)xSplit;\r
+                               String label = axisLabel(maxX * percent);\r
+                               int labelWidth = metrics.stringWidth(label);\r
+                               int x = (int)(yAxisWidth + chartWidth * percent);\r
+                               if (i != 0 && i != xSplit) {\r
+                                       g.setColor(Color.lightGray);\r
+                                       g.drawLine(x, chartY + 1, x, chartY + chartHeight);\r
+                                       g.setColor(Color.black);\r
+                               }\r
+                               g.drawLine(x, y - 4, x, y - 8);\r
+                               if (isExpanded) {\r
+                                       x -= labelWidth / 2;\r
+                                       if (i == xSplit) x = Math.min(x, width - labelWidth);\r
+                                       g.drawString(label, x, y + numberHeight);\r
+                               }\r
+                       }\r
+               }\r
+\r
+               maxY = 1;\r
+               {\r
+                       int ySplit = isExpanded ? Math.min(10, chartHeight / (numberHeight * 3)) : 4;\r
+                       for (int i = 0; i <= ySplit; i++) {\r
+                               float percent = i / (float)ySplit;\r
+                               String label = axisLabel(maxY * percent);\r
+                               int labelWidth = metrics.stringWidth(label);\r
+                               int y = (int)(chartY + chartHeight - chartHeight * percent);\r
+                               if (isExpanded) g.drawString(label, yAxisWidth - 6 - labelWidth, y + numberHeight / 2);\r
+                               if (i != 0 && i != ySplit) {\r
+                                       g.setColor(Color.lightGray);\r
+                                       g.drawLine(chartX, y, chartX + chartWidth - 1, y);\r
+                                       g.setColor(Color.black);\r
+                               }\r
+                               g.drawLine(yAxisWidth - 4, y, yAxisWidth, y);\r
+                       }\r
+               }\r
+\r
+               {\r
+                       int titleWidth = metrics.stringWidth(title);\r
+                       int x = yAxisWidth + chartWidth / 2 - titleWidth / 2;\r
+                       int y = chartY + chartHeight / 2 - numberHeight / 2;\r
+                       g.setColor(Color.white);\r
+                       g.fillRect(x - 2, y - 2, titleWidth + 4, numberHeight + 4);\r
+                       g.setColor(Color.lightGray);\r
+                       g.drawString(title, x, y + numberHeight);\r
+               }\r
+\r
+               g.setColor(Color.blue);\r
+               g.setStroke(new BasicStroke(isExpanded ? 3 : 2));\r
+               int lastX = -1, lastY = -1;\r
+               for (Point point : points) {\r
+                       Point pixel = pointToPixel(point);\r
+                       if (lastX != -1) g.drawLine(lastX, lastY, (int)pixel.x, (int)pixel.y);\r
+                       lastX = (int)pixel.x;\r
+                       lastY = (int)pixel.y;\r
+               }\r
+               g.drawLine(lastX, lastY, chartX + chartWidth - 1, lastY);\r
+               for (int i = 0, n = points.size(); i < n; i++) {\r
+                       Point point = points.get(i);\r
+                       Point pixel = pointToPixel(point);\r
+                       if (overIndex == i)\r
+                               g.setColor(Color.red);\r
+                       else\r
+                               g.setColor(Color.black);\r
+                       String label = valueLabel(point.y);\r
+                       int labelWidth = metrics.stringWidth(label);\r
+                       int pointSize = isExpanded ? POINT_SIZE_EXPANDED : POINT_SIZE;\r
+                       int x = (int)pixel.x - pointSize / 2;\r
+                       int y = (int)pixel.y - pointSize / 2;\r
+                       g.fillOval(x, y, pointSize, pointSize);\r
+                       if (isExpanded) {\r
+                               g.setColor(Color.black);\r
+                               x = Math.max(chartX + 2, Math.min(chartX + chartWidth - labelWidth, x));\r
+                               y -= 3;\r
+                               if (y < chartY + numberHeight + 3)\r
+                                       y += 27;\r
+                               else if (n > 1) {\r
+                                       Point comparePoint = i == n - 1 ? points.get(i - 1) : points.get(i + 1);\r
+                                       if (y < chartY + chartHeight - 27 && comparePoint.y > point.y) y += 27;\r
+                               }\r
+                               g.drawString(label, x, y);\r
+                       }\r
+               }\r
+       }\r
+\r
+       private String valueLabel (float value) {\r
+               value = (int)(value * 1000) / 10f;\r
+               if (value % 1 == 0)\r
+                       return String.valueOf((int)value) + '%';\r
+               else\r
+                       return String.valueOf(value) + '%';\r
+       }\r
+\r
+       private String axisLabel (float value) {\r
+               value = (int)(value * 100);\r
+               if (value % 1 == 0)\r
+                       return String.valueOf((int)value) + '%';\r
+               else\r
+                       return String.valueOf(value) + '%';\r
+       }\r
+\r
+       static public class Point {\r
+               public float x;\r
+               public float y;\r
+\r
+               public Point () {\r
+               }\r
+\r
+               public Point (float x, float y) {\r
+                       this.x = x;\r
+                       this.y = y;\r
+               }\r
+       }\r
+\r
+       public boolean isExpanded () {\r
+               return isExpanded;\r
+       }\r
+\r
+       public void setExpanded (boolean isExpanded) {\r
+               this.isExpanded = isExpanded;\r
+       }\r
+}\r
diff --git a/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/CountPanel.java b/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/CountPanel.java
new file mode 100644 (file)
index 0000000..5552572
--- /dev/null
@@ -0,0 +1,64 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.awt.GridBagConstraints;\r
+import java.awt.Insets;\r
+\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JSpinner;\r
+import javax.swing.SpinnerNumberModel;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+\r
+class CountPanel extends EditorPanel {\r
+       JSpinner maxSpinner, minSpinner;\r
+\r
+       public CountPanel (final ParticleEditor editor) {\r
+               super("Count", null);\r
+\r
+               initializeComponents();\r
+\r
+               maxSpinner.setValue(editor.getEmitter().getMaxParticleCount());\r
+               maxSpinner.addChangeListener(new ChangeListener() {\r
+                       public void stateChanged (ChangeEvent event) {\r
+                               synchronized (editor.effect) {\r
+                                       editor.getEmitter().setMaxParticleCount((Integer)maxSpinner.getValue());\r
+                               }\r
+                       }\r
+               });\r
+\r
+               minSpinner.setValue(editor.getEmitter().getMinParticleCount());\r
+               minSpinner.addChangeListener(new ChangeListener() {\r
+                       public void stateChanged (ChangeEvent event) {\r
+                               synchronized (editor.effect) {\r
+                                       editor.getEmitter().setMinParticleCount((Integer)minSpinner.getValue());\r
+                               }\r
+                       }\r
+               });\r
+       }\r
+\r
+       private void initializeComponents () {\r
+               JPanel contentPanel = getContentPanel();\r
+               {\r
+                       JLabel label = new JLabel("Min:");\r
+                       contentPanel.add(label, new GridBagConstraints(0, 1, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                               new Insets(0, 0, 0, 6), 0, 0));\r
+               }\r
+               {\r
+                       minSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 99999, 1));\r
+                       contentPanel.add(minSpinner, new GridBagConstraints(1, 1, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,\r
+                               new Insets(0, 0, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       JLabel label = new JLabel("Max:");\r
+                       contentPanel.add(label, new GridBagConstraints(2, 1, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                               new Insets(0, 12, 0, 6), 0, 0));\r
+               }\r
+               {\r
+                       maxSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 99999, 1));\r
+                       contentPanel.add(maxSpinner, new GridBagConstraints(3, 1, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,\r
+                               new Insets(0, 0, 0, 0), 0, 0));\r
+               }\r
+       }\r
+}\r
diff --git a/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/EditorPanel.java b/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/EditorPanel.java
new file mode 100644 (file)
index 0000000..0539d09
--- /dev/null
@@ -0,0 +1,139 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.awt.Cursor;\r
+import java.awt.Font;\r
+import java.awt.GridBagConstraints;\r
+import java.awt.GridBagLayout;\r
+import java.awt.Insets;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JToggleButton;\r
+\r
+import com.badlogic.gdx.graphics.particles.ParticleEmitter.ParticleValue;\r
+\r
+class EditorPanel extends JPanel {\r
+       private final String name;\r
+       private final ParticleValue value;\r
+       private JPanel titlePanel;\r
+       JToggleButton activeButton;\r
+       private JPanel contentPanel;\r
+       JToggleButton advancedButton;\r
+       JPanel advancedPanel;\r
+       private boolean hasAdvanced;\r
+\r
+       public EditorPanel (String name, ParticleValue value) {\r
+               this.name = name;\r
+               this.value = value;\r
+\r
+               initializeComponents();\r
+\r
+               titlePanel.addMouseListener(new MouseAdapter() {\r
+                       public void mouseClicked (MouseEvent event) {\r
+                               if (!activeButton.isVisible()) return;\r
+                               activeButton.setSelected(!activeButton.isSelected());\r
+                               updateActive();\r
+                       }\r
+               });\r
+               activeButton.addActionListener(new ActionListener() {\r
+                       public void actionPerformed (ActionEvent event) {\r
+                               updateActive();\r
+                       }\r
+               });\r
+               advancedButton.addActionListener(new ActionListener() {\r
+                       public void actionPerformed (ActionEvent event) {\r
+                               advancedPanel.setVisible(advancedButton.isSelected());\r
+                       }\r
+               });\r
+\r
+               if (value != null) {\r
+                       activeButton.setSelected(value.isActive());\r
+                       updateActive();\r
+               }\r
+\r
+               boolean alwaysActive = value == null ? true : value.isAlwaysActive();\r
+               activeButton.setVisible(!alwaysActive);\r
+               if (alwaysActive) contentPanel.setVisible(true);\r
+               if (alwaysActive) titlePanel.setCursor(null);\r
+       }\r
+\r
+       void updateActive () {\r
+               contentPanel.setVisible(activeButton.isSelected());\r
+               advancedPanel.setVisible(activeButton.isSelected() && advancedButton.isSelected());\r
+               advancedButton.setVisible(activeButton.isSelected() && hasAdvanced);\r
+               if (value != null) value.setActive(activeButton.isSelected());\r
+       }\r
+\r
+       public void update (ParticleEditor editor) {\r
+       }\r
+\r
+       public void setHasAdvanced (boolean hasAdvanced) {\r
+               this.hasAdvanced = hasAdvanced;\r
+               advancedButton.setVisible(hasAdvanced && (value.isActive() || value.isAlwaysActive()));\r
+       }\r
+\r
+       public JPanel getContentPanel () {\r
+               return contentPanel;\r
+       }\r
+\r
+       public JPanel getAdvancedPanel () {\r
+               return advancedPanel;\r
+       }\r
+\r
+       public String getName () {\r
+               return name;\r
+       }\r
+\r
+       public void setEmbedded () {\r
+               GridBagLayout layout = (GridBagLayout)getLayout();\r
+               GridBagConstraints constraints = layout.getConstraints(contentPanel);\r
+               constraints.insets = new Insets(0, 0, 0, 0);\r
+               layout.setConstraints(contentPanel, constraints);\r
+\r
+               titlePanel.setVisible(false);\r
+       }\r
+\r
+       private void initializeComponents () {\r
+               setLayout(new GridBagLayout());\r
+               {\r
+                       titlePanel = new JPanel(new GridBagLayout());\r
+                       add(titlePanel, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,\r
+                               new Insets(3, 0, 3, 0), 0, 0));\r
+                       titlePanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));\r
+                       {\r
+                               JLabel label = new JLabel(name);\r
+                               titlePanel.add(label, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE,\r
+                                       new Insets(3, 6, 3, 6), 0, 0));\r
+                               label.setFont(label.getFont().deriveFont(Font.BOLD));\r
+                       }\r
+                       {\r
+                               advancedButton = new JToggleButton("Advanced");\r
+                               titlePanel.add(advancedButton, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,\r
+                                       GridBagConstraints.NONE, new Insets(0, 0, 0, 6), 0, 0));\r
+                               advancedButton.setVisible(false);\r
+                       }\r
+                       {\r
+                               activeButton = new JToggleButton("Active");\r
+                               titlePanel.add(activeButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,\r
+                                       GridBagConstraints.NONE, new Insets(0, 0, 0, 6), 0, 0));\r
+                       }\r
+               }\r
+               {\r
+                       contentPanel = new JPanel(new GridBagLayout());\r
+                       add(contentPanel, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,\r
+                               new Insets(0, 6, 6, 6), 0, 0));\r
+                       contentPanel.setVisible(false);\r
+               }\r
+               {\r
+                       advancedPanel = new JPanel(new GridBagLayout());\r
+                       add(advancedPanel, new GridBagConstraints(1, 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,\r
+                               new Insets(0, 6, 6, 6), 0, 0));\r
+                       advancedPanel.setVisible(false);\r
+               }\r
+       }\r
+}\r
diff --git a/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/EffectPanel.java b/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/EffectPanel.java
new file mode 100644 (file)
index 0000000..e540c7a
--- /dev/null
@@ -0,0 +1,269 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.awt.FileDialog;\r
+import java.awt.GridBagConstraints;\r
+import java.awt.GridBagLayout;\r
+import java.awt.Insets;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.io.File;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.JButton;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JSeparator;\r
+import javax.swing.JTable;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.event.ListSelectionEvent;\r
+import javax.swing.event.ListSelectionListener;\r
+import javax.swing.event.TableModelEvent;\r
+import javax.swing.event.TableModelListener;\r
+import javax.swing.table.DefaultTableModel;\r
+\r
+import com.badlogic.gdx.Files.FileType;\r
+import com.badlogic.gdx.Gdx;\r
+\r
+class EffectPanel extends JPanel {\r
+       ParticleEditor editor;\r
+       JTable emitterTable;\r
+       DefaultTableModel emitterTableModel;\r
+       int editIndex;\r
+       String lastDir;\r
+\r
+       public EffectPanel (ParticleEditor editor) {\r
+               this.editor = editor;\r
+               initializeComponents();\r
+       }\r
+\r
+       public ParticleEmitter newEmitter (String name, boolean select) {\r
+               final ParticleEmitter emitter = new ParticleEmitter();\r
+               emitter.setFlip(false, true);\r
+               emitter.setMaxParticleCount(15);\r
+               emitter.setImagePath("data/particle.png");\r
+               synchronized (editor.effect) {\r
+                       ArrayList<ParticleEmitter> emitters = editor.effect.getEmitters();\r
+                       if (emitters.isEmpty())\r
+                               emitter.setPosition(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);\r
+                       else {\r
+                               ParticleEmitter p = emitters.get(0);\r
+                               emitter.setPosition(p.getX(), p.getY());\r
+                       }\r
+                       emitters.add(emitter);\r
+               }\r
+               emitterTableModel.addRow(new Object[] {name, true});\r
+               if (select) {\r
+                       editor.reloadRows();\r
+                       int row = emitterTableModel.getRowCount() - 1;\r
+                       emitterTable.getSelectionModel().setSelectionInterval(row, row);\r
+               }\r
+               return emitter;\r
+       }\r
+\r
+       void emitterSelected () {\r
+               int row = emitterTable.getSelectedRow();\r
+               if (row == -1) {\r
+                       row = editIndex;\r
+                       emitterTable.getSelectionModel().setSelectionInterval(row, row);\r
+               }\r
+               if (row == editIndex) return;\r
+               editIndex = row;\r
+               editor.reloadRows();\r
+       }\r
+\r
+       void openEffect () {\r
+               FileDialog dialog = new FileDialog(editor, "Open Effect", FileDialog.LOAD);\r
+               if (lastDir != null) dialog.setDirectory(lastDir);\r
+               dialog.setVisible(true);\r
+               final String file = dialog.getFile();\r
+               final String dir = dialog.getDirectory();\r
+               if (dir == null || file == null || file.trim().length() == 0) return;\r
+               lastDir = dir;\r
+               synchronized (editor.effect) {\r
+                       ParticleEffect effect = new ParticleEffect();\r
+                       try {\r
+                               effect.loadEmitters(Gdx.files.getFileHandle(new File(dir, file).getAbsolutePath(), FileType.Absolute));\r
+                               editor.effect = effect;\r
+                               emitterTableModel.getDataVector().removeAllElements();\r
+                               editor.particleData.clear();\r
+                       } catch (Exception ex) {\r
+                               System.out.println("Error loading effect: " + new File(dir, file).getAbsolutePath());\r
+                               ex.printStackTrace();\r
+                               JOptionPane.showMessageDialog(editor, "Error opening effect.");\r
+                               return;\r
+                       }\r
+                       for (ParticleEmitter emitter : effect.getEmitters()) {\r
+                               emitter.setPosition(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);\r
+                               emitterTableModel.addRow(new Object[] {emitter.getName(), true});\r
+                       }\r
+               }\r
+               editIndex = 0;\r
+               emitterTable.getSelectionModel().setSelectionInterval(editIndex, editIndex);\r
+               editor.reloadRows();\r
+       }\r
+\r
+       void saveEffect () {\r
+               FileDialog dialog = new FileDialog(editor, "Save Effect", FileDialog.SAVE);\r
+               if (lastDir != null) dialog.setDirectory(lastDir);\r
+               dialog.setVisible(true);\r
+               String file = dialog.getFile();\r
+               String dir = dialog.getDirectory();\r
+               if (dir == null || file == null || file.trim().length() == 0) return;\r
+               lastDir = dir;\r
+               synchronized (editor.effect) {\r
+                       int index = 0;\r
+                       for (ParticleEmitter particles : editor.effect.getEmitters())\r
+                               particles.setName((String)emitterTableModel.getValueAt(index++, 0));\r
+                       try {\r
+                               editor.effect.save(new File(dir, file));\r
+                       } catch (Exception ex) {\r
+                               System.out.println("Error saving effect: " + new File(dir, file).getAbsolutePath());\r
+                               ex.printStackTrace();\r
+                               JOptionPane.showMessageDialog(editor, "Error saving effect.");\r
+                       }\r
+               }\r
+       }\r
+\r
+       void deleteEmitter () {\r
+               synchronized (editor.effect) {\r
+                       if (editor.effect.getEmitters().size() == 1) return;\r
+                       int row = emitterTable.getSelectedRow();\r
+                       if (row == -1) return;\r
+                       if (row <= editIndex) {\r
+                               int oldEditIndex = editIndex;\r
+                               editIndex = Math.max(0, editIndex - 1);\r
+                               if (oldEditIndex == row) editor.reloadRows();\r
+                       }\r
+                       editor.effect.getEmitters().remove(row);\r
+                       emitterTableModel.removeRow(row);\r
+               }\r
+               emitterTable.getSelectionModel().setSelectionInterval(editIndex, editIndex);\r
+       }\r
+\r
+       void move (int direction) {\r
+               if (direction < 0 && editIndex == 0) return;\r
+               synchronized (editor.effect) {\r
+                       ArrayList<ParticleEmitter> emitters = editor.effect.getEmitters();\r
+                       if (direction > 0 && editIndex == emitters.size() - 1) return;\r
+                       int insertIndex = editIndex + direction;\r
+                       Object name = emitterTableModel.getValueAt(editIndex, 0);\r
+                       emitterTableModel.removeRow(editIndex);\r
+                       ParticleEmitter particles = emitters.remove(editIndex);\r
+                       emitterTableModel.insertRow(insertIndex, new Object[] {name});\r
+                       emitters.add(insertIndex, particles);\r
+                       editIndex = insertIndex;\r
+               }\r
+               emitterTable.getSelectionModel().setSelectionInterval(editIndex, editIndex);\r
+       }\r
+\r
+       void emitterChecked (int index, boolean checked) {\r
+               synchronized (editor.effect) {\r
+                       editor.setEnabled(editor.effect.getEmitters().get(index), checked);\r
+                       editor.effect.start();\r
+               }\r
+       }\r
+\r
+       private void initializeComponents () {\r
+               setLayout(new GridBagLayout());\r
+               {\r
+                       JPanel sideButtons = new JPanel(new GridBagLayout());\r
+                       add(sideButtons, new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,\r
+                               new Insets(0, 0, 0, 0), 0, 0));\r
+                       {\r
+                               JButton newButton = new JButton("New");\r
+                               sideButtons.add(newButton, new GridBagConstraints(0, -1, 1, 1, 0, 0, GridBagConstraints.CENTER,\r
+                                       GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0));\r
+                               newButton.addActionListener(new ActionListener() {\r
+                                       public void actionPerformed (ActionEvent event) {\r
+                                               newEmitter("Untitled", true);\r
+                                       }\r
+                               });\r
+                       }\r
+                       {\r
+                               JButton deleteButton = new JButton("Delete");\r
+                               sideButtons.add(deleteButton, new GridBagConstraints(0, -1, 1, 1, 0, 0, GridBagConstraints.CENTER,\r
+                                       GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0));\r
+                               deleteButton.addActionListener(new ActionListener() {\r
+                                       public void actionPerformed (ActionEvent event) {\r
+                                               deleteEmitter();\r
+                                       }\r
+                               });\r
+                       }\r
+                       {\r
+                               sideButtons.add(new JSeparator(JSeparator.HORIZONTAL), new GridBagConstraints(0, -1, 1, 1, 0, 0,\r
+                                       GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0));\r
+                       }\r
+                       {\r
+                               JButton saveButton = new JButton("Save");\r
+                               sideButtons.add(saveButton, new GridBagConstraints(0, -1, 1, 1, 0, 0, GridBagConstraints.CENTER,\r
+                                       GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0));\r
+                               saveButton.addActionListener(new ActionListener() {\r
+                                       public void actionPerformed (ActionEvent event) {\r
+                                               saveEffect();\r
+                                       }\r
+                               });\r
+                       }\r
+                       {\r
+                               JButton openButton = new JButton("Open");\r
+                               sideButtons.add(openButton, new GridBagConstraints(0, -1, 1, 1, 0, 0, GridBagConstraints.CENTER,\r
+                                       GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0));\r
+                               openButton.addActionListener(new ActionListener() {\r
+                                       public void actionPerformed (ActionEvent event) {\r
+                                               openEffect();\r
+                                       }\r
+                               });\r
+                       }\r
+                       {\r
+                               JButton upButton = new JButton("Up");\r
+                               sideButtons.add(upButton, new GridBagConstraints(0, -1, 1, 1, 0, 1, GridBagConstraints.SOUTH,\r
+                                       GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0));\r
+                               upButton.addActionListener(new ActionListener() {\r
+                                       public void actionPerformed (ActionEvent event) {\r
+                                               move(-1);\r
+                                       }\r
+                               });\r
+                       }\r
+                       {\r
+                               JButton downButton = new JButton("Down");\r
+                               sideButtons.add(downButton, new GridBagConstraints(0, -1, 1, 1, 0, 0, GridBagConstraints.CENTER,\r
+                                       GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));\r
+                               downButton.addActionListener(new ActionListener() {\r
+                                       public void actionPerformed (ActionEvent event) {\r
+                                               move(1);\r
+                                       }\r
+                               });\r
+                       }\r
+               }\r
+               {\r
+                       JScrollPane scroll = new JScrollPane();\r
+                       add(scroll, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0,\r
+                               0, 0, 6), 0, 0));\r
+                       {\r
+                               emitterTable = new JTable() {\r
+                                       public Class getColumnClass (int column) {\r
+                                               return column == 1 ? Boolean.class : super.getColumnClass(column);\r
+                                       }\r
+                               };\r
+                               emitterTable.getTableHeader().setReorderingAllowed(false);\r
+                               emitterTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+                               scroll.setViewportView(emitterTable);\r
+                               emitterTableModel = new DefaultTableModel(new String[0][0], new String[] {"Emitter", ""});\r
+                               emitterTable.setModel(emitterTableModel);\r
+                               emitterTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {\r
+                                       public void valueChanged (ListSelectionEvent event) {\r
+                                               if (event.getValueIsAdjusting()) return;\r
+                                               emitterSelected();\r
+                                       }\r
+                               });\r
+                               emitterTableModel.addTableModelListener(new TableModelListener() {\r
+                                       public void tableChanged (TableModelEvent event) {\r
+                                               if (event.getColumn() != 1) return;\r
+                                               emitterChecked(event.getFirstRow(), (Boolean)emitterTable.getValueAt(event.getFirstRow(), 1));\r
+                                       }\r
+                               });\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/GradientPanel.java b/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/GradientPanel.java
new file mode 100644 (file)
index 0000000..0c3ed53
--- /dev/null
@@ -0,0 +1,378 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.awt.Color;\r
+import java.awt.Dimension;\r
+import java.awt.GradientPaint;\r
+import java.awt.Graphics;\r
+import java.awt.Graphics2D;\r
+import java.awt.GridBagConstraints;\r
+import java.awt.GridBagLayout;\r
+import java.awt.Insets;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.MouseMotionAdapter;\r
+import java.util.ArrayList;\r
+\r
+import javax.swing.BorderFactory;\r
+import javax.swing.JColorChooser;\r
+import javax.swing.JPanel;\r
+import javax.swing.JSlider;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+\r
+import com.badlogic.gdx.graphics.particles.ParticleEmitter.GradientColorValue;\r
+\r
+class GradientPanel extends EditorPanel {\r
+       private final GradientColorValue value;\r
+       private GradientEditor gradientEditor;\r
+       ColorSlider saturationSlider, lightnessSlider;\r
+       JPanel colorPanel;\r
+       private ColorSlider hueSlider;\r
+\r
+       public GradientPanel (String name, GradientColorValue value) {\r
+               super(name, value);\r
+               this.value = value;\r
+\r
+               initializeComponents();\r
+\r
+               gradientEditor.percentages.clear();\r
+               for (float percent : value.getTimeline())\r
+                       gradientEditor.percentages.add(percent);\r
+\r
+               gradientEditor.colors.clear();\r
+               float[] colors = value.getColors();\r
+               for (int i = 0; i < colors.length;) {\r
+                       float r = colors[i++];\r
+                       float g = colors[i++];\r
+                       float b = colors[i++];\r
+                       gradientEditor.colors.add(new Color(r, g, b));\r
+               }\r
+               if (gradientEditor.colors.isEmpty() || gradientEditor.percentages.isEmpty()) {\r
+                       gradientEditor.percentages.clear();\r
+                       gradientEditor.percentages.add(0f);\r
+                       gradientEditor.percentages.add(1f);\r
+                       gradientEditor.colors.clear();\r
+                       gradientEditor.colors.add(Color.white);\r
+               }\r
+               setColor(gradientEditor.colors.get(0));\r
+       }\r
+\r
+       public Dimension getPreferredSize () {\r
+               Dimension size = super.getPreferredSize();\r
+               size.width = 10;\r
+               return size;\r
+       }\r
+\r
+       private void initializeComponents () {\r
+               JPanel contentPanel = getContentPanel();\r
+               {\r
+                       gradientEditor = new GradientEditor() {\r
+                               public void handleSelected (Color color) {\r
+                                       GradientPanel.this.setColor(color);\r
+                               }\r
+                       };\r
+                       contentPanel.add(gradientEditor, new GridBagConstraints(0, 1, 3, 1, 1.0, 0.0, GridBagConstraints.CENTER,\r
+                               GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 10));\r
+               }\r
+               {\r
+                       hueSlider = new ColorSlider(new Color[] {Color.red, Color.yellow, Color.green, Color.cyan, Color.blue, Color.magenta,\r
+                               Color.red}) {\r
+                               protected void colorPicked () {\r
+                                       saturationSlider.setColors(new Color[] {new Color(Color.HSBtoRGB(getPercentage(), 1, 1)), Color.white});\r
+                                       updateColor();\r
+                               }\r
+                       };\r
+                       contentPanel.add(hueSlider, new GridBagConstraints(1, 2, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER,\r
+                               GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0));\r
+               }\r
+               {\r
+                       saturationSlider = new ColorSlider(new Color[] {Color.red, Color.white}) {\r
+                               protected void colorPicked () {\r
+                                       updateColor();\r
+                               }\r
+                       };\r
+                       contentPanel.add(saturationSlider, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,\r
+                               GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 6), 0, 0));\r
+               }\r
+               {\r
+                       lightnessSlider = new ColorSlider(new Color[0]) {\r
+                               protected void colorPicked () {\r
+                                       updateColor();\r
+                               }\r
+                       };\r
+                       contentPanel.add(lightnessSlider, new GridBagConstraints(2, 3, 1, 1, 1, 0.0, GridBagConstraints.CENTER,\r
+                               GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       colorPanel = new JPanel() {\r
+                               public Dimension getPreferredSize () {\r
+                                       Dimension size = super.getPreferredSize();\r
+                                       size.width = 52;\r
+                                       return size;\r
+                               }\r
+                       };\r
+                       contentPanel.add(colorPanel, new GridBagConstraints(0, 2, 1, 2, 0.0, 0.0, GridBagConstraints.CENTER,\r
+                               GridBagConstraints.BOTH, new Insets(3, 0, 0, 6), 0, 0));\r
+               }\r
+\r
+               colorPanel.addMouseListener(new MouseAdapter() {\r
+                       public void mouseClicked (MouseEvent e) {\r
+                               Color color = JColorChooser.showDialog(colorPanel, "Set Color", colorPanel.getBackground());\r
+                               if (color != null) setColor(color);\r
+                       }\r
+               });\r
+               colorPanel.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.black));\r
+       }\r
+\r
+       public void setColor (Color color) {\r
+               float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);\r
+               hueSlider.setPercentage(hsb[0]);\r
+               saturationSlider.setPercentage(1 - hsb[1]);\r
+               lightnessSlider.setPercentage(1 - hsb[2]);\r
+               colorPanel.setBackground(color);\r
+       }\r
+\r
+       void updateColor () {\r
+               Color color = new Color(Color.HSBtoRGB(hueSlider.getPercentage(), 1 - saturationSlider.getPercentage(), 1));\r
+               lightnessSlider.setColors(new Color[] {color, Color.black});\r
+               color = new Color(Color.HSBtoRGB(hueSlider.getPercentage(), 1 - saturationSlider.getPercentage(), 1 - lightnessSlider\r
+                       .getPercentage()));\r
+               colorPanel.setBackground(color);\r
+               gradientEditor.setColor(color);\r
+\r
+               float[] colors = new float[gradientEditor.colors.size() * 3];\r
+               int i = 0;\r
+               for (Color c : gradientEditor.colors) {\r
+                       colors[i++] = c.getRed() / 255f;\r
+                       colors[i++] = c.getGreen() / 255f;\r
+                       colors[i++] = c.getBlue() / 255f;\r
+               }\r
+               float[] percentages = new float[gradientEditor.percentages.size()];\r
+               i = 0;\r
+               for (Float percent : gradientEditor.percentages)\r
+                       percentages[i++] = percent;\r
+               value.setColors(colors);\r
+               value.setTimeline(percentages);\r
+       }\r
+\r
+       public class GradientEditor extends JPanel {\r
+               ArrayList<Color> colors = new ArrayList();\r
+               ArrayList<Float> percentages = new ArrayList();\r
+\r
+               int handleWidth = 12;\r
+               int handleHeight = 12;\r
+               int gradientX = handleWidth / 2;\r
+               int gradientY = 0;\r
+               int gradientWidth;\r
+               int gradientHeight;\r
+               int dragIndex = -1;\r
+               int selectedIndex;\r
+\r
+               public GradientEditor () {\r
+                       setPreferredSize(new Dimension(100, 30));\r
+\r
+                       addMouseListener(new MouseAdapter() {\r
+                               public void mousePressed (MouseEvent event) {\r
+                                       dragIndex = -1;\r
+                                       int mouseX = event.getX();\r
+                                       int mouseY = event.getY();\r
+                                       int y = gradientY + gradientHeight;\r
+                                       for (int i = 0, n = colors.size(); i < n; i++) {\r
+                                               int x = gradientX + (int)(percentages.get(i) * gradientWidth) - handleWidth / 2;\r
+                                               if (mouseX >= x && mouseX <= x + handleWidth && mouseY >= gradientY && mouseY <= y + handleHeight) {\r
+                                                       dragIndex = selectedIndex = i;\r
+                                                       handleSelected(colors.get(selectedIndex));\r
+                                                       repaint();\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               public void mouseReleased (MouseEvent event) {\r
+                                       if (dragIndex != -1) {\r
+                                               dragIndex = -1;\r
+                                               repaint();\r
+                                       }\r
+                               }\r
+\r
+                               public void mouseClicked (MouseEvent event) {\r
+                                       int mouseX = event.getX();\r
+                                       int mouseY = event.getY();\r
+                                       if (event.getClickCount() == 2) {\r
+                                               if (percentages.size() <= 1) return;\r
+                                               if (selectedIndex == -1 || selectedIndex == 0) return;\r
+                                               int y = gradientY + gradientHeight;\r
+                                               int x = gradientX + (int)(percentages.get(selectedIndex) * gradientWidth) - handleWidth / 2;\r
+                                               if (mouseX >= x && mouseX <= x + handleWidth && mouseY >= gradientY && mouseY <= y + handleHeight) {\r
+                                                       percentages.remove(selectedIndex);\r
+                                                       colors.remove(selectedIndex);\r
+                                                       selectedIndex--;\r
+                                                       dragIndex = selectedIndex;\r
+                                                       if (percentages.size() == 2) percentages.set(1, 1f);\r
+                                                       handleSelected(colors.get(selectedIndex));\r
+                                                       repaint();\r
+                                               }\r
+                                               return;\r
+                                       }\r
+                                       if (mouseX < gradientX || mouseX > gradientX + gradientWidth) return;\r
+                                       if (mouseY < gradientY || mouseY > gradientY + gradientHeight) return;\r
+                                       float percent = (event.getX() - gradientX) / (float)gradientWidth;\r
+                                       if (percentages.size() == 1) percent = 1f;\r
+                                       for (int i = 0, n = percentages.size(); i <= n; i++) {\r
+                                               if (i == n || percent < percentages.get(i)) {\r
+                                                       percentages.add(i, percent);\r
+                                                       colors.add(i, colors.get(i - 1));\r
+                                                       dragIndex = selectedIndex = i;\r
+                                                       handleSelected(colors.get(selectedIndex));\r
+                                                       repaint();\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       });\r
+                       addMouseMotionListener(new MouseMotionAdapter() {\r
+                               public void mouseDragged (MouseEvent event) {\r
+                                       if (dragIndex == -1 || dragIndex == 0 || dragIndex == percentages.size() - 1) return;\r
+                                       float percent = (event.getX() - gradientX) / (float)gradientWidth;\r
+                                       percent = Math.max(percent, percentages.get(dragIndex - 1) + 0.01f);\r
+                                       percent = Math.min(percent, percentages.get(dragIndex + 1) - 0.01f);\r
+                                       percentages.set(dragIndex, percent);\r
+                                       repaint();\r
+                               }\r
+                       });\r
+               }\r
+\r
+               public void setColor (Color color) {\r
+                       if (selectedIndex == -1) return;\r
+                       colors.set(selectedIndex, color);\r
+                       repaint();\r
+               }\r
+\r
+               public void handleSelected (Color color) {\r
+               }\r
+\r
+               protected void paintComponent (Graphics graphics) {\r
+                       super.paintComponent(graphics);\r
+                       Graphics2D g = (Graphics2D)graphics;\r
+                       int width = getWidth() - 1;\r
+                       int height = getHeight();\r
+\r
+                       gradientWidth = width - handleWidth;\r
+                       gradientHeight = height - 16;\r
+\r
+                       g.translate(gradientX, gradientY);\r
+                       for (int i = 0, n = colors.size() == 1 ? 1 : colors.size() - 1; i < n; i++) {\r
+                               Color color1 = colors.get(i);\r
+                               Color color2 = colors.size() == 1 ? color1 : colors.get(i + 1);\r
+                               float percent1 = percentages.get(i);\r
+                               float percent2 = colors.size() == 1 ? 1 : percentages.get(i + 1);\r
+                               int point1 = (int)(percent1 * gradientWidth);\r
+                               int point2 = (int)Math.ceil(percent2 * gradientWidth);\r
+                               g.setPaint(new GradientPaint(point1, 0, color1, point2, 0, color2, false));\r
+                               g.fillRect(point1, 0, point2 - point1, gradientHeight);\r
+                       }\r
+                       g.setPaint(null);\r
+                       g.setColor(Color.black);\r
+                       g.drawRect(0, 0, gradientWidth, gradientHeight);\r
+\r
+                       int y = gradientHeight;\r
+                       int[] yPoints = new int[3];\r
+                       yPoints[0] = y;\r
+                       yPoints[1] = y + handleHeight;\r
+                       yPoints[2] = y + handleHeight;\r
+                       int[] xPoints = new int[3];\r
+                       for (int i = 0, n = colors.size(); i < n; i++) {\r
+                               int x = (int)(percentages.get(i) * gradientWidth);\r
+                               xPoints[0] = x;\r
+                               xPoints[1] = x - handleWidth / 2;\r
+                               xPoints[2] = x + handleWidth / 2;\r
+                               if (i == selectedIndex) {\r
+                                       g.setColor(colors.get(i));\r
+                                       g.fillPolygon(xPoints, yPoints, 3);\r
+                                       g.fillRect(xPoints[1], yPoints[1] + 2, handleWidth + 1, 2);\r
+                                       g.setColor(Color.black);\r
+                               }\r
+                               g.drawPolygon(xPoints, yPoints, 3);\r
+                       }\r
+                       g.translate(-gradientX, -gradientY);\r
+               }\r
+       }\r
+\r
+       static public class ColorSlider extends JPanel {\r
+               Color[] paletteColors;\r
+               JSlider slider;\r
+               private ColorPicker colorPicker;\r
+\r
+               public ColorSlider (Color[] paletteColors) {\r
+                       this.paletteColors = paletteColors;\r
+                       setLayout(new GridBagLayout());\r
+                       {\r
+                               slider = new JSlider(0, 1000, 0);\r
+                               slider.setPaintTrack(false);\r
+                               add(slider, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,\r
+                                       new Insets(0, 6, 0, 6), 0, 0));\r
+                       }\r
+                       {\r
+                               colorPicker = new ColorPicker();\r
+                               add(colorPicker, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,\r
+                                       GridBagConstraints.HORIZONTAL, new Insets(0, 6, 0, 6), 0, 0));\r
+                       }\r
+\r
+                       slider.addChangeListener(new ChangeListener() {\r
+                               public void stateChanged (ChangeEvent event) {\r
+                                       colorPicked();\r
+                               }\r
+                       });\r
+               }\r
+\r
+               public Dimension getPreferredSize () {\r
+                       Dimension size = super.getPreferredSize();\r
+                       size.width = 10;\r
+                       return size;\r
+               }\r
+\r
+               public void setPercentage (float percent) {\r
+                       slider.setValue((int)(1000 * percent));\r
+               }\r
+\r
+               public float getPercentage () {\r
+                       return slider.getValue() / 1000f;\r
+               }\r
+\r
+               protected void colorPicked () {\r
+               }\r
+\r
+               public void setColors (Color[] colors) {\r
+                       paletteColors = colors;\r
+                       repaint();\r
+               }\r
+\r
+               public class ColorPicker extends JPanel {\r
+                       public ColorPicker () {\r
+                               addMouseListener(new MouseAdapter() {\r
+                                       public void mouseClicked (MouseEvent event) {\r
+                                               slider.setValue((int)(event.getX() / (float)getWidth() * 1000));\r
+                                       }\r
+                               });\r
+                       }\r
+\r
+                       protected void paintComponent (Graphics graphics) {\r
+                               Graphics2D g = (Graphics2D)graphics;\r
+                               int width = getWidth() - 1;\r
+                               int height = getHeight() - 1;\r
+                               for (int i = 0, n = paletteColors.length - 1; i < n; i++) {\r
+                                       Color color1 = paletteColors[i];\r
+                                       Color color2 = paletteColors[i + 1];\r
+                                       float point1 = i / (float)n * width;\r
+                                       float point2 = (i + 1) / (float)n * width;\r
+                                       g.setPaint(new GradientPaint(point1, 0, color1, point2, 0, color2, false));\r
+                                       g.fillRect((int)point1, 0, (int)Math.ceil(point2 - point1), height);\r
+                               }\r
+                               g.setPaint(null);\r
+                               g.setColor(Color.black);\r
+                               g.drawRect(0, 0, width, height);\r
+                       }\r
+               }\r
+       }\r
+}\r
diff --git a/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/ImagePanel.java b/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/ImagePanel.java
new file mode 100644 (file)
index 0000000..a35f803
--- /dev/null
@@ -0,0 +1,81 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.awt.FileDialog;\r
+import java.awt.GridBagConstraints;\r
+import java.awt.Insets;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.io.File;\r
+\r
+import javax.swing.ImageIcon;\r
+import javax.swing.JButton;\r
+import javax.swing.JLabel;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPanel;\r
+\r
+import com.badlogic.gdx.graphics.particles.ParticleEmitter;\r
+\r
+class ImagePanel extends EditorPanel {\r
+       JLabel imageLabel;\r
+       JLabel widthLabel;\r
+       JLabel heightLabel;\r
+       String lastDir;\r
+\r
+       public ImagePanel (final ParticleEditor editor) {\r
+               super("Image", null);\r
+               JPanel contentPanel = getContentPanel();\r
+               {\r
+                       JButton openButton = new JButton("Open");\r
+                       contentPanel.add(openButton, new GridBagConstraints(2, 1, 1, 1, 0, 0, GridBagConstraints.NORTHWEST,\r
+                               GridBagConstraints.NONE, new Insets(0, 0, 6, 6), 0, 0));\r
+                       openButton.addActionListener(new ActionListener() {\r
+                               public void actionPerformed (ActionEvent event) {\r
+                                       FileDialog dialog = new FileDialog(editor, "Open Image", FileDialog.LOAD);\r
+                                       if (lastDir != null) dialog.setDirectory(lastDir);\r
+                                       dialog.setVisible(true);\r
+                                       final String file = dialog.getFile();\r
+                                       final String dir = dialog.getDirectory();\r
+                                       if (dir == null || file == null || file.trim().length() == 0) return;\r
+                                       lastDir = dir;\r
+                                       try {\r
+                                               ImageIcon icon = new ImageIcon(new File(dir, file).toURI().toURL());\r
+                                               synchronized (editor.effect) {\r
+                                                       final ParticleEmitter emitter = editor.getEmitter();\r
+                                                       editor.setIcon(emitter, icon);\r
+                                                       imageLabel.setIcon(icon);\r
+                                                       widthLabel.setText("Width: " + icon.getIconWidth());\r
+                                                       heightLabel.setText("Height: " + icon.getIconHeight());\r
+                                                       revalidate();\r
+                                                       emitter.setImagePath(new File(dir, file).getAbsolutePath());\r
+                                                       emitter.setTexture(null);\r
+                                               }\r
+                                       } catch (Exception ex) {\r
+                                               ex.printStackTrace();\r
+                                       }\r
+                               }\r
+                       });\r
+               }\r
+               {\r
+                       widthLabel = new JLabel();\r
+                       contentPanel.add(widthLabel, new GridBagConstraints(2, 2, 1, 1, 0, 0, GridBagConstraints.NORTHWEST,\r
+                               GridBagConstraints.NONE, new Insets(0, 0, 6, 6), 0, 0));\r
+               }\r
+               {\r
+                       heightLabel = new JLabel();\r
+                       contentPanel.add(heightLabel, new GridBagConstraints(2, 3, 1, 1, 0, 1, GridBagConstraints.NORTHWEST,\r
+                               GridBagConstraints.NONE, new Insets(0, 0, 0, 6), 0, 0));\r
+               }\r
+               {\r
+                       imageLabel = new JLabel();\r
+                       contentPanel.add(imageLabel, new GridBagConstraints(3, 1, 1, 3, 1, 0, GridBagConstraints.NORTHWEST,\r
+                               GridBagConstraints.NONE, new Insets(0, 6, 0, 0), 0, 0));\r
+               }\r
+               ImageIcon icon = editor.getIcon(editor.getEmitter());\r
+               if (icon != null) {\r
+                       imageLabel.setIcon(icon);\r
+                       widthLabel.setText("Width: " + icon.getIconWidth());\r
+                       heightLabel.setText("Height: " + icon.getIconHeight());\r
+               }\r
+       }\r
+}\r
diff --git a/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/NumericPanel.java b/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/NumericPanel.java
new file mode 100644 (file)
index 0000000..ad78b5f
--- /dev/null
@@ -0,0 +1,48 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.awt.GridBagConstraints;\r
+import java.awt.Insets;\r
+\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JSpinner;\r
+import javax.swing.SpinnerNumberModel;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+\r
+import com.badlogic.gdx.graphics.particles.ParticleEmitter.NumericValue;\r
+\r
+class NumericPanel extends EditorPanel {\r
+       private final NumericValue value;\r
+       JSpinner valueSpinner;\r
+\r
+       public NumericPanel (String name, final NumericValue value) {\r
+               super(name, value);\r
+               this.value = value;\r
+\r
+               initializeComponents();\r
+\r
+               valueSpinner.setValue(value.getValue());\r
+\r
+               valueSpinner.addChangeListener(new ChangeListener() {\r
+                       public void stateChanged (ChangeEvent event) {\r
+                               value.setValue((Float)valueSpinner.getValue());\r
+                       }\r
+               });\r
+       }\r
+\r
+       private void initializeComponents () {\r
+               JPanel contentPanel = getContentPanel();\r
+               {\r
+                       JLabel label = new JLabel("Value:");\r
+                       contentPanel.add(label, new GridBagConstraints(0, 1, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                               new Insets(0, 0, 0, 6), 0, 0));\r
+               }\r
+               {\r
+                       valueSpinner = new JSpinner(new SpinnerNumberModel(new Float(0), new Float(-99999), new Float(99999), new Float(0.1f)));\r
+                       contentPanel.add(valueSpinner, new GridBagConstraints(1, 1, 1, 1, 1, 0, GridBagConstraints.WEST,\r
+                               GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));\r
+               }\r
+       }\r
+}\r
diff --git a/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/OptionsPanel.java b/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/OptionsPanel.java
new file mode 100644 (file)
index 0000000..578daf6
--- /dev/null
@@ -0,0 +1,131 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.awt.GridBagConstraints;\r
+import java.awt.Insets;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+\r
+import javax.swing.JCheckBox;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+\r
+import com.badlogic.gdx.graphics.particles.ParticleEmitter;\r
+\r
+class OptionsPanel extends EditorPanel {\r
+       JCheckBox attachedCheckBox;\r
+       JCheckBox continuousCheckbox;\r
+       JCheckBox alignedCheckbox;\r
+       JCheckBox additiveCheckbox;\r
+       JCheckBox behindCheckbox;\r
+\r
+       public OptionsPanel (final ParticleEditor editor) {\r
+               super("Options", null);\r
+\r
+               initializeComponents();\r
+\r
+               attachedCheckBox.addActionListener(new ActionListener() {\r
+                       public void actionPerformed (ActionEvent event) {\r
+                               synchronized (editor.effect) {\r
+                                       editor.getEmitter().setAttached(attachedCheckBox.isSelected());\r
+                               }\r
+                       }\r
+               });\r
+\r
+               continuousCheckbox.setSelected(editor.getEmitter().isContinuous());\r
+               continuousCheckbox.addActionListener(new ActionListener() {\r
+                       public void actionPerformed (ActionEvent event) {\r
+                               synchronized (editor.effect) {\r
+                                       editor.getEmitter().setContinuous(continuousCheckbox.isSelected());\r
+                               }\r
+                       }\r
+               });\r
+\r
+               alignedCheckbox.addChangeListener(new ChangeListener() {\r
+                       public void stateChanged (ChangeEvent event) {\r
+                               synchronized (editor.effect) {\r
+                                       editor.getEmitter().setAligned(alignedCheckbox.isSelected());\r
+                               }\r
+                       }\r
+               });\r
+\r
+               additiveCheckbox.addChangeListener(new ChangeListener() {\r
+                       public void stateChanged (ChangeEvent event) {\r
+                               synchronized (editor.effect) {\r
+                                       editor.getEmitter().setAdditive(additiveCheckbox.isSelected());\r
+                               }\r
+                       }\r
+               });\r
+\r
+               behindCheckbox.addChangeListener(new ChangeListener() {\r
+                       public void stateChanged (ChangeEvent event) {\r
+                               synchronized (editor.effect) {\r
+                                       editor.getEmitter().setBehind(behindCheckbox.isSelected());\r
+                               }\r
+                       }\r
+               });\r
+\r
+               ParticleEmitter particles = editor.getEmitter();\r
+               attachedCheckBox.setSelected(particles.isAttached());\r
+               continuousCheckbox.setSelected(particles.isContinuous());\r
+               alignedCheckbox.setSelected(particles.isAligned());\r
+               additiveCheckbox.setSelected(particles.isAdditive());\r
+               behindCheckbox.setSelected(particles.isBehind());\r
+       }\r
+\r
+       private void initializeComponents () {\r
+               JPanel contentPanel = getContentPanel();\r
+               {\r
+                       JLabel label = new JLabel("Additive:");\r
+                       contentPanel.add(label, new GridBagConstraints(0, 1, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                               new Insets(6, 0, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       additiveCheckbox = new JCheckBox();\r
+                       contentPanel.add(additiveCheckbox, new GridBagConstraints(1, 1, 1, 1, 0, 0, GridBagConstraints.WEST,\r
+                               GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       JLabel label = new JLabel("Attached:");\r
+                       contentPanel.add(label, new GridBagConstraints(0, 2, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                               new Insets(6, 0, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       attachedCheckBox = new JCheckBox();\r
+                       contentPanel.add(attachedCheckBox, new GridBagConstraints(1, 2, 1, 1, 1, 0, GridBagConstraints.WEST,\r
+                               GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       JLabel label = new JLabel("Continuous:");\r
+                       contentPanel.add(label, new GridBagConstraints(0, 3, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                               new Insets(6, 0, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       continuousCheckbox = new JCheckBox();\r
+                       contentPanel.add(continuousCheckbox, new GridBagConstraints(1, 3, 1, 1, 0, 0, GridBagConstraints.WEST,\r
+                               GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       JLabel label = new JLabel("Aligned:");\r
+                       contentPanel.add(label, new GridBagConstraints(0, 4, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                               new Insets(6, 0, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       alignedCheckbox = new JCheckBox();\r
+                       contentPanel.add(alignedCheckbox, new GridBagConstraints(1, 4, 1, 1, 0, 0, GridBagConstraints.WEST,\r
+                               GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       JLabel label = new JLabel("Behind:");\r
+                       contentPanel.add(label, new GridBagConstraints(0, 5, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                               new Insets(6, 0, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       behindCheckbox = new JCheckBox();\r
+                       contentPanel.add(behindCheckbox, new GridBagConstraints(1, 5, 1, 1, 0, 0, GridBagConstraints.WEST,\r
+                               GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0));\r
+               }\r
+       }\r
+}\r
diff --git a/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/ParticleEditor.java b/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/ParticleEditor.java
new file mode 100644 (file)
index 0000000..f7d9472
--- /dev/null
@@ -0,0 +1,396 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Canvas;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.EventQueue;\r
+import java.awt.Graphics;\r
+import java.awt.GridBagConstraints;\r
+import java.awt.GridBagLayout;\r
+import java.awt.Insets;\r
+import java.io.File;\r
+import java.net.MalformedURLException;\r
+import java.net.URL;\r
+import java.util.HashMap;\r
+\r
+import javax.swing.BorderFactory;\r
+import javax.swing.ImageIcon;\r
+import javax.swing.JComponent;\r
+import javax.swing.JFrame;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JSplitPane;\r
+import javax.swing.UIManager;\r
+import javax.swing.UIManager.LookAndFeelInfo;\r
+import javax.swing.border.CompoundBorder;\r
+import javax.swing.plaf.basic.BasicSplitPaneUI;\r
+\r
+import org.lwjgl.LWJGLException;\r
+import org.lwjgl.opengl.Display;\r
+\r
+import com.badlogic.gdx.Files.FileType;\r
+import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.GdxRuntimeException;\r
+import com.badlogic.gdx.InputListener;\r
+import com.badlogic.gdx.RenderListener;\r
+import com.badlogic.gdx.backends.desktop.LwjglApplication;\r
+import com.badlogic.gdx.graphics.BitmapFont;\r
+import com.badlogic.gdx.graphics.Color;\r
+import com.badlogic.gdx.graphics.GL10;\r
+import com.badlogic.gdx.graphics.Sprite;\r
+import com.badlogic.gdx.graphics.SpriteBatch;\r
+import com.badlogic.gdx.graphics.Texture.TextureFilter;\r
+import com.badlogic.gdx.graphics.Texture.TextureWrap;\r
+import com.badlogic.gdx.math.Matrix4;\r
+\r
+public class ParticleEditor extends JFrame implements RenderListener, InputListener {\r
+       LwjglApplication app;\r
+       Canvas glCanvas;\r
+       JPanel rowsPanel;\r
+       EffectPanel effectPanel;\r
+       private JSplitPane splitPane;\r
+\r
+       ParticleEffect effect = new ParticleEffect();\r
+       final HashMap<ParticleEmitter, ParticleData> particleData = new HashMap();\r
+       private float maxActiveTimer;\r
+       private int maxActive, lastMaxActive;\r
+       private boolean mouseDown;\r
+       private int activeCount;\r
+       private BitmapFont font;\r
+       private SpriteBatch spriteBatch;\r
+       private Sprite bgImage; // BOZO - Add setting background image to UI.\r
+       private int mouseX, mouseY;\r
+\r
+       public ParticleEditor () {\r
+               super("GDX Particle Editor");\r
+\r
+               glCanvas = new Canvas() {\r
+                       private final Dimension minSize = new Dimension();\r
+\r
+                       public final void addNotify () {\r
+                               super.addNotify();\r
+                               app = new LwjglApplication("ParticleEditor", 200, 200, false) {\r
+                                       protected void setupDisplay () throws LWJGLException {\r
+                                               try {\r
+                                                       Display.setParent(glCanvas);\r
+                                               } catch (LWJGLException ex) {\r
+                                                       throw new GdxRuntimeException("Error setting display parent.", ex);\r
+                                               }\r
+                                               super.setupDisplay();\r
+                                       }\r
+                               };\r
+                               app.getGraphics().setRenderListener(ParticleEditor.this);\r
+                       }\r
+\r
+                       public Dimension getMinimumSize () {\r
+                               return minSize;\r
+                       }\r
+               };\r
+\r
+               initializeComponents();\r
+\r
+               setSize(950, 950);\r
+               setLocationRelativeTo(null);\r
+               setDefaultCloseOperation(DISPOSE_ON_CLOSE);\r
+               setVisible(true);\r
+       }\r
+\r
+       void reloadRows () {\r
+               EventQueue.invokeLater(new Runnable() {\r
+                       public void run () {\r
+                               rowsPanel.removeAll();\r
+                               synchronized (effect) {\r
+                                       ParticleEmitter emitter = getEmitter();\r
+                                       addRow(new ImagePanel(ParticleEditor.this));\r
+                                       addRow(new RangedNumericPanel("Delay", emitter.getDelay()));\r
+                                       addRow(new RangedNumericPanel("Duration", emitter.getDuration()));\r
+                                       addRow(new CountPanel(ParticleEditor.this));\r
+                                       addRow(new ScaledNumericPanel("Emission", "Duration", emitter.getEmission()));\r
+                                       addRow(new ScaledNumericPanel("Life", "Duration", emitter.getLife()));\r
+                                       addRow(new ScaledNumericPanel("Life Offset", "Duration", emitter.getLifeOffset()));\r
+                                       addRow(new RangedNumericPanel("X Offset", emitter.getXOffsetValue()));\r
+                                       addRow(new RangedNumericPanel("Y Offset", emitter.getYOffsetValue()));\r
+                                       addRow(new SpawnPanel(emitter.getSpawnShape(), ParticleEditor.this));\r
+                                       addRow(new ScaledNumericPanel("Spawn Width", "Duration", emitter.getSpawnWidth()));\r
+                                       addRow(new ScaledNumericPanel("Spawn Height", "Duration", emitter.getSpawnHeight()));\r
+                                       addRow(new ScaledNumericPanel("Size", "Life", emitter.getSize()));\r
+                                       addRow(new ScaledNumericPanel("Velocity", "Life", emitter.getVelocity()));\r
+                                       addRow(new ScaledNumericPanel("Angle", "Life", emitter.getAngle()));\r
+                                       addRow(new ScaledNumericPanel("Rotation", "Life", emitter.getRotation()));\r
+                                       addRow(new ScaledNumericPanel("Wind", "Life", emitter.getWind()));\r
+                                       addRow(new ScaledNumericPanel("Gravity", "Life", emitter.getGravity()));\r
+                                       addRow(new GradientPanel("Tint", emitter.getTint()));\r
+                                       addRow(new PercentagePanel("Transparency", "Life", emitter.getTransparency()));\r
+                                       addRow(new OptionsPanel(ParticleEditor.this));\r
+                                       for (Component component : rowsPanel.getComponents())\r
+                                               if (component instanceof EditorPanel) ((EditorPanel)component).update(ParticleEditor.this);\r
+                               }\r
+                               rowsPanel.repaint();\r
+                       }\r
+               });\r
+       }\r
+\r
+       void addRow (JPanel row) {\r
+               row.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, java.awt.Color.black));\r
+               rowsPanel.add(row, new GridBagConstraints(0, -1, 1, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,\r
+                       new Insets(0, 0, 0, 0), 0, 0));\r
+       }\r
+\r
+       public void setVisible (String name, boolean visible) {\r
+               for (Component component : rowsPanel.getComponents())\r
+                       if (component instanceof EditorPanel && ((EditorPanel)component).getName().equals(name)) component.setVisible(visible);\r
+       }\r
+\r
+       public ParticleEmitter getEmitter () {\r
+               return effect.getEmitters().get(effectPanel.editIndex);\r
+       }\r
+\r
+       public ImageIcon getIcon (ParticleEmitter emitter) {\r
+               ParticleData data = particleData.get(emitter);\r
+               if (data == null) particleData.put(emitter, data = new ParticleData());\r
+               String imagePath = emitter.getImagePath();\r
+               if (data.icon == null && imagePath != null) {\r
+                       try {\r
+                               URL url;\r
+                               File file = new File(imagePath);\r
+                               if (file.exists())\r
+                                       url = file.toURI().toURL();\r
+                               else {\r
+                                       url = ParticleEditor.class.getResource(imagePath);\r
+                                       if (url == null) return null;\r
+                               }\r
+                               data.icon = new ImageIcon(url);\r
+                       } catch (MalformedURLException ex) {\r
+                               ex.printStackTrace();\r
+                       }\r
+               }\r
+               return data.icon;\r
+       }\r
+\r
+       public void setIcon (ParticleEmitter emitters, ImageIcon icon) {\r
+               ParticleData data = particleData.get(emitters);\r
+               if (data == null) particleData.put(emitters, data = new ParticleData());\r
+               data.icon = icon;\r
+       }\r
+\r
+       public void setEnabled (ParticleEmitter emitter, boolean enabled) {\r
+               ParticleData data = particleData.get(emitter);\r
+               if (data == null) particleData.put(emitter, data = new ParticleData());\r
+               data.enabled = enabled;\r
+       }\r
+\r
+       public boolean isEnabled (ParticleEmitter emitter) {\r
+               ParticleData data = particleData.get(emitter);\r
+               if (data == null) return true;\r
+               return data.enabled;\r
+       }\r
+\r
+       private void initializeComponents () {\r
+               // {\r
+               // JMenuBar menuBar = new JMenuBar();\r
+               // setJMenuBar(menuBar);\r
+               // JPopupMenu.setDefaultLightWeightPopupEnabled(false);\r
+               // JMenu fileMenu = new JMenu("File");\r
+               // menuBar.add(fileMenu);\r
+               // }\r
+               splitPane = new JSplitPane();\r
+               splitPane.setUI(new BasicSplitPaneUI() {\r
+                       public void paint (Graphics g, JComponent jc) {\r
+                       }\r
+               });\r
+               splitPane.setDividerSize(4);\r
+               getContentPane().add(splitPane, BorderLayout.CENTER);\r
+               {\r
+                       JPanel propertiesPanel = new JPanel(new GridBagLayout());\r
+                       splitPane.add(propertiesPanel, JSplitPane.RIGHT);\r
+                       propertiesPanel.setBorder(new CompoundBorder(BorderFactory.createEmptyBorder(3, 0, 6, 6), BorderFactory\r
+                               .createTitledBorder("Emitter Properties")));\r
+                       {\r
+                               JScrollPane scroll = new JScrollPane();\r
+                               propertiesPanel.add(scroll, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTH,\r
+                                       GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));\r
+                               scroll.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));\r
+                               {\r
+                                       rowsPanel = new JPanel(new GridBagLayout());\r
+                                       scroll.setViewportView(rowsPanel);\r
+                                       scroll.getVerticalScrollBar().setUnitIncrement(70);\r
+                               }\r
+                       }\r
+               }\r
+               {\r
+                       JSplitPane leftSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT);\r
+                       leftSplit.setUI(new BasicSplitPaneUI() {\r
+                               public void paint (Graphics g, JComponent jc) {\r
+                               }\r
+                       });\r
+                       leftSplit.setDividerSize(4);\r
+                       splitPane.add(leftSplit, JSplitPane.LEFT);\r
+                       {\r
+                               JPanel spacer = new JPanel(new BorderLayout());\r
+                               leftSplit.add(spacer, JSplitPane.TOP);\r
+                               spacer.add(glCanvas);\r
+                               spacer.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 4));\r
+                       }\r
+                       {\r
+                               JPanel emittersPanel = new JPanel(new BorderLayout());\r
+                               leftSplit.add(emittersPanel, JSplitPane.BOTTOM);\r
+                               emittersPanel.setBorder(new CompoundBorder(BorderFactory.createEmptyBorder(0, 6, 6, 0), BorderFactory\r
+                                       .createTitledBorder("Effect Emitters")));\r
+                               {\r
+                                       effectPanel = new EffectPanel(this);\r
+                                       emittersPanel.add(effectPanel);\r
+                               }\r
+                       }\r
+                       leftSplit.setDividerLocation(625);\r
+               }\r
+               splitPane.setDividerLocation(325);\r
+       }\r
+\r
+       public void surfaceCreated () {\r
+               if (spriteBatch != null) return;\r
+               spriteBatch = new SpriteBatch();\r
+\r
+               font = new BitmapFont(Gdx.files.getFileHandle("data/default.fnt", FileType.Internal), Gdx.files.getFileHandle(\r
+                       "data/default.png", FileType.Internal), true);\r
+               effectPanel.newEmitter("Untitled", true);\r
+               // if (resources.openFile("/editor-bg.png") != null) bgImage = new Image(gl, "/editor-bg.png");\r
+\r
+               Gdx.input.addInputListener(this);\r
+       }\r
+\r
+       public void surfaceChanged (int width, int height) {\r
+               int viewWidth = Gdx.graphics.getWidth();\r
+               int viewHeight = Gdx.graphics.getHeight();\r
+\r
+               spriteBatch.setProjectionMatrix(new Matrix4().setToOrtho(0, viewWidth, viewHeight, 0, 0, 1));\r
+\r
+               synchronized (effect) {\r
+                       effect.setPosition(viewWidth / 2, viewHeight / 2);\r
+               }\r
+       }\r
+\r
+       public void render () {\r
+               synchronized (effect) {\r
+                       int viewWidth = Gdx.graphics.getWidth();\r
+                       int viewHeight = Gdx.graphics.getHeight();\r
+                       if (viewWidth != glCanvas.getWidth() || viewHeight != glCanvas.getHeight()) {\r
+                               viewWidth = Math.max(1, glCanvas.getWidth());\r
+                               viewHeight = Math.max(1, glCanvas.getHeight());\r
+                               app.setSize(viewWidth, viewHeight);\r
+                       }\r
+\r
+                       float delta = Gdx.graphics.getDeltaTime();\r
+\r
+                       Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);\r
+\r
+                       spriteBatch.begin();\r
+                       spriteBatch.enableBlending();\r
+                       spriteBatch.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);\r
+\r
+                       if (bgImage != null) {\r
+                               bgImage.setPosition(viewWidth / 2 - bgImage.getWidth() / 2, viewHeight / 2 - bgImage.getHeight() / 2);\r
+                               bgImage.draw(spriteBatch);\r
+                       }\r
+\r
+                       activeCount = 0;\r
+                       for (ParticleEmitter emitter : effect.getEmitters()) {\r
+                               if (emitter.getTexture() == null && emitter.getImagePath() != null) loadImage(emitter);\r
+                               boolean enabled = isEnabled(emitter);\r
+                               if (enabled) {\r
+                                       if (emitter.getTexture() != null) emitter.draw(spriteBatch, delta);\r
+                                       activeCount += emitter.getActiveCount();\r
+                               }\r
+                       }\r
+                       if (effect.isComplete()) effect.start();\r
+\r
+                       maxActive = Math.max(maxActive, activeCount);\r
+                       maxActiveTimer += delta;\r
+                       if (maxActiveTimer > 3) {\r
+                               maxActiveTimer = 0;\r
+                               lastMaxActive = maxActive;\r
+                               maxActive = 0;\r
+                       }\r
+\r
+                       if (mouseDown) {\r
+                               // gl.drawLine(mouseX - 6, mouseY, mouseX + 5, mouseY);\r
+                               // gl.drawLine(mouseX, mouseY - 5, mouseX, mouseY + 6);\r
+                       }\r
+\r
+                       font.draw(spriteBatch, "FPS: " + Gdx.graphics.getFramesPerSecond(), 10, 10, Color.WHITE);\r
+                       font.draw(spriteBatch, "Count: " + activeCount, 10, 30, Color.WHITE);\r
+                       font.draw(spriteBatch, "Max: " + lastMaxActive, 10, 50, Color.WHITE);\r
+                       font.draw(spriteBatch, (int)(getEmitter().getPercentComplete() * 100) + "%", 10, 70, Color.WHITE);\r
+\r
+                       spriteBatch.end();\r
+\r
+                       // gl.drawLine((int)(viewWidth * getCurrentParticles().getPercentComplete()), viewHeight - 1, viewWidth, viewHeight - 1);\r
+               }\r
+       }\r
+\r
+       private void loadImage (ParticleEmitter emitter) {\r
+               final String imagePath = emitter.getImagePath();\r
+               try {\r
+                       emitter.setTexture(Gdx.graphics.newTexture(Gdx.files.getFileHandle(imagePath, FileType.Absolute), TextureFilter.Linear,\r
+                               TextureFilter.Linear, TextureWrap.ClampToEdge, TextureWrap.ClampToEdge));\r
+               } catch (GdxRuntimeException ex) {\r
+                       EventQueue.invokeLater(new Runnable() {\r
+                               public void run () {\r
+                                       JOptionPane.showMessageDialog(ParticleEditor.this, "Error loading image:\n" + imagePath);\r
+                               }\r
+                       });\r
+                       emitter.setImagePath(null);\r
+               }\r
+       }\r
+\r
+       public boolean keyDown (int keycode) {\r
+               return false;\r
+       }\r
+\r
+       public boolean keyUp (int keycode) {\r
+               return false;\r
+       }\r
+\r
+       public boolean keyTyped (char character) {\r
+               return false;\r
+       }\r
+\r
+       public boolean touchDown (int x, int y, int pointer) {\r
+               synchronized (effect) {\r
+                       effect.setPosition(x, y);\r
+               }\r
+               return false;\r
+       }\r
+\r
+       public boolean touchUp (int x, int y, int pointer) {\r
+               return false;\r
+       }\r
+\r
+       public boolean touchDragged (int x, int y, int pointer) {\r
+               synchronized (effect) {\r
+                       effect.setPosition(x, y);\r
+               }\r
+               return false;\r
+       }\r
+\r
+       static class ParticleData {\r
+               public ImageIcon icon;\r
+               public String imagePath;\r
+               public boolean enabled = true;\r
+       }\r
+\r
+       public static void main (String[] args) {\r
+               for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {\r
+                       if ("Nimbus".equals(info.getName())) {\r
+                               try {\r
+                                       UIManager.setLookAndFeel(info.getClassName());\r
+                               } catch (Throwable ignored) {\r
+                               }\r
+                               break;\r
+                       }\r
+               }\r
+               new ParticleEditor();\r
+       }\r
+}\r
diff --git a/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/PercentagePanel.java b/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/PercentagePanel.java
new file mode 100644 (file)
index 0000000..7bddf82
--- /dev/null
@@ -0,0 +1,75 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.awt.Dimension;\r
+import java.awt.GridBagConstraints;\r
+import java.awt.GridBagLayout;\r
+import java.awt.Insets;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+\r
+import javax.swing.BorderFactory;\r
+import javax.swing.JButton;\r
+import javax.swing.JPanel;\r
+\r
+import com.badlogic.gdx.graphics.particles.ParticleEmitter.ScaledNumericValue;\r
+\r
+class PercentagePanel extends EditorPanel {\r
+       final ScaledNumericValue value;\r
+       JButton expandButton;\r
+       Chart chart;\r
+\r
+       public PercentagePanel (String name, String chartTitle, final ScaledNumericValue value) {\r
+               super(name, value);\r
+               this.value = value;\r
+\r
+               initializeComponents(chartTitle);\r
+\r
+               chart.setValues(value.getTimeline(), value.getScaling());\r
+\r
+               expandButton.addActionListener(new ActionListener() {\r
+                       public void actionPerformed (ActionEvent event) {\r
+                               chart.setExpanded(!chart.isExpanded());\r
+                               boolean expanded = chart.isExpanded();\r
+                               GridBagLayout layout = (GridBagLayout)getContentPanel().getLayout();\r
+                               GridBagConstraints chartConstraints = layout.getConstraints(chart);\r
+                               GridBagConstraints expandButtonConstraints = layout.getConstraints(expandButton);\r
+                               if (expanded) {\r
+                                       chart.setPreferredSize(new Dimension(150, 200));\r
+                                       expandButton.setText("-");\r
+                                       chartConstraints.weightx = 1;\r
+                                       expandButtonConstraints.weightx = 0;\r
+                               } else {\r
+                                       chart.setPreferredSize(new Dimension(150, 62));\r
+                                       expandButton.setText("+");\r
+                                       chartConstraints.weightx = 0;\r
+                                       expandButtonConstraints.weightx = 1;\r
+                               }\r
+                               layout.setConstraints(chart, chartConstraints);\r
+                               layout.setConstraints(expandButton, expandButtonConstraints);\r
+                               chart.revalidate();\r
+                       }\r
+               });\r
+       }\r
+\r
+       private void initializeComponents (String chartTitle) {\r
+               JPanel contentPanel = getContentPanel();\r
+               {\r
+                       chart = new Chart(chartTitle) {\r
+                               public void pointsChanged () {\r
+                                       value.setTimeline(chart.getValuesX());\r
+                                       value.setScaling(chart.getValuesY());\r
+                               }\r
+                       };\r
+                       chart.setPreferredSize(new Dimension(150, 62));\r
+                       contentPanel.add(chart, new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.BOTH,\r
+                               new Insets(0, 0, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       expandButton = new JButton("+");\r
+                       expandButton.setBorder(BorderFactory.createEmptyBorder(4, 10, 4, 10));\r
+                       contentPanel.add(expandButton, new GridBagConstraints(1, 0, 1, 1, 1, 0, GridBagConstraints.NORTHWEST,\r
+                               GridBagConstraints.NONE, new Insets(0, 6, 0, 0), 0, 0));\r
+               }\r
+       }\r
+}\r
diff --git a/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/RangedNumericPanel.java b/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/RangedNumericPanel.java
new file mode 100644 (file)
index 0000000..4359d7f
--- /dev/null
@@ -0,0 +1,85 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.awt.GridBagConstraints;\r
+import java.awt.Insets;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+\r
+import javax.swing.BorderFactory;\r
+import javax.swing.JButton;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JSpinner;\r
+import javax.swing.SpinnerNumberModel;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+\r
+import com.badlogic.gdx.graphics.particles.ParticleEmitter.RangedNumericValue;\r
+\r
+class RangedNumericPanel extends EditorPanel {\r
+       private final RangedNumericValue value;\r
+       JSpinner minSpinner, maxSpinner;\r
+       JButton rangeButton;\r
+       JLabel label;\r
+\r
+       public RangedNumericPanel (String name, final RangedNumericValue value) {\r
+               super(name, value);\r
+               this.value = value;\r
+\r
+               initializeComponents();\r
+\r
+               minSpinner.setValue(value.getLowMin());\r
+               maxSpinner.setValue(value.getLowMax());\r
+\r
+               minSpinner.addChangeListener(new ChangeListener() {\r
+                       public void stateChanged (ChangeEvent event) {\r
+                               value.setLowMin((Float)minSpinner.getValue());\r
+                               if (!maxSpinner.isVisible()) value.setLowMax((Float)minSpinner.getValue());\r
+                       }\r
+               });\r
+\r
+               maxSpinner.addChangeListener(new ChangeListener() {\r
+                       public void stateChanged (ChangeEvent event) {\r
+                               value.setLowMax((Float)maxSpinner.getValue());\r
+                       }\r
+               });\r
+\r
+               rangeButton.addActionListener(new ActionListener() {\r
+                       public void actionPerformed (ActionEvent event) {\r
+                               boolean visible = !maxSpinner.isVisible();\r
+                               maxSpinner.setVisible(visible);\r
+                               rangeButton.setText(visible ? "<" : ">");\r
+                               JSpinner spinner = visible ? maxSpinner : minSpinner;\r
+                               value.setLowMax((Float)spinner.getValue());\r
+                       }\r
+               });\r
+\r
+               if (value.getLowMin() == value.getLowMax()) rangeButton.doClick(0);\r
+       }\r
+\r
+       private void initializeComponents () {\r
+               JPanel contentPanel = getContentPanel();\r
+               {\r
+                       label = new JLabel("Value:");\r
+                       contentPanel.add(label, new GridBagConstraints(2, 2, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                               new Insets(0, 0, 0, 6), 0, 0));\r
+               }\r
+               {\r
+                       minSpinner = new JSpinner(new SpinnerNumberModel(new Float(0), new Float(-99999), new Float(99999), new Float(1f)));\r
+                       contentPanel.add(minSpinner, new GridBagConstraints(3, 2, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,\r
+                               new Insets(0, 0, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       maxSpinner = new JSpinner(new SpinnerNumberModel(new Float(0), new Float(-99999), new Float(99999), new Float(1f)));\r
+                       contentPanel.add(maxSpinner, new GridBagConstraints(4, 2, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,\r
+                               new Insets(0, 6, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       rangeButton = new JButton("<");\r
+                       rangeButton.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));\r
+                       contentPanel.add(rangeButton, new GridBagConstraints(5, 2, 1, 1, 1.0, 0, GridBagConstraints.WEST,\r
+                               GridBagConstraints.NONE, new Insets(0, 1, 0, 0), 0, 0));\r
+               }\r
+       }\r
+}\r
diff --git a/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/ScaledNumericPanel.java b/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/ScaledNumericPanel.java
new file mode 100644 (file)
index 0000000..a37f697
--- /dev/null
@@ -0,0 +1,212 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.awt.Dimension;\r
+import java.awt.GridBagConstraints;\r
+import java.awt.GridBagLayout;\r
+import java.awt.Insets;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+\r
+import javax.swing.BorderFactory;\r
+import javax.swing.JButton;\r
+import javax.swing.JCheckBox;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JSpinner;\r
+import javax.swing.SpinnerNumberModel;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+\r
+import com.badlogic.gdx.graphics.particles.ParticleEmitter.ScaledNumericValue;\r
+\r
+class ScaledNumericPanel extends EditorPanel {\r
+       final ScaledNumericValue value;\r
+       JSpinner lowMinSpinner, lowMaxSpinner;\r
+       JSpinner highMinSpinner, highMaxSpinner;\r
+       JCheckBox relativeCheckBox;\r
+       Chart chart;\r
+       JPanel formPanel;\r
+       JButton expandButton;\r
+       JButton lowRangeButton;\r
+       JButton highRangeButton;\r
+\r
+       public ScaledNumericPanel (String name, String chartTitle, final ScaledNumericValue value) {\r
+               super(name, value);\r
+               this.value = value;\r
+\r
+               initializeComponents(chartTitle);\r
+\r
+               lowMinSpinner.setValue(value.getLowMin());\r
+               lowMaxSpinner.setValue(value.getLowMax());\r
+               highMinSpinner.setValue(value.getHighMin());\r
+               highMaxSpinner.setValue(value.getHighMax());\r
+               chart.setValues(value.getTimeline(), value.getScaling());\r
+               relativeCheckBox.setSelected(value.isRelative());\r
+\r
+               lowMinSpinner.addChangeListener(new ChangeListener() {\r
+                       public void stateChanged (ChangeEvent event) {\r
+                               value.setLowMin((Float)lowMinSpinner.getValue());\r
+                               if (!lowMaxSpinner.isVisible()) value.setLowMax((Float)lowMinSpinner.getValue());\r
+                       }\r
+               });\r
+               lowMaxSpinner.addChangeListener(new ChangeListener() {\r
+                       public void stateChanged (ChangeEvent event) {\r
+                               value.setLowMax((Float)lowMaxSpinner.getValue());\r
+                       }\r
+               });\r
+               highMinSpinner.addChangeListener(new ChangeListener() {\r
+                       public void stateChanged (ChangeEvent event) {\r
+                               value.setHighMin((Float)highMinSpinner.getValue());\r
+                               if (!highMaxSpinner.isVisible()) value.setHighMax((Float)highMinSpinner.getValue());\r
+                       }\r
+               });\r
+               highMaxSpinner.addChangeListener(new ChangeListener() {\r
+                       public void stateChanged (ChangeEvent event) {\r
+                               value.setHighMax((Float)highMaxSpinner.getValue());\r
+                       }\r
+               });\r
+\r
+               relativeCheckBox.addActionListener(new ActionListener() {\r
+                       public void actionPerformed (ActionEvent event) {\r
+                               value.setRelative(relativeCheckBox.isSelected());\r
+                       }\r
+               });\r
+\r
+               lowRangeButton.addActionListener(new ActionListener() {\r
+                       public void actionPerformed (ActionEvent event) {\r
+                               boolean visible = !lowMaxSpinner.isVisible();\r
+                               lowMaxSpinner.setVisible(visible);\r
+                               lowRangeButton.setText(visible ? "<" : ">");\r
+                               GridBagLayout layout = (GridBagLayout)formPanel.getLayout();\r
+                               GridBagConstraints constraints = layout.getConstraints(lowRangeButton);\r
+                               constraints.gridx = visible ? 5 : 4;\r
+                               layout.setConstraints(lowRangeButton, constraints);\r
+                               JSpinner spinner = visible ? lowMaxSpinner : lowMinSpinner;\r
+                               value.setLowMax((Float)spinner.getValue());\r
+                       }\r
+               });\r
+\r
+               highRangeButton.addActionListener(new ActionListener() {\r
+                       public void actionPerformed (ActionEvent event) {\r
+                               boolean visible = !highMaxSpinner.isVisible();\r
+                               highMaxSpinner.setVisible(visible);\r
+                               highRangeButton.setText(visible ? "<" : ">");\r
+                               GridBagLayout layout = (GridBagLayout)formPanel.getLayout();\r
+                               GridBagConstraints constraints = layout.getConstraints(highRangeButton);\r
+                               constraints.gridx = visible ? 5 : 4;\r
+                               layout.setConstraints(highRangeButton, constraints);\r
+                               JSpinner spinner = visible ? highMaxSpinner : highMinSpinner;\r
+                               value.setHighMax((Float)spinner.getValue());\r
+                       }\r
+               });\r
+\r
+               expandButton.addActionListener(new ActionListener() {\r
+                       public void actionPerformed (ActionEvent event) {\r
+                               chart.setExpanded(!chart.isExpanded());\r
+                               boolean expanded = chart.isExpanded();\r
+                               GridBagLayout layout = (GridBagLayout)getContentPanel().getLayout();\r
+                               GridBagConstraints chartConstraints = layout.getConstraints(chart);\r
+                               GridBagConstraints expandButtonConstraints = layout.getConstraints(expandButton);\r
+                               if (expanded) {\r
+                                       chart.setPreferredSize(new Dimension(150, 200));\r
+                                       expandButton.setText("\96");\r
+                                       chartConstraints.weightx = 1;\r
+                                       expandButtonConstraints.weightx = 0;\r
+                               } else {\r
+                                       chart.setPreferredSize(new Dimension(150, 30));\r
+                                       expandButton.setText("+");\r
+                                       chartConstraints.weightx = 0;\r
+                                       expandButtonConstraints.weightx = 1;\r
+                               }\r
+                               layout.setConstraints(chart, chartConstraints);\r
+                               layout.setConstraints(expandButton, expandButtonConstraints);\r
+                               relativeCheckBox.setVisible(!expanded);\r
+                               formPanel.setVisible(!expanded);\r
+                               chart.revalidate();\r
+                       }\r
+               });\r
+\r
+               if (value.getLowMin() == value.getLowMax()) lowRangeButton.doClick(0);\r
+               if (value.getHighMin() == value.getHighMax()) highRangeButton.doClick(0);\r
+       }\r
+\r
+       public JPanel getFormPanel () {\r
+               return formPanel;\r
+       }\r
+\r
+       private void initializeComponents (String chartTitle) {\r
+               JPanel contentPanel = getContentPanel();\r
+               {\r
+                       formPanel = new JPanel(new GridBagLayout());\r
+                       contentPanel.add(formPanel, new GridBagConstraints(5, 5, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                               new Insets(0, 0, 0, 6), 0, 0));\r
+                       {\r
+                               JLabel label = new JLabel("High:");\r
+                               formPanel.add(label, new GridBagConstraints(2, 1, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                                       new Insets(0, 0, 0, 6), 0, 0));\r
+                       }\r
+                       {\r
+                               highMinSpinner = new JSpinner(\r
+                                       new SpinnerNumberModel(new Float(0), new Float(-99999), new Float(99999), new Float(1f)));\r
+                               formPanel.add(highMinSpinner, new GridBagConstraints(3, 1, 1, 1, 0, 0, GridBagConstraints.WEST,\r
+                                       GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));\r
+                       }\r
+                       {\r
+                               highMaxSpinner = new JSpinner(\r
+                                       new SpinnerNumberModel(new Float(0), new Float(-99999), new Float(99999), new Float(1f)));\r
+                               formPanel.add(highMaxSpinner, new GridBagConstraints(4, 1, 1, 1, 0, 0, GridBagConstraints.WEST,\r
+                                       GridBagConstraints.NONE, new Insets(0, 6, 0, 0), 0, 0));\r
+                       }\r
+                       {\r
+                               highRangeButton = new JButton("<");\r
+                               highRangeButton.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));\r
+                               formPanel.add(highRangeButton, new GridBagConstraints(5, 1, 1, 1, 0.0, 0, GridBagConstraints.WEST,\r
+                                       GridBagConstraints.NONE, new Insets(0, 1, 0, 0), 0, 0));\r
+                       }\r
+                       {\r
+                               JLabel label = new JLabel("Low:");\r
+                               formPanel.add(label, new GridBagConstraints(2, 2, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                                       new Insets(0, 0, 0, 6), 0, 0));\r
+                       }\r
+                       {\r
+                               lowMinSpinner = new JSpinner(new SpinnerNumberModel(new Float(0), new Float(-99999), new Float(99999), new Float(1f)));\r
+                               formPanel.add(lowMinSpinner, new GridBagConstraints(3, 2, 1, 1, 0, 0, GridBagConstraints.WEST,\r
+                                       GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));\r
+                       }\r
+                       {\r
+                               lowMaxSpinner = new JSpinner(new SpinnerNumberModel(new Float(0), new Float(-99999), new Float(99999), new Float(1f)));\r
+                               formPanel.add(lowMaxSpinner, new GridBagConstraints(4, 2, 1, 1, 0, 0, GridBagConstraints.WEST,\r
+                                       GridBagConstraints.NONE, new Insets(0, 6, 0, 0), 0, 0));\r
+                       }\r
+                       {\r
+                               lowRangeButton = new JButton("<");\r
+                               lowRangeButton.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));\r
+                               formPanel.add(lowRangeButton, new GridBagConstraints(5, 2, 1, 1, 0.0, 0, GridBagConstraints.WEST,\r
+                                       GridBagConstraints.NONE, new Insets(0, 1, 0, 0), 0, 0));\r
+                       }\r
+               }\r
+               {\r
+                       chart = new Chart(chartTitle) {\r
+                               public void pointsChanged () {\r
+                                       value.setTimeline(chart.getValuesX());\r
+                                       value.setScaling(chart.getValuesY());\r
+                               }\r
+                       };\r
+                       contentPanel.add(chart, new GridBagConstraints(6, 5, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,\r
+                               new Insets(0, 0, 0, 0), 0, 0));\r
+                       chart.setPreferredSize(new Dimension(150, 30));\r
+               }\r
+               {\r
+                       expandButton = new JButton("+");\r
+                       contentPanel.add(expandButton, new GridBagConstraints(7, 5, 1, 1, 1, 0, GridBagConstraints.SOUTHWEST,\r
+                               GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0));\r
+                       expandButton.setBorder(BorderFactory.createEmptyBorder(4, 8, 4, 8));\r
+               }\r
+               {\r
+                       relativeCheckBox = new JCheckBox("Relative");\r
+                       contentPanel.add(relativeCheckBox, new GridBagConstraints(7, 5, 1, 1, 0, 0, GridBagConstraints.NORTHWEST,\r
+                               GridBagConstraints.NONE, new Insets(0, 6, 0, 0), 0, 0));\r
+               }\r
+       }\r
+}\r
diff --git a/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/SpawnPanel.java b/extensions/particle-editor/src/com/badlogic/gdx/graphics/particles/SpawnPanel.java
new file mode 100644 (file)
index 0000000..e7d3ce7
--- /dev/null
@@ -0,0 +1,130 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.awt.Dimension;\r
+import java.awt.GridBagConstraints;\r
+import java.awt.Insets;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+\r
+import javax.swing.DefaultComboBoxModel;\r
+import javax.swing.JCheckBox;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+\r
+import com.badlogic.gdx.graphics.particles.ParticleEmitter.SpawnEllipseSide;\r
+import com.badlogic.gdx.graphics.particles.ParticleEmitter.SpawnShape;\r
+import com.badlogic.gdx.graphics.particles.ParticleEmitter.SpawnShapeValue;\r
+\r
+class SpawnPanel extends EditorPanel {\r
+       JComboBox shapeCombo;\r
+       JCheckBox edgesCheckbox;\r
+       JLabel edgesLabel;\r
+       JComboBox sideCombo;\r
+       JLabel sideLabel;\r
+\r
+       public SpawnPanel (final SpawnShapeValue spawnShapeValue, final ParticleEditor editor) {\r
+               super("Spawn", null);\r
+\r
+               initializeComponents();\r
+\r
+               edgesCheckbox.setSelected(spawnShapeValue.isEdges());\r
+               sideCombo.setSelectedItem(spawnShapeValue.getShape());\r
+\r
+               shapeCombo.addActionListener(new ActionListener() {\r
+                       public void actionPerformed (ActionEvent event) {\r
+                               SpawnShape shape = (SpawnShape)shapeCombo.getSelectedItem();\r
+                               spawnShapeValue.setShape(shape);\r
+                               switch (shape) {\r
+                               case line:\r
+                               case square:\r
+                                       setEdgesVisible(false);\r
+                                       editor.setVisible("Spawn Width", true);\r
+                                       editor.setVisible("Spawn Height", true);\r
+                                       break;\r
+                               case ellipse:\r
+                                       setEdgesVisible(true);\r
+                                       editor.setVisible("Spawn Width", true);\r
+                                       editor.setVisible("Spawn Height", true);\r
+                                       break;\r
+                               case point:\r
+                                       setEdgesVisible(false);\r
+                                       editor.setVisible("Spawn Width", false);\r
+                                       editor.setVisible("Spawn Height", false);\r
+                                       break;\r
+                               }\r
+                       }\r
+               });\r
+\r
+               edgesCheckbox.addActionListener(new ActionListener() {\r
+                       public void actionPerformed (ActionEvent event) {\r
+                               spawnShapeValue.setEdges(edgesCheckbox.isSelected());\r
+                               setEdgesVisible(true);\r
+                       }\r
+               });\r
+\r
+               sideCombo.addActionListener(new ActionListener() {\r
+                       public void actionPerformed (ActionEvent event) {\r
+                               SpawnEllipseSide side = (SpawnEllipseSide)sideCombo.getSelectedItem();\r
+                               spawnShapeValue.setSide(side);\r
+                       }\r
+               });\r
+\r
+               shapeCombo.setSelectedItem(spawnShapeValue.getShape());\r
+       }\r
+\r
+       public void update (ParticleEditor editor) {\r
+               shapeCombo.setSelectedItem(editor.getEmitter().getSpawnShape().getShape());\r
+       }\r
+\r
+       void setEdgesVisible (boolean visible) {\r
+               edgesCheckbox.setVisible(visible);\r
+               edgesLabel.setVisible(visible);\r
+               visible = visible && edgesCheckbox.isSelected();\r
+               sideCombo.setVisible(visible);\r
+               sideLabel.setVisible(visible);\r
+       }\r
+\r
+       private void initializeComponents () {\r
+               JPanel contentPanel = getContentPanel();\r
+               {\r
+                       JLabel label = new JLabel("Shape:");\r
+                       contentPanel.add(label, new GridBagConstraints(0, 1, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                               new Insets(0, 0, 0, 6), 0, 0));\r
+               }\r
+               {\r
+                       shapeCombo = new JComboBox();\r
+                       shapeCombo.setModel(new DefaultComboBoxModel(SpawnShape.values()));\r
+                       contentPanel.add(shapeCombo, new GridBagConstraints(1, 1, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,\r
+                               new Insets(0, 0, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       edgesLabel = new JLabel("Edges:");\r
+                       contentPanel.add(edgesLabel, new GridBagConstraints(2, 1, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                               new Insets(0, 12, 0, 6), 0, 0));\r
+               }\r
+               {\r
+                       edgesCheckbox = new JCheckBox();\r
+                       contentPanel.add(edgesCheckbox, new GridBagConstraints(3, 1, 1, 1, 0, 0, GridBagConstraints.WEST,\r
+                               GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       sideLabel = new JLabel("Side:");\r
+                       contentPanel.add(sideLabel, new GridBagConstraints(4, 1, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE,\r
+                               new Insets(0, 12, 0, 6), 0, 0));\r
+               }\r
+               {\r
+                       sideCombo = new JComboBox();\r
+                       sideCombo.setModel(new DefaultComboBoxModel(SpawnEllipseSide.values()));\r
+                       contentPanel.add(sideCombo, new GridBagConstraints(5, 1, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,\r
+                               new Insets(0, 0, 0, 0), 0, 0));\r
+               }\r
+               {\r
+                       JPanel spacer = new JPanel();\r
+                       spacer.setPreferredSize(new Dimension());\r
+                       contentPanel.add(spacer, new GridBagConstraints(6, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,\r
+                               new Insets(0, 0, 0, 0), 0, 0));\r
+               }\r
+       }\r
+}\r
index 9ee71fa..2821334 100644 (file)
@@ -111,7 +111,7 @@ public class TwlRenderer implements Renderer {
        }\r
 \r
        public void setClipRect (Rect rect) {\r
-               if (rendering) spriteBatch.renderMesh();\r
+               if (rendering) spriteBatch.flush();\r
                if (rect == null) {\r
                        Gdx.gl.glDisable(GL10.GL_SCISSOR_TEST);\r
                        hasScissor = false;\r
index 75a7c9a..c4ffe77 100644 (file)
@@ -39,7 +39,7 @@ public interface Files {
         * \r
         */\r
        public enum FileType {\r
-               Internal, External, Absolut\r
+               Internal, External, Absolute\r
        }\r
 \r
        /**\r
index 8bba261..882253a 100644 (file)
@@ -304,6 +304,10 @@ public class Sprite {
                spriteBatch.draw(texture, vertices, 0, 20);\r
        }\r
 \r
+       public void setTexture (Texture texture) {\r
+               this.texture = texture;\r
+       }\r
+\r
        public Texture getTexture () {\r
                return texture;\r
        }\r
index 2a1397e..4138ec4 100644 (file)
@@ -635,7 +635,14 @@ public class SpriteBatch {
                idx += length;\r
        }\r
 \r
-       public void renderMesh () {\r
+       /**\r
+        * Causes any pending sprites to be rendered, without ending the SpriteBatch.\r
+        */\r
+       public void flush () {\r
+               renderMesh();\r
+       }\r
+       \r
+       private void renderMesh () {\r
                if (idx == 0) return;\r
 \r
                renderCalls++;\r
diff --git a/gdx/src/com/badlogic/gdx/graphics/particles/ParticleEffect.java b/gdx/src/com/badlogic/gdx/graphics/particles/ParticleEffect.java
new file mode 100644 (file)
index 0000000..0e1cf3c
--- /dev/null
@@ -0,0 +1,135 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.File;\r
+import java.io.FileWriter;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.InputStreamReader;\r
+import java.io.Writer;\r
+import java.util.ArrayList;\r
+\r
+import com.badlogic.gdx.Files.FileType;\r
+import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.GdxRuntimeException;\r
+import com.badlogic.gdx.files.FileHandle;\r
+import com.badlogic.gdx.graphics.SpriteBatch;\r
+import com.badlogic.gdx.graphics.Texture.TextureFilter;\r
+import com.badlogic.gdx.graphics.Texture.TextureWrap;\r
+\r
+// BOZO - Cache particle images? Or add hook to customize loading of particle images?\r
+\r
+public class ParticleEffect {\r
+       private ArrayList<ParticleEmitter> emitters = new ArrayList();\r
+\r
+       public void start () {\r
+               for (ParticleEmitter particles : emitters)\r
+                       particles.start();\r
+       }\r
+\r
+       public void draw (SpriteBatch spriteBatch, float delta) {\r
+               for (int i = 0, n = emitters.size(); i < n; i++)\r
+                       emitters.get(i).draw(spriteBatch, delta);\r
+       }\r
+\r
+       public void allowCompletion () {\r
+               for (int i = 0, n = emitters.size(); i < n; i++) {\r
+                       ParticleEmitter emitter = emitters.get(i);\r
+                       emitter.setContinuous(false);\r
+                       emitter.durationTimer = emitter.duration;\r
+               }\r
+       }\r
+\r
+       public boolean isComplete () {\r
+               for (int i = 0, n = emitters.size(); i < n; i++) {\r
+                       ParticleEmitter emitter = emitters.get(i);\r
+                       if (emitter.isContinuous()) return false;\r
+                       if (!emitter.isComplete()) return false;\r
+               }\r
+               return true;\r
+       }\r
+\r
+       public void setDuration (int duration) {\r
+               for (int i = 0, n = emitters.size(); i < n; i++) {\r
+                       ParticleEmitter emitter = emitters.get(i);\r
+                       emitter.setContinuous(false);\r
+                       emitter.duration = duration;\r
+                       emitter.durationTimer = 0;\r
+               }\r
+       }\r
+\r
+       public void setPosition (int x, int y) {\r
+               for (int i = 0, n = emitters.size(); i < n; i++)\r
+                       emitters.get(i).setPosition(x, y);\r
+       }\r
+\r
+       public ArrayList<ParticleEmitter> getEmitters () {\r
+               return emitters;\r
+       }\r
+\r
+       public void save (File file) {\r
+               Writer output = null;\r
+               try {\r
+                       output = new FileWriter(file);\r
+                       int index = 0;\r
+                       for (int i = 0, n = emitters.size(); i < n; i++) {\r
+                               ParticleEmitter emitter = emitters.get(i);\r
+                               if (index++ > 0) output.write("\n\n");\r
+                               emitter.save(output);\r
+                               output.write("- Image Path -\n");\r
+                               output.write(emitter.getImagePath() + "\n");\r
+                       }\r
+               } catch (IOException ex) {\r
+                       throw new GdxRuntimeException("Error saving effect: " + file, ex);\r
+               } finally {\r
+                       try {\r
+                               if (output != null) output.close();\r
+                       } catch (IOException ex) {\r
+                       }\r
+               }\r
+       }\r
+\r
+       public void load (FileHandle effectFile, String imagesDir, FileType fileType) {\r
+               loadEmitters(effectFile);\r
+               loadEmitterImages(imagesDir, fileType);\r
+       }\r
+\r
+       void loadEmitters (FileHandle file) {\r
+               InputStream input = file.getInputStream();\r
+               if (input == null) throw new GdxRuntimeException("Effect file not found: " + file);\r
+               emitters.clear();\r
+               BufferedReader reader = null;\r
+               try {\r
+                       reader = new BufferedReader(new InputStreamReader(input), 512);\r
+                       while (true) {\r
+                               ParticleEmitter emitter = new ParticleEmitter(reader);\r
+                               reader.readLine();\r
+                               emitter.setImagePath(ParticleEmitter.readString(reader, "Image Path"));\r
+                               emitters.add(emitter);\r
+                               if (reader.readLine() == null) break;\r
+                               if (reader.readLine() == null) break;\r
+                       }\r
+               } catch (IOException ex) {\r
+                       throw new GdxRuntimeException("Error loading effect: " + file, ex);\r
+               } finally {\r
+                       try {\r
+                               if (reader != null) reader.close();\r
+                       } catch (IOException ex) {\r
+                       }\r
+               }\r
+       }\r
+\r
+       private void loadEmitterImages (String imagesDir, FileType fileType) {\r
+               imagesDir = imagesDir.replace('\\', '/');\r
+               if (!imagesDir.endsWith("/")) imagesDir += '/';\r
+               for (int i = 0, n = emitters.size(); i < n; i++) {\r
+                       ParticleEmitter emitter = emitters.get(i);\r
+                       String imagePath = emitter.getImagePath();\r
+                       if (imagePath == null) continue;\r
+                       imagePath = imagesDir + new File(imagePath).getName();\r
+                       emitter.setTexture(Gdx.graphics.newTexture(Gdx.files.getFileHandle(imagePath, fileType), TextureFilter.Linear,\r
+                               TextureFilter.Linear, TextureWrap.ClampToEdge, TextureWrap.ClampToEdge));\r
+               }\r
+       }\r
+}\r
diff --git a/gdx/src/com/badlogic/gdx/graphics/particles/ParticleEmitter.java b/gdx/src/com/badlogic/gdx/graphics/particles/ParticleEmitter.java
new file mode 100644 (file)
index 0000000..975a304
--- /dev/null
@@ -0,0 +1,1154 @@
+\r
+package com.badlogic.gdx.graphics.particles;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.IOException;\r
+import java.io.Writer;\r
+import java.util.BitSet;\r
+\r
+import com.badlogic.gdx.Files.FileType;\r
+import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.graphics.GL10;\r
+import com.badlogic.gdx.graphics.Sprite;\r
+import com.badlogic.gdx.graphics.SpriteBatch;\r
+import com.badlogic.gdx.graphics.Texture;\r
+import com.badlogic.gdx.graphics.Texture.TextureFilter;\r
+import com.badlogic.gdx.graphics.Texture.TextureWrap;\r
+import com.badlogic.gdx.utils.MathUtils;\r
+\r
+// BOZO - Support point particles?\r
+// BOZO - Add a duplicate emitter button.\r
+\r
+public class ParticleEmitter {\r
+       private RangedNumericValue delayValue = new RangedNumericValue();\r
+       private ScaledNumericValue lifeOffsetValue = new ScaledNumericValue();\r
+       private RangedNumericValue durationValue = new RangedNumericValue();\r
+       private ScaledNumericValue lifeValue = new ScaledNumericValue();\r
+       private ScaledNumericValue emissionValue = new ScaledNumericValue();\r
+       private ScaledNumericValue sizeValue = new ScaledNumericValue();\r
+       private ScaledNumericValue rotationValue = new ScaledNumericValue();\r
+       private ScaledNumericValue velocityValue = new ScaledNumericValue();\r
+       private ScaledNumericValue angleValue = new ScaledNumericValue();\r
+       private ScaledNumericValue windValue = new ScaledNumericValue();\r
+       private ScaledNumericValue gravityValue = new ScaledNumericValue();\r
+       private ScaledNumericValue transparencyValue = new ScaledNumericValue();\r
+       private GradientColorValue tintValue = new GradientColorValue();\r
+       private RangedNumericValue xOffsetValue = new ScaledNumericValue();\r
+       private RangedNumericValue yOffsetValue = new ScaledNumericValue();\r
+       private ScaledNumericValue spawnWidthValue = new ScaledNumericValue();\r
+       private ScaledNumericValue spawnHeightValue = new ScaledNumericValue();\r
+       private SpawnShapeValue spawnShapeValue = new SpawnShapeValue();\r
+\r
+       private Texture texture;\r
+       private Particle[] particles;\r
+       private int minParticleCount, maxParticleCount = 4;\r
+       private float imageAspectRatio;\r
+       private int x, y;\r
+       private String name;\r
+       private String imagePath;\r
+       private int activeCount;\r
+       private BitSet active;\r
+       private boolean firstUpdate;\r
+       private boolean flipX, flipY;\r
+\r
+       private float emission, emissionDiff, emissionDelta;\r
+       private float lifeOffset, lifeOffsetDiff;\r
+       private float life, lifeDiff;\r
+       private int spawnWidth, spawnWidthDiff;\r
+       private int spawnHeight, spawnHeightDiff;\r
+       public float duration = 1, durationTimer;\r
+       private float delay, delayTimer;\r
+\r
+       private boolean attached;\r
+       private boolean continuous;\r
+       private boolean aligned;\r
+       private boolean behind;\r
+       private boolean additive = true;\r
+\r
+       public ParticleEmitter () {\r
+               this((Texture)null);\r
+       }\r
+\r
+       public ParticleEmitter (Texture texture) {\r
+               this.texture = texture;\r
+\r
+               initialize();\r
+\r
+               durationValue.setLow(3, 3);\r
+\r
+               emissionValue.setHigh(10, 10);\r
+\r
+               lifeValue.setHigh(1, 1);\r
+\r
+               sizeValue.setHigh(32, 32);\r
+\r
+               rotationValue.setLow(1, 360);\r
+               rotationValue.setHigh(180, 180);\r
+               rotationValue.setTimeline(new float[] {0, 1});\r
+               rotationValue.setScaling(new float[] {0, 1});\r
+               rotationValue.setRelative(true);\r
+\r
+               angleValue.setHigh(1, 360);\r
+               angleValue.setActive(true);\r
+\r
+               velocityValue.setHigh(80, 80);\r
+               velocityValue.setActive(true);\r
+\r
+               transparencyValue.setHigh(1, 1);\r
+               transparencyValue.setTimeline(new float[] {0, 0.2f, 0.8f, 1});\r
+               transparencyValue.setScaling(new float[] {0, 1, 1, 0});\r
+       }\r
+\r
+       public ParticleEmitter (BufferedReader reader) throws IOException {\r
+               initialize();\r
+               load(reader);\r
+       }\r
+\r
+       public ParticleEmitter (ParticleEmitter emitter) {\r
+               texture = emitter.texture;\r
+               imageAspectRatio = emitter.imageAspectRatio;\r
+               setMaxParticleCount(emitter.maxParticleCount);\r
+               minParticleCount = emitter.minParticleCount;\r
+               delayValue.load(emitter.delayValue);\r
+               durationValue.load(emitter.durationValue);\r
+               emissionValue.load(emitter.emissionValue);\r
+               lifeValue.load(emitter.lifeValue);\r
+               lifeOffsetValue.load(emitter.lifeOffsetValue);\r
+               sizeValue.load(emitter.sizeValue);\r
+               rotationValue.load(emitter.rotationValue);\r
+               velocityValue.load(emitter.velocityValue);\r
+               angleValue.load(emitter.angleValue);\r
+               windValue.load(emitter.windValue);\r
+               gravityValue.load(emitter.gravityValue);\r
+               transparencyValue.load(emitter.transparencyValue);\r
+               tintValue.load(emitter.tintValue);\r
+               xOffsetValue.load(emitter.xOffsetValue);\r
+               yOffsetValue.load(emitter.yOffsetValue);\r
+               spawnWidthValue.load(emitter.spawnWidthValue);\r
+               spawnHeightValue.load(emitter.spawnHeightValue);\r
+               spawnShapeValue.load(emitter.spawnShapeValue);\r
+               attached = emitter.attached;\r
+               continuous = emitter.continuous;\r
+               aligned = emitter.aligned;\r
+               behind = emitter.behind;\r
+               additive = emitter.additive;\r
+       }\r
+\r
+       private void initialize () {\r
+               durationValue.setAlwaysActive(true);\r
+               emissionValue.setAlwaysActive(true);\r
+               lifeValue.setAlwaysActive(true);\r
+               sizeValue.setAlwaysActive(true);\r
+               transparencyValue.setAlwaysActive(true);\r
+               spawnShapeValue.setAlwaysActive(true);\r
+               spawnWidthValue.setAlwaysActive(true);\r
+               spawnHeightValue.setAlwaysActive(true);\r
+       }\r
+\r
+       public void setMaxParticleCount (int maxParticleCount) {\r
+               this.maxParticleCount = maxParticleCount;\r
+               active = new BitSet(maxParticleCount);\r
+               activeCount = 0;\r
+               particles = new Particle[maxParticleCount];\r
+       }\r
+\r
+       public void addParticle () {\r
+               int activeCount = this.activeCount;\r
+               if (activeCount == maxParticleCount) return;\r
+               BitSet active = this.active;\r
+               int index = active.nextClearBit(0);\r
+               activateParticle(index);\r
+               active.set(index);\r
+               this.activeCount = activeCount + 1;\r
+       }\r
+\r
+       public void addParticles (int count) {\r
+               count = Math.min(count, maxParticleCount - activeCount);\r
+               if (count == 0) return;\r
+               BitSet active = this.active;\r
+               for (int i = 0; i < count; i++) {\r
+                       int index = active.nextClearBit(0);\r
+                       activateParticle(index);\r
+                       active.set(index);\r
+               }\r
+               this.activeCount += count;\r
+       }\r
+\r
+       public void draw (SpriteBatch spriteBatch, float delta) {\r
+               delta = Math.min(delta, 0.250f);\r
+\r
+               if (additive) spriteBatch.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE);\r
+\r
+               BitSet active = this.active;\r
+               int activeCount = this.activeCount;\r
+               int index = 0;\r
+               while (true) {\r
+                       index = active.nextSetBit(index);\r
+                       if (index == -1) break;\r
+                       if (updateParticle(index, delta))\r
+                               particles[index].draw(spriteBatch);\r
+                       else {\r
+                               active.clear(index);\r
+                               activeCount--;\r
+                       }\r
+                       index++;\r
+               }\r
+               this.activeCount = activeCount;\r
+\r
+               if (additive) spriteBatch.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);\r
+\r
+               if (delayTimer < delay) {\r
+                       delayTimer += delta;\r
+                       return;\r
+               }\r
+\r
+               if (firstUpdate) {\r
+                       firstUpdate = false;\r
+                       addParticle();\r
+               }\r
+\r
+               if (durationTimer < duration)\r
+                       durationTimer += delta;\r
+               else {\r
+                       if (!continuous) return;\r
+                       restart();\r
+               }\r
+\r
+               emissionDelta += delta;\r
+               float emissionTime = emission + emissionDiff * emissionValue.getScale(durationTimer / (float)duration);\r
+               if (emissionTime > 0) {\r
+                       emissionTime = 1 / emissionTime;\r
+                       if (emissionDelta >= emissionTime) {\r
+                               int emitCount = (int)(emissionDelta / emissionTime);\r
+                               emitCount = Math.min(emitCount, maxParticleCount - activeCount);\r
+                               emissionDelta -= emitCount * emissionTime;\r
+                               emissionDelta %= emissionTime;\r
+                               addParticles(emitCount);\r
+                       }\r
+               }\r
+               if (activeCount < minParticleCount) addParticles(minParticleCount - activeCount);\r
+       }\r
+\r
+       public void start () {\r
+               firstUpdate = true;\r
+               restart();\r
+       }\r
+\r
+       private void restart () {\r
+               delay = delayValue.active ? delayValue.newLowValue() : 0;\r
+               delayTimer = 0;\r
+\r
+               durationTimer -= duration;\r
+               duration = durationValue.newLowValue();\r
+\r
+               emission = emissionValue.newLowValue();\r
+               emissionDiff = emissionValue.newHighValue();\r
+               if (!emissionValue.isRelative()) emissionDiff -= emission;\r
+\r
+               life = lifeValue.newLowValue();\r
+               lifeDiff = lifeValue.newHighValue();\r
+               if (!lifeValue.isRelative()) lifeDiff -= life;\r
+\r
+               lifeOffset = lifeOffsetValue.active ? lifeOffsetValue.newLowValue() : 0;\r
+               lifeOffsetDiff = lifeOffsetValue.newHighValue();\r
+               if (!lifeOffsetValue.isRelative()) lifeOffsetDiff -= lifeOffset;\r
+\r
+               spawnWidth = (int)spawnWidthValue.newLowValue();\r
+               spawnWidthDiff = (int)spawnWidthValue.newHighValue();\r
+               if (!spawnWidthValue.isRelative()) spawnWidthDiff -= spawnWidth;\r
+\r
+               spawnHeight = (int)spawnHeightValue.newLowValue();\r
+               spawnHeightDiff = (int)spawnHeightValue.newHighValue();\r
+               if (!spawnHeightValue.isRelative()) spawnHeightDiff -= spawnHeight;\r
+       }\r
+\r
+       public void activateParticle (int index) {\r
+               Particle particle = particles[index];\r
+               if (particle == null) {\r
+                       particles[index] = particle = new Particle(texture);\r
+                       particle.flip(flipX, flipY);\r
+               }\r
+\r
+               float percent = durationTimer / (float)duration;\r
+\r
+               float offsetTime = lifeOffset + lifeOffsetDiff * lifeOffsetValue.getScale(percent);\r
+               particle.life = particle.currentLife = life + lifeDiff * lifeValue.getScale(percent);\r
+\r
+               if (velocityValue.active) {\r
+                       particle.velocity = velocityValue.newLowValue();\r
+                       particle.velocityDiff = velocityValue.newHighValue();\r
+                       if (!velocityValue.isRelative()) particle.velocityDiff -= particle.velocity;\r
+               }\r
+\r
+               particle.angle = angleValue.newLowValue();\r
+               particle.angleDiff = angleValue.newHighValue();\r
+               if (!angleValue.isRelative()) particle.angleDiff -= particle.angle;\r
+\r
+               particle.size = sizeValue.newLowValue() / texture.getWidth();\r
+               particle.sizeDiff = sizeValue.newHighValue() / texture.getWidth();\r
+               if (!sizeValue.isRelative()) particle.sizeDiff -= particle.size;\r
+\r
+               if (rotationValue.active) {\r
+                       particle.rotation = particle.currentRotation = rotationValue.newLowValue();\r
+                       particle.rotationDiff = rotationValue.newHighValue();\r
+                       if (!rotationValue.isRelative()) particle.rotationDiff -= particle.rotation;\r
+               }\r
+\r
+               if (windValue.active) {\r
+                       particle.wind = windValue.newLowValue();\r
+                       particle.windDiff = windValue.newHighValue();\r
+                       if (!windValue.isRelative()) particle.windDiff -= particle.wind;\r
+               }\r
+\r
+               if (gravityValue.active) {\r
+                       particle.gravity = gravityValue.newLowValue();\r
+                       particle.gravityDiff = gravityValue.newHighValue();\r
+                       if (!gravityValue.isRelative()) particle.gravityDiff -= particle.gravity;\r
+               }\r
+\r
+               particle.transparency = transparencyValue.newLowValue();\r
+               particle.transparencyDiff = transparencyValue.newHighValue() - particle.transparency;\r
+\r
+               // Spawn.\r
+               int x = this.x;\r
+               if (xOffsetValue.active) x += (int)xOffsetValue.newLowValue();\r
+               int y = this.y;\r
+               if (yOffsetValue.active) y += (int)yOffsetValue.newLowValue();\r
+               switch (spawnShapeValue.shape) {\r
+               case square: {\r
+                       int width = spawnWidth + (int)(spawnWidthDiff * spawnWidthValue.getScale(percent));\r
+                       int height = spawnHeight + (int)(spawnHeightDiff * spawnHeightValue.getScale(percent));\r
+                       x += MathUtils.random(width) - width / 2;\r
+                       y += MathUtils.random(height) - height / 2;\r
+                       break;\r
+               }\r
+               case ellipse: {\r
+                       int width = spawnWidth + (int)(spawnWidthDiff * spawnWidthValue.getScale(percent));\r
+                       int height = spawnHeight + (int)(spawnHeightDiff * spawnHeightValue.getScale(percent));\r
+                       int radiusX = width / 2;\r
+                       int radiusY = height / 2;\r
+                       if (radiusX == 0 || radiusY == 0) break;\r
+                       float scaleY = radiusX / (float)radiusY;\r
+                       if (spawnShapeValue.edges) {\r
+                               float angle;\r
+                               switch (spawnShapeValue.side) {\r
+                               case top:\r
+                                       angle = -MathUtils.random(179f);\r
+                                       break;\r
+                               case bottom:\r
+                                       angle = MathUtils.random(179f);\r
+                                       break;\r
+                               default:\r
+                                       angle = MathUtils.random(360f);\r
+                                       break;\r
+                               }\r
+                               x += MathUtils.cosDeg(angle) * radiusX;\r
+                               y += MathUtils.sinDeg(angle) * radiusX / scaleY;\r
+                       } else {\r
+                               int radius2 = radiusX * radiusX;\r
+                               while (true) {\r
+                                       int px = MathUtils.random(width) - radiusX;\r
+                                       int py = MathUtils.random(width) - radiusX;\r
+                                       if (px * px + py * py <= radius2) {\r
+                                               x += px;\r
+                                               y += py / scaleY;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+                       break;\r
+               }\r
+               case line: {\r
+                       int width = spawnWidth + (int)(spawnWidthDiff * spawnWidthValue.getScale(percent));\r
+                       int height = spawnHeight + (int)(spawnHeightDiff * spawnHeightValue.getScale(percent));\r
+                       if (width != 0) {\r
+                               float lineX = width * MathUtils.random();\r
+                               x += lineX;\r
+                               y += lineX * (height / (float)width);\r
+                       }\r
+                       break;\r
+               }\r
+               }\r
+\r
+               particle.setRotation(particle.currentRotation);\r
+               particle.setBounds(x - texture.getWidth() / 2, y - texture.getHeight() / 2, texture.getWidth(), texture.getHeight());\r
+\r
+               updateParticle(index, offsetTime);\r
+       }\r
+\r
+       public boolean updateParticle (int index, float delta) {\r
+               Particle particle = particles[index];\r
+               float life = particle.currentLife - delta;\r
+               if (life <= 0) return false;\r
+               particle.currentLife = life;\r
+\r
+               float percent = 1 - particle.currentLife / particle.life;\r
+\r
+               particle.setScale(particle.size + particle.sizeDiff * sizeValue.getScale(percent));\r
+\r
+               float angle = particle.angle + particle.angleDiff * angleValue.getScale(percent);\r
+\r
+               if (rotationValue.active) {\r
+                       float rotation = particle.rotation + particle.rotationDiff * rotationValue.getScale(percent);\r
+                       if (aligned) rotation += angle;\r
+                       rotation -= particle.currentRotation;\r
+                       if (rotation != 0) {\r
+                               particle.currentRotation += rotation;\r
+                               particle.rotate(rotation);\r
+                       }\r
+               }\r
+\r
+               if (velocityValue.active) {\r
+                       float velocity = (particle.velocity + particle.velocityDiff * velocityValue.getScale(percent)) * delta;\r
+                       float velocityX = velocity;\r
+                       float velocityY = velocity;\r
+                       if (angleValue.active) {\r
+                               velocityX *= MathUtils.cosDeg(angle);\r
+                               velocityY *= MathUtils.sinDeg(angle);\r
+                       }\r
+                       if (windValue.active) velocityX += (particle.wind + particle.windDiff * windValue.getScale(percent)) * delta;\r
+                       if (gravityValue.active)\r
+                               velocityY += (particle.gravity + particle.gravityDiff * gravityValue.getScale(percent)) * delta;\r
+                       particle.translate(velocityX, velocityY);\r
+               }\r
+\r
+               float[] color = tintValue.getColor(percent);\r
+               particle.setColor(color[0], color[1], color[2],\r
+                       particle.transparency + particle.transparencyDiff * transparencyValue.getScale(percent));\r
+\r
+               return true;\r
+       }\r
+\r
+       public void setPosition (int x, int y) {\r
+               if (attached) {\r
+                       int xAmount = x - this.x;\r
+                       int yAmount = y - this.y;\r
+                       BitSet active = this.active;\r
+                       int index = 0;\r
+                       while (true) {\r
+                               index = active.nextSetBit(index);\r
+                               if (index == -1) break;\r
+                               Particle particle = particles[index];\r
+                               particle.translate(xAmount, yAmount);\r
+                               index++;\r
+                       }\r
+               }\r
+               this.x = x;\r
+               this.y = y;\r
+       }\r
+\r
+       public void setTexture (Texture texture) {\r
+               this.texture = texture;\r
+               if (texture == null) return;\r
+               imageAspectRatio = texture.getHeight() / (float)texture.getWidth();\r
+               float originX = texture.getWidth() / 2;\r
+               float originY = texture.getHeight() / 2;\r
+               for (int i = 0, n = particles.length; i < n; i++) {\r
+                       Particle particle = particles[i];\r
+                       if (particle == null) break;\r
+                       particle.setTexture(texture);\r
+                       particle.setOrigin(originX, originY);\r
+               }\r
+       }\r
+\r
+       public Texture getTexture () {\r
+               return texture;\r
+       }\r
+\r
+       public String getName () {\r
+               return name;\r
+       }\r
+\r
+       public void setName (String name) {\r
+               this.name = name;\r
+       }\r
+\r
+       public ScaledNumericValue getLife () {\r
+               return lifeValue;\r
+       }\r
+\r
+       public ScaledNumericValue getSize () {\r
+               return sizeValue;\r
+       }\r
+\r
+       public ScaledNumericValue getRotation () {\r
+               return rotationValue;\r
+       }\r
+\r
+       public GradientColorValue getTint () {\r
+               return tintValue;\r
+       }\r
+\r
+       public ScaledNumericValue getVelocity () {\r
+               return velocityValue;\r
+       }\r
+\r
+       public ScaledNumericValue getWind () {\r
+               return windValue;\r
+       }\r
+\r
+       public ScaledNumericValue getGravity () {\r
+               return gravityValue;\r
+       }\r
+\r
+       public ScaledNumericValue getAngle () {\r
+               return angleValue;\r
+       }\r
+\r
+       public ScaledNumericValue getEmission () {\r
+               return emissionValue;\r
+       }\r
+\r
+       public ScaledNumericValue getTransparency () {\r
+               return transparencyValue;\r
+       }\r
+\r
+       public RangedNumericValue getDuration () {\r
+               return durationValue;\r
+       }\r
+\r
+       public RangedNumericValue getDelay () {\r
+               return delayValue;\r
+       }\r
+\r
+       public ScaledNumericValue getLifeOffset () {\r
+               return lifeOffsetValue;\r
+       }\r
+\r
+       public RangedNumericValue getXOffsetValue () {\r
+               return xOffsetValue;\r
+       }\r
+\r
+       public RangedNumericValue getYOffsetValue () {\r
+               return yOffsetValue;\r
+       }\r
+\r
+       public ScaledNumericValue getSpawnWidth () {\r
+               return spawnWidthValue;\r
+       }\r
+\r
+       public ScaledNumericValue getSpawnHeight () {\r
+               return spawnHeightValue;\r
+       }\r
+\r
+       public SpawnShapeValue getSpawnShape () {\r
+               return spawnShapeValue;\r
+       }\r
+\r
+       public boolean isAttached () {\r
+               return attached;\r
+       }\r
+\r
+       public void setAttached (boolean attached) {\r
+               this.attached = attached;\r
+       }\r
+\r
+       public boolean isContinuous () {\r
+               return continuous;\r
+       }\r
+\r
+       public void setContinuous (boolean continuous) {\r
+               this.continuous = continuous;\r
+       }\r
+\r
+       public boolean isAligned () {\r
+               return aligned;\r
+       }\r
+\r
+       public void setAligned (boolean aligned) {\r
+               this.aligned = aligned;\r
+       }\r
+\r
+       public boolean isAdditive () {\r
+               return additive;\r
+       }\r
+\r
+       public void setAdditive (boolean additive) {\r
+               this.additive = additive;\r
+       }\r
+\r
+       public boolean isBehind () {\r
+               return behind;\r
+       }\r
+\r
+       public void setBehind (boolean behind) {\r
+               this.behind = behind;\r
+       }\r
+\r
+       public int getMinParticleCount () {\r
+               return minParticleCount;\r
+       }\r
+\r
+       public void setMinParticleCount (int minParticleCount) {\r
+               this.minParticleCount = minParticleCount;\r
+       }\r
+\r
+       public int getMaxParticleCount () {\r
+               return maxParticleCount;\r
+       }\r
+\r
+       public boolean isComplete () {\r
+               if (delayTimer < delay) return false;\r
+               return durationTimer >= duration && activeCount == 0;\r
+       }\r
+\r
+       public float getPercentComplete () {\r
+               if (delayTimer < delay) return 0;\r
+               return Math.min(1, durationTimer / (float)duration);\r
+       }\r
+\r
+       public int getX () {\r
+               return x;\r
+       }\r
+\r
+       public int getY () {\r
+               return y;\r
+       }\r
+\r
+       public int getActiveCount () {\r
+               return activeCount;\r
+       }\r
+\r
+       public int getDrawCount () {\r
+               return active.length();\r
+       }\r
+\r
+       public String getImagePath () {\r
+               return imagePath;\r
+       }\r
+\r
+       public void setImagePath (String imagePath) {\r
+               this.imagePath = imagePath;\r
+       }\r
+\r
+       public void setFlip (boolean flipX, boolean flipY) {\r
+               this.flipX = flipX;\r
+               this.flipY = flipY;\r
+       }\r
+\r
+       public void save (Writer output) throws IOException {\r
+               output.write(name + "\n");\r
+               output.write("- Delay -\n");\r
+               delayValue.save(output);\r
+               output.write("- Duration - \n");\r
+               durationValue.save(output);\r
+               output.write("- Count - \n");\r
+               output.write("min: " + minParticleCount + "\n");\r
+               output.write("max: " + maxParticleCount + "\n");\r
+               output.write("- Emission - \n");\r
+               emissionValue.save(output);\r
+               output.write("- Life - \n");\r
+               lifeValue.save(output);\r
+               output.write("- Life Offset - \n");\r
+               lifeOffsetValue.save(output);\r
+               output.write("- X Offset - \n");\r
+               xOffsetValue.save(output);\r
+               output.write("- Y Offset - \n");\r
+               yOffsetValue.save(output);\r
+               output.write("- Spawn Shape - \n");\r
+               spawnShapeValue.save(output);\r
+               output.write("- Spawn Width - \n");\r
+               spawnWidthValue.save(output);\r
+               output.write("- Spawn Height - \n");\r
+               spawnHeightValue.save(output);\r
+               output.write("- Size - \n");\r
+               sizeValue.save(output);\r
+               output.write("- Velocity - \n");\r
+               velocityValue.save(output);\r
+               output.write("- Angle - \n");\r
+               angleValue.save(output);\r
+               output.write("- Rotation - \n");\r
+               rotationValue.save(output);\r
+               output.write("- Wind - \n");\r
+               windValue.save(output);\r
+               output.write("- Gravity - \n");\r
+               gravityValue.save(output);\r
+               output.write("- Tint - \n");\r
+               tintValue.save(output);\r
+               output.write("- Transparency - \n");\r
+               transparencyValue.save(output);\r
+               output.write("- Options - \n");\r
+               output.write("attached: " + attached + "\n");\r
+               output.write("continuous: " + continuous + "\n");\r
+               output.write("aligned: " + aligned + "\n");\r
+               output.write("additive: " + additive + "\n");\r
+               output.write("behind: " + behind + "\n");\r
+       }\r
+\r
+       public void load (BufferedReader reader) throws IOException {\r
+               name = readString(reader, "name");\r
+               reader.readLine();\r
+               delayValue.load(reader);\r
+               reader.readLine();\r
+               durationValue.load(reader);\r
+               reader.readLine();\r
+               setMinParticleCount(readInt(reader, "minParticleCount"));\r
+               setMaxParticleCount(readInt(reader, "maxParticleCount"));\r
+               reader.readLine();\r
+               emissionValue.load(reader);\r
+               reader.readLine();\r
+               lifeValue.load(reader);\r
+               reader.readLine();\r
+               lifeOffsetValue.load(reader);\r
+               reader.readLine();\r
+               xOffsetValue.load(reader);\r
+               reader.readLine();\r
+               yOffsetValue.load(reader);\r
+               reader.readLine();\r
+               spawnShapeValue.load(reader);\r
+               reader.readLine();\r
+               spawnWidthValue.load(reader);\r
+               reader.readLine();\r
+               spawnHeightValue.load(reader);\r
+               reader.readLine();\r
+               sizeValue.load(reader);\r
+               reader.readLine();\r
+               velocityValue.load(reader);\r
+               reader.readLine();\r
+               angleValue.load(reader);\r
+               reader.readLine();\r
+               rotationValue.load(reader);\r
+               reader.readLine();\r
+               windValue.load(reader);\r
+               reader.readLine();\r
+               gravityValue.load(reader);\r
+               reader.readLine();\r
+               tintValue.load(reader);\r
+               reader.readLine();\r
+               transparencyValue.load(reader);\r
+               reader.readLine();\r
+               attached = readBoolean(reader, "attached");\r
+               continuous = readBoolean(reader, "continuous");\r
+               aligned = readBoolean(reader, "aligned");\r
+               additive = readBoolean(reader, "additive");\r
+               behind = readBoolean(reader, "behind");\r
+       }\r
+\r
+       static String readString (BufferedReader reader, String name) throws IOException {\r
+               String line = reader.readLine();\r
+               if (line == null) throw new IOException("Missing value: " + name);\r
+               return line.substring(line.indexOf(":") + 1).trim();\r
+       }\r
+\r
+       static boolean readBoolean (BufferedReader reader, String name) throws IOException {\r
+               return Boolean.parseBoolean(readString(reader, name));\r
+       }\r
+\r
+       static int readInt (BufferedReader reader, String name) throws IOException {\r
+               return Integer.parseInt(readString(reader, name));\r
+       }\r
+\r
+       static float readFloat (BufferedReader reader, String name) throws IOException {\r
+               return Float.parseFloat(readString(reader, name));\r
+       }\r
+\r
+       static class Particle extends Sprite {\r
+               float life, currentLife;\r
+               float size, sizeDiff;\r
+               float rotation, currentRotation, rotationDiff;\r
+               float velocity, velocityDiff;\r
+               float angle, angleDiff;\r
+               float transparency, transparencyDiff;\r
+               float wind, windDiff;\r
+               float gravity, gravityDiff;\r
+\r
+               public Particle (Texture texture) {\r
+                       super(texture);\r
+               }\r
+       }\r
+\r
+       static public class ParticleValue {\r
+               boolean active;\r
+               boolean alwaysActive;\r
+\r
+               public void setAlwaysActive (boolean alwaysActive) {\r
+                       this.alwaysActive = alwaysActive;\r
+               }\r
+\r
+               public boolean isAlwaysActive () {\r
+                       return alwaysActive;\r
+               }\r
+\r
+               public boolean isActive () {\r
+                       return active;\r
+               }\r
+\r
+               public void setActive (boolean active) {\r
+                       this.active = active;\r
+               }\r
+\r
+               public void save (Writer output) throws IOException {\r
+                       if (!alwaysActive)\r
+                               output.write("active: " + active + "\n");\r
+                       else\r
+                               active = true;\r
+               }\r
+\r
+               public void load (BufferedReader reader) throws IOException {\r
+                       if (!alwaysActive)\r
+                               active = readBoolean(reader, "active");\r
+                       else\r
+                               active = true;\r
+               }\r
+\r
+               public void load (ParticleValue value) {\r
+                       active = value.active;\r
+                       alwaysActive = value.alwaysActive;\r
+               }\r
+       }\r
+\r
+       static public class NumericValue extends ParticleValue {\r
+               private float value;\r
+\r
+               public float getValue () {\r
+                       return value;\r
+               }\r
+\r
+               public void setValue (float value) {\r
+                       this.value = value;\r
+               }\r
+\r
+               public void save (Writer output) throws IOException {\r
+                       super.save(output);\r
+                       if (!active) return;\r
+                       output.write("value: " + value + "\n");\r
+               }\r
+\r
+               public void load (BufferedReader reader) throws IOException {\r
+                       super.load(reader);\r
+                       if (!active) return;\r
+                       value = readFloat(reader, "value");\r
+               }\r
+\r
+               public void load (NumericValue value) {\r
+                       super.load(value);\r
+                       this.value = value.value;\r
+               }\r
+       }\r
+\r
+       static public class RangedNumericValue extends ParticleValue {\r
+               private float lowMin, lowMax;\r
+\r
+               public float newLowValue () {\r
+                       return lowMin + (lowMax - lowMin) * MathUtils.random();\r
+               }\r
+\r
+               public void setLow (int value) {\r
+                       lowMin = value;\r
+                       lowMax = value;\r
+               }\r
+\r
+               public void setLow (int min, int max) {\r
+                       lowMin = min;\r
+                       lowMax = max;\r
+               }\r
+\r
+               public float getLowMin () {\r
+                       return lowMin;\r
+               }\r
+\r
+               public void setLowMin (float lowMin) {\r
+                       this.lowMin = lowMin;\r
+               }\r
+\r
+               public float getLowMax () {\r
+                       return lowMax;\r
+               }\r
+\r
+               public void setLowMax (float lowMax) {\r
+                       this.lowMax = lowMax;\r
+               }\r
+\r
+               public void save (Writer output) throws IOException {\r
+                       super.save(output);\r
+                       if (!active) return;\r
+                       output.write("lowMin: " + lowMin + "\n");\r
+                       output.write("lowMax: " + lowMax + "\n");\r
+               }\r
+\r
+               public void load (BufferedReader reader) throws IOException {\r
+                       super.load(reader);\r
+                       if (!active) return;\r
+                       lowMin = readFloat(reader, "lowMin");\r
+                       lowMax = readFloat(reader, "lowMax");\r
+               }\r
+\r
+               public void load (RangedNumericValue value) {\r
+                       super.load(value);\r
+                       lowMax = value.lowMax;\r
+                       lowMin = value.lowMin;\r
+               }\r
+       }\r
+\r
+       static public class ScaledNumericValue extends RangedNumericValue {\r
+               private float[] scaling = {1};\r
+               private float[] timeline = {0};\r
+               private float highMin, highMax;\r
+               private boolean relative;\r
+\r
+               public float newHighValue () {\r
+                       return highMin + (highMax - highMin) * MathUtils.random();\r
+               }\r
+\r
+               public void setHigh (int value) {\r
+                       highMin = value;\r
+                       highMax = value;\r
+               }\r
+\r
+               public void setHigh (int min, int max) {\r
+                       highMin = min;\r
+                       highMax = max;\r
+               }\r
+\r
+               public float getHighMin () {\r
+                       return highMin;\r
+               }\r
+\r
+               public void setHighMin (float highMin) {\r
+                       this.highMin = highMin;\r
+               }\r
+\r
+               public float getHighMax () {\r
+                       return highMax;\r
+               }\r
+\r
+               public void setHighMax (float highMax) {\r
+                       this.highMax = highMax;\r
+               }\r
+\r
+               public float[] getScaling () {\r
+                       return scaling;\r
+               }\r
+\r
+               public void setScaling (float[] values) {\r
+                       this.scaling = values;\r
+               }\r
+\r
+               public float[] getTimeline () {\r
+                       return timeline;\r
+               }\r
+\r
+               public void setTimeline (float[] timeline) {\r
+                       this.timeline = timeline;\r
+               }\r
+\r
+               public boolean isRelative () {\r
+                       return relative;\r
+               }\r
+\r
+               public void setRelative (boolean relative) {\r
+                       this.relative = relative;\r
+               }\r
+\r
+               public float getScale (float percent) {\r
+                       int endIndex = -1;\r
+                       float[] timeline = this.timeline;\r
+                       int n = timeline.length;\r
+                       for (int i = 1; i < n; i++) {\r
+                               float t = timeline[i];\r
+                               if (t > percent) {\r
+                                       endIndex = i;\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (endIndex == -1) return scaling[n - 1];\r
+                       float[] scaling = this.scaling;\r
+                       int startIndex = endIndex - 1;\r
+                       float startValue = scaling[startIndex];\r
+                       float startTime = timeline[startIndex];\r
+                       return startValue + (scaling[endIndex] - startValue) * ((percent - startTime) / (timeline[endIndex] - startTime));\r
+               }\r
+\r
+               public void save (Writer output) throws IOException {\r
+                       super.save(output);\r
+                       if (!active) return;\r
+                       output.write("highMin: " + highMin + "\n");\r
+                       output.write("highMax: " + highMax + "\n");\r
+                       output.write("relative: " + relative + "\n");\r
+                       output.write("scalingCount: " + scaling.length + "\n");\r
+                       for (int i = 0; i < scaling.length; i++)\r
+                               output.write("scaling" + i + ": " + scaling[i] + "\n");\r
+                       output.write("timelineCount: " + timeline.length + "\n");\r
+                       for (int i = 0; i < timeline.length; i++)\r
+                               output.write("timeline" + i + ": " + timeline[i] + "\n");\r
+               }\r
+\r
+               public void load (BufferedReader reader) throws IOException {\r
+                       super.load(reader);\r
+                       if (!active) return;\r
+                       highMin = readFloat(reader, "highMin");\r
+                       highMax = readFloat(reader, "highMax");\r
+                       relative = readBoolean(reader, "relative");\r
+                       scaling = new float[readInt(reader, "scalingCount")];\r
+                       for (int i = 0; i < scaling.length; i++)\r
+                               scaling[i] = readFloat(reader, "scaling" + i);\r
+                       timeline = new float[readInt(reader, "timelineCount")];\r
+                       for (int i = 0; i < timeline.length; i++)\r
+                               timeline[i] = readFloat(reader, "timeline" + i);\r
+               }\r
+\r
+               public void load (ScaledNumericValue value) {\r
+                       super.load(value);\r
+                       highMax = value.highMax;\r
+                       highMin = value.highMin;\r
+                       scaling = new float[value.scaling.length];\r
+                       System.arraycopy(value.scaling, 0, scaling, 0, scaling.length);\r
+                       timeline = new float[value.timeline.length];\r
+                       System.arraycopy(value.timeline, 0, timeline, 0, timeline.length);\r
+                       relative = value.relative;\r
+               }\r
+       }\r
+\r
+       static public class GradientColorValue extends ParticleValue {\r
+               static private float[] temp = new float[4];\r
+\r
+               private float[] colors = {1, 1, 1};\r
+               private float[] timeline = {0};\r
+\r
+               public GradientColorValue () {\r
+                       alwaysActive = true;\r
+               }\r
+\r
+               public float[] getTimeline () {\r
+                       return timeline;\r
+               }\r
+\r
+               public void setTimeline (float[] timeline) {\r
+                       this.timeline = timeline;\r
+               }\r
+\r
+               public float[] getColors () {\r
+                       return colors;\r
+               }\r
+\r
+               public void setColors (float[] colors) {\r
+                       this.colors = colors;\r
+               }\r
+\r
+               public float[] getColor (float percent) {\r
+                       int startIndex = 0, endIndex = -1;\r
+                       float[] timeline = this.timeline;\r
+                       int n = timeline.length;\r
+                       for (int i = 1; i < n; i++) {\r
+                               float t = timeline[i];\r
+                               if (t > percent) {\r
+                                       endIndex = i;\r
+                                       break;\r
+                               }\r
+                               startIndex = i;\r
+                       }\r
+                       float startTime = timeline[startIndex];\r
+                       startIndex *= 3;\r
+                       float r1 = colors[startIndex];\r
+                       float g1 = colors[startIndex + 1];\r
+                       float b1 = colors[startIndex + 2];\r
+                       if (endIndex == -1) {\r
+                               temp[0] = r1;\r
+                               temp[1] = g1;\r
+                               temp[2] = b1;\r
+                               return temp;\r
+                       }\r
+                       float factor = (percent - startTime) / (timeline[endIndex] - startTime);\r
+                       endIndex *= 3;\r
+                       temp[0] = r1 + (colors[endIndex] - r1) * factor;\r
+                       temp[1] = g1 + (colors[endIndex + 1] - g1) * factor;\r
+                       temp[2] = b1 + (colors[endIndex + 2] - b1) * factor;\r
+                       return temp;\r
+               }\r
+\r
+               public void save (Writer output) throws IOException {\r
+                       super.save(output);\r
+                       if (!active) return;\r
+                       output.write("colorsCount: " + colors.length + "\n");\r
+                       for (int i = 0; i < colors.length; i++)\r
+                               output.write("colors" + i + ": " + colors[i] + "\n");\r
+                       output.write("timelineCount: " + timeline.length + "\n");\r
+                       for (int i = 0; i < timeline.length; i++)\r
+                               output.write("timeline" + i + ": " + timeline[i] + "\n");\r
+               }\r
+\r
+               public void load (BufferedReader reader) throws IOException {\r
+                       super.load(reader);\r
+                       if (!active) return;\r
+                       colors = new float[readInt(reader, "colorsCount")];\r
+                       for (int i = 0; i < colors.length; i++)\r
+                               colors[i] = readFloat(reader, "colors" + i);\r
+                       timeline = new float[readInt(reader, "timelineCount")];\r
+                       for (int i = 0; i < timeline.length; i++)\r
+                               timeline[i] = readFloat(reader, "timeline" + i);\r
+               }\r
+\r
+               public void load (GradientColorValue value) {\r
+                       super.load(value);\r
+                       colors = new float[3];\r
+                       System.arraycopy(value.colors, 0, colors, 0, colors.length);\r
+                       timeline = new float[value.timeline.length];\r
+                       System.arraycopy(value.timeline, 0, timeline, 0, timeline.length);\r
+               }\r
+       }\r
+\r
+       static public class SpawnShapeValue extends ParticleValue {\r
+               SpawnShape shape = SpawnShape.point;\r
+               boolean edges;\r
+               SpawnEllipseSide side = SpawnEllipseSide.both;\r
+\r
+               public SpawnShape getShape () {\r
+                       return shape;\r
+               }\r
+\r
+               public void setShape (SpawnShape shape) {\r
+                       this.shape = shape;\r
+               }\r
+\r
+               public boolean isEdges () {\r
+                       return edges;\r
+               }\r
+\r
+               public void setEdges (boolean edges) {\r
+                       this.edges = edges;\r
+               }\r
+\r
+               public SpawnEllipseSide getSide () {\r
+                       return side;\r
+               }\r
+\r
+               public void setSide (SpawnEllipseSide side) {\r
+                       this.side = side;\r
+               }\r
+\r
+               public void save (Writer output) throws IOException {\r
+                       super.save(output);\r
+                       if (!active) return;\r
+                       output.write("shape: " + shape + "\n");\r
+                       if (shape == SpawnShape.ellipse) {\r
+                               output.write("edges: " + edges + "\n");\r
+                               output.write("side: " + side + "\n");\r
+                       }\r
+               }\r
+\r
+               public void load (BufferedReader reader) throws IOException {\r
+                       super.load(reader);\r
+                       if (!active) return;\r
+                       shape = SpawnShape.valueOf(readString(reader, "shape"));\r
+                       if (shape == SpawnShape.ellipse) {\r
+                               edges = readBoolean(reader, "edges");\r
+                               side = SpawnEllipseSide.valueOf(readString(reader, "side"));\r
+                       }\r
+               }\r
+\r
+               public void load (SpawnShapeValue value) {\r
+                       super.load(value);\r
+                       shape = value.shape;\r
+                       edges = value.edges;\r
+                       side = value.side;\r
+               }\r
+       }\r
+\r
+       static public enum SpawnShape {\r
+               point, line, square, ellipse\r
+       }\r
+\r
+       static public enum SpawnEllipseSide {\r
+               both, top, bottom\r
+       }\r
+}\r
index 5bba36c..5daa8bb 100644 (file)
@@ -9,9 +9,9 @@ package com.badlogic.gdx.utils;
  * Roquen on JavaGaming.org for random numbers.<br>\r
  */\r
 public class MathUtils {\r
-       static public final float PI = 3.1415926535897932384626433832795028841971f;\r
+       static public final float PI = 3.1415927f;\r
 \r
-       static private final int SIN_BITS = 14; // Adjust for accuracy.\r
+       static private final int SIN_BITS = 13; // Adjust for accuracy.\r
        static private final int SIN_MASK = ~(-1 << SIN_BITS);\r
        static private final int SIN_COUNT = SIN_MASK + 1;\r
 \r
@@ -85,7 +85,7 @@ public class MathUtils {
                                mul = 1;\r
                        add = 0;\r
                }\r
-               float invDiv = 1 / (((x < y) ? y : x) * INV_ATAN2_DIM_MINUS_1);\r
+               float invDiv = 1 / ((x < y ? y : x) * INV_ATAN2_DIM_MINUS_1);\r
                int xi = (int)(x * invDiv);\r
                int yi = (int)(y * invDiv);\r
                return (atan2[yi * ATAN2_DIM + xi] + add) * mul;\r
@@ -93,13 +93,6 @@ public class MathUtils {
 \r
        // ---\r
 \r
-       static public final float sqrt (float value) {\r
-               // BOZO - Want to call android.util.FloatMath.sqrt, but then what to do for desktop?\r
-               return (float)Math.sqrt(value);\r
-       }\r
-\r
-       // ---\r
-\r
        static private int randomSeed = (int)System.currentTimeMillis();\r
 \r
        /**\r
@@ -109,13 +102,13 @@ public class MathUtils {
        static public final int random (int range) {\r
                int seed = randomSeed * 1103515245 + 12345;\r
                randomSeed = seed;\r
-               return ((seed >>> 15) * (range + 1)) >>> 17;\r
+               return (seed >>> 15) * (range + 1) >>> 17;\r
        }\r
 \r
        static public final int random (int start, int end) {\r
                int seed = randomSeed * 1103515245 + 12345;\r
                randomSeed = seed;\r
-               return (((seed >>> 15) * ((end - start) + 1)) >>> 17) + start;\r
+               return ((seed >>> 15) * (end - start + 1) >>> 17) + start;\r
        }\r
 \r
        static public final boolean randomBoolean () {\r
@@ -127,25 +120,25 @@ public class MathUtils {
        static public final float random () {\r
                int seed = randomSeed * 1103515245 + 12345;\r
                randomSeed = seed;\r
-               return (seed >>> 8) * (1f / (1 << 24));\r
+               return (seed >>> 8) * 1f / (1 << 24);\r
        }\r
 \r
        static public final float random (float range) {\r
                int seed = randomSeed * 1103515245 + 12345;\r
                randomSeed = seed;\r
-               return (seed >>> 8) * (1f / (1 << 24)) * range;\r
+               return (seed >>> 8) * 1f / (1 << 24) * range;\r
        }\r
 \r
        static public final float random (float start, float end) {\r
                int seed = randomSeed * 1103515245 + 12345;\r
                randomSeed = seed;\r
-               return start + (seed >>> 8) * (1f / (1 << 24)) * (end - start);\r
+               return start + (seed >>> 8) * 1f / (1 << 24) * (end - start);\r
        }\r
 \r
        // ---\r
 \r
        static public int nextPowerOfTwo (int value) {\r
-               return 1 << (32 - Integer.numberOfLeadingZeros(value - 1));\r
+               return 1 << 32 - Integer.numberOfLeadingZeros(value - 1);\r
        }\r
 \r
        // ---\r