Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

adding files

  • Loading branch information...
commit fb608293026b9bc18f21b7250c01330e0e8069ed 1 parent 73efc86
@mattdesl authored
Showing with 1,631 additions and 0 deletions.
  1. +13 −0 .classpath
  2. +17 −0 .project
  3. BIN  lib/jinput.jar
  4. BIN  lib/lwjgl.jar
  5. BIN  lib/lwjgl_util.jar
  6. BIN  native/linux/libjinput-linux.so
  7. BIN  native/linux/libjinput-linux64.so
  8. BIN  native/linux/liblwjgl.so
  9. BIN  native/linux/liblwjgl64.so
  10. BIN  native/linux/libopenal.so
  11. BIN  native/linux/libopenal64.so
  12. BIN  native/macosx/libjinput-osx.jnilib
  13. BIN  native/macosx/liblwjgl.jnilib
  14. BIN  native/solaris/liblwjgl.so
  15. BIN  native/solaris/liblwjgl64.so
  16. BIN  native/solaris/libopenal.so
  17. BIN  native/solaris/libopenal64.so
  18. BIN  native/windows/OpenAL32.dll
  19. BIN  native/windows/OpenAL64.dll
  20. BIN  native/windows/jinput-dx8.dll
  21. BIN  native/windows/jinput-dx8_64.dll
  22. BIN  native/windows/jinput-raw.dll
  23. BIN  native/windows/jinput-raw_64.dll
  24. BIN  native/windows/lwjgl.dll
  25. BIN  native/windows/lwjgl64.dll
  26. +835 −0 src/de/matthiasmann/twl/utils/PNGDecoder.java
  27. +243 −0 src/mdesl/graphics/SpriteBatch.java
  28. +95 −0 src/mdesl/graphics/Texture.java
  29. +114 −0 src/mdesl/graphics/glutils/ShaderProgram.java
  30. +88 −0 src/mdesl/graphics/glutils/VertexArray.java
  31. +18 −0 src/mdesl/graphics/glutils/VertexAttrib.java
  32. +21 −0 src/mdesl/graphics/glutils/VertexData.java
  33. +105 −0 src/mdesl/test/VertexArrayExample.java
  34. +82 −0 src/mdesl/util/MathUtil.java
  35. BIN  src/res/font0.png
  36. BIN  src/res/small.png
View
13 .classpath
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="lib" path="lib/jinput.jar"/>
+ <classpathentry kind="lib" path="lib/lwjgl_util.jar"/>
+ <classpathentry kind="lib" path="lib/lwjgl.jar">
+ <attributes>
+ <attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="lwjgl-basics/native/macosx"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
View
17 .project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>lwjgl-basics</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
View
BIN  lib/jinput.jar
Binary file not shown
View
BIN  lib/lwjgl.jar
Binary file not shown
View
BIN  lib/lwjgl_util.jar
Binary file not shown
View
BIN  native/linux/libjinput-linux.so
Binary file not shown
View
BIN  native/linux/libjinput-linux64.so
Binary file not shown
View
BIN  native/linux/liblwjgl.so
Binary file not shown
View
BIN  native/linux/liblwjgl64.so
Binary file not shown
View
BIN  native/linux/libopenal.so
Binary file not shown
View
BIN  native/linux/libopenal64.so
Binary file not shown
View
BIN  native/macosx/libjinput-osx.jnilib
Binary file not shown
View
BIN  native/macosx/liblwjgl.jnilib
Binary file not shown
View
BIN  native/solaris/liblwjgl.so
Binary file not shown
View
BIN  native/solaris/liblwjgl64.so
Binary file not shown
View
BIN  native/solaris/libopenal.so
Binary file not shown
View
BIN  native/solaris/libopenal64.so
Binary file not shown
View
BIN  native/windows/OpenAL32.dll
Binary file not shown
View
BIN  native/windows/OpenAL64.dll
Binary file not shown
View
BIN  native/windows/jinput-dx8.dll
Binary file not shown
View
BIN  native/windows/jinput-dx8_64.dll
Binary file not shown
View
BIN  native/windows/jinput-raw.dll
Binary file not shown
View
BIN  native/windows/jinput-raw_64.dll
Binary file not shown
View
BIN  native/windows/lwjgl.dll
Binary file not shown
View
BIN  native/windows/lwjgl64.dll
Binary file not shown
View
835 src/de/matthiasmann/twl/utils/PNGDecoder.java
@@ -0,0 +1,835 @@
+/*
+ * Copyright (c) 2008-2010, Matthias Mann
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Matthias Mann nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.matthiasmann.twl.utils;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.zip.CRC32;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+
+/**
+ * A PNGDecoder. The slick PNG decoder is based on this class :)
+ *
+ * @author Matthias Mann
+ */
+public class PNGDecoder {
+
+ public enum Format {
+ ALPHA(1, true),
+ LUMINANCE(1, false),
+ LUMINANCE_ALPHA(2, true),
+ RGB(3, false),
+ RGBA(4, true),
+ BGRA(4, true),
+ ABGR(4, true);
+
+ final int numComponents;
+ final boolean hasAlpha;
+
+ private Format(int numComponents, boolean hasAlpha) {
+ this.numComponents = numComponents;
+ this.hasAlpha = hasAlpha;
+ }
+
+ public int getNumComponents() {
+ return numComponents;
+ }
+
+ public boolean isHasAlpha() {
+ return hasAlpha;
+ }
+ }
+
+ private static final byte[] SIGNATURE = {(byte)137, 80, 78, 71, 13, 10, 26, 10};
+
+ private static final int IHDR = 0x49484452;
+ private static final int PLTE = 0x504C5445;
+ private static final int tRNS = 0x74524E53;
+ private static final int IDAT = 0x49444154;
+ private static final int IEND = 0x49454E44;
+
+ private static final byte COLOR_GREYSCALE = 0;
+ private static final byte COLOR_TRUECOLOR = 2;
+ private static final byte COLOR_INDEXED = 3;
+ private static final byte COLOR_GREYALPHA = 4;
+ private static final byte COLOR_TRUEALPHA = 6;
+
+ private final InputStream input;
+ private final CRC32 crc;
+ private final byte[] buffer;
+
+ private int chunkLength;
+ private int chunkType;
+ private int chunkRemaining;
+
+ private int width;
+ private int height;
+ private int bitdepth;
+ private int colorType;
+ private int bytesPerPixel;
+ private byte[] palette;
+ private byte[] paletteA;
+ private byte[] transPixel;
+
+ public PNGDecoder(InputStream input) throws IOException {
+ this.input = input;
+ this.crc = new CRC32();
+ this.buffer = new byte[4096];
+
+ readFully(buffer, 0, SIGNATURE.length);
+ if(!checkSignature(buffer)) {
+ throw new IOException("Not a valid PNG file");
+ }
+
+ openChunk(IHDR);
+ readIHDR();
+ closeChunk();
+
+ searchIDAT: for(;;) {
+ openChunk();
+ switch (chunkType) {
+ case IDAT:
+ break searchIDAT;
+ case PLTE:
+ readPLTE();
+ break;
+ case tRNS:
+ readtRNS();
+ break;
+ }
+ closeChunk();
+ }
+
+ if(colorType == COLOR_INDEXED && palette == null) {
+ throw new IOException("Missing PLTE chunk");
+ }
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * Checks if the image has a real alpha channel.
+ * This method does not check for the presence of a tRNS chunk.
+ *
+ * @return true if the image has an alpha channel
+ * @see #hasAlpha()
+ */
+ public boolean hasAlphaChannel() {
+ return colorType == COLOR_TRUEALPHA || colorType == COLOR_GREYALPHA;
+ }
+
+ /**
+ * Checks if the image has transparency information either from
+ * an alpha channel or from a tRNS chunk.
+ *
+ * @return true if the image has transparency
+ * @see #hasAlphaChannel()
+ * @see #overwriteTRNS(byte, byte, byte)
+ */
+ public boolean hasAlpha() {
+ return hasAlphaChannel() ||
+ paletteA != null || transPixel != null;
+ }
+
+ public boolean isRGB() {
+ return colorType == COLOR_TRUEALPHA ||
+ colorType == COLOR_TRUECOLOR ||
+ colorType == COLOR_INDEXED;
+ }
+
+ /**
+ * Overwrites the tRNS chunk entry to make a selected color transparent.
+ * <p>This can only be invoked when the image has no alpha channel.</p>
+ * <p>Calling this method causes {@link #hasAlpha()} to return true.</p>
+ *
+ * @param r the red component of the color to make transparent
+ * @param g the green component of the color to make transparent
+ * @param b the blue component of the color to make transparent
+ * @throws UnsupportedOperationException if the tRNS chunk data can't be set
+ * @see #hasAlphaChannel()
+ */
+ public void overwriteTRNS(byte r, byte g, byte b) {
+ if(hasAlphaChannel()) {
+ throw new UnsupportedOperationException("image has an alpha channel");
+ }
+ byte[] pal = this.palette;
+ if(pal == null) {
+ transPixel = new byte[] { 0, r, 0, g, 0, b };
+ } else {
+ paletteA = new byte[pal.length/3];
+ for(int i=0,j=0 ; i<pal.length ; i+=3,j++) {
+ if(pal[i] != r || pal[i+1] != g || pal[i+2] != b) {
+ paletteA[j] = (byte)0xFF;
+ }
+ }
+ }
+ }
+
+ /**
+ * Computes the implemented format conversion for the desired format.
+ *
+ * @param fmt the desired format
+ * @return format which best matches the desired format
+ * @throws UnsupportedOperationException if this PNG file can't be decoded
+ */
+ public Format decideTextureFormat(Format fmt) {
+ switch (colorType) {
+ case COLOR_TRUECOLOR:
+ switch (fmt) {
+ case ABGR:
+ case RGBA:
+ case BGRA:
+ case RGB: return fmt;
+ default: return Format.RGB;
+ }
+ case COLOR_TRUEALPHA:
+ switch (fmt) {
+ case ABGR:
+ case RGBA:
+ case BGRA:
+ case RGB: return fmt;
+ default: return Format.RGBA;
+ }
+ case COLOR_GREYSCALE:
+ switch (fmt) {
+ case LUMINANCE:
+ case ALPHA: return fmt;
+ default: return Format.LUMINANCE;
+ }
+ case COLOR_GREYALPHA:
+ return Format.LUMINANCE_ALPHA;
+ case COLOR_INDEXED:
+ switch (fmt) {
+ case ABGR:
+ case RGBA:
+ case BGRA: return fmt;
+ default: return Format.RGBA;
+ }
+ default:
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+ }
+
+ /**
+ * Decodes the image into the specified buffer. The first line is placed at
+ * the current position. After decode the buffer position is at the end of
+ * the last line.
+ *
+ * @param buffer the buffer
+ * @param stride the stride in bytes from start of a line to start of the next line, can be negative.
+ * @param fmt the target format into which the image should be decoded.
+ * @throws IOException if a read or data error occurred
+ * @throws IllegalArgumentException if the start position of a line falls outside the buffer
+ * @throws UnsupportedOperationException if the image can't be decoded into the desired format
+ */
+ public void decode(ByteBuffer buffer, int stride, Format fmt) throws IOException {
+ final int offset = buffer.position();
+ final int lineSize = ((width * bitdepth + 7) / 8) * bytesPerPixel;
+ byte[] curLine = new byte[lineSize+1];
+ byte[] prevLine = new byte[lineSize+1];
+ byte[] palLine = (bitdepth < 8) ? new byte[width+1] : null;
+
+ final Inflater inflater = new Inflater();
+ try {
+ for(int y=0 ; y<height ; y++) {
+ readChunkUnzip(inflater, curLine, 0, curLine.length);
+ unfilter(curLine, prevLine);
+
+ buffer.position(offset + y*stride);
+
+ switch (colorType) {
+ case COLOR_TRUECOLOR:
+ switch (fmt) {
+ case ABGR: copyRGBtoABGR(buffer, curLine); break;
+ case RGBA: copyRGBtoRGBA(buffer, curLine); break;
+ case BGRA: copyRGBtoBGRA(buffer, curLine); break;
+ case RGB: copy(buffer, curLine); break;
+ default: throw new UnsupportedOperationException("Unsupported format for this image");
+ }
+ break;
+ case COLOR_TRUEALPHA:
+ switch (fmt) {
+ case ABGR: copyRGBAtoABGR(buffer, curLine); break;
+ case RGBA: copy(buffer, curLine); break;
+ case BGRA: copyRGBAtoBGRA(buffer, curLine); break;
+ case RGB: copyRGBAtoRGB(buffer, curLine); break;
+ default: throw new UnsupportedOperationException("Unsupported format for this image");
+ }
+ break;
+ case COLOR_GREYSCALE:
+ switch (fmt) {
+ case LUMINANCE:
+ case ALPHA: copy(buffer, curLine); break;
+ default: throw new UnsupportedOperationException("Unsupported format for this image");
+ }
+ break;
+ case COLOR_GREYALPHA:
+ switch (fmt) {
+ case LUMINANCE_ALPHA: copy(buffer, curLine); break;
+ default: throw new UnsupportedOperationException("Unsupported format for this image");
+ }
+ break;
+ case COLOR_INDEXED:
+ switch(bitdepth) {
+ case 8: palLine = curLine; break;
+ case 4: expand4(curLine, palLine); break;
+ case 2: expand2(curLine, palLine); break;
+ case 1: expand1(curLine, palLine); break;
+ default: throw new UnsupportedOperationException("Unsupported bitdepth for this image");
+ }
+ switch (fmt) {
+ case ABGR: copyPALtoABGR(buffer, palLine); break;
+ case RGBA: copyPALtoRGBA(buffer, palLine); break;
+ case BGRA: copyPALtoBGRA(buffer, palLine); break;
+ default: throw new UnsupportedOperationException("Unsupported format for this image");
+ }
+ break;
+ default:
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ byte[] tmp = curLine;
+ curLine = prevLine;
+ prevLine = tmp;
+ }
+ } finally {
+ inflater.end();
+ }
+ }
+
+ /**
+ * Decodes the image into the specified buffer. The last line is placed at
+ * the current position. After decode the buffer position is at the end of
+ * the first line.
+ *
+ * @param buffer the buffer
+ * @param stride the stride in bytes from start of a line to start of the next line, must be positive.
+ * @param fmt the target format into which the image should be decoded.
+ * @throws IOException if a read or data error occurred
+ * @throws IllegalArgumentException if the start position of a line falls outside the buffer
+ * @throws UnsupportedOperationException if the image can't be decoded into the desired format
+ */
+ public void decodeFlipped(ByteBuffer buffer, int stride, Format fmt) throws IOException {
+ if(stride <= 0) {
+ throw new IllegalArgumentException("stride");
+ }
+ int pos = buffer.position();
+ int posDelta = (height-1) * stride;
+ buffer.position(pos + posDelta);
+ decode(buffer, -stride, fmt);
+ buffer.position(buffer.position() + posDelta);
+ }
+
+ private void copy(ByteBuffer buffer, byte[] curLine) {
+ buffer.put(curLine, 1, curLine.length-1);
+ }
+
+ private void copyRGBtoABGR(ByteBuffer buffer, byte[] curLine) {
+ if(transPixel != null) {
+ byte tr = transPixel[1];
+ byte tg = transPixel[3];
+ byte tb = transPixel[5];
+ for(int i=1,n=curLine.length ; i<n ; i+=3) {
+ byte r = curLine[i];
+ byte g = curLine[i+1];
+ byte b = curLine[i+2];
+ byte a = (byte)0xFF;
+ if(r==tr && g==tg && b==tb) {
+ a = 0;
+ }
+ buffer.put(a).put(b).put(g).put(r);
+ }
+ } else {
+ for(int i=1,n=curLine.length ; i<n ; i+=3) {
+ buffer.put((byte)0xFF).put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]);
+ }
+ }
+ }
+
+ private void copyRGBtoRGBA(ByteBuffer buffer, byte[] curLine) {
+ if(transPixel != null) {
+ byte tr = transPixel[1];
+ byte tg = transPixel[3];
+ byte tb = transPixel[5];
+ for(int i=1,n=curLine.length ; i<n ; i+=3) {
+ byte r = curLine[i];
+ byte g = curLine[i+1];
+ byte b = curLine[i+2];
+ byte a = (byte)0xFF;
+ if(r==tr && g==tg && b==tb) {
+ a = 0;
+ }
+ buffer.put(r).put(g).put(b).put(a);
+ }
+ } else {
+ for(int i=1,n=curLine.length ; i<n ; i+=3) {
+ buffer.put(curLine[i]).put(curLine[i+1]).put(curLine[i+2]).put((byte)0xFF);
+ }
+ }
+ }
+
+ private void copyRGBtoBGRA(ByteBuffer buffer, byte[] curLine) {
+ if(transPixel != null) {
+ byte tr = transPixel[1];
+ byte tg = transPixel[3];
+ byte tb = transPixel[5];
+ for(int i=1,n=curLine.length ; i<n ; i+=3) {
+ byte r = curLine[i];
+ byte g = curLine[i+1];
+ byte b = curLine[i+2];
+ byte a = (byte)0xFF;
+ if(r==tr && g==tg && b==tb) {
+ a = 0;
+ }
+ buffer.put(b).put(g).put(r).put(a);
+ }
+ } else {
+ for(int i=1,n=curLine.length ; i<n ; i+=3) {
+ buffer.put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]).put((byte)0xFF);
+ }
+ }
+ }
+
+ private void copyRGBAtoABGR(ByteBuffer buffer, byte[] curLine) {
+ for(int i=1,n=curLine.length ; i<n ; i+=4) {
+ buffer.put(curLine[i+3]).put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]);
+ }
+ }
+
+ private void copyRGBAtoBGRA(ByteBuffer buffer, byte[] curLine) {
+ for(int i=1,n=curLine.length ; i<n ; i+=4) {
+ buffer.put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]).put(curLine[i+3]);
+ }
+ }
+
+ private void copyRGBAtoRGB(ByteBuffer buffer, byte[] curLine) {
+ for(int i=1,n=curLine.length ; i<n ; i+=4) {
+ buffer.put(curLine[i]).put(curLine[i+1]).put(curLine[i+2]);
+ }
+ }
+
+ private void copyPALtoABGR(ByteBuffer buffer, byte[] curLine) {
+ if(paletteA != null) {
+ for(int i=1,n=curLine.length ; i<n ; i+=1) {
+ int idx = curLine[i] & 255;
+ byte r = palette[idx*3 + 0];
+ byte g = palette[idx*3 + 1];
+ byte b = palette[idx*3 + 2];
+ byte a = paletteA[idx];
+ buffer.put(a).put(b).put(g).put(r);
+ }
+ } else {
+ for(int i=1,n=curLine.length ; i<n ; i+=1) {
+ int idx = curLine[i] & 255;
+ byte r = palette[idx*3 + 0];
+ byte g = palette[idx*3 + 1];
+ byte b = palette[idx*3 + 2];
+ byte a = (byte)0xFF;
+ buffer.put(a).put(b).put(g).put(r);
+ }
+ }
+ }
+
+ private void copyPALtoRGBA(ByteBuffer buffer, byte[] curLine) {
+ if(paletteA != null) {
+ for(int i=1,n=curLine.length ; i<n ; i+=1) {
+ int idx = curLine[i] & 255;
+ byte r = palette[idx*3 + 0];
+ byte g = palette[idx*3 + 1];
+ byte b = palette[idx*3 + 2];
+ byte a = paletteA[idx];
+ buffer.put(r).put(g).put(b).put(a);
+ }
+ } else {
+ for(int i=1,n=curLine.length ; i<n ; i+=1) {
+ int idx = curLine[i] & 255;
+ byte r = palette[idx*3 + 0];
+ byte g = palette[idx*3 + 1];
+ byte b = palette[idx*3 + 2];
+ byte a = (byte)0xFF;
+ buffer.put(r).put(g).put(b).put(a);
+ }
+ }
+ }
+
+ private void copyPALtoBGRA(ByteBuffer buffer, byte[] curLine) {
+ if(paletteA != null) {
+ for(int i=1,n=curLine.length ; i<n ; i+=1) {
+ int idx = curLine[i] & 255;
+ byte r = palette[idx*3 + 0];
+ byte g = palette[idx*3 + 1];
+ byte b = palette[idx*3 + 2];
+ byte a = paletteA[idx];
+ buffer.put(b).put(g).put(r).put(a);
+ }
+ } else {
+ for(int i=1,n=curLine.length ; i<n ; i+=1) {
+ int idx = curLine[i] & 255;
+ byte r = palette[idx*3 + 0];
+ byte g = palette[idx*3 + 1];
+ byte b = palette[idx*3 + 2];
+ byte a = (byte)0xFF;
+ buffer.put(b).put(g).put(r).put(a);
+ }
+ }
+ }
+
+ private void expand4(byte[] src, byte[] dst) {
+ for(int i=1,n=dst.length ; i<n ; i+=2) {
+ int val = src[1 + (i >> 1)] & 255;
+ switch(n-i) {
+ default: dst[i+1] = (byte)(val & 15);
+ case 1: dst[i ] = (byte)(val >> 4);
+ }
+ }
+ }
+
+ private void expand2(byte[] src, byte[] dst) {
+ for(int i=1,n=dst.length ; i<n ; i+=4) {
+ int val = src[1 + (i >> 2)] & 255;
+ switch(n-i) {
+ default: dst[i+3] = (byte)((val ) & 3);
+ case 3: dst[i+2] = (byte)((val >> 2) & 3);
+ case 2: dst[i+1] = (byte)((val >> 4) & 3);
+ case 1: dst[i ] = (byte)((val >> 6) );
+ }
+ }
+ }
+
+ private void expand1(byte[] src, byte[] dst) {
+ for(int i=1,n=dst.length ; i<n ; i+=8) {
+ int val = src[1 + (i >> 3)] & 255;
+ switch(n-i) {
+ default: dst[i+7] = (byte)((val ) & 1);
+ case 7: dst[i+6] = (byte)((val >> 1) & 1);
+ case 6: dst[i+5] = (byte)((val >> 2) & 1);
+ case 5: dst[i+4] = (byte)((val >> 3) & 1);
+ case 4: dst[i+3] = (byte)((val >> 4) & 1);
+ case 3: dst[i+2] = (byte)((val >> 5) & 1);
+ case 2: dst[i+1] = (byte)((val >> 6) & 1);
+ case 1: dst[i ] = (byte)((val >> 7) );
+ }
+ }
+ }
+
+ private void unfilter(byte[] curLine, byte[] prevLine) throws IOException {
+ switch (curLine[0]) {
+ case 0: // none
+ break;
+ case 1:
+ unfilterSub(curLine);
+ break;
+ case 2:
+ unfilterUp(curLine, prevLine);
+ break;
+ case 3:
+ unfilterAverage(curLine, prevLine);
+ break;
+ case 4:
+ unfilterPaeth(curLine, prevLine);
+ break;
+ default:
+ throw new IOException("invalide filter type in scanline: " + curLine[0]);
+ }
+ }
+
+ private void unfilterSub(byte[] curLine) {
+ final int bpp = this.bytesPerPixel;
+ for(int i=bpp+1,n=curLine.length ; i<n ; ++i) {
+ curLine[i] += curLine[i-bpp];
+ }
+ }
+
+ private void unfilterUp(byte[] curLine, byte[] prevLine) {
+ final int bpp = this.bytesPerPixel;
+ for(int i=1,n=curLine.length ; i<n ; ++i) {
+ curLine[i] += prevLine[i];
+ }
+ }
+
+ private void unfilterAverage(byte[] curLine, byte[] prevLine) {
+ final int bpp = this.bytesPerPixel;
+
+ int i;
+ for(i=1 ; i<=bpp ; ++i) {
+ curLine[i] += (byte)((prevLine[i] & 0xFF) >>> 1);
+ }
+ for(int n=curLine.length ; i<n ; ++i) {
+ curLine[i] += (byte)(((prevLine[i] & 0xFF) + (curLine[i - bpp] & 0xFF)) >>> 1);
+ }
+ }
+
+ private void unfilterPaeth(byte[] curLine, byte[] prevLine) {
+ final int bpp = this.bytesPerPixel;
+
+ int i;
+ for(i=1 ; i<=bpp ; ++i) {
+ curLine[i] += prevLine[i];
+ }
+ for(int n=curLine.length ; i<n ; ++i) {
+ int a = curLine[i - bpp] & 255;
+ int b = prevLine[i] & 255;
+ int c = prevLine[i - bpp] & 255;
+ int p = a + b - c;
+ int pa = p - a; if(pa < 0) pa = -pa;
+ int pb = p - b; if(pb < 0) pb = -pb;
+ int pc = p - c; if(pc < 0) pc = -pc;
+ if(pa<=pb && pa<=pc)
+ c = a;
+ else if(pb<=pc)
+ c = b;
+ curLine[i] += (byte)c;
+ }
+ }
+
+ private void readIHDR() throws IOException {
+ checkChunkLength(13);
+ readChunk(buffer, 0, 13);
+ width = readInt(buffer, 0);
+ height = readInt(buffer, 4);
+ bitdepth = buffer[8] & 255;
+ colorType = buffer[9] & 255;
+
+ switch (colorType) {
+ case COLOR_GREYSCALE:
+ if(bitdepth != 8) {
+ throw new IOException("Unsupported bit depth: " + bitdepth);
+ }
+ bytesPerPixel = 1;
+ break;
+ case COLOR_GREYALPHA:
+ if(bitdepth != 8) {
+ throw new IOException("Unsupported bit depth: " + bitdepth);
+ }
+ bytesPerPixel = 2;
+ break;
+ case COLOR_TRUECOLOR:
+ if(bitdepth != 8) {
+ throw new IOException("Unsupported bit depth: " + bitdepth);
+ }
+ bytesPerPixel = 3;
+ break;
+ case COLOR_TRUEALPHA:
+ if(bitdepth != 8) {
+ throw new IOException("Unsupported bit depth: " + bitdepth);
+ }
+ bytesPerPixel = 4;
+ break;
+ case COLOR_INDEXED:
+ switch(bitdepth) {
+ case 8:
+ case 4:
+ case 2:
+ case 1:
+ bytesPerPixel = 1;
+ break;
+ default:
+ throw new IOException("Unsupported bit depth: " + bitdepth);
+ }
+ break;
+ default:
+ throw new IOException("unsupported color format: " + colorType);
+ }
+
+ if(buffer[10] != 0) {
+ throw new IOException("unsupported compression method");
+ }
+ if(buffer[11] != 0) {
+ throw new IOException("unsupported filtering method");
+ }
+ if(buffer[12] != 0) {
+ throw new IOException("unsupported interlace method");
+ }
+ }
+
+ private void readPLTE() throws IOException {
+ int paletteEntries = chunkLength / 3;
+ if(paletteEntries < 1 || paletteEntries > 256 || (chunkLength % 3) != 0) {
+ throw new IOException("PLTE chunk has wrong length");
+ }
+ palette = new byte[paletteEntries*3];
+ readChunk(palette, 0, palette.length);
+ }
+
+ private void readtRNS() throws IOException {
+ switch (colorType) {
+ case COLOR_GREYSCALE:
+ checkChunkLength(2);
+ transPixel = new byte[2];
+ readChunk(transPixel, 0, 2);
+ break;
+ case COLOR_TRUECOLOR:
+ checkChunkLength(6);
+ transPixel = new byte[6];
+ readChunk(transPixel, 0, 6);
+ break;
+ case COLOR_INDEXED:
+ if(palette == null) {
+ throw new IOException("tRNS chunk without PLTE chunk");
+ }
+ paletteA = new byte[palette.length/3];
+ Arrays.fill(paletteA, (byte)0xFF);
+ readChunk(paletteA, 0, paletteA.length);
+ break;
+ default:
+ // just ignore it
+ }
+ }
+
+ private void closeChunk() throws IOException {
+ if(chunkRemaining > 0) {
+ // just skip the rest and the CRC
+ skip(chunkRemaining + 4);
+ } else {
+ readFully(buffer, 0, 4);
+ int expectedCrc = readInt(buffer, 0);
+ int computedCrc = (int)crc.getValue();
+ if(computedCrc != expectedCrc) {
+ throw new IOException("Invalid CRC");
+ }
+ }
+ chunkRemaining = 0;
+ chunkLength = 0;
+ chunkType = 0;
+ }
+
+ private void openChunk() throws IOException {
+ readFully(buffer, 0, 8);
+ chunkLength = readInt(buffer, 0);
+ chunkType = readInt(buffer, 4);
+ chunkRemaining = chunkLength;
+ crc.reset();
+ crc.update(buffer, 4, 4); // only chunkType
+ }
+
+ private void openChunk(int expected) throws IOException {
+ openChunk();
+ if(chunkType != expected) {
+ throw new IOException("Expected chunk: " + Integer.toHexString(expected));
+ }
+ }
+
+ private void checkChunkLength(int expected) throws IOException {
+ if(chunkLength != expected) {
+ throw new IOException("Chunk has wrong size");
+ }
+ }
+
+ private int readChunk(byte[] buffer, int offset, int length) throws IOException {
+ if(length > chunkRemaining) {
+ length = chunkRemaining;
+ }
+ readFully(buffer, offset, length);
+ crc.update(buffer, offset, length);
+ chunkRemaining -= length;
+ return length;
+ }
+
+ private void refillInflater(Inflater inflater) throws IOException {
+ while(chunkRemaining == 0) {
+ closeChunk();
+ openChunk(IDAT);
+ }
+ int read = readChunk(buffer, 0, buffer.length);
+ inflater.setInput(buffer, 0, read);
+ }
+
+ private void readChunkUnzip(Inflater inflater, byte[] buffer, int offset, int length) throws IOException {
+ assert(buffer != this.buffer);
+ try {
+ do {
+ int read = inflater.inflate(buffer, offset, length);
+ if(read <= 0) {
+ if(inflater.finished()) {
+ throw new EOFException();
+ }
+ if(inflater.needsInput()) {
+ refillInflater(inflater);
+ } else {
+ throw new IOException("Can't inflate " + length + " bytes");
+ }
+ } else {
+ offset += read;
+ length -= read;
+ }
+ } while(length > 0);
+ } catch (DataFormatException ex) {
+ throw (IOException)(new IOException("inflate error").initCause(ex));
+ }
+ }
+
+ private void readFully(byte[] buffer, int offset, int length) throws IOException {
+ do {
+ int read = input.read(buffer, offset, length);
+ if(read < 0) {
+ throw new EOFException();
+ }
+ offset += read;
+ length -= read;
+ } while(length > 0);
+ }
+
+ private int readInt(byte[] buffer, int offset) {
+ return
+ ((buffer[offset ] ) << 24) |
+ ((buffer[offset+1] & 255) << 16) |
+ ((buffer[offset+2] & 255) << 8) |
+ ((buffer[offset+3] & 255) );
+ }
+
+ private void skip(long amount) throws IOException {
+ while(amount > 0) {
+ long skipped = input.skip(amount);
+ if(skipped < 0) {
+ throw new EOFException();
+ }
+ amount -= skipped;
+ }
+ }
+
+ private static boolean checkSignature(byte[] buffer) {
+ for(int i=0 ; i<SIGNATURE.length ; i++) {
+ if(buffer[i] != SIGNATURE[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
View
243 src/mdesl/graphics/SpriteBatch.java
@@ -0,0 +1,243 @@
+package mdesl.graphics;
+
+import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
+import static org.lwjgl.opengl.GL20.glUniform1i;
+import static org.lwjgl.opengl.GL20.glUniformMatrix4;
+
+import java.nio.FloatBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+import mdesl.graphics.glutils.ShaderProgram;
+import mdesl.graphics.glutils.VertexArray;
+import mdesl.graphics.glutils.VertexAttrib;
+import mdesl.graphics.glutils.VertexData;
+import mdesl.util.MathUtil;
+
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opengl.Display;
+import org.lwjgl.util.vector.Matrix4f;
+
+
+
+public class SpriteBatch {
+ public static final String TEXCOORD_0 = "tex0";
+ public static final String PROJECTION_MATRIX = "projMatrix";
+
+ public static final String ATTR_COLOR = "Color";
+ public static final String ATTR_POSITION = "Position";
+ public static final String ATTR_TEXCOORD = "TexCoord";
+
+ public static final String DEFAULT_VERT_SHADER = "uniform mat4 "+PROJECTION_MATRIX+";\n" +
+ "attribute vec4 "+ATTR_COLOR+";\n" +
+ "attribute vec2 "+ATTR_TEXCOORD+";\n" +
+ "attribute vec2 "+ATTR_POSITION+";\n" +
+ "varying vec4 vColor;\n" +
+ "varying vec2 vTexCoord; \n" +
+ "void main() {\n" +
+ " vColor = "+ATTR_COLOR+";\n" +
+ " vTexCoord = "+ATTR_TEXCOORD+";\n" +
+ " gl_Position = "+PROJECTION_MATRIX+" * vec4("+ATTR_POSITION+".xy, 0, 1);\n" +
+ "}";
+
+ public static final String DEFAULT_FRAG_SHADER = "uniform sampler2D "+TEXCOORD_0+";\n" +
+ "varying vec4 vColor;\n" +
+ "varying vec2 vTexCoord;\n" +
+ "void main(void) {\n" +
+ " vec4 texColor = texture2D("+TEXCOORD_0+", vTexCoord);\n" +
+ " gl_FragColor = vColor * texColor;\n" +
+ "}";
+
+ public static final List<VertexAttrib> ATTRIBUTES = Arrays.asList(
+ new VertexAttrib(0, ATTR_POSITION, 2),
+ new VertexAttrib(1, ATTR_COLOR, 4),
+ new VertexAttrib(2, ATTR_TEXCOORD, 2));
+
+ static ShaderProgram defaultShader;
+ public static int renderCalls = 0;
+
+ protected FloatBuffer buf16;
+ protected Matrix4f projMatrix;
+
+ protected Texture texture;
+ protected ShaderProgram program;
+
+ protected VertexData data;
+
+ private int idx;
+ private int maxIndex;
+
+ private float r=1f, g=1f, b=1f, a=1f;
+ private boolean drawing = false;
+
+ static ShaderProgram getDefaultShader() {
+ return defaultShader==null
+ ? new ShaderProgram(DEFAULT_VERT_SHADER, DEFAULT_FRAG_SHADER, ATTRIBUTES)
+ : defaultShader;
+ }
+
+ public SpriteBatch(ShaderProgram program, int size) {
+ this.program = program;
+
+ //later we can do some abstraction to replace this with VBOs...
+ this.data = new VertexArray(size * 6, ATTRIBUTES);
+
+ //max indices before we need to flush the renderer
+ maxIndex = size * 6;
+
+ updateProjection();
+ }
+
+ public SpriteBatch(int size) {
+ this(getDefaultShader(), size);
+ }
+
+ public SpriteBatch() {
+ this(1000);
+ }
+
+ public void setColor(float r, float g, float b, float a) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+ }
+
+ /**
+ * Call to update the projection matrix with the screen and send the uniform to the current shader.
+ */
+ public void updateProjection() {
+ projMatrix = MathUtil.toOrtho2D(projMatrix, 0, 0, Display.getWidth(), Display.getHeight());
+
+ program.begin();
+
+ //upload projection matrix
+ int proj = program.getUniformLocation(PROJECTION_MATRIX);
+ if (proj!=-1) { //if the uniform is active
+ //ShaderProgram should probably have this as a convenience method
+ if (buf16==null)
+ buf16 = BufferUtils.createFloatBuffer(16);
+ buf16.clear();
+ projMatrix.store(buf16);
+ buf16.flip();
+ glUniformMatrix4(proj, true, buf16);
+ }
+
+ //upload texcoord 0
+ int tex0 = program.getUniformLocation(TEXCOORD_0);
+ if (tex0!=-1)
+ glUniform1i(tex0, 0);
+
+ program.end();
+ }
+
+ public void begin() {
+ if (drawing) throw new IllegalStateException("must not be drawing before calling begin()");
+ drawing = true;
+ program.begin();
+ idx = 0;
+ renderCalls = 0;
+ texture = null;
+ }
+
+ public void end() {
+ if (!drawing) throw new IllegalStateException("must be drawing before calling end()");
+ drawing = false;
+ flush();
+ program.end();
+ }
+
+ public void flush() {
+ if (idx>0) {
+ data.flip();
+ render();
+ idx = 0;
+ data.clear();
+ }
+ }
+
+ public void drawRegion(Texture tex, float srcX, float srcY, float srcWidth,
+ float srcHeight, float dstX, float dstY) {
+ drawRegion(tex, srcX, srcY, srcWidth, srcHeight, dstX, dstY, srcWidth, srcHeight);
+ }
+
+ public void drawRegion(Texture tex, float srcX, float srcY, float srcWidth,
+ float srcHeight, float dstX, float dstY, float dstWidth,
+ float dstHeight) {
+ float u = srcX / tex.width;
+ float v = srcY / tex.height;
+ float u2 = (srcX+srcWidth) / tex.width;
+ float v2 = (srcY+srcHeight) / tex.height;
+ draw(tex, dstX, dstY, dstWidth, dstHeight, u, v, u2, v2);
+ }
+
+ public void draw(Texture tex, float x, float y) {
+ draw(tex, x, y, tex.width, tex.height);
+ }
+
+ public void draw(Texture tex, float x, float y, float width, float height) {
+ draw(tex, x, y, width, height, 0, 0, 1, 1);
+ }
+
+ public void draw(Texture tex, float x, float y, float width, float height,
+ float u, float v, float u2, float v2) {
+ checkFlush(tex);
+
+ //top left, top right, bottom left
+ vertex(x, y, r, g, b, a, u, v);
+ vertex(x+width, y, r, g, b, a, u2, v);
+ vertex(x, y+height, r, g, b, a, u, v2);
+
+ //top right, bottom right, bottom left
+ vertex(x+width, y, r, g, b, a, u2, v);
+ vertex(x+width, y+height, r, g, b, a, u2, v2);
+ vertex(x, y+height, r, g, b, a, u, v2);
+ }
+
+
+ /**
+ * Renders a texture using custom vertex attributes; e.g. for different vertex colours.
+ * This will ignore the current batch color.
+ *
+ * @param tex the texture to use
+ * @param vertices an array of 6 vertices, each holding 8 attributes (total = 48 elements)
+ * @param offset the offset from the vertices array to start from
+ */
+ public void draw(Texture tex, float[] vertices, int offset) {
+ checkFlush(tex);
+ data.put(vertices, offset, data.getTotalNumComponents() * 6);
+ idx += 6;
+ }
+
+ VertexData vertex(float x, float y, float r, float g, float b, float a,
+ float u, float v) {
+ data.put(x).put(y).put(r).put(g).put(b).put(a).put(u).put(v);
+ idx++;
+ return data;
+ }
+
+ protected void checkFlush(Texture texture) {
+ if (texture==null || texture==null)
+ throw new NullPointerException("null texture");
+
+ //we need to bind a different texture/type. this is
+ //for convenience; ideally the user should order
+ //their rendering wisely to minimize texture binds
+ if (texture!=this.texture || idx >= maxIndex) {
+ //apply the last texture
+ flush();
+ this.texture = texture;
+ }
+ }
+
+ private void render() {
+ if (texture!=null)
+ texture.bind();
+ data.bind();
+ data.draw(GL_TRIANGLES, 0, idx);
+ data.unbind();
+ renderCalls++;
+ }
+
+
+}
View
95 src/mdesl/graphics/Texture.java
@@ -0,0 +1,95 @@
+package mdesl.graphics;
+
+import static org.lwjgl.opengl.GL11.*;
+import static org.lwjgl.opengl.GL12.*;
+import static org.lwjgl.opengl.GL11.GL_PACK_ALIGNMENT;
+import static org.lwjgl.opengl.GL11.GL_RGBA;
+import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
+import static org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER;
+import static org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER;
+import static org.lwjgl.opengl.GL11.GL_TEXTURE_WRAP_S;
+import static org.lwjgl.opengl.GL11.GL_TEXTURE_WRAP_T;
+import static org.lwjgl.opengl.GL11.GL_UNPACK_ALIGNMENT;
+import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE;
+import static org.lwjgl.opengl.GL11.glBindTexture;
+import static org.lwjgl.opengl.GL11.glEnable;
+import static org.lwjgl.opengl.GL11.glGenTextures;
+import static org.lwjgl.opengl.GL11.glPixelStorei;
+import static org.lwjgl.opengl.GL11.glTexImage2D;
+import static org.lwjgl.opengl.GL11.glTexParameteri;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.ByteBuffer;
+
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opengl.GL12;
+
+import de.matthiasmann.twl.utils.PNGDecoder;
+
+public class Texture {
+ static int bound = 0;
+
+ public final int target = GL_TEXTURE_2D;
+ public final int id;
+ public final int width;
+ public final int height;
+
+ public static final int LINEAR = GL_LINEAR;
+ public static final int NEAREST = GL_NEAREST;
+
+ public static final int CLAMP = GL_CLAMP;
+ public static final int CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE;
+ public static final int REPEAT = GL_REPEAT;
+
+ public static void clearLastBind() {
+ bound = 0;
+ }
+
+ public Texture(URL pngRef) throws IOException {
+ this(pngRef, GL_NEAREST);
+ }
+
+ public Texture(URL pngRef, int filter) throws IOException {
+ this(pngRef, filter, GL_CLAMP_TO_EDGE);
+ }
+
+ public Texture(URL pngRef, int filter, int wrap) throws IOException {
+ InputStream input = null;
+ try {
+ input = pngRef.openStream();
+ PNGDecoder dec = new PNGDecoder(input);
+
+ width = dec.getWidth();
+ height = dec.getHeight();
+ ByteBuffer buf = BufferUtils.createByteBuffer(4 * width * height);
+ dec.decode(buf, width * 4, PNGDecoder.Format.RGBA);
+ buf.flip();
+
+ glEnable(target);
+ id = glGenTextures();
+
+ bind();
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+
+ glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
+ glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
+ glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap);
+ glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap);
+
+ glTexImage2D(target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
+ } finally {
+ if (input!=null) {
+ try { input.close(); } catch (IOException e) {}
+ }
+ }
+ }
+
+ public void bind() {
+ if (id != bound)
+ glBindTexture(target, id);
+ }
+}
View
114 src/mdesl/graphics/glutils/ShaderProgram.java
@@ -0,0 +1,114 @@
+package mdesl.graphics.glutils;
+
+import static org.lwjgl.opengl.GL11.GL_FALSE;
+import static org.lwjgl.opengl.GL20.GL_COMPILE_STATUS;
+import static org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER;
+import static org.lwjgl.opengl.GL20.GL_INFO_LOG_LENGTH;
+import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
+import static org.lwjgl.opengl.GL20.GL_VERTEX_SHADER;
+import static org.lwjgl.opengl.GL20.glAttachShader;
+import static org.lwjgl.opengl.GL20.glBindAttribLocation;
+import static org.lwjgl.opengl.GL20.glCompileShader;
+import static org.lwjgl.opengl.GL20.glCreateProgram;
+import static org.lwjgl.opengl.GL20.glCreateShader;
+import static org.lwjgl.opengl.GL20.glDeleteProgram;
+import static org.lwjgl.opengl.GL20.glDeleteShader;
+import static org.lwjgl.opengl.GL20.glDetachShader;
+import static org.lwjgl.opengl.GL20.glGetProgram;
+import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;
+import static org.lwjgl.opengl.GL20.glGetShader;
+import static org.lwjgl.opengl.GL20.glGetShaderInfoLog;
+import static org.lwjgl.opengl.GL20.glGetUniformLocation;
+import static org.lwjgl.opengl.GL20.glLinkProgram;
+import static org.lwjgl.opengl.GL20.glShaderSource;
+import static org.lwjgl.opengl.GL20.glUseProgram;
+
+import java.util.List;
+
+/**
+ * A bare-bones ShaderProgram utility based on ra4king's ArcSynthesis Java ports.
+ *
+ * @author ra4king
+ */
+public class ShaderProgram {
+
+ public final int program;
+ public final int vertex;
+ public final int fragment;
+ protected String log;
+
+ public ShaderProgram(String vertexSource, String fragmentSource) {
+ this(vertexSource, fragmentSource, null);
+ }
+
+ public ShaderProgram(String vertexShader, String fragmentShader, List<VertexAttrib> attributes) {
+ vertex = compileShader(vertexShader, GL_VERTEX_SHADER);
+ fragment = compileShader(fragmentShader, GL_FRAGMENT_SHADER);
+ program = glCreateProgram();
+ glAttachShader(program, vertex);
+ glAttachShader(program, fragment);
+
+ if (attributes != null)
+ for (VertexAttrib a : attributes)
+ glBindAttribLocation(program, a.location, a.name);
+
+ glLinkProgram(program);
+
+ String infoLog = glGetProgramInfoLog(program, glGetProgram(program, GL_INFO_LOG_LENGTH));
+
+ if (glGetProgram(program, GL_LINK_STATUS) == GL_FALSE)
+ throw new RuntimeException(
+ "Failure in linking program. Error log:\n" + infoLog);
+
+ if (infoLog!=null && infoLog.trim().length()!=0)
+ log += infoLog;
+
+ glDetachShader(program, vertex);
+ glDetachShader(program, fragment);
+ glDeleteShader(vertex);
+ glDeleteShader(fragment);
+ }
+
+ private int compileShader(String source, int type) {
+ int shader = glCreateShader(type);
+ glShaderSource(shader, source);
+ glCompileShader(shader);
+
+ String infoLog = glGetShaderInfoLog(shader,
+ glGetShader(shader, GL_INFO_LOG_LENGTH));
+
+ if (glGetShader(shader, GL_COMPILE_STATUS) == GL_FALSE)
+ throw new RuntimeException("Failure in compiling " + getName(type)
+ + ". Error log:\n" + infoLog);
+
+ if (infoLog!=null && infoLog.trim().length()!=0)
+ log += getName(type) +": "+infoLog + "\n";
+
+ return shader;
+ }
+
+ private String getName(int shaderType) {
+ if (shaderType == GL_VERTEX_SHADER)
+ return "GL_VERTEX_SHADER";
+ if (shaderType == GL_FRAGMENT_SHADER)
+ return "GL_FRAGMENT_SHADER";
+ else
+ return "shader";
+ }
+
+ public void begin() {
+ glUseProgram(program);
+ }
+
+ public void end() {
+ glUseProgram(0);
+ }
+
+ public void destroy() {
+ glDeleteProgram(program);
+ }
+
+ public int getUniformLocation(String str) {
+ return glGetUniformLocation(program, str);
+ }
+}
View
88 src/mdesl/graphics/glutils/VertexArray.java
@@ -0,0 +1,88 @@
+package mdesl.graphics.glutils;
+
+import static org.lwjgl.opengl.GL11.glDrawArrays;
+import static org.lwjgl.opengl.GL20.glDisableVertexAttribArray;
+import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray;
+import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
+
+import java.nio.FloatBuffer;
+import java.util.List;
+
+import org.lwjgl.BufferUtils;
+
+public class VertexArray implements VertexData {
+
+ protected List<VertexAttrib> attributes;
+
+ private int totalNumComponents;
+ private int stride;
+ private FloatBuffer buffer;
+ private int vertCount;
+
+ public VertexArray(int vertCount, List<VertexAttrib> attributes) {
+ this.attributes = attributes;
+ for (VertexAttrib a : attributes)
+ totalNumComponents += a.numComponents;
+ this.vertCount = vertCount;
+
+ //our buffer which holds our data
+ this.buffer = BufferUtils.createFloatBuffer(vertCount * totalNumComponents);
+ }
+
+ public VertexArray flip() {
+ buffer.flip();
+ return this;
+ }
+
+ public VertexArray clear() {
+ buffer.clear();
+ return this;
+ }
+
+ public VertexArray put(float[] verts, int offset, int length) {
+ buffer.put(verts, offset, length);
+ return this;
+ }
+
+ public VertexArray put(float f) {
+ buffer.put(f);
+ return this;
+ }
+
+ public FloatBuffer buffer() {
+ return buffer;
+ }
+
+ public int getTotalNumComponents() {
+ return totalNumComponents;
+ }
+
+ public int getVertexCount() {
+ return vertCount;
+ }
+
+ public void bind() {
+ int offset = 0;
+ //4 bytes per float
+ int stride = totalNumComponents * 4;
+
+ for (int i=0; i<attributes.size(); i++) {
+ VertexAttrib a = attributes.get(i);
+ buffer.position(offset);
+ glEnableVertexAttribArray(a.location);
+ glVertexAttribPointer(a.location, a.numComponents, false, stride, buffer);
+ offset += a.numComponents;
+ }
+ }
+
+ public void draw(int geom, int first, int count) {
+ glDrawArrays(geom, first, count);
+ }
+
+ public void unbind() {
+ for (int i=0; i<attributes.size(); i++) {
+ VertexAttrib a = attributes.get(i);
+ glDisableVertexAttribArray(a.location);
+ }
+ }
+}
View
18 src/mdesl/graphics/glutils/VertexAttrib.java
@@ -0,0 +1,18 @@
+package mdesl.graphics.glutils;
+
+public class VertexAttrib {
+
+ public final String name;
+ public final int numComponents;
+ public final int location;
+
+ public VertexAttrib(int location, String name, int numComponents) {
+ this.location = location;
+ this.name = name;
+ this.numComponents = numComponents;
+ }
+
+ public String toString() {
+ return name +" (" + numComponents+")";
+ }
+}
View
21 src/mdesl/graphics/glutils/VertexData.java
@@ -0,0 +1,21 @@
+package mdesl.graphics.glutils;
+
+import java.nio.FloatBuffer;
+
+public interface VertexData {
+ public void bind();
+ public void draw(int geom, int first, int count);
+ public void unbind();
+
+ public VertexData clear();
+ public VertexData flip();
+ public VertexData put(float[] verts, int offset, int length);
+ public VertexData put(float v);
+
+ public FloatBuffer buffer();
+
+ public int getTotalNumComponents();
+ public int getVertexCount();
+
+}
+
View
105 src/mdesl/test/VertexArrayExample.java
@@ -0,0 +1,105 @@
+package mdesl.test;
+
+import static org.lwjgl.opengl.GL11.GL_BLEND;
+import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
+import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
+import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA;
+import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
+import static org.lwjgl.opengl.GL11.glBlendFunc;
+import static org.lwjgl.opengl.GL11.glClear;
+import static org.lwjgl.opengl.GL11.glClearColor;
+import static org.lwjgl.opengl.GL11.glDisable;
+import static org.lwjgl.opengl.GL11.glEnable;
+import static org.lwjgl.opengl.GL11.glViewport;
+
+import java.io.IOException;
+import java.net.URL;
+
+import mdesl.graphics.SpriteBatch;
+import mdesl.graphics.Texture;
+
+import org.lwjgl.LWJGLException;
+import org.lwjgl.opengl.Display;
+import org.lwjgl.opengl.DisplayMode;
+
+public class VertexArrayExample {
+
+ public static void main(String[] args) throws LWJGLException {
+ new VertexArrayExample().start();
+ }
+
+ public void start() throws LWJGLException {
+ Display.setTitle("Vertex Array Example");
+ Display.setResizable(true);
+ Display.setDisplayMode(new DisplayMode(800, 600));
+ Display.setVSyncEnabled(true);
+ Display.create();
+
+ create();
+
+ while (!Display.isCloseRequested()) {
+ if (Display.wasResized())
+ resize();
+ render();
+
+ Display.update();
+ Display.sync(60);
+ }
+
+ Display.destroy();
+ }
+
+ Texture tex, tex2;
+ SpriteBatch batch;
+
+ static URL getResource(String ref) {
+ URL url = VertexArrayExample.class.getClassLoader().getResource(ref);
+ if (url==null)
+ throw new RuntimeException("could not find resource: "+ref);
+ return url;
+ }
+
+ protected void resize() {
+ glViewport(0, 0, Display.getWidth(), Display.getHeight());
+ batch.updateProjection();
+ }
+
+ protected void create() {
+ glDisable(GL_DEPTH_TEST);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glClearColor(0f, 0f, 0f, 0f);
+ glViewport(0, 0, Display.getWidth(), Display.getHeight());
+
+ //Load some textures
+ try {
+ tex = new Texture(getResource("res/small.png"), Texture.LINEAR);
+ tex2 = new Texture(getResource("res/font0.png"));
+ } catch (IOException e) {
+ throw new RuntimeException("couldn't decode textures");
+ }
+
+ batch = new SpriteBatch(1000);
+ }
+
+ protected void render() {
+ glClear(GL_COLOR_BUFFER_BIT);
+ batch.begin();
+
+ batch.draw(tex, 50, 50);
+ batch.draw(tex, 150, 150, tex.width*2, tex.height*2);
+
+ batch.draw(tex2, 350, 25);
+
+ batch.setColor(1f, 0f, 0f, 1f); //tint red
+ batch.drawRegion(tex2, 25, 50, 100, 32,
+ 350, 350);
+ batch.setColor(1f, 1f, 1f, 1f); //reset color..
+
+ batch.end();
+
+ //simple debugging...
+ //Display.setTitle("Render calls: "+batch.renderCalls);
+ //batch.renderCalls = 0;
+ }
+}
View
82 src/mdesl/util/MathUtil.java
@@ -0,0 +1,82 @@
+package mdesl.util;
+
+import org.lwjgl.util.vector.Matrix4f;
+
+public class MathUtil {
+
+ /**
+ * Sets the given matrix to an orthographic 2D projection matrix, and returns it. If the given matrix
+ * is null, a new one will be created and returned.
+ *
+ * @param m the matrix to re-use, or null to create a new matrix
+ * @param x
+ * @param y
+ * @param width
+ * @param height
+ * @return the given matrix, or a newly created matrix if none was specified
+ */
+ public static Matrix4f toOrtho2D(Matrix4f m, float x, float y, float width, float height) {
+ return toOrtho(m, x, x + width, y + height, y, 1, -1);
+ }
+
+ /**
+ * Sets the given matrix to an orthographic 2D projection matrix, and returns it. If the given matrix
+ * is null, a new one will be created and returned.
+ *
+ * @param m the matrix to re-use, or null to create a new matrix
+ * @param x
+ * @param y
+ * @param width
+ * @param height
+ * @param near near clipping plane
+ * @param far far clipping plane
+ * @return the given matrix, or a newly created matrix if none was specified
+ */
+ public static Matrix4f toOrtho2D(Matrix4f m, float x, float y, float width, float height, float near, float far) {
+ return toOrtho(m, x, x + width, y, y + height, near, far);
+ }
+
+ /**
+ * Sets the given matrix to an orthographic projection matrix, and returns it. If the given matrix
+ * is null, a new one will be created and returned.
+ *
+ * @param m the matrix to re-use, or null to create a new matrix
+ * @param left
+ * @param right
+ * @param bottom
+ * @param top
+ * @param near near clipping plane
+ * @param far far clipping plane
+ * @return the given matrix, or a newly created matrix if none was specified
+ */
+ public static Matrix4f toOrtho(Matrix4f m, float left, float right, float bottom, float top,
+ float near, float far) {
+ if (m==null)
+ m = new Matrix4f();
+ float x_orth = 2 / (right - left);
+ float y_orth = 2 / (top - bottom);
+ float z_orth = -2 / (far - near);
+
+ float tx = -(right + left) / (right - left);
+ float ty = -(top + bottom) / (top - bottom);
+ float tz = -(far + near) / (far - near);
+
+ m.m00 = x_orth;
+ m.m10 = 0;
+ m.m20 = 0;
+ m.m30 = 0;
+ m.m01 = 0;
+ m.m11 = y_orth;
+ m.m21 = 0;
+ m.m31 = 0;
+ m.m02 = 0;
+ m.m12 = 0;
+ m.m22 = z_orth;
+ m.m32 = 0;
+ m.m03 = tx;
+ m.m13 = ty;
+ m.m23 = tz;
+ m.m33 = 1;
+ return m;
+ }
+}
View
BIN  src/res/font0.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  src/res/small.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Please sign in to comment.
Something went wrong with that request. Please try again.