commit 9f68d51064cd96bd3a158883c59b405edb4ca607 Author: rnentjes Date: Fri Jan 17 12:34:48 2014 +0100 Initial commit diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..67c6927 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +jogl-compute-shaders-fireworks \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..217af47 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..b663fc9 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..40a2428 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..9d32e50 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..15218c1 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,630 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + localhost + 5050 + + + + + + + + + 1389957748775 + 1389957748775 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No facets are configured + + + + + + + + Dart SDK + + + + + + + + 1.7 + + + + + + + + jogl-compute-shaders-fireworks + + + + + + + + 1.7 + + + + + + + + + + + + + + + + diff --git a/jogl-compute-shaders-fireworks.iml b/jogl-compute-shaders-fireworks.iml new file mode 100644 index 0000000..026a0ed --- /dev/null +++ b/jogl-compute-shaders-fireworks.iml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/gluegen-rt-natives-linux-amd64.jar b/lib/gluegen-rt-natives-linux-amd64.jar new file mode 100644 index 0000000..674c039 Binary files /dev/null and b/lib/gluegen-rt-natives-linux-amd64.jar differ diff --git a/lib/gluegen-rt-natives-linux-i586.jar b/lib/gluegen-rt-natives-linux-i586.jar new file mode 100644 index 0000000..7f87a04 Binary files /dev/null and b/lib/gluegen-rt-natives-linux-i586.jar differ diff --git a/lib/gluegen-rt-natives-macosx-universal.jar b/lib/gluegen-rt-natives-macosx-universal.jar new file mode 100644 index 0000000..731a1e6 Binary files /dev/null and b/lib/gluegen-rt-natives-macosx-universal.jar differ diff --git a/lib/gluegen-rt-natives-windows-amd64.jar b/lib/gluegen-rt-natives-windows-amd64.jar new file mode 100644 index 0000000..f53b697 Binary files /dev/null and b/lib/gluegen-rt-natives-windows-amd64.jar differ diff --git a/lib/gluegen-rt-natives-windows-i586.jar b/lib/gluegen-rt-natives-windows-i586.jar new file mode 100644 index 0000000..102bbb2 Binary files /dev/null and b/lib/gluegen-rt-natives-windows-i586.jar differ diff --git a/lib/gluegen-rt.jar b/lib/gluegen-rt.jar new file mode 100644 index 0000000..24c5ee3 Binary files /dev/null and b/lib/gluegen-rt.jar differ diff --git a/lib/jogl-all-natives-linux-amd64.jar b/lib/jogl-all-natives-linux-amd64.jar new file mode 100644 index 0000000..0f94622 Binary files /dev/null and b/lib/jogl-all-natives-linux-amd64.jar differ diff --git a/lib/jogl-all-natives-linux-i586.jar b/lib/jogl-all-natives-linux-i586.jar new file mode 100644 index 0000000..6ef2990 Binary files /dev/null and b/lib/jogl-all-natives-linux-i586.jar differ diff --git a/lib/jogl-all-natives-macosx-universal.jar b/lib/jogl-all-natives-macosx-universal.jar new file mode 100644 index 0000000..e5acd77 Binary files /dev/null and b/lib/jogl-all-natives-macosx-universal.jar differ diff --git a/lib/jogl-all-natives-windows-amd64.jar b/lib/jogl-all-natives-windows-amd64.jar new file mode 100644 index 0000000..e0d1eb4 Binary files /dev/null and b/lib/jogl-all-natives-windows-amd64.jar differ diff --git a/lib/jogl-all-natives-windows-i586.jar b/lib/jogl-all-natives-windows-i586.jar new file mode 100644 index 0000000..f318482 Binary files /dev/null and b/lib/jogl-all-natives-windows-i586.jar differ diff --git a/lib/jogl-all-noawt.jar b/lib/jogl-all-noawt.jar new file mode 100644 index 0000000..eb5d897 Binary files /dev/null and b/lib/jogl-all-noawt.jar differ diff --git a/lib/slf4j-api-1.7.5.jar b/lib/slf4j-api-1.7.5.jar new file mode 100644 index 0000000..8766455 Binary files /dev/null and b/lib/slf4j-api-1.7.5.jar differ diff --git a/lib/slf4j-simple-1.7.2.jar b/lib/slf4j-simple-1.7.2.jar new file mode 100644 index 0000000..9bf06ec Binary files /dev/null and b/lib/slf4j-simple-1.7.2.jar differ diff --git a/src/com/persesgames/jogl/Keyboard.java b/src/com/persesgames/jogl/Keyboard.java new file mode 100644 index 0000000..1239581 --- /dev/null +++ b/src/com/persesgames/jogl/Keyboard.java @@ -0,0 +1,41 @@ +package com.persesgames.jogl; + +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.KeyListener; + +/** + * Date: 12/30/13 + * Time: 9:25 PM + */ +public class Keyboard implements KeyListener { + + private boolean [] pressed = new boolean[127]; + private boolean [] released = new boolean[127]; + + @Override + public void keyPressed(KeyEvent keyEvent) { + if (keyEvent.getKeyCode() > 0 && keyEvent.getKeyCode() < pressed.length) { + pressed[keyEvent.getKeyCode()] = true; + } + } + + @Override + public void keyReleased(KeyEvent keyEvent) { + if (keyEvent.getKeyCode() > 0 && keyEvent.getKeyCode() < pressed.length) { + pressed[keyEvent.getKeyCode()] = false; + released[keyEvent.getKeyCode()] = true; + } + } + + public boolean isPressed(short keyCode) { + return keyCode < pressed.length && pressed[keyCode]; + } + + public boolean isReleased(short keyCode) { + boolean result = keyCode < pressed.length && released[keyCode]; + + released[keyCode] = false; + + return result; + } +} diff --git a/src/com/persesgames/jogl/Renderer.java b/src/com/persesgames/jogl/Renderer.java new file mode 100644 index 0000000..1f302ea --- /dev/null +++ b/src/com/persesgames/jogl/Renderer.java @@ -0,0 +1,216 @@ +package com.persesgames.jogl; + +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.opengl.GLWindow; +import com.persesgames.jogl.explosion.ExplosionComputeHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.media.opengl.*; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +/** + * Date: 10/25/13 + * Time: 7:42 PM + */ +public class Renderer implements GLEventListener { + private final static Logger logger = LoggerFactory.getLogger(Renderer.class); + + private final static int MAX_ENTITIES_PER_COLOR = 2000000; + + private final Random random = new Random(System.nanoTime()); + + private volatile boolean stopped = false; + private volatile boolean dirty = true; + + private final GLWindow glWindow; + + private int width = 100, height = 100; + + private Keyboard keyboard; + + private boolean checkError = false; + + private long lastLog = System.nanoTime(); + private long start = System.currentTimeMillis(); + private Timer timer = new Timer(TimeUnit.SECONDS, 1); + + private ExplosionComputeHandler explosionComputeHandler; + + public Renderer(GLWindow glWindow, Keyboard keyboard) { + this.glWindow = glWindow; + this.keyboard = keyboard; + } + + public void stop() { + stopped = true; + } + + public void redraw() { + dirty = true; + } + + public void run() { + Renderer.this.glWindow.display(); + + while(!stopped) { + if (dirty) { + //logger.info("rendering+" + System.currentTimeMillis()); + Renderer.this.glWindow.display(); + //Renderer.this.glWindow.swapBuffers(); + dirty = true; + } else { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + logger.warn(e.getMessage(), e); + } + } + + stopped = keyboard.isPressed(KeyEvent.VK_ESCAPE); + } + + Renderer.this.glWindow.destroy(); + } + + @Override + public void init(GLAutoDrawable drawable) { + timer.start("init"); + + GL4 gl = drawable.getGL().getGL4(); + + gl.setSwapInterval(0); + + // debug init + //gl = new DebugGL4(gl); + + logger.info("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities()); + logger.info("INIT GL IS: " + gl.getClass().getName()); + logger.info("GL_VENDOR: " + gl.glGetString(GL.GL_VENDOR)); + logger.info("GL_RENDERER: " + gl.glGetString(GL.GL_RENDERER)); + logger.info("GL_VERSION: " + gl.glGetString(GL.GL_VERSION)); + + int [] result = new int[3]; + gl.glGetIntegerv(GL2.GL_MAX_VERTEX_ATTRIBS, result, 0); + logger.info("GL_MAX_VERTEX_ATTRIBS=" + result[0]); + + gl.glGetIntegerv(GL4.GL_MAX_COMPUTE_WORK_GROUP_SIZE, result, 0); + logger.info("GL_MAX_COMPUTE_WORK_GROUP_SIZE= {},{},{}", result[0], result[1], result[2]); + + gl.glGetIntegerv(GL4.GL_MAX_COMPUTE_WORK_GROUP_COUNT, result, 0); + logger.info("GL_MAX_COMPUTE_WORK_GROUP_COUNT= {},{},{}", result[0], result[1], result[2]); + + explosionComputeHandler = new ExplosionComputeHandler(gl); + explosionComputeHandler.init(); + explosionComputeHandler.createNewExplosionData(); + explosionComputeHandler.updateGpu(); + + timer.stop("init"); + } + + + @Override + public void dispose(GLAutoDrawable drawable) { + explosionComputeHandler.dispose(); + + } + + private long lastDelta = System.nanoTime(); + private float delta = 0f; + private void calculateCurrentDelta() { + long nanoDelta = System.nanoTime() - lastDelta; + + delta = (nanoDelta / 1000000000f); + + lastDelta = System.nanoTime(); + } + + @Override + public void display(GLAutoDrawable drawable) { + //logger.info("display+" + System.currentTimeMillis()); + + calculateCurrentDelta(); + + GL4 gl = drawable.getGL().getGL4(); + + if (checkError) { + // debug + gl.glGetError(); + gl = new DebugGL4(gl); + } + + if (keyboard.isPressed(KeyEvent.VK_SHIFT)) { + explosionComputeHandler.createNewExplosionData(); + explosionComputeHandler.updateGpu(); + } + + if (keyboard.isReleased(KeyEvent.VK_SPACE)) { + explosionComputeHandler.createNewExplosionData(); + explosionComputeHandler.updateGpu(); + } + + gl.glEnable(GL.GL_BLEND); + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_DST_ALPHA); + + gl.glViewport(0, 0, width, height); + + // Clear screen + gl.glClearColor(0.1f, 0.0f, 0.1f, 1f); + gl.glClear(GL2ES2.GL_COLOR_BUFFER_BIT); + + timer.start("getCount"); + + /* + timer.start("BindBuffer"); + gl.glBindBuffer(GL4.GL_ATOMIC_COUNTER_BUFFER, atomicHandle); + timer.stop("BindBuffer"); + // again we map the buffer to userCounters, but this time for read-only access + timer.start("MapBufferRange"); + ByteBuffer last = gl.glMapBufferRange(GL4.GL_ATOMIC_COUNTER_BUFFER, 0, 4, GL4.GL_MAP_READ_BIT); + timer.stop("MapBufferRange"); + particleCount = last.getInt(); + timer.start("UnmapBuffer"); + gl.glUnmapBuffer(GL4.GL_ATOMIC_COUNTER_BUFFER); + timer.stop("UnmapBuffer"); + */ + + timer.stop("getCount"); + + timer.start("compute"); + + explosionComputeHandler.execute(delta); + explosionComputeHandler.cleanUp(); + + timer.stop("compute"); + + timer.start("getGpuData"); + + explosionComputeHandler.getGpuData(); + + timer.stop("getGpuData"); + + timer.start("draw"); + + explosionComputeHandler.render(); + + timer.stop("draw"); + + timer.log(); + + if (lastLog < System.nanoTime() - TimeUnit.SECONDS.toNanos(1)) { + lastLog = System.nanoTime(); + + logger.info("Explosion particles: {}", explosionComputeHandler.getParticleCount()); + } + } + + @Override + public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) { + logger.info("reshape+" + System.currentTimeMillis()); + + this.width = w; + this.height = h; + } + +} diff --git a/src/com/persesgames/jogl/TestJogl.java b/src/com/persesgames/jogl/TestJogl.java new file mode 100644 index 0000000..6f66559 --- /dev/null +++ b/src/com/persesgames/jogl/TestJogl.java @@ -0,0 +1,50 @@ +package com.persesgames.jogl; + +import com.jogamp.newt.opengl.GLWindow; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; + +/** + * Date: 10/25/13 + * Time: 7:27 PM + */ +public class TestJogl { + + public static void main(String [] args) { + TestJogl test = new TestJogl(); + + test.run(); + } + + private final Renderer renderer; + + public TestJogl() { + GLCapabilities caps = new GLCapabilities(GLProfile.get(GLProfile.GL4ES3)); + + caps.setDoubleBuffered(true); + + GLWindow glWindow = GLWindow.create(caps); + + glWindow.setTitle("jogl-triangle"); + + glWindow.setSize(800, 800); + + glWindow.setFullscreen(false); + glWindow.setUndecorated(false); + glWindow.setPointerVisible(true); + glWindow.setVisible(true); + + Keyboard keyboard = new Keyboard(); + glWindow.addKeyListener(keyboard); + + renderer = new Renderer(glWindow, keyboard); + + glWindow.addGLEventListener(renderer); + } + + public void run() { + renderer.run(); + } + +} diff --git a/src/com/persesgames/jogl/Timer.java b/src/com/persesgames/jogl/Timer.java new file mode 100644 index 0000000..55fb9a7 --- /dev/null +++ b/src/com/persesgames/jogl/Timer.java @@ -0,0 +1,106 @@ +package com.persesgames.jogl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Date: 1/4/14 + * Time: 8:00 PM + */ +public class Timer { + private final static Logger logger = LoggerFactory.getLogger(Timer.class); + + private Map start = new HashMap<>(); + private Map times = new HashMap<>(); + private Map calls = new HashMap<>(); + private long nanoStart = System.nanoTime(); + + private final TimeUnit unit; + private final int units; + + private long lastLog = System.nanoTime(); + + public Timer(TimeUnit unit, int units) { + this.unit = unit; + this.units = units; + } + + private long getStart(String timer) { + Long result = start.get(timer); + + if (result == null) { + result = System.nanoTime(); + start.put(timer, result); + } + + return result; + } + + private long getTime(String timer) { + Long result = times.get(timer); + + if (result == null) { + result = 0l; + + times.put(timer, result); + } + + return result; + } + + private long getCalls(String timer) { + Long result = calls.get(timer); + + if (result == null) { + result = 0l; + + calls.put(timer, result); + } + + return result; + } + + public void start(String timer) { + start.put(timer, System.nanoTime()); + } + + public void stop(String timer) { + long start = getStart(timer); + + long delta = System.nanoTime() - start; + + long time = getTime(timer); + + time += delta; + + times.put(timer, time); + calls.put(timer, getCalls(timer) + 1); + } + + public void reset() { + lastLog = System.nanoTime(); + + start.clear(); + times.clear(); + calls.clear(); + } + + public void log() { + if (System.nanoTime() > (lastLog + unit.toNanos(units))) { + + for (String timer : times.keySet()) { + logger.info("Timer '{}' calls '{}' time '{}ms' time/call '{}ms'", timer, getCalls(timer), (getTime(timer) / 1000000d), (getTime(timer) / 1000000d) / (double)getCalls(timer)); + } + + reset(); + } + } + + + + +} diff --git a/src/com/persesgames/jogl/Util.java b/src/com/persesgames/jogl/Util.java new file mode 100644 index 0000000..eeda565 --- /dev/null +++ b/src/com/persesgames/jogl/Util.java @@ -0,0 +1,27 @@ +package com.persesgames.jogl; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Date: 10/25/13 + * Time: 11:03 PM + */ +public class Util { + + public static String loadAsText(Class cls, String name) { + byte [] buffer = new byte[1024]; + int nr; + try (InputStream in = cls.getResourceAsStream(name); ByteArrayOutputStream out = new ByteArrayOutputStream()) { + while((nr = in.read(buffer)) > 0) { + out.write(buffer,0,nr); + } + + return new String(out.toByteArray(), "UTF-8"); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + +} diff --git a/src/com/persesgames/jogl/explosion/ExplosionComputeHandler.java b/src/com/persesgames/jogl/explosion/ExplosionComputeHandler.java new file mode 100644 index 0000000..52bacbb --- /dev/null +++ b/src/com/persesgames/jogl/explosion/ExplosionComputeHandler.java @@ -0,0 +1,203 @@ +package com.persesgames.jogl.explosion; + +import com.jogamp.common.nio.Buffers; +import com.persesgames.jogl.shader.ComputeProgram; +import com.persesgames.jogl.shader.ShaderProgram; +import com.persesgames.jogl.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL4; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.Random; + +/** + * Date: 1/15/14 + * Time: 8:23 PM + */ +public class ExplosionComputeHandler { + private final static Logger logger = LoggerFactory.getLogger(ExplosionComputeHandler.class); + + private final static int EXPLOSION_PARTICLES = 256; + private final static int MAX_EXPLOSION_PARTICLES = 1000000; + + private final Random random = new Random(System.nanoTime()); + + private GL4 gl; + + private ShaderProgram explProgram; + + private ComputeProgram explosionProgram; + private ComputeProgram explCleanUpProgram; + + private FloatBuffer explBuffer = Buffers.newDirectFloatBuffer(MAX_EXPLOSION_PARTICLES * 8); + private IntBuffer atomicBuffer = Buffers.newDirectIntBuffer(5); + + + private int newParticleCount = 0; + private int particleCount = 0; + + // opengl handles + private int explHandle; + private int atomicHandle; + + public ExplosionComputeHandler(GL4 gl) { + this.gl = gl; + } + + public void createNewExplosionData() { + float x = random.nextFloat() * 2f - 1f; + float y = random.nextFloat() * 2f - 1f; + + if (newParticleCount > (MAX_EXPLOSION_PARTICLES - EXPLOSION_PARTICLES)) { + return; + } + + float r = random.nextFloat() * 0.25f; + float g = random.nextFloat() * 0.25f; + float b = random.nextFloat() * 0.25f; + + for (int i = newParticleCount; i < newParticleCount + EXPLOSION_PARTICLES; i++) { + int offset = i * 8; + + float angle = (float) (random.nextFloat() * Math.PI * 2f); + float velocity = (random.nextFloat() * 0.25f) + (random.nextFloat() * 0.25f); + + explBuffer.put(offset + 0, x); + explBuffer.put(offset + 1, y); + explBuffer.put(offset + 2, (float) (Math.sin(angle) * velocity)); + explBuffer.put(offset + 3, (float) (Math.cos(angle) * velocity)); + + explBuffer.put(offset + 4, r); + explBuffer.put(offset + 5, g); + explBuffer.put(offset + 6, b); + explBuffer.put(offset + 7, random.nextFloat() * 0.5f + 0.5f); + } + + newParticleCount = newParticleCount + EXPLOSION_PARTICLES; + } + + public void init() { + explosionProgram = new ComputeProgram(gl, Util.loadAsText(getClass(), "explosion.comp")); + explosionProgram.getUniformLocation("delta"); + + explCleanUpProgram = new ComputeProgram(gl, Util.loadAsText(getClass(), "explosionCleanUp.comp")); + + explProgram = new ShaderProgram(gl, + Util.loadAsText(getClass(), "explShader.vert"), + Util.loadAsText(getClass(), "explShader.frag")); + + int[] tmpHandle = new int[2]; + gl.glGenBuffers(2, tmpHandle, 0); + + explHandle = tmpHandle[0]; + atomicHandle = tmpHandle[1]; + + // Select the VBO, GPU memory data, to use for vertices + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, explHandle); + + // transfer data to VBO, this perform the copy of data from CPU -> GPU memory + gl.glBufferData(GL.GL_ARRAY_BUFFER, explBuffer.limit() * 4, explBuffer, GL.GL_DYNAMIC_DRAW); + } + + public void updateGpu() { + // Select the VBO, GPU memory data, to use for vertices + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, explHandle); + + // transfer data to VBO, this perform the copy of data from CPU -> GPU memory + // gl.glBufferData(GL.GL_ARRAY_BUFFER, particleCount * 4 * 8, explBuffer, GL.GL_DYNAMIC_DRAW); + gl.glBufferSubData(GL.GL_ARRAY_BUFFER, particleCount * 4 * 8, newParticleCount * 4 * 8, explBuffer); + + // Select the VBO, GPU memory data, to use for vertices + gl.glBindBuffer(GL4.GL_ATOMIC_COUNTER_BUFFER, atomicHandle); + + atomicBuffer.put(0, particleCount + newParticleCount); + + // transfer data to VBO, this perform the copy of data from CPU -> GPU memory + gl.glBufferData(GL4.GL_ATOMIC_COUNTER_BUFFER, 4, atomicBuffer, GL.GL_DYNAMIC_DRAW); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, 0); + gl.glBindBuffer(GL4.GL_ATOMIC_COUNTER_BUFFER, 0); + + newParticleCount = 0; + } + + public void execute(float delta) { + explosionProgram.begin(); + + gl.glUniform1f(explosionProgram.getUniformLocation("delta"), delta); + + gl.glBindBufferBase(GL4.GL_SHADER_STORAGE_BUFFER, 0, explHandle); + gl.glBindBufferBase(GL4.GL_ATOMIC_COUNTER_BUFFER, 1, atomicHandle); + + explosionProgram.compute((particleCount / 512) + 1, 1, 1); + + gl.glBindBufferBase(GL4.GL_SHADER_STORAGE_BUFFER, 0, 0); + gl.glBindBufferBase(GL4.GL_ATOMIC_COUNTER_BUFFER, 1, 0); + + explosionProgram.end(); + } + + public void cleanUp() { + explCleanUpProgram.begin(); + + gl.glBindBufferBase(GL4.GL_SHADER_STORAGE_BUFFER, 0, explHandle); + gl.glBindBufferBase(GL4.GL_ATOMIC_COUNTER_BUFFER, 1, atomicHandle); + + explCleanUpProgram.compute((particleCount / 512) + 1, 1, 1); + + gl.glBindBufferBase(GL4.GL_SHADER_STORAGE_BUFFER, 0, 0); + gl.glBindBufferBase(GL4.GL_ATOMIC_COUNTER_BUFFER, 1, 0); + + explCleanUpProgram.end(); + } + + public void render() { + explProgram.begin(); + + if (particleCount > 0) { + gl.glEnableVertexAttribArray(0); + gl.glEnableVertexAttribArray(1); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, explHandle); + + // Associate Vertex attribute 0 with the last bound VBO + gl.glVertexAttribPointer(0 /* the vertex attribute */, 2, + GL2ES2.GL_FLOAT, false /* normalized? */, 32 /* stride */, + 0 /* The bound VBO data offset */); + + // Associate Vertex attribute 0 with the last bound VBO + gl.glVertexAttribPointer(1 /* the vertex attribute */, 4, + GL2ES2.GL_FLOAT, false /* normalized? */, 32 /* stride */, + 16 /* The bound VBO data offset */); + + gl.glDrawArrays(GL2ES2.GL_POINTS, 0, particleCount); + + gl.glDisableVertexAttribArray(0); + gl.glDisableVertexAttribArray(1); + } + + explProgram.end(); + } + + public void getGpuData() { + gl.glBindBuffer(GL4.GL_ATOMIC_COUNTER_BUFFER, atomicHandle); + gl.glGetBufferSubData(GL4.GL_ATOMIC_COUNTER_BUFFER, 0, 4, atomicBuffer); + particleCount = atomicBuffer.get(0); + newParticleCount = 0; + gl.glBindBuffer(GL4.GL_ATOMIC_COUNTER_BUFFER, 0); + } + + public int getParticleCount() { + return particleCount; + } + + public void dispose() { + explProgram.dispose(); + explosionProgram.dispose(); + explCleanUpProgram.dispose(); + } +} diff --git a/src/com/persesgames/jogl/explosion/explShader.frag b/src/com/persesgames/jogl/explosion/explShader.frag new file mode 100644 index 0000000..148482e --- /dev/null +++ b/src/com/persesgames/jogl/explosion/explShader.frag @@ -0,0 +1,19 @@ +#if __VERSION__ >= 130 + #define varying in + out vec4 mgl_FragColor; + #define texture2D texture + #define gl_FragColor mgl_FragColor +#endif + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +varying vec4 varying_Color; + +void main (void) { + float alpha = 1 - smoothstep(0, 1, 2 * distance(gl_PointCoord.st, vec2(0.5, 0.5))); + + gl_FragColor = vec4(varying_Color.rgb, varying_Color.a * alpha); +} diff --git a/src/com/persesgames/jogl/explosion/explShader.vert b/src/com/persesgames/jogl/explosion/explShader.vert new file mode 100644 index 0000000..d106de2 --- /dev/null +++ b/src/com/persesgames/jogl/explosion/explShader.vert @@ -0,0 +1,22 @@ +#if __VERSION__ >= 130 + #define attribute in + #define varying out +#endif + +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +attribute vec4 attribute_Position; +attribute vec4 attribute_Color; + +varying vec4 varying_Color; + +void main(void) { + mat4 uniform_Projection = mat4(1); + + gl_PointSize = 15f; + gl_Position = uniform_Projection * attribute_Position; //vec4(attribute_Position.x, attribute_Position.y, 0, 1); + varying_Color = attribute_Color; +} diff --git a/src/com/persesgames/jogl/explosion/explosion.comp b/src/com/persesgames/jogl/explosion/explosion.comp new file mode 100644 index 0000000..fc1bfa6 --- /dev/null +++ b/src/com/persesgames/jogl/explosion/explosion.comp @@ -0,0 +1,42 @@ +#version 430 core +#define pi 3.141592653589793238462643383279 + +precision mediump float; +precision mediump int; + +struct explosion { + vec4 positionVelocity; + vec4 color; +}; + +layout (std430, binding = 0) buffer entities { + explosion e[]; +}; + +layout(binding = 1, offset = 0) uniform atomic_uint lastCounter; + +uniform float delta; + +layout(local_size_x = 512) in; + +void main() { + uint gid = gl_GlobalInvocationID.x; + uint last = atomicCounter(lastCounter); + + if (gid < last) { + vec2 position = e[gid].positionVelocity.xy; + vec2 velocity = e[gid].positionVelocity.zw; + vec4 color = e[gid].color; + + if (color.a > 0.0) { + //velocity = velocity * 0.99; + position = position + velocity * delta * color.a; + color.a = color.a - (delta / 2); + } else { + color.a = 0.0; + } + + e[gid].positionVelocity = vec4(position, velocity); + e[gid].color = color; + } +} diff --git a/src/com/persesgames/jogl/explosion/explosionCleanUp.comp b/src/com/persesgames/jogl/explosion/explosionCleanUp.comp new file mode 100644 index 0000000..ef1da46 --- /dev/null +++ b/src/com/persesgames/jogl/explosion/explosionCleanUp.comp @@ -0,0 +1,36 @@ +#version 430 core +#define pi 3.141592653589793238462643383279 + +precision mediump float; +precision mediump int; + +struct explosion { + vec4 positionVelocity; + vec4 color; +}; + +layout (std430, binding = 0) buffer entities { + explosion e[]; +}; + +layout(binding = 1, offset = 0) uniform atomic_uint lastCounter; + +layout (local_size_x = 512) in; + +void main() { + uint gid = gl_GlobalInvocationID.x; + uint last = atomicCounter(lastCounter); + + if (gid < last && last > 0) { + vec4 color = e[gid].color; + + if (color.a == 0.0) { + uint old = atomicCounterDecrement(lastCounter); + + e[gid].positionVelocity = e[old].positionVelocity; + e[gid].color = e[old].color; + + e[old].color.a = 0; + } + } +} diff --git a/src/com/persesgames/jogl/shader/ComputeProgram.java b/src/com/persesgames/jogl/shader/ComputeProgram.java new file mode 100644 index 0000000..3d7bf20 --- /dev/null +++ b/src/com/persesgames/jogl/shader/ComputeProgram.java @@ -0,0 +1,126 @@ +package com.persesgames.jogl.shader; + +import com.persesgames.jogl.Renderer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL4; +import java.util.HashMap; +import java.util.Map; + +/*-----------------------------------------------------+ + | App | + | pPPAPPp | + | APP PPa | + | APA pPP PapA PapA | + | PPA APA pP P pP P | + | APPPPPPPA PPp Ap Ap Ap Ap | + | apPPA aPP P P | + | APA pPP p p | + | pPP PPA | + | PPp PPPp | + | | + | Created by: App Software | + | Email: info@appsoftware.nl | + | Web: http://www.appsoftware.nl/ | + | | + +-----------------------------------------------------*/ + +public class ComputeProgram { + private final static Logger logger = LoggerFactory.getLogger(Renderer.class); + + protected GL4 gl; + + private int shaderProgram; + private int computeShader; + + private Map uniformLocations = new HashMap<>(); + private Map attribLocations = new HashMap<>(); + + public ComputeProgram(GL4 gl, String compute) { + this.gl = gl; + + + computeShader = createAndCompileShader(GL4.GL_COMPUTE_SHADER, compute); + + shaderProgram = gl.glCreateProgram(); + + gl.glAttachShader(shaderProgram, computeShader); + + gl.glLinkProgram(shaderProgram); + } + + public int getUniformLocation(String uniform) { + Integer result = uniformLocations.get(uniform); + + if (result == null) { + result = gl.glGetUniformLocation(shaderProgram, uniform); + + uniformLocations.put(uniform, result); + } + + return result; + } + + public int getAttribLocation(String attrib) { + Integer result = attribLocations.get(attrib); + + if (result == null) { + result = gl.glGetAttribLocation(shaderProgram, attrib); + + attribLocations.put(attrib, result); + } + + return result; + } + + public void bindAttributeLocation(int location, String name) { + gl.glBindAttribLocation(shaderProgram, location, name); + } + + public void begin() { + gl.glUseProgram(shaderProgram); + } + + public void compute(int x, int y, int z) { + gl.glDispatchCompute(x, y, z); + } + + public void end() { + gl.glUseProgram(0); + } + + public void dispose() { + gl.glDetachShader(shaderProgram, computeShader); + gl.glDeleteShader(computeShader); + + gl.glDeleteProgram(shaderProgram); + } + + private int createAndCompileShader(int type, String shaderString) { + int shader = gl.glCreateShader(type); + + String[] vlines = new String[]{shaderString}; + int[] vlengths = new int[]{vlines[0].length()}; + + gl.glShaderSource(shader, vlines.length, vlines, vlengths, 0); + gl.glCompileShader(shader); + + int[] compiled = new int[1]; + gl.glGetShaderiv(shader, GL2ES2.GL_COMPILE_STATUS, compiled, 0); + + if (compiled[0] == 0) { + int[] logLength = new int[1]; + gl.glGetShaderiv(shader, GL2ES2.GL_INFO_LOG_LENGTH, logLength, 0); + + byte[] log = new byte[logLength[0]]; + gl.glGetShaderInfoLog(shader, logLength[0], (int[]) null, 0, log, 0); + + throw new IllegalStateException("Error compiling the shader: " + new String(log)); + } + + return shader; + } + +} diff --git a/src/com/persesgames/jogl/shader/ShaderProgram.java b/src/com/persesgames/jogl/shader/ShaderProgram.java new file mode 100644 index 0000000..2a1780c --- /dev/null +++ b/src/com/persesgames/jogl/shader/ShaderProgram.java @@ -0,0 +1,131 @@ +package com.persesgames.jogl.shader; + +import com.persesgames.jogl.Renderer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.media.opengl.GL2ES2; +import java.util.HashMap; +import java.util.Map; + +/*-----------------------------------------------------+ + | App | + | pPPAPPp | + | APP PPa | + | APA pPP PapA PapA | + | PPA APA pP P pP P | + | APPPPPPPA PPp Ap Ap Ap Ap | + | apPPA aPP P P | + | APA pPP p p | + | pPP PPA | + | PPp PPPp | + | | + | Created by: App Software | + | Email: info@appsoftware.nl | + | Web: http://www.appsoftware.nl/ | + | | + +-----------------------------------------------------*/ + +public class ShaderProgram { + private final static Logger logger = LoggerFactory.getLogger(Renderer.class); + + private GL2ES2 gl; + + private int shaderProgram; + private int vertShader; + private int fragShader; + + private Map uniformLocations = new HashMap<>(); + private Map attribLocations = new HashMap<>(); + + public ShaderProgram(GL2ES2 gl, String vertex, String fragment) { + this.gl = gl; + + if (gl.isGL3core()) { + logger.info("GL3 core detected: explicit adding #version 130 to shaders"); + + vertex = "#version 130\n" + vertex; + fragment = "#version 130\n" + fragment; + } + + vertShader = createAndCompileShader(GL2ES2.GL_VERTEX_SHADER, vertex); + fragShader = createAndCompileShader(GL2ES2.GL_FRAGMENT_SHADER, fragment); + + shaderProgram = gl.glCreateProgram(); + + gl.glAttachShader(shaderProgram, vertShader); + gl.glAttachShader(shaderProgram, fragShader); + + gl.glLinkProgram(shaderProgram); + } + + public int getUniformLocation(String uniform) { + Integer result = uniformLocations.get(uniform); + + if (result == null) { + result = gl.glGetUniformLocation(shaderProgram, uniform); + + uniformLocations.put(uniform, result); + } + + return result; + } + + public int getAttribLocation(String attrib) { + Integer result = attribLocations.get(attrib); + + if (result == null) { + result = gl.glGetAttribLocation(shaderProgram, attrib); + + attribLocations.put(attrib, result); + } + + return result; + } + + public void bindAttributeLocation(int location, String name) { + gl.glBindAttribLocation(shaderProgram, location, name); + } + + public void begin() { + gl.glUseProgram(shaderProgram); + } + + public void end() { + gl.glUseProgram(0); + } + + public void dispose() { + gl.glDetachShader(shaderProgram, vertShader); + gl.glDeleteShader(vertShader); + gl.glDetachShader(shaderProgram, fragShader); + gl.glDeleteShader(fragShader); + gl.glDeleteProgram(shaderProgram); + } + + private int createAndCompileShader(int type, String shaderString) { + int shader = gl.glCreateShader(type); + + String[] vlines = new String[]{shaderString}; + int[] vlengths = new int[]{vlines[0].length()}; + + gl.glShaderSource(shader, vlines.length, vlines, vlengths, 0); + gl.glCompileShader(shader); + + int[] compiled = new int[1]; + gl.glGetShaderiv(shader, GL2ES2.GL_COMPILE_STATUS, compiled, 0); + + if (compiled[0] == 0) { + int[] logLength = new int[1]; + gl.glGetShaderiv(shader, GL2ES2.GL_INFO_LOG_LENGTH, logLength, 0); + + byte[] log = new byte[logLength[0]]; + gl.glGetShaderInfoLog(shader, logLength[0], (int[]) null, 0, log, 0); + + throw new IllegalStateException("Error compiling the shader: " + new String(log)); + } + + return shader; + } + +}