| @@ -0,0 +1,121 @@ | ||
| package org.spout.server.client.loader; | ||
|
|
||
| import java.io.BufferedInputStream; | ||
| import java.io.File; | ||
| import java.io.InputStream; | ||
| import java.net.URL; | ||
| import java.util.ArrayList; | ||
|
|
||
|
|
||
| /** | ||
| * A simple wrapper around resource loading should anyone decide to change | ||
| * their minds how this is meant to work in the future. | ||
| * | ||
| * @author Kevin Glass | ||
| */ | ||
| public class ResourceLoader { | ||
| /** The list of locations to be searched */ | ||
| private static ArrayList<ResourceLocation> locations = new ArrayList<ResourceLocation>(); | ||
|
|
||
| static { | ||
| locations.add(new ClasspathLocation()); | ||
| locations.add(new FileSystemLocation(new File("."))); | ||
| } | ||
|
|
||
| /** | ||
| * Add a location that will be searched for resources | ||
| * | ||
| * @param location The location that will be searched for resoruces | ||
| */ | ||
| public static void addResourceLocation(ResourceLocation location) { | ||
| locations.add(location); | ||
| } | ||
|
|
||
| /** | ||
| * Remove a location that will be no longer be searched for resources | ||
| * | ||
| * @param location The location that will be removed from the search list | ||
| */ | ||
| public static void removeResourceLocation(ResourceLocation location) { | ||
| locations.remove(location); | ||
| } | ||
|
|
||
| /** | ||
| * Remove all the locations, no resources will be found until | ||
| * new locations have been added | ||
| */ | ||
| public static void removeAllResourceLocations() { | ||
| locations.clear(); | ||
| } | ||
|
|
||
| /** | ||
| * Get a resource | ||
| * | ||
| * @param ref The reference to the resource to retrieve | ||
| * @return A stream from which the resource can be read | ||
| */ | ||
| public static InputStream getResourceAsStream(String ref) { | ||
| InputStream in = null; | ||
|
|
||
| for (int i=0;i<locations.size();i++) { | ||
| ResourceLocation location = (ResourceLocation) locations.get(i); | ||
| in = location.getResourceAsStream(ref); | ||
| if (in != null) { | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (in == null) | ||
| { | ||
| throw new RuntimeException("Resource not found: "+ref); | ||
| } | ||
|
|
||
| return new BufferedInputStream(in); | ||
| } | ||
|
|
||
| /** | ||
| * Check if a resource is available from any given resource loader | ||
| * | ||
| * @param ref A reference to the resource that should be checked | ||
| * @return True if the resource can be located | ||
| */ | ||
| public static boolean resourceExists(String ref) { | ||
| URL url = null; | ||
|
|
||
| for (int i=0;i<locations.size();i++) { | ||
| ResourceLocation location = (ResourceLocation) locations.get(i); | ||
| url = location.getResource(ref); | ||
| if (url != null) { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * Get a resource as a URL | ||
| * | ||
| * @param ref The reference to the resource to retrieve | ||
| * @return A URL from which the resource can be read | ||
| */ | ||
| public static URL getResource(String ref) { | ||
|
|
||
| URL url = null; | ||
|
|
||
| for (int i=0;i<locations.size();i++) { | ||
| ResourceLocation location = (ResourceLocation) locations.get(i); | ||
| url = location.getResource(ref); | ||
| if (url != null) { | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (url == null) | ||
| { | ||
| throw new RuntimeException("Resource not found: "+ref); | ||
| } | ||
|
|
||
| return url; | ||
| } | ||
| } |
| @@ -0,0 +1,29 @@ | ||
| package org.spout.server.client.loader; | ||
|
|
||
| import java.io.InputStream; | ||
| import java.net.URL; | ||
|
|
||
| /** | ||
| * A location from which resources can be loaded | ||
| * | ||
| * @author kevin | ||
| */ | ||
| public interface ResourceLocation { | ||
|
|
||
| /** | ||
| * Get a resource as an input stream | ||
| * | ||
| * @param ref The reference to the resource to retrieve | ||
| * @return A stream from which the resource can be read or | ||
| * null if the resource can't be found in this location | ||
| */ | ||
| public InputStream getResourceAsStream(String ref); | ||
|
|
||
| /** | ||
| * Get a resource as a URL | ||
| * | ||
| * @param ref The reference to the resource to retrieve | ||
| * @return A URL from which the resource can be read | ||
| */ | ||
| public URL getResource(String ref); | ||
| } |
| @@ -0,0 +1,53 @@ | ||
| package org.spout.server.client.loader; | ||
|
|
||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.net.URI; | ||
| import java.net.URISyntaxException; | ||
| import java.net.URL; | ||
| import java.util.jar.JarEntry; | ||
| import java.util.jar.JarFile; | ||
|
|
||
| import org.spout.api.Spout; | ||
| import org.spout.api.plugin.Plugin; | ||
|
|
||
|
|
||
| public class URILocation implements ResourceLocation { | ||
|
|
||
| public InputStream getResourceAsStream(String ref) { | ||
| URI uri; | ||
| try { | ||
| uri = getResource(ref).toURI(); | ||
| } catch (URISyntaxException e) { | ||
| return null; | ||
| } | ||
| if(uri == null) return null; | ||
|
|
||
| String plugin = uri.getHost(); | ||
| String path = uri.getPath(); | ||
| Plugin p = Spout.getGame().getPluginManager().getPlugin(plugin); | ||
| if(p == null) throw new RuntimeException("Plugin " + plugin + " Not Found"); | ||
| try { | ||
| JarFile pfile = new JarFile(p.getFile()); | ||
| JarEntry entry = pfile.getJarEntry(path); | ||
| return pfile.getInputStream(entry); | ||
| } catch (IOException e) { | ||
| // TODO Auto-generated catch block | ||
| e.printStackTrace(); | ||
| } | ||
|
|
||
|
|
||
|
|
||
| return null; | ||
| } | ||
|
|
||
| public URL getResource(String ref) { | ||
| try { | ||
| URI a = new URI(ref); | ||
| return a.toURL(); | ||
| } catch (Exception e) { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,79 @@ | ||
| package org.spout.server.client.mesh; | ||
|
|
||
| import java.util.ArrayList; | ||
|
|
||
| import org.spout.server.client.renderer.vertexformat.*; | ||
| import org.spout.api.render.RenderEffect; | ||
| import org.spout.api.render.Renderer; | ||
|
|
||
| public abstract class BaseMesh { | ||
| ArrayList<PositionNormalTexture> verts = new ArrayList<PositionNormalTexture>(); | ||
|
|
||
| ArrayList<RenderEffect> effects = new ArrayList<RenderEffect>(); | ||
|
|
||
| boolean dirty = false; | ||
|
|
||
| public void addRenderEffect(RenderEffect effect){ | ||
| effects.add(effect); | ||
| } | ||
| public void removeRenderEffect(RenderEffect effect){ | ||
| effects.remove(effect); | ||
| } | ||
| public RenderEffect[] getEffects(){ | ||
| return (RenderEffect[])effects.toArray(); | ||
| } | ||
|
|
||
|
|
||
| private void preBatch(Renderer batcher){ | ||
| for(RenderEffect effect : effects){ | ||
| effect.preBatch(batcher); | ||
| } | ||
| } | ||
|
|
||
| private void postBatch(Renderer batcher){ | ||
| for(RenderEffect effect: effects){ | ||
| effect.postBatch(batcher); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| protected void batch(Renderer batcher){ | ||
| for(PositionNormalTexture vert : verts){ | ||
| batcher.addTexCoord(vert.getTexture()); | ||
| batcher.addNormal(vert.getNormal()); | ||
| batcher.addVertex(vert.getPosition()); | ||
| } | ||
| } | ||
|
|
||
| private void preRender(Renderer batcher){ | ||
| for(RenderEffect effect : effects){ | ||
| effect.preDraw(batcher); | ||
| } | ||
| } | ||
| private void postRender(Renderer batcher){ | ||
| for(RenderEffect effect : effects){ | ||
| effect.postDraw(batcher); | ||
| } | ||
| } | ||
|
|
||
| protected void render(Renderer batcher){ | ||
| batcher.render(); | ||
| } | ||
|
|
||
|
|
||
| public void draw(Renderer batcher){ | ||
| this.preBatch(batcher); | ||
| this.batch(batcher); | ||
| this.postBatch(batcher); | ||
|
|
||
| this.preRender(batcher); | ||
| this.render(batcher); | ||
| this.postRender(batcher); | ||
|
|
||
| } | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
| } |
| @@ -0,0 +1,19 @@ | ||
| package org.spout.server.client.mesh; | ||
|
|
||
| import org.spout.api.math.Vector3; | ||
|
|
||
| public class CubeMesh extends BaseMesh { | ||
|
|
||
| Vector3 scale = Vector3.ONE; | ||
|
|
||
|
|
||
| public CubeMesh(){ | ||
|
|
||
| } | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
| } |
| @@ -0,0 +1,8 @@ | ||
| package org.spout.server.client.renderer; | ||
|
|
||
| public enum BatchModes { | ||
| GL11, | ||
| GL30, | ||
| GLES20, | ||
|
|
||
| } |
| @@ -0,0 +1,293 @@ | ||
| package org.spout.server.client.renderer; | ||
| import java.awt.Color; | ||
| import java.io.FileNotFoundException; | ||
|
|
||
| import gnu.trove.list.array.*; | ||
|
|
||
|
|
||
| import org.spout.api.math.Vector2; | ||
| import org.spout.api.math.Vector3; | ||
| import org.spout.api.math.Vector4; | ||
| import org.spout.api.render.Shader; | ||
| import org.spout.api.render.Renderer; | ||
| import org.spout.server.client.renderer.shader.EmptyShader; | ||
|
|
||
|
|
||
| public abstract class BatchVertexRenderer implements Renderer { | ||
|
|
||
| public static BatchModes GLMode = BatchModes.GL11; | ||
|
|
||
| public static Renderer constructNewBatch(int renderMode){ | ||
| if(GLMode == BatchModes.GL11) return new GL11BatchVertexRenderer(renderMode); | ||
| if(GLMode == BatchModes.GL30) return new GL30BatchVertexRenderer(renderMode); | ||
| if(GLMode == BatchModes.GLES20) return new GLES20BatchVertexRenderer(renderMode); | ||
| throw new IllegalArgumentException("GL Mode:" + GLMode + " Not reconized"); | ||
| } | ||
|
|
||
|
|
||
|
|
||
| boolean batching = false; | ||
| boolean flushed = false; | ||
|
|
||
| int renderMode; | ||
|
|
||
| //Using FloatArrayList because I need O(1) access time | ||
| //and fast ToArray() | ||
| TFloatArrayList vertexBuffer = new TFloatArrayList(); | ||
| TFloatArrayList colorBuffer = new TFloatArrayList(); | ||
| TFloatArrayList normalBuffer = new TFloatArrayList(); | ||
| TFloatArrayList uvBuffer = new TFloatArrayList(); | ||
|
|
||
|
|
||
| int numVerticies = 0; | ||
|
|
||
| boolean useColors = false; | ||
| boolean useNormals = false; | ||
| boolean useTextures = false; | ||
|
|
||
| Shader activeShader = null; | ||
|
|
||
| public BatchVertexRenderer(int mode){ | ||
| renderMode = mode; | ||
| } | ||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#begin() | ||
| */ | ||
| public void begin(){ | ||
| if(batching) throw new IllegalStateException("Already Batching!"); | ||
| batching = true; | ||
| flushed = false; | ||
| vertexBuffer.clear(); | ||
| colorBuffer.clear(); | ||
| normalBuffer.clear(); | ||
| uvBuffer.clear(); | ||
|
|
||
|
|
||
| numVerticies = 0; | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#end() | ||
| */ | ||
| public void end(){ | ||
| if(!batching) throw new IllegalStateException("Not Batching!"); | ||
| batching = false; | ||
| flush(); | ||
| } | ||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#flush() | ||
| */ | ||
| public final void flush(){ | ||
| if( vertexBuffer.size() % 4 != 0) throw new IllegalStateException("Vertex Size Mismatch (How did this happen?)"); | ||
| if( useColors){ | ||
| if(colorBuffer.size() % 4 != 0) throw new IllegalStateException("Color Size Mismatch (How did this happen?)"); | ||
| if(colorBuffer.size() / 4 != numVerticies) throw new IllegalStateException("Color Buffer size does not match numVerticies"); | ||
|
|
||
| } | ||
| if(useNormals){ | ||
| if(normalBuffer.size() % 4 != 0) throw new IllegalStateException("Normal Size Mismatch (How did this happen?)"); | ||
| if(normalBuffer.size() / 4 != numVerticies) throw new IllegalStateException("Normal Buffer size does not match numVerticies"); | ||
|
|
||
| } | ||
| if(useTextures){ | ||
| if(uvBuffer.size() % 2 != 0) throw new IllegalStateException("UV size Mismatch (How did this happen?)"); | ||
| if(uvBuffer.size() / 2 != numVerticies) throw new IllegalStateException("UV Buffer size does not match numVerticies"); | ||
|
|
||
| } | ||
|
|
||
|
|
||
| //Call the overriden flush | ||
| doFlush(); | ||
|
|
||
| //clean up after flush | ||
| postFlush(); | ||
|
|
||
| } | ||
|
|
||
| protected abstract void doFlush(); | ||
|
|
||
| protected void postFlush(){ | ||
| flushed = true; | ||
| } | ||
|
|
||
| /** | ||
| * The act of drawing. The Batch will check if it's possible to render | ||
| * as well as setup for rendering. If it's possible to render, it will call doRender() | ||
| * | ||
| * | ||
| */ | ||
| protected abstract void doRender(); | ||
|
|
||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#render() | ||
| */ | ||
| public final void render(){ | ||
| checkRender(); | ||
|
|
||
| doRender(); | ||
|
|
||
|
|
||
| } | ||
|
|
||
| protected void checkRender(){ | ||
| if(batching) throw new IllegalStateException("Cannot Render While Batching"); | ||
| if(!flushed) throw new IllegalStateException("Cannon Render Without Flushing the Batch"); | ||
| } | ||
|
|
||
|
|
||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#addVertex(float, float, float, float) | ||
| */ | ||
| public void addVertex(float x, float y, float z, float w){ | ||
| vertexBuffer.add(x); | ||
| vertexBuffer.add(y); | ||
| vertexBuffer.add(z); | ||
| vertexBuffer.add(w); | ||
|
|
||
| numVerticies++; | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#addVertex(float, float, float) | ||
| */ | ||
| public void addVertex(float x, float y, float z){ | ||
| addVertex(x,y,z,1.0f); | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#addVertex(float, float) | ||
| */ | ||
| public void addVertex(float x, float y){ | ||
| addVertex(x,y,0.0f,1.0f); | ||
| } | ||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#addVertex(org.spout.api.math.Vector3) | ||
| */ | ||
| public void addVertex(Vector3 vertex){ | ||
| addVertex(vertex.getX(), vertex.getY(), vertex.getZ()); | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#AddVertex(org.spout.api.math.Vector2) | ||
| */ | ||
| public void AddVertex(Vector2 vertex){ | ||
| addVertex(vertex.getX(), vertex.getY()); | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#AddVertex(org.spout.api.math.Vector4) | ||
| */ | ||
| public void AddVertex(Vector4 vertex){ | ||
| addVertex(vertex.getX(), vertex.getY(), vertex.getZ(), vertex.getZ()); | ||
| } | ||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#addColor(float, float, float) | ||
| */ | ||
| public void addColor(float r, float g, float b){ | ||
| addColor(r,g,b,1.0f); | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#addColor(float, float, float, float) | ||
| */ | ||
| public void addColor(float r, float g, float b, float a){ | ||
| colorBuffer.add(r); | ||
| colorBuffer.add(g); | ||
| colorBuffer.add(b); | ||
| colorBuffer.add(a); | ||
| } | ||
|
|
||
| public void addColor(int r, int g, int b, int a){ | ||
| addColor(r/255.0f, g/255.0f, b/255.0f, a/255.0f); | ||
| } | ||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#addColor(org.spout.api.util.Color) | ||
| */ | ||
| public void addColor(Color color){ | ||
| addColor(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); | ||
| } | ||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#addNormal(float, float, float, float) | ||
| */ | ||
| public void addNormal(float x, float y, float z, float w){ | ||
| normalBuffer.add(x); | ||
| normalBuffer.add(y); | ||
| normalBuffer.add(z); | ||
| normalBuffer.add(w); | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#addNormal(float, float, float) | ||
| */ | ||
| public void addNormal(float x, float y, float z){ | ||
| addNormal(x,y,z,1.0f); | ||
| } | ||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#addNormal(org.spout.api.math.Vector3) | ||
| */ | ||
| public void addNormal(Vector3 vertex){ | ||
| addNormal(vertex.getX(), vertex.getY(), vertex.getZ()); | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#addNormal(org.spout.api.math.Vector4) | ||
| */ | ||
| public void addNormal(Vector4 vertex){ | ||
| addNormal(vertex.getX(), vertex.getY(), vertex.getZ(), vertex.getZ()); | ||
| } | ||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#addTexCoord(float, float) | ||
| */ | ||
| public void addTexCoord(float u, float v){ | ||
| uvBuffer.add(u); | ||
| uvBuffer.add(v); | ||
| } | ||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#addTexCoord(org.spout.api.math.Vector2) | ||
| */ | ||
| public void addTexCoord(Vector2 uv){ | ||
| addTexCoord(uv.getX(), uv.getY()); | ||
| } | ||
|
|
||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#setShader(org.spout.client.renderer.shader.Shader) | ||
| */ | ||
| public void setShader(Shader shader){ | ||
| if(shader == null){ | ||
| try { | ||
| activeShader = new EmptyShader(); | ||
| } catch (FileNotFoundException e) { | ||
| // TODO Auto-generated catch block | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
| else activeShader = shader; | ||
| } | ||
|
|
||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#enableColors() | ||
| */ | ||
| public void enableColors(){ | ||
| useColors = true; | ||
| } | ||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#enableNormals() | ||
| */ | ||
| public void enableNormals(){ | ||
| useNormals = true; | ||
| } | ||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.Renderer#enableTextures() | ||
| */ | ||
| public void enableTextures(){ | ||
| useTextures = true; | ||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,54 @@ | ||
| package org.spout.server.client.renderer; | ||
|
|
||
| import org.lwjgl.opengl.GL11; | ||
| import org.spout.server.client.renderer.shader.BasicShader; | ||
|
|
||
| public class GL11BatchVertexRenderer extends BatchVertexRenderer { | ||
|
|
||
| int displayList; | ||
|
|
||
|
|
||
| public GL11BatchVertexRenderer(int mode){ | ||
| super(mode); | ||
| displayList = GL11.glGenLists(1); | ||
|
|
||
| } | ||
|
|
||
|
|
||
| protected void doFlush(){ | ||
| if(!(activeShader instanceof BasicShader)) throw new IllegalStateException("Need Basic Shader in 1.1 mode"); | ||
|
|
||
|
|
||
|
|
||
| GL11.glNewList(displayList, GL11.GL_COMPILE); | ||
| ((BasicShader)activeShader).assign(true); | ||
| GL11.glPushMatrix(); | ||
| GL11.glBegin(renderMode); | ||
| for(int i = 0; i < numVerticies; i+= 1){ | ||
| int index = i *4; | ||
| if(useColors) GL11.glColor3f(colorBuffer.get(index), colorBuffer.get(index+1), colorBuffer.get(index+2)); | ||
| if(useNormals) GL11.glNormal3f(normalBuffer.get(index), normalBuffer.get(index + 1), normalBuffer.get(index + 2)); | ||
| if(useTextures) GL11.glTexCoord2f(uvBuffer.get((i*2)), uvBuffer.get((i*2) + 1)); | ||
| GL11.glVertex4f(vertexBuffer.get(index), vertexBuffer.get(index+1), vertexBuffer.get(index+2), vertexBuffer.get(index+3)); | ||
| } | ||
| GL11.glEnd(); | ||
| GL11.glPopMatrix(); | ||
| GL11.glEndList(); | ||
|
|
||
| } | ||
|
|
||
|
|
||
|
|
||
| @Override | ||
| public void doRender() { | ||
|
|
||
| GL11.glPushMatrix(); | ||
| GL11.glCallList(displayList); | ||
| GL11.glPopMatrix(); | ||
|
|
||
|
|
||
| } | ||
|
|
||
|
|
||
|
|
||
| } |
| @@ -0,0 +1,114 @@ | ||
| package org.spout.server.client.renderer; | ||
| import java.nio.*; | ||
|
|
||
| import org.lwjgl.BufferUtils; | ||
| import org.lwjgl.opengl.GL11; | ||
| import org.lwjgl.opengl.GL15; | ||
| import org.lwjgl.opengl.GL30; | ||
|
|
||
| public class GL30BatchVertexRenderer extends BatchVertexRenderer { | ||
| final int SIZE_FLOAT = 4; | ||
|
|
||
| int vao; | ||
| int vbos = -1; | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
| /** | ||
| * Batch Renderer using OpenGL 3.0 mode. | ||
| * | ||
| * @param renderMode | ||
| * Mode to render in | ||
| */ | ||
| public GL30BatchVertexRenderer(int renderMode){ | ||
| super(renderMode); | ||
| GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY); | ||
| vao = GL30.glGenVertexArrays(); | ||
| GL30.glBindVertexArray(vao); | ||
| GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY); | ||
|
|
||
|
|
||
| } | ||
|
|
||
|
|
||
|
|
||
| protected void doFlush(){ | ||
| if(activeShader == null) throw new IllegalStateException("Batch must have a shader attached"); | ||
| if(vbos != -1) GL15.glDeleteBuffers(vbos); | ||
|
|
||
| GL30.glBindVertexArray(vao); | ||
| int size = numVerticies * 4 * SIZE_FLOAT; | ||
| if(useColors) size += numVerticies * 4 * SIZE_FLOAT; | ||
| if(useNormals) size += numVerticies * 4 * SIZE_FLOAT; | ||
| if(useTextures) size += numVerticies * 2 * SIZE_FLOAT; | ||
|
|
||
| vbos = GL15.glGenBuffers(); | ||
|
|
||
| int offset = 0; | ||
| GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbos); | ||
| GL15.glBufferData(GL15.GL_ARRAY_BUFFER, size, GL15.GL_STATIC_DRAW); | ||
|
|
||
| FloatBuffer vBuffer = BufferUtils.createFloatBuffer(vertexBuffer.size()); | ||
| vBuffer.clear(); | ||
| vBuffer.put(vertexBuffer.toArray()); | ||
| vBuffer.flip(); | ||
| //GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vBuffer, GL15.GL_STATIC_DRAW); | ||
| GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, offset, vBuffer); | ||
| activeShader.enableAttribute("vPosition", 4, GL11.GL_FLOAT,0, offset); | ||
| offset += numVerticies * 4 * SIZE_FLOAT; | ||
| if(useColors){ | ||
|
|
||
| vBuffer = BufferUtils.createFloatBuffer(colorBuffer.size()); | ||
| vBuffer.clear(); | ||
| vBuffer.put(colorBuffer.toArray()); | ||
| vBuffer.flip(); | ||
| GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, offset, vBuffer); | ||
|
|
||
| activeShader.enableAttribute("vColor", 4, GL11.GL_FLOAT,0,offset); | ||
| offset += numVerticies * 4 * SIZE_FLOAT; | ||
| } | ||
| if(useNormals){ | ||
|
|
||
| vBuffer = BufferUtils.createFloatBuffer(normalBuffer.size()); | ||
| vBuffer.clear(); | ||
| vBuffer.put(normalBuffer.toArray()); | ||
| vBuffer.flip(); | ||
| GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, offset, vBuffer); | ||
|
|
||
| activeShader.enableAttribute("vNormal", 4, GL11.GL_FLOAT,0, offset); | ||
| offset += numVerticies * 4 * SIZE_FLOAT; | ||
| } | ||
| if(useTextures){ | ||
|
|
||
| vBuffer = BufferUtils.createFloatBuffer(uvBuffer.size()); | ||
| vBuffer.clear(); | ||
| vBuffer.put(uvBuffer.toArray()); | ||
| vBuffer.flip(); | ||
| GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, offset, vBuffer); | ||
|
|
||
| activeShader.enableAttribute("vTexCoord", 2, GL11.GL_FLOAT,0, offset); | ||
| offset += numVerticies * 2 * SIZE_FLOAT; | ||
| } | ||
|
|
||
|
|
||
|
|
||
| activeShader.assign(); | ||
| } | ||
|
|
||
| /** | ||
| * Draws this batch | ||
| */ | ||
| public void doRender(){ | ||
| GL30.glBindVertexArray(vao); | ||
| GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbos); | ||
|
|
||
| activeShader.assign(); | ||
| GL11.glDrawArrays(renderMode, 0, numVerticies); | ||
|
|
||
|
|
||
| } | ||
|
|
||
|
|
||
| } |
| @@ -0,0 +1,21 @@ | ||
| package org.spout.server.client.renderer; | ||
|
|
||
|
|
||
|
|
||
| public class GLES20BatchVertexRenderer extends BatchVertexRenderer { | ||
|
|
||
| public GLES20BatchVertexRenderer(int mode) { | ||
| super(mode); | ||
| } | ||
|
|
||
| @Override | ||
| protected void doFlush() { | ||
|
|
||
| } | ||
|
|
||
| @Override | ||
| protected void doRender() { | ||
|
|
||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,41 @@ | ||
| package org.spout.server.client.renderer; | ||
|
|
||
| import java.awt.geom.Rectangle2D; | ||
|
|
||
| import org.lwjgl.opengl.GL11; | ||
| import org.spout.api.render.Renderer; | ||
| import org.spout.server.client.texture.Texture; | ||
|
|
||
|
|
||
| public class SpriteBatch { | ||
|
|
||
| Renderer renderer; | ||
|
|
||
| boolean isBatching = false; | ||
|
|
||
| public SpriteBatch(){ | ||
| renderer = BatchVertexRenderer.constructNewBatch(GL11.GL_TRIANGLES); | ||
| } | ||
|
|
||
|
|
||
| public void begin(){ | ||
| if(isBatching) throw new IllegalStateException("Already Batching!"); | ||
| renderer.begin(); | ||
| } | ||
|
|
||
| public void end(){ | ||
| if(!isBatching) throw new IllegalStateException("Cannot end batching without a begin"); | ||
| renderer.end(); | ||
| renderer.render(); | ||
| } | ||
|
|
||
| public void drawSprite(Rectangle2D.Float rect, Texture texture, int color){ | ||
| if(!isBatching) throw new IllegalStateException("Cannot Draw without Begin!"); | ||
|
|
||
|
|
||
| } | ||
|
|
||
|
|
||
|
|
||
|
|
||
| } |
| @@ -0,0 +1,60 @@ | ||
| package org.spout.server.client.renderer.shader; | ||
|
|
||
|
|
||
| import java.nio.FloatBuffer; | ||
|
|
||
| import org.spout.api.math.Matrix; | ||
| import org.lwjgl.BufferUtils; | ||
| import org.lwjgl.opengl.GL11; | ||
| import org.spout.server.client.renderer.shader.variables.Mat4ShaderVariable; | ||
|
|
||
| public class BasicShader extends ClientShader { | ||
| FloatBuffer matrixBuffer = BufferUtils.createFloatBuffer(4*4); | ||
|
|
||
| public BasicShader() { | ||
| super(null, null); | ||
|
|
||
| } | ||
|
|
||
| public void assign(boolean compatabilityMode){ | ||
| if(!variables.containsKey("Projection"))throw new IllegalStateException("Basic Shader must have a projection matrix assigned"); | ||
| if(!variables.containsKey("View")) throw new IllegalStateException("Basic Shader must have a view matrix assigned"); | ||
|
|
||
| if(compatabilityMode){ | ||
| GL11.glMatrixMode(GL11.GL_PROJECTION); | ||
| matrixBuffer.clear(); | ||
| matrixBuffer.put(getProjectionMatrix().toArray()); | ||
| matrixBuffer.flip(); | ||
|
|
||
| GL11.glLoadMatrix(matrixBuffer); | ||
|
|
||
| GL11.glMatrixMode(GL11.GL_MODELVIEW); | ||
| matrixBuffer.clear(); | ||
| matrixBuffer.put(getViewMatrix().toArray()); | ||
| matrixBuffer.flip(); | ||
|
|
||
| GL11.glLoadMatrix(matrixBuffer); | ||
|
|
||
| }else{ | ||
| super.assign(); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| public void setViewMatrix(Matrix mat){ | ||
| setUniform("View", mat); | ||
| } | ||
|
|
||
| public Matrix getViewMatrix() { | ||
| return ((Mat4ShaderVariable)variables.get("View")).get(); | ||
|
|
||
| } | ||
| public Matrix getProjectionMatrix() { | ||
| return ((Mat4ShaderVariable)variables.get("Projection")).get(); | ||
| } | ||
|
|
||
|
|
||
| public void setProjectionMatrix(Matrix mat){ | ||
| setUniform("Projection", mat); | ||
| } | ||
| } |
| @@ -0,0 +1,253 @@ | ||
| package org.spout.server.client.renderer.shader; | ||
|
|
||
| import java.awt.Color; | ||
| import java.io.*; | ||
| import java.util.HashMap; | ||
| import java.util.Scanner; | ||
|
|
||
| import org.lwjgl.opengl.GL11; | ||
| import org.lwjgl.opengl.GL20; | ||
| import org.spout.server.client.renderer.BatchModes; | ||
| import org.spout.server.client.renderer.BatchVertexRenderer; | ||
| import org.spout.server.client.texture.Texture; | ||
| import org.spout.server.client.renderer.shader.variables.AttributeShaderVariable; | ||
| import org.spout.server.client.renderer.shader.variables.ColorShaderVariable; | ||
| import org.spout.server.client.renderer.shader.variables.FloatShaderVariable; | ||
| import org.spout.server.client.renderer.shader.variables.IntShaderVariable; | ||
| import org.spout.server.client.renderer.shader.variables.Mat2ShaderVariable; | ||
| import org.spout.server.client.renderer.shader.variables.Mat3ShaderVariable; | ||
| import org.spout.server.client.renderer.shader.variables.Mat4ShaderVariable; | ||
| import org.spout.server.client.renderer.shader.variables.ShaderVariable; | ||
| import org.spout.server.client.renderer.shader.variables.TextureSamplerShaderVariable; | ||
| import org.spout.server.client.renderer.shader.variables.Vec2ShaderVariable; | ||
| import org.spout.server.client.renderer.shader.variables.Vec3ShaderVariable; | ||
| import org.spout.server.client.renderer.shader.variables.Vec4ShaderVariable; | ||
| import org.spout.api.math.*; | ||
| import org.spout.api.render.Shader; | ||
|
|
||
|
|
||
|
|
||
|
|
||
| /** | ||
| * Represents a Shader Object in OpenGL | ||
| * @author RoyAwesome | ||
| * | ||
| */ | ||
| public class ClientShader implements Shader { | ||
| int program; | ||
| int textures = 0; | ||
|
|
||
| HashMap<String, ShaderVariable> variables = new HashMap<String, ShaderVariable>(); | ||
|
|
||
| public static boolean validateShader = true; | ||
|
|
||
|
|
||
|
|
||
| public ClientShader(String vertexShader, String fragmentShader){ | ||
| if(BatchVertexRenderer.GLMode == BatchModes.GL11) return; | ||
| System.out.println("Compiling "+ vertexShader + " and " + fragmentShader); | ||
| //Create a new Shader object on the GPU | ||
| program = GL20.glCreateProgram(); | ||
|
|
||
| //Compile the vertex shader | ||
| String vshader; | ||
| if(vertexShader == null) vshader = fallbackVertexShader; | ||
| else{ | ||
| try{ | ||
| vshader = readShaderSource(vertexShader); | ||
| } | ||
| catch(FileNotFoundException e){ | ||
| System.out.println("Vertex Shader: "+ vertexShader + " Not found, using fallback"); | ||
| vshader = fallbackVertexShader; | ||
| } | ||
| } | ||
| int vShader = compileShader(vshader , GL20.GL_VERTEX_SHADER); | ||
| GL20.glAttachShader(program, vShader); | ||
| String fshader; | ||
| if(fragmentShader == null) fshader = fallbackFragmentShader; | ||
| else{ | ||
| try{ | ||
| fshader = readShaderSource(fragmentShader); | ||
| } | ||
| catch(FileNotFoundException e){ | ||
| System.out.println("Fragment Shader: "+ fragmentShader + " Not found, using fallback"); | ||
| fshader = fallbackFragmentShader; | ||
| } | ||
| } | ||
|
|
||
| int fShader = compileShader(fshader, GL20.GL_FRAGMENT_SHADER); | ||
| GL20.glAttachShader(program, fShader); | ||
|
|
||
| GL20.glLinkProgram(program); | ||
|
|
||
| int status = GL20.glGetProgram(program, GL20.GL_LINK_STATUS); | ||
| if(status != GL11.GL_TRUE){ | ||
| String error = GL20.glGetProgramInfoLog(program, 255); | ||
| throw new ShaderCompileException("Link Error: "+error); | ||
| } | ||
| if(validateShader){ | ||
| GL20.glValidateProgram(this.program); | ||
| if(GL20.glGetProgram(program, GL20.GL_VALIDATE_STATUS) != GL11.GL_TRUE){ | ||
| String info = GL20.glGetProgramInfoLog(program, 255); | ||
| System.out.println("Validate Log: \n"+info); | ||
| } | ||
|
|
||
| System.out.println("Attached Shaders: "+GL20.glGetProgram(program, GL20.GL_ATTACHED_SHADERS)); | ||
| int activeAttributes = GL20.glGetProgram(program, GL20.GL_ACTIVE_ATTRIBUTES); | ||
| System.out.println("Active Attributes: "+ activeAttributes); | ||
| int maxAttributeLength = GL20.glGetProgram(program, GL20.GL_ACTIVE_ATTRIBUTE_MAX_LENGTH); | ||
| for(int i = 0; i< activeAttributes; i++){ | ||
| System.out.println("\t"+GL20.glGetActiveAttrib(program, i, maxAttributeLength)); | ||
| } | ||
|
|
||
| int activeUniforms = GL20.glGetProgram(program, GL20.GL_ACTIVE_UNIFORMS); | ||
| System.out.println("Active Uniforms: "+ activeUniforms); | ||
| int maxUniformLength = GL20.glGetProgram(program, GL20.GL_ACTIVE_UNIFORM_MAX_LENGTH); | ||
| for(int i = 0; i< activeUniforms; i++){ | ||
| System.out.println("\t"+GL20.glGetActiveUniform(program, i, maxUniformLength)); | ||
| } | ||
|
|
||
| } | ||
| System.out.println("Compiled Shader with id: "+ program); | ||
|
|
||
| } | ||
|
|
||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.shader.IShader#setUniform(java.lang.String, int) | ||
| */ | ||
| public void setUniform(String name, int value){ | ||
| variables.put(name, new IntShaderVariable(program, name, value)); | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.shader.IShader#setUniform(java.lang.String, float) | ||
| */ | ||
| public void setUniform(String name, float value){ | ||
| variables.put(name, new FloatShaderVariable(program, name, value)); | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.shader.IShader#setUniform(java.lang.String, org.spout.api.math.Vector2) | ||
| */ | ||
| public void setUniform(String name, Vector2 value){ | ||
| variables.put(name, new Vec2ShaderVariable(program, name, value)); | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.shader.IShader#setUniform(java.lang.String, org.spout.api.math.Vector3) | ||
| */ | ||
| public void setUniform(String name, Vector3 value){ | ||
| variables.put(name, new Vec3ShaderVariable(program, name, value)); | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.shader.IShader#setUniform(java.lang.String, org.spout.api.math.Vector4) | ||
| */ | ||
| public void setUniform(String name, Vector4 value){ | ||
| variables.put(name, new Vec4ShaderVariable(program, name, value)); | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.shader.IShader#setUniform(java.lang.String, org.spout.api.math.Matrix) | ||
| */ | ||
| public void setUniform(String name, Matrix value){ | ||
| if(value.getDimension() == 2){ | ||
| variables.put(name, new Mat2ShaderVariable(program, name, value)); | ||
| }else if(value.getDimension() == 3){ | ||
| variables.put(name, new Mat3ShaderVariable(program, name, value)); | ||
| }else if(value.getDimension() == 4){ | ||
| variables.put(name, new Mat4ShaderVariable(program, name, value)); | ||
| } | ||
|
|
||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.shader.IShader#setUniform(java.lang.String, org.spout.api.util.Color) | ||
| */ | ||
| public void setUniform(String name, Color value){ | ||
| variables.put(name, new ColorShaderVariable(program, name, value)); | ||
| } | ||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.shader.IShader#setUniform(java.lang.String, org.newdawn.slick.opengl.Texture) | ||
| */ | ||
| public void setUniform(String name, Texture value){ | ||
| if(variables.containsKey(name)){ | ||
| ShaderVariable texture = variables.get(name); | ||
| if(!(texture instanceof TextureSamplerShaderVariable)) throw new IllegalStateException(name + " is not a texture!"); | ||
| TextureSamplerShaderVariable t = (TextureSamplerShaderVariable)texture; | ||
| t.set(value); | ||
| }else{ | ||
| textures++; | ||
| variables.put(name, new TextureSamplerShaderVariable(program, name, value, textures)); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.shader.IShader#enableAttribute(java.lang.String, int, int, int, long) | ||
| */ | ||
| public void enableAttribute(String name, int size, int type, int stride, long offset){ | ||
| variables.put(name, new AttributeShaderVariable(program, name, size, type,stride, offset)); | ||
| } | ||
|
|
||
| /* (non-Javadoc) | ||
| * @see org.spout.client.renderer.shader.IShader#assign() | ||
| */ | ||
| public void assign(){ | ||
| GL20.glUseProgram(program); | ||
| for(ShaderVariable v : variables.values()){ | ||
| v.assign(); | ||
| } | ||
| } | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
| private int compileShader(String source, int type){ | ||
|
|
||
|
|
||
| int shader = GL20.glCreateShader(type); | ||
| GL20.glShaderSource(shader, source); | ||
| GL20.glCompileShader(shader); | ||
| int status = GL20.glGetShader(shader, GL20.GL_COMPILE_STATUS); | ||
| if(status != GL11.GL_TRUE){ | ||
| String error = GL20.glGetShaderInfoLog(shader, 255); | ||
| throw new ShaderCompileException("Compile Error in " + ((type == GL20.GL_FRAGMENT_SHADER)? "Fragment Shader": "VertexShader") + ": "+error); | ||
| } | ||
| return shader; | ||
| } | ||
|
|
||
|
|
||
| private String readShaderSource(String file) throws FileNotFoundException{ | ||
|
|
||
| FileInputStream in = new FileInputStream(file); | ||
| Scanner scan = new Scanner(in); | ||
|
|
||
| StringBuilder src = new StringBuilder(); | ||
|
|
||
| while(scan.hasNextLine()){ | ||
| src.append(scan.nextLine() + "\n"); | ||
| } | ||
|
|
||
| return src.toString(); | ||
|
|
||
| } | ||
|
|
||
| String fallbackVertexShader = "attribute vec4 vPosition;\n" + | ||
| "attribute vec4 vColor;\n" + | ||
| "attribute vec2 vTexCoord; \n"+ | ||
| "varying vec4 color;\n" + | ||
| "varying vec2 uvcoord; \n" + | ||
| "uniform mat4 Projection; \n"+ | ||
| "uniform mat4 View; \n" + | ||
| "void main() \n" + | ||
| "{\n gl_Position = Projection * View * vPosition; \n" + | ||
| " uvcoord = vTexCoord; \n" + | ||
| "color = vColor; \n"+ | ||
| "} \n"; | ||
|
|
||
| String fallbackFragmentShader = "varying vec4 color; //in \n" + | ||
| "varying vec2 uvcoord; \n" + | ||
| "uniform sampler2D texture; \n" + | ||
| "void main()\n{\n" + | ||
| "gl_FragColor = color; \n} \n"; | ||
|
|
||
|
|
||
| } |
| @@ -0,0 +1,22 @@ | ||
| package org.spout.server.client.renderer.shader; | ||
|
|
||
| import java.io.FileNotFoundException; | ||
|
|
||
|
|
||
| /** | ||
| * Empty Shader for 1.1 only. Do not use this for 3.0 ever. | ||
| * @author RoyAwesome | ||
| * | ||
| */ | ||
| public class EmptyShader extends ClientShader { | ||
|
|
||
| public EmptyShader() | ||
| throws FileNotFoundException { | ||
| super(null, null); | ||
|
|
||
| } | ||
|
|
||
| public void assign(){ | ||
| return; | ||
| } | ||
| } |
| @@ -0,0 +1,9 @@ | ||
| package org.spout.server.client.renderer.shader; | ||
|
|
||
| public class ShaderCompileException extends RuntimeException { | ||
| private static final long serialVersionUID = 1L; | ||
|
|
||
| public ShaderCompileException(String text){ | ||
| super(text); | ||
| } | ||
| } |
| @@ -0,0 +1,8 @@ | ||
| package org.spout.server.client.renderer.shader; | ||
|
|
||
| @SuppressWarnings("serial") | ||
| public class ShaderVariableNotFoundException extends RuntimeException { | ||
| public ShaderVariableNotFoundException(String variableName){ | ||
| super("Variable: " + variableName + " Not Found (was it optimized out?"); | ||
| } | ||
| } |
| @@ -0,0 +1,31 @@ | ||
| package org.spout.server.client.renderer.shader.variables; | ||
|
|
||
|
|
||
| import org.lwjgl.opengl.GL20; | ||
|
|
||
|
|
||
| public class AttributeShaderVariable extends ShaderVariable { | ||
|
|
||
| int size; | ||
| int type; | ||
| long offset; | ||
|
|
||
| public AttributeShaderVariable(int program, String name, int size, int type, int stride, long offset) { | ||
| super(program, name); | ||
| this.location = GL20.glGetAttribLocation(program, name); | ||
|
|
||
| this.size = size; | ||
| this.type = type; | ||
| this.offset = offset; | ||
|
|
||
|
|
||
| } | ||
|
|
||
| @Override | ||
| public void assign() { | ||
| GL20.glEnableVertexAttribArray(location); | ||
| GL20.glVertexAttribPointer(location, size, type, false, 0, offset); | ||
|
|
||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,22 @@ | ||
| package org.spout.server.client.renderer.shader.variables; | ||
|
|
||
| import java.awt.Color; | ||
|
|
||
| import org.lwjgl.opengl.GL20; | ||
|
|
||
| public class ColorShaderVariable extends ShaderVariable { | ||
|
|
||
| Color value; | ||
|
|
||
| public ColorShaderVariable(int program, String name, Color value) { | ||
| super(program, name); | ||
| this.value = value; | ||
| } | ||
|
|
||
| @Override | ||
| public void assign() { | ||
| GL20.glUniform4f(this.location, value.getRed() / 255.0f, value.getGreen() / 255.0f, value.getBlue() / 255.0f, value.getAlpha() / 255.0f); | ||
|
|
||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,19 @@ | ||
| package org.spout.server.client.renderer.shader.variables; | ||
|
|
||
| import org.lwjgl.opengl.GL20; | ||
|
|
||
| public class FloatShaderVariable extends ShaderVariable { | ||
| float value; | ||
|
|
||
| public FloatShaderVariable(int program, String name, float value) { | ||
| super(program, name); | ||
| this.value = value; | ||
| } | ||
|
|
||
| @Override | ||
| public void assign() { | ||
| GL20.glUniform1f(location, value); | ||
|
|
||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,18 @@ | ||
| package org.spout.server.client.renderer.shader.variables; | ||
|
|
||
| import org.lwjgl.opengl.GL20; | ||
|
|
||
| public class IntShaderVariable extends ShaderVariable { | ||
| int value; | ||
|
|
||
| public IntShaderVariable(int program, String name, int value) { | ||
| super(program, name); | ||
| this.value = value; | ||
| } | ||
|
|
||
| @Override | ||
| public void assign() { | ||
| GL20.glUniform1i(location, value); | ||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,25 @@ | ||
| package org.spout.server.client.renderer.shader.variables; | ||
|
|
||
| import java.nio.FloatBuffer; | ||
|
|
||
| import org.spout.api.math.Matrix; | ||
| import org.lwjgl.opengl.GL20; | ||
|
|
||
| public class Mat2ShaderVariable extends ShaderVariable { | ||
| Matrix value; | ||
|
|
||
| public Mat2ShaderVariable(int program, String name, Matrix value) { | ||
| super(program, name); | ||
| this.value = value; | ||
| } | ||
|
|
||
| @Override | ||
| public void assign() { | ||
| FloatBuffer buff = FloatBuffer.allocate(2); | ||
| buff.put(value.toArray()); | ||
| buff.flip(); | ||
|
|
||
| GL20.glUniformMatrix2(location, false, buff); | ||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,26 @@ | ||
| package org.spout.server.client.renderer.shader.variables; | ||
| import java.nio.FloatBuffer; | ||
|
|
||
| import org.spout.api.math.Matrix; | ||
| import org.lwjgl.opengl.GL20; | ||
|
|
||
|
|
||
| public class Mat3ShaderVariable extends ShaderVariable { | ||
|
|
||
| Matrix value; | ||
| public Mat3ShaderVariable(int program, String name, Matrix value) { | ||
| super(program, name); | ||
| this.value = value; | ||
| } | ||
|
|
||
| @Override | ||
| public void assign() { | ||
| FloatBuffer buff = FloatBuffer.allocate(3*3); | ||
| buff.put(value.toArray()); | ||
| buff.flip(); | ||
|
|
||
| GL20.glUniformMatrix3(location, false, buff); | ||
|
|
||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,32 @@ | ||
| package org.spout.server.client.renderer.shader.variables; | ||
| import java.nio.FloatBuffer; | ||
|
|
||
| import org.spout.api.math.Matrix; | ||
| import org.lwjgl.BufferUtils; | ||
| import org.lwjgl.opengl.GL20; | ||
|
|
||
|
|
||
| public class Mat4ShaderVariable extends ShaderVariable { | ||
|
|
||
| Matrix value; | ||
| public Mat4ShaderVariable(int program, String name, Matrix value) { | ||
| super(program, name); | ||
| this.value = value; | ||
|
|
||
| } | ||
|
|
||
| public Matrix get(){ | ||
| return value; | ||
| } | ||
|
|
||
| @Override | ||
| public void assign() { | ||
| FloatBuffer buff = BufferUtils.createFloatBuffer(4*4); | ||
| buff.put(value.toArray()); | ||
| buff.flip(); | ||
|
|
||
| GL20.glUniformMatrix4(location, false, buff); | ||
|
|
||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,36 @@ | ||
| package org.spout.server.client.renderer.shader.variables; | ||
|
|
||
| import org.lwjgl.opengl.GL20; | ||
| import org.spout.server.client.renderer.BatchModes; | ||
| import org.spout.server.client.renderer.BatchVertexRenderer; | ||
| import org.spout.server.client.renderer.shader.ShaderVariableNotFoundException; | ||
|
|
||
| public abstract class ShaderVariable { | ||
| public static final boolean variableError = false; | ||
|
|
||
| int program; | ||
| int location; | ||
| @SuppressWarnings("unused") | ||
| public ShaderVariable(int program, String name){ | ||
| if(BatchVertexRenderer.GLMode == BatchModes.GL11) return; //Shaders don't exist in OpenGL 1.1 | ||
| this.program = program; | ||
| GL20.glUseProgram(program); | ||
| //If we are an attribute, we aren't a uniform. Don't continue | ||
| if(this instanceof AttributeShaderVariable) return; | ||
|
|
||
| this.location = GL20.glGetUniformLocation(program, name); | ||
|
|
||
| //Error Checking. In production, leave this as a warning, because OpenGL doesn't care if you try to put something | ||
| //into a variable that doesn't exist (it ignores it). | ||
| // | ||
| //If we want to have a debug mode, switch the final bool to true to throw an exception if the variable doesn't exist. | ||
| //This is the same as treating warnings as errors, and could be useful for debugging shaders. | ||
| if(this.location == -1 && !variableError){ | ||
| System.out.println("[Warning] Shader Variable: "+ name + " not found! (Was it optimized out?)"); | ||
| }else if(this.location == -1 && variableError){ | ||
| throw new ShaderVariableNotFoundException(name); | ||
| } | ||
| } | ||
| public abstract void assign(); | ||
|
|
||
| } |
| @@ -0,0 +1,30 @@ | ||
| package org.spout.server.client.renderer.shader.variables; | ||
|
|
||
| import org.lwjgl.opengl.GL11; | ||
| import org.lwjgl.opengl.GL13; | ||
| import org.lwjgl.opengl.GL30; | ||
| import org.spout.server.client.texture.Texture; | ||
|
|
||
| public class TextureSamplerShaderVariable extends ShaderVariable { | ||
| int textureID; | ||
| int textureNumber; | ||
|
|
||
|
|
||
| public TextureSamplerShaderVariable(int program, String name, Texture texture, int bindNum) { | ||
| super(program, name); | ||
| textureID = texture.getTextureID(); | ||
| this.textureNumber = bindNum; | ||
| } | ||
|
|
||
| public void set(Texture texture){ | ||
| textureID = texture.getTextureID(); | ||
| } | ||
|
|
||
| @Override | ||
| public void assign() { | ||
| GL13.glActiveTexture(GL13.GL_TEXTURE0 + textureNumber); | ||
| GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID); | ||
| GL30.glUniform1ui(location, textureID); | ||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,21 @@ | ||
| package org.spout.server.client.renderer.shader.variables; | ||
|
|
||
| import org.spout.api.math.Vector2; | ||
| import org.lwjgl.opengl.GL20; | ||
|
|
||
| public class Vec2ShaderVariable extends ShaderVariable { | ||
| Vector2 value; | ||
| public Vec2ShaderVariable(int program, String name, Vector2 value){ | ||
| super(program, name); | ||
| this.value = value; | ||
| } | ||
|
|
||
| @Override | ||
| public void assign() { | ||
| GL20.glUniform2f(location, value.getX(), value.getY()); | ||
|
|
||
| } | ||
|
|
||
|
|
||
|
|
||
| } |
| @@ -0,0 +1,19 @@ | ||
| package org.spout.server.client.renderer.shader.variables; | ||
|
|
||
| import org.spout.api.math.Vector3; | ||
| import org.lwjgl.opengl.GL20; | ||
|
|
||
| public class Vec3ShaderVariable extends ShaderVariable { | ||
| Vector3 value; | ||
| public Vec3ShaderVariable(int program, String name, Vector3 value) { | ||
| super(program, name); | ||
| this.value = value; | ||
| } | ||
|
|
||
| @Override | ||
| public void assign() { | ||
| GL20.glUniform3f(location, value.getX(), value.getY(), value.getZ()); | ||
|
|
||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,17 @@ | ||
| package org.spout.server.client.renderer.shader.variables; | ||
|
|
||
| import org.spout.api.math.Vector4; | ||
| import org.lwjgl.opengl.GL20; | ||
| public class Vec4ShaderVariable extends ShaderVariable { | ||
| Vector4 value; | ||
| public Vec4ShaderVariable(int program, String name, Vector4 value) { | ||
| super(program, name); | ||
| this.value = value; | ||
| } | ||
|
|
||
| @Override | ||
| public void assign() { | ||
| GL20.glUniform4f(location, value.getX(), value.getY(), value.getZ(), value.getW()); | ||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,34 @@ | ||
| package org.spout.server.client.renderer.vertexformat; | ||
|
|
||
| import java.awt.Color; | ||
|
|
||
| import org.spout.api.math.Vector3; | ||
|
|
||
|
|
||
| public class PositionColor extends VertexFormat { | ||
|
|
||
| Vector3 position; | ||
| Color color; | ||
|
|
||
| public PositionColor(Vector3 position, Color c){ | ||
| this.position = position; | ||
| this.color = c; | ||
| } | ||
|
|
||
| public Vector3 getPosition() { | ||
| return position; | ||
| } | ||
|
|
||
| public Color getColor() { | ||
| return color; | ||
| } | ||
|
|
||
| public void setPosition(Vector3 position) { | ||
| this.position = position; | ||
| } | ||
|
|
||
| public void setColor(Color color) { | ||
| this.color = color; | ||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,43 @@ | ||
| package org.spout.server.client.renderer.vertexformat; | ||
|
|
||
| import org.spout.api.math.Vector2; | ||
| import org.spout.api.math.Vector3; | ||
|
|
||
| public class PositionNormalTexture extends VertexFormat { | ||
| Vector3 position; | ||
| Vector3 normal; | ||
| Vector2 texture; | ||
|
|
||
| public PositionNormalTexture(Vector3 position, Vector3 normal, Vector2 uv){ | ||
| this.position = position; | ||
| this.normal = normal; | ||
| this.texture = uv; | ||
| } | ||
|
|
||
| public Vector3 getPosition() { | ||
| return position; | ||
| } | ||
|
|
||
| public Vector3 getNormal() { | ||
| return normal; | ||
| } | ||
|
|
||
| public Vector2 getTexture() { | ||
| return texture; | ||
| } | ||
|
|
||
| public void setPosition(Vector3 position) { | ||
| this.position = position; | ||
| } | ||
|
|
||
| public void setNormal(Vector3 normal) { | ||
| this.normal = normal; | ||
| } | ||
|
|
||
| public void setTexture(Vector2 texture) { | ||
| this.texture = texture; | ||
| } | ||
|
|
||
|
|
||
|
|
||
| } |
| @@ -0,0 +1,16 @@ | ||
| package org.spout.server.client.renderer.vertexformat; | ||
|
|
||
| import java.awt.Color; | ||
|
|
||
| import org.spout.api.math.Vector2; | ||
| import org.spout.api.math.Vector3; | ||
|
|
||
|
|
||
| public class VertexFormat { | ||
| Vector3 position; | ||
| Vector3 normal; | ||
| Color color; | ||
| Vector2 texture; | ||
|
|
||
|
|
||
| } |
| @@ -0,0 +1,8 @@ | ||
| package org.spout.server.client.renderer.vertexformat.vertexattributes; | ||
|
|
||
| public enum VertexAttributes { | ||
| Position, | ||
| Color, | ||
| Normal, | ||
| Texture, | ||
| } |
| @@ -0,0 +1,42 @@ | ||
| package org.spout.server.client.texture; | ||
| import java.io.IOException; | ||
| import java.util.ArrayList; | ||
|
|
||
| /** | ||
| * A collection of IOException that failed image data loading | ||
| * | ||
| * @author kevin | ||
| */ | ||
| @SuppressWarnings("serial") | ||
| public class CompositeIOException extends IOException { | ||
| /** The list of exceptions causing this one */ | ||
| private ArrayList<Exception> exceptions = new ArrayList<Exception>(); | ||
|
|
||
| /** | ||
| * Create a new composite IO Exception | ||
| */ | ||
| public CompositeIOException() { | ||
| super(); | ||
| } | ||
|
|
||
| /** | ||
| * Add an exception that caused this exceptino | ||
| * | ||
| * @param e The exception | ||
| */ | ||
| public void addException(Exception e) { | ||
| exceptions.add(e); | ||
| } | ||
|
|
||
| /** | ||
| * @see java.lang.Throwable#getMessage() | ||
| */ | ||
| public String getMessage() { | ||
| String msg = "Composite Exception: \n"; | ||
| for (int i=0;i<exceptions.size();i++) { | ||
| msg += "\t"+((IOException) exceptions.get(i)).getMessage()+"\n"; | ||
| } | ||
|
|
||
| return msg; | ||
| } | ||
| } |
| @@ -0,0 +1,151 @@ | ||
| package org.spout.server.client.texture; | ||
|
|
||
| import java.io.BufferedInputStream; | ||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.nio.ByteBuffer; | ||
| import java.util.ArrayList; | ||
|
|
||
| //import org.newdawn.slick.util.Log; | ||
|
|
||
| /** | ||
| * A composite data source that checks multiple loaders in order of | ||
| * preference | ||
| * | ||
| * @author kevin | ||
| */ | ||
| public class CompositeImageData implements LoadableImageData { | ||
| /** The list of images sources in order of preference to try loading the data with */ | ||
| private ArrayList<LoadableImageData> sources = new ArrayList<LoadableImageData>(); | ||
| /** The data source that worked and was used - or null if no luck */ | ||
| private LoadableImageData picked; | ||
|
|
||
| /** | ||
| * Add a potentional source of image data | ||
| * | ||
| * @param data The data source to try | ||
| */ | ||
| public void add(LoadableImageData data) { | ||
| sources.add(data); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.LoadableImageData#loadImage(java.io.InputStream) | ||
| */ | ||
| public ByteBuffer loadImage(InputStream fis) throws IOException { | ||
| return loadImage(fis, false, null); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.LoadableImageData#loadImage(java.io.InputStream, boolean, int[]) | ||
| */ | ||
| public ByteBuffer loadImage(InputStream fis, boolean flipped, int[] transparent) throws IOException { | ||
| return loadImage(fis, flipped, false, transparent); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.LoadableImageData#loadImage(java.io.InputStream, boolean, boolean, int[]) | ||
| */ | ||
| public ByteBuffer loadImage(InputStream is, boolean flipped, boolean forceAlpha, int[] transparent) throws IOException { | ||
| CompositeIOException exception = new CompositeIOException(); | ||
| ByteBuffer buffer = null; | ||
|
|
||
| BufferedInputStream in = new BufferedInputStream(is, is.available()); | ||
| in.mark(is.available()); | ||
|
|
||
| // cycle through our source until one of them works | ||
| for (int i=0;i<sources.size();i++) { | ||
| in.reset(); | ||
| try { | ||
| LoadableImageData data = (LoadableImageData) sources.get(i); | ||
|
|
||
| buffer = data.loadImage(in, flipped, forceAlpha, transparent); | ||
| picked = data; | ||
| break; | ||
| } catch (Exception e) { | ||
| //Log.warn(sources.get(i).getClass()+" failed to read the data", e); | ||
| exception.addException(e); | ||
| } | ||
| } | ||
|
|
||
| if (picked == null) { | ||
| throw exception; | ||
| } | ||
|
|
||
| return buffer; | ||
| } | ||
|
|
||
| /** | ||
| * Check the state of the image data and throw a | ||
| * runtime exception if theres a problem | ||
| */ | ||
| private void checkPicked() { | ||
| if (picked == null) { | ||
| throw new RuntimeException("Attempt to make use of uninitialised or invalid composite image data"); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getDepth() | ||
| */ | ||
| public int getDepth() { | ||
| checkPicked(); | ||
|
|
||
| return picked.getDepth(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getHeight() | ||
| */ | ||
| public int getHeight() { | ||
| checkPicked(); | ||
|
|
||
| return picked.getHeight(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getImageBufferData() | ||
| */ | ||
| public ByteBuffer getImageBufferData() { | ||
| checkPicked(); | ||
|
|
||
| return picked.getImageBufferData(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getTexHeight() | ||
| */ | ||
| public int getTexHeight() { | ||
| checkPicked(); | ||
|
|
||
| return picked.getTexHeight(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getTexWidth() | ||
| */ | ||
| public int getTexWidth() { | ||
| checkPicked(); | ||
|
|
||
| return picked.getTexWidth(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getWidth() | ||
| */ | ||
| public int getWidth() { | ||
| checkPicked(); | ||
|
|
||
| return picked.getWidth(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.LoadableImageData#configureEdging(boolean) | ||
| */ | ||
| public void configureEdging(boolean edging) { | ||
| for (int i=0;i<sources.size();i++) { | ||
| ((LoadableImageData) sources.get(i)).configureEdging(edging); | ||
| } | ||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,184 @@ | ||
| package org.spout.server.client.texture; | ||
|
|
||
| import java.io.IOException; | ||
| import java.nio.ByteBuffer; | ||
| import java.nio.ByteOrder; | ||
| import java.nio.IntBuffer; | ||
|
|
||
| import org.lwjgl.LWJGLException; | ||
| import org.lwjgl.input.Cursor; | ||
| import org.spout.api.Spout; | ||
| //import org.newdawn.slick.util.ResourceLoader; | ||
| import org.spout.server.client.loader.ResourceLoader; | ||
|
|
||
| /** | ||
| * A utility to load cursors (thanks go to Kappa for the animated cursor | ||
| * loader) | ||
| * | ||
| * @author Kevin Glass | ||
| * @author Kappa-One | ||
| */ | ||
| public class CursorLoader { | ||
| /** The single instnace of this loader to exist */ | ||
| private static CursorLoader single = new CursorLoader(); | ||
|
|
||
| /** | ||
| * Retrieve the single instance of this loader - convinient huh? | ||
| * | ||
| * @return The single instance of the cursor loader | ||
| */ | ||
| public static CursorLoader get() { | ||
| return single; | ||
| } | ||
|
|
||
| /** | ||
| * Create a new cursor loader | ||
| */ | ||
| private CursorLoader() { | ||
| } | ||
|
|
||
| /** | ||
| * Get a cursor based on a image reference on the classpath | ||
| * | ||
| * @param ref The reference to the image to be loaded | ||
| * @param x The x-coordinate of the cursor hotspot (left -> right) | ||
| * @param y The y-coordinate of the cursor hotspot (bottom -> top) | ||
| * @return The create cursor | ||
| * @throws IOException Indicates a failure to load the image | ||
| * @throws LWJGLException Indicates a failure to create the hardware cursor | ||
| */ | ||
| public Cursor getCursor(String ref,int x,int y) throws IOException, LWJGLException { | ||
| LoadableImageData imageData = null; | ||
|
|
||
| imageData = ImageDataFactory.getImageDataFor(ref); | ||
| imageData.configureEdging(false); | ||
|
|
||
| ByteBuffer buf = imageData.loadImage(ResourceLoader.getResourceAsStream(ref), true, true, null); | ||
| for (int i=0;i<buf.limit();i+=4) { | ||
| byte red = buf.get(i); | ||
| byte green = buf.get(i+1); | ||
| byte blue = buf.get(i+2); | ||
| byte alpha = buf.get(i+3); | ||
|
|
||
| buf.put(i+2, red); | ||
| buf.put(i+1, green); | ||
| buf.put(i, blue); | ||
| buf.put(i+3, alpha); | ||
| } | ||
|
|
||
| try { | ||
| int yspot = imageData.getHeight() - y - 1; | ||
| if (yspot < 0) { | ||
| yspot = 0; | ||
| } | ||
|
|
||
| return new Cursor(imageData.getTexWidth(), imageData.getTexHeight(), x, yspot, 1, buf.asIntBuffer(), null); | ||
| } catch (Throwable e) { | ||
| Spout.getGame().getLogger().info("Chances are you cursor is too small for this platform"); | ||
| throw new LWJGLException(e); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * Get a cursor based on a set of image data | ||
| * | ||
| * @param buf The image data (stored in RGBA) to load the cursor from | ||
| * @param x The x-coordinate of the cursor hotspot (left -> right) | ||
| * @param y The y-coordinate of the cursor hotspot (bottom -> top) | ||
| * @param width The width of the image data provided | ||
| * @param height The height of the image data provided | ||
| * @return The create cursor | ||
| * @throws IOException Indicates a failure to load the image | ||
| * @throws LWJGLException Indicates a failure to create the hardware cursor | ||
| */ | ||
| public Cursor getCursor(ByteBuffer buf,int x,int y,int width,int height) throws IOException, LWJGLException { | ||
| for (int i=0;i<buf.limit();i+=4) { | ||
| byte red = buf.get(i); | ||
| byte green = buf.get(i+1); | ||
| byte blue = buf.get(i+2); | ||
| byte alpha = buf.get(i+3); | ||
|
|
||
| buf.put(i+2, red); | ||
| buf.put(i+1, green); | ||
| buf.put(i, blue); | ||
| buf.put(i+3, alpha); | ||
| } | ||
|
|
||
| try { | ||
| int yspot = height - y - 1; | ||
| if (yspot < 0) { | ||
| yspot = 0; | ||
| } | ||
| return new Cursor(width,height, x, yspot, 1, buf.asIntBuffer(), null); | ||
| } catch (Throwable e) { | ||
| Spout.getGame().getLogger().info("Chances are you cursor is too small for this platform"); | ||
| throw new LWJGLException(e); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Get a cursor based on a set of image data | ||
| * | ||
| * @param imageData The data from which the cursor can read it's contents | ||
| * @param x The x-coordinate of the cursor hotspot (left -> right) | ||
| * @param y The y-coordinate of the cursor hotspot (bottom -> top) | ||
| * @return The create cursor | ||
| * @throws IOException Indicates a failure to load the image | ||
| * @throws LWJGLException Indicates a failure to create the hardware cursor | ||
| */ | ||
| public Cursor getCursor(ImageData imageData,int x,int y) throws IOException, LWJGLException { | ||
| ByteBuffer buf = imageData.getImageBufferData(); | ||
| for (int i=0;i<buf.limit();i+=4) { | ||
| byte red = buf.get(i); | ||
| byte green = buf.get(i+1); | ||
| byte blue = buf.get(i+2); | ||
| byte alpha = buf.get(i+3); | ||
|
|
||
| buf.put(i+2, red); | ||
| buf.put(i+1, green); | ||
| buf.put(i, blue); | ||
| buf.put(i+3, alpha); | ||
| } | ||
|
|
||
| try { | ||
| int yspot = imageData.getHeight() - y - 1; | ||
| if (yspot < 0) { | ||
| yspot = 0; | ||
| } | ||
| return new Cursor(imageData.getTexWidth(), imageData.getTexHeight(), x, yspot, 1, buf.asIntBuffer(), null); | ||
| } catch (Throwable e) { | ||
| Spout.getGame().getLogger().info("Chances are you cursor is too small for this platform"); | ||
| throw new LWJGLException(e); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Get a cursor based on a image reference on the classpath. The image | ||
| * is assumed to be a set/strip of cursor animation frames running from top to | ||
| * bottom. | ||
| * | ||
| * @param ref The reference to the image to be loaded | ||
| * @param x The x-coordinate of the cursor hotspot (left -> right) | ||
| * @param y The y-coordinate of the cursor hotspot (bottom -> top) | ||
| * @param width The x width of the cursor | ||
| * @param height The y height of the cursor | ||
| * @param cursorDelays image delays between changing frames in animation | ||
| * | ||
| * @return The created cursor | ||
| * @throws IOException Indicates a failure to load the image | ||
| * @throws LWJGLException Indicates a failure to create the hardware cursor | ||
| */ | ||
| public Cursor getAnimatedCursor(String ref,int x,int y, int width, int height, int[] cursorDelays) throws IOException, LWJGLException { | ||
| IntBuffer cursorDelaysBuffer = ByteBuffer.allocateDirect(cursorDelays.length*4).order(ByteOrder.nativeOrder()).asIntBuffer(); | ||
| for (int i=0;i<cursorDelays.length;i++) { | ||
| cursorDelaysBuffer.put(cursorDelays[i]); | ||
| } | ||
| cursorDelaysBuffer.flip(); | ||
|
|
||
| LoadableImageData imageData = new TGAImageData(); | ||
| ByteBuffer buf = imageData.loadImage(ResourceLoader.getResourceAsStream(ref), false, null); | ||
|
|
||
| return new Cursor(width, height, x, y, cursorDelays.length, buf.asIntBuffer(), cursorDelaysBuffer); | ||
| } | ||
| } |
| @@ -0,0 +1,233 @@ | ||
| package org.spout.server.client.texture; | ||
|
|
||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
|
|
||
| import org.spout.server.client.loader.DeferredResource; | ||
| import org.spout.server.client.loader.LoadingList; | ||
|
|
||
| /** | ||
| * A texture proxy that can be used to load a texture at a later date while still | ||
| * allowing elements to reference it | ||
| * | ||
| * @author kevin | ||
| */ | ||
| public class DeferredTexture extends TextureImpl implements DeferredResource { | ||
| /** The stream to read the texture from */ | ||
| private InputStream in; | ||
| /** The name of the resource to load */ | ||
| private String resourceName; | ||
| /** True if the image should be flipped */ | ||
| private boolean flipped; | ||
| /** The filter to apply to the texture */ | ||
| private int filter; | ||
| /** The texture we're proxying for */ | ||
| private TextureImpl target; | ||
| /** The color to be transparent */ | ||
| private int[] trans; | ||
|
|
||
| /** | ||
| * Create a new deferred texture | ||
| * | ||
| * @param in The input stream from which to read the texture | ||
| * @param resourceName The name to give the resource | ||
| * @param flipped True if the image should be flipped | ||
| * @param filter The filter to apply | ||
| * @param trans The colour to defined as transparent | ||
| */ | ||
| public DeferredTexture(InputStream in, String resourceName, boolean flipped, int filter, int[] trans) { | ||
| this.in = in; | ||
| this.resourceName = resourceName; | ||
| this.flipped = flipped; | ||
| this.filter = filter; | ||
| this.trans = trans; | ||
|
|
||
| LoadingList.get().add(this); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.loading.DeferredResource#load() | ||
| */ | ||
| public void load() throws IOException { | ||
| boolean before = InternalTextureLoader.get().isDeferredLoading(); | ||
| InternalTextureLoader.get().setDeferredLoading(false); | ||
| target = InternalTextureLoader.get().getTexture(in, resourceName, flipped, filter, trans); | ||
| InternalTextureLoader.get().setDeferredLoading(before); | ||
| } | ||
|
|
||
| /** | ||
| * Check if the target has been obtained already | ||
| */ | ||
| private void checkTarget() { | ||
| if (target == null) { | ||
| try { | ||
| load(); | ||
| LoadingList.get().remove(this); | ||
| return; | ||
| } catch (IOException e) { | ||
| throw new RuntimeException("Attempt to use deferred texture before loading and resource not found: "+resourceName); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#bind() | ||
| */ | ||
| public void bind() { | ||
| checkTarget(); | ||
|
|
||
| target.bind(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#getHeight() | ||
| */ | ||
| public float getHeight() { | ||
| checkTarget(); | ||
|
|
||
| return target.getHeight(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#getImageHeight() | ||
| */ | ||
| public int getImageHeight() { | ||
| checkTarget(); | ||
| return target.getImageHeight(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#getImageWidth() | ||
| */ | ||
| public int getImageWidth() { | ||
| checkTarget(); | ||
| return target.getImageWidth(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#getTextureHeight() | ||
| */ | ||
| public int getTextureHeight() { | ||
| checkTarget(); | ||
| return target.getTextureHeight(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#getTextureID() | ||
| */ | ||
| public int getTextureID() { | ||
| checkTarget(); | ||
| return target.getTextureID(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#getTextureRef() | ||
| */ | ||
| public String getTextureRef() { | ||
| checkTarget(); | ||
| return target.getTextureRef(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#getTextureWidth() | ||
| */ | ||
| public int getTextureWidth() { | ||
| checkTarget(); | ||
| return target.getTextureWidth(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#getWidth() | ||
| */ | ||
| public float getWidth() { | ||
| checkTarget(); | ||
| return target.getWidth(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#release() | ||
| */ | ||
| public void release() { | ||
| checkTarget(); | ||
| target.release(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#setAlpha(boolean) | ||
| */ | ||
| public void setAlpha(boolean alpha) { | ||
| checkTarget(); | ||
| target.setAlpha(alpha); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#setHeight(int) | ||
| */ | ||
| public void setHeight(int height) { | ||
| checkTarget(); | ||
| target.setHeight(height); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#setTextureHeight(int) | ||
| */ | ||
| public void setTextureHeight(int texHeight) { | ||
| checkTarget(); | ||
| target.setTextureHeight(texHeight); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#setTextureID(int) | ||
| */ | ||
| public void setTextureID(int textureID) { | ||
| checkTarget(); | ||
| target.setTextureID(textureID); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#setTextureWidth(int) | ||
| */ | ||
| public void setTextureWidth(int texWidth) { | ||
| checkTarget(); | ||
| target.setTextureWidth(texWidth); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#setWidth(int) | ||
| */ | ||
| public void setWidth(int width) { | ||
| checkTarget(); | ||
| target.setWidth(width); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.TextureImpl#getTextureData() | ||
| */ | ||
| public byte[] getTextureData() { | ||
| checkTarget(); | ||
| return target.getTextureData(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.loading.DeferredResource#getDescription() | ||
| */ | ||
| public String getDescription() { | ||
| return resourceName; | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.Texture#hasAlpha() | ||
| */ | ||
| public boolean hasAlpha() { | ||
| checkTarget(); | ||
| return target.hasAlpha(); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.Texture#setTextureFilter(int) | ||
| */ | ||
| public void setTextureFilter(int textureFilter) { | ||
| checkTarget(); | ||
| target.setTextureFilter(textureFilter); | ||
| } | ||
| } |
| @@ -0,0 +1,71 @@ | ||
| package org.spout.server.client.texture; | ||
|
|
||
| import java.nio.ByteBuffer; | ||
|
|
||
| import org.lwjgl.BufferUtils; | ||
|
|
||
| /** | ||
| * An image data implementation which represents an empty texture | ||
| * | ||
| * @author kevin | ||
| */ | ||
| public class EmptyImageData implements ImageData { | ||
| /** The width of the data */ | ||
| private int width; | ||
| /** The height of the data */ | ||
| private int height; | ||
|
|
||
| /** | ||
| * Create an empty image data source | ||
| * | ||
| * @param width The width of the source | ||
| * @param height The height of the source | ||
| */ | ||
| public EmptyImageData(int width, int height) { | ||
| this.width = width; | ||
| this.height = height; | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getDepth() | ||
| */ | ||
| public int getDepth() { | ||
| return 32; | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getHeight() | ||
| */ | ||
| public int getHeight() { | ||
| return height; | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getImageBufferData() | ||
| */ | ||
| public ByteBuffer getImageBufferData() { | ||
| return BufferUtils.createByteBuffer(getTexWidth() * getTexHeight() * 4); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getTexHeight() | ||
| */ | ||
| public int getTexHeight() { | ||
| return InternalTextureLoader.get2Fold(height); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getTexWidth() | ||
| */ | ||
| public int getTexWidth() { | ||
| return InternalTextureLoader.get2Fold(width); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getWidth() | ||
| */ | ||
| public int getWidth() { | ||
| return width; | ||
| } | ||
|
|
||
| } |
| @@ -0,0 +1,55 @@ | ||
| package org.spout.server.client.texture; | ||
|
|
||
| import java.nio.ByteBuffer; | ||
|
|
||
| /** | ||
| * A description of any class providing ImageData in a form suitable for OpenGL texture | ||
| * creation. | ||
| * | ||
| * @author kevin | ||
| */ | ||
| public interface ImageData { | ||
|
|
||
| /** | ||
| * Get the last bit depth read from a TGA | ||
| * | ||
| * @return The last bit depth read | ||
| */ | ||
| public int getDepth(); | ||
|
|
||
| /** | ||
| * Get the last width read from a TGA | ||
| * | ||
| * @return Get the last width in pixels fread from a TGA | ||
| */ | ||
| public int getWidth(); | ||
|
|
||
| /** | ||
| * Get the last height read from a TGA | ||
| * | ||
| * @return Get the last height in pixels fread from a TGA | ||
| */ | ||
| public int getHeight(); | ||
|
|
||
| /** | ||
| * Get the last required texture width for a loaded image | ||
| * | ||
| * @return Get the ast required texture width for a loaded image | ||
| */ | ||
| public int getTexWidth(); | ||
|
|
||
| /** | ||
| * Get the ast required texture height for a loaded image | ||
| * | ||
| * @return Get the ast required texture height for a loaded image | ||
| */ | ||
| public int getTexHeight(); | ||
|
|
||
| /** | ||
| * Get the store image | ||
| * | ||
| * @return The stored image | ||
| */ | ||
| public ByteBuffer getImageBufferData(); | ||
|
|
||
| } |
| @@ -0,0 +1,77 @@ | ||
| package org.spout.server.client.texture; | ||
|
|
||
| import java.security.AccessController; | ||
| import java.security.PrivilegedAction; | ||
|
|
||
| import org.spout.api.Spout; | ||
|
|
||
|
|
||
| /** | ||
| * A static utility to create the appropriate image data for a particular reference. | ||
| * | ||
| * @author kevin | ||
| */ | ||
| public class ImageDataFactory { | ||
| /** True if we're going to use the native PNG loader - cached so it doesn't have | ||
| * the security check repeatedly | ||
| */ | ||
| private static boolean usePngLoader = true; | ||
| /** True if the PNG loader property has been checked */ | ||
| private static boolean pngLoaderPropertyChecked = false; | ||
|
|
||
| /** The name of the PNG loader configuration property */ | ||
| private static final String PNG_LOADER = "org.newdawn.slick.pngloader"; | ||
|
|
||
| /** | ||
| * Check PNG loader property. If set the native PNG loader will | ||
| * not be used. | ||
| */ | ||
| private static void checkProperty() { | ||
| if (!pngLoaderPropertyChecked) { | ||
| pngLoaderPropertyChecked = true; | ||
|
|
||
| try { | ||
| AccessController.doPrivileged(new PrivilegedAction<Object>() { | ||
| public Object run() { | ||
| String val = System.getProperty(PNG_LOADER); | ||
| if ("false".equalsIgnoreCase(val)) { | ||
| usePngLoader = false; | ||
| } | ||
|
|
||
| Spout.getGame().getLogger().info("Use Java PNG Loader = " + usePngLoader); | ||
| return null; | ||
| } | ||
| }); | ||
| } catch (Throwable e) { | ||
| // ignore, security failure - probably an applet | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Create an image data that is appropriate for the reference supplied | ||
| * | ||
| * @param ref The reference to the image to retrieve | ||
| * @return The image data that can be used to retrieve the data for that resource | ||
| */ | ||
| public static LoadableImageData getImageDataFor(String ref) { | ||
| checkProperty(); | ||
|
|
||
| ref = ref.toLowerCase(); | ||
|
|
||
| if (ref.endsWith(".tga")) { | ||
| return new TGAImageData(); | ||
| } | ||
| if (ref.endsWith(".png")) { | ||
| CompositeImageData data = new CompositeImageData(); | ||
| if (usePngLoader) { | ||
| data.add(new PNGImageData()); | ||
| } | ||
| data.add(new ImageIOImageData()); | ||
|
|
||
| return data; | ||
| } | ||
|
|
||
| return new ImageIOImageData(); | ||
| } | ||
| } |
| @@ -0,0 +1,244 @@ | ||
| package org.spout.server.client.texture; | ||
|
|
||
| import java.awt.Color; | ||
| import java.awt.Graphics2D; | ||
| import java.awt.color.ColorSpace; | ||
| import java.awt.image.BufferedImage; | ||
| import java.awt.image.ColorModel; | ||
| import java.awt.image.ComponentColorModel; | ||
| import java.awt.image.DataBuffer; | ||
| import java.awt.image.DataBufferByte; | ||
| import java.awt.image.Raster; | ||
| import java.awt.image.WritableRaster; | ||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.nio.ByteBuffer; | ||
| import java.nio.ByteOrder; | ||
| import java.util.Hashtable; | ||
|
|
||
| import javax.imageio.ImageIO; | ||
|
|
||
| /** | ||
| * An image data provider that uses ImageIO to retrieve image data in a format | ||
| * suitable for creating OpenGL textures. This implementation is used when | ||
| * formats not natively supported by the library are required. | ||
| * | ||
| * @author kevin | ||
| */ | ||
| public class ImageIOImageData implements LoadableImageData { | ||
| /** The colour model including alpha for the GL image */ | ||
| private static final ColorModel glAlphaColorModel = | ||
| new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), | ||
| new int[] {8,8,8,8}, | ||
| true, | ||
| false, | ||
| ComponentColorModel.TRANSLUCENT, | ||
| DataBuffer.TYPE_BYTE); | ||
|
|
||
| /** The colour model for the GL image */ | ||
| private static final ColorModel glColorModel = | ||
| new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), | ||
| new int[] {8,8,8,0}, | ||
| false, | ||
| false, | ||
| ComponentColorModel.OPAQUE, | ||
| DataBuffer.TYPE_BYTE); | ||
|
|
||
| /** The bit depth of the image */ | ||
| private int depth; | ||
| /** The height of the image */ | ||
| private int height; | ||
| /** The width of the image */ | ||
| private int width; | ||
| /** The width of the texture that should be created for the image */ | ||
| private int texWidth; | ||
| /** The height of the texture that should be created for the image */ | ||
| private int texHeight; | ||
| /** True if we should edge */ | ||
| private boolean edging = true; | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getDepth() | ||
| */ | ||
| public int getDepth() { | ||
| return depth; | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getHeight() | ||
| */ | ||
| public int getHeight() { | ||
| return height; | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getTexHeight() | ||
| */ | ||
| public int getTexHeight() { | ||
| return texHeight; | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getTexWidth() | ||
| */ | ||
| public int getTexWidth() { | ||
| return texWidth; | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getWidth() | ||
| */ | ||
| public int getWidth() { | ||
| return width; | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.LoadableImageData#loadImage(java.io.InputStream) | ||
| */ | ||
| public ByteBuffer loadImage(InputStream fis) throws IOException { | ||
| return loadImage(fis, true, null); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.LoadableImageData#loadImage(java.io.InputStream, boolean, int[]) | ||
| */ | ||
| public ByteBuffer loadImage(InputStream fis, boolean flipped, int[] transparent) throws IOException { | ||
| return loadImage(fis, flipped, false, transparent); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.LoadableImageData#loadImage(java.io.InputStream, boolean, boolean, int[]) | ||
| */ | ||
| public ByteBuffer loadImage(InputStream fis, boolean flipped, boolean forceAlpha, int[] transparent) throws IOException { | ||
| if (transparent != null) { | ||
| forceAlpha = true; | ||
| } | ||
|
|
||
| BufferedImage bufferedImage = ImageIO.read(fis); | ||
| return imageToByteBuffer(bufferedImage, flipped, forceAlpha, transparent); | ||
| } | ||
|
|
||
| public ByteBuffer imageToByteBuffer(BufferedImage image, boolean flipped, boolean forceAlpha, int[] transparent) { | ||
| ByteBuffer imageBuffer = null; | ||
| WritableRaster raster; | ||
| BufferedImage texImage; | ||
|
|
||
| int texWidth = 2; | ||
| int texHeight = 2; | ||
|
|
||
| // find the closest power of 2 for the width and height | ||
| // of the produced texture | ||
|
|
||
| while (texWidth < image.getWidth()) { | ||
| texWidth *= 2; | ||
| } | ||
| while (texHeight < image.getHeight()) { | ||
| texHeight *= 2; | ||
| } | ||
|
|
||
| this.width = image.getWidth(); | ||
| this.height = image.getHeight(); | ||
| this.texHeight = texHeight; | ||
| this.texWidth = texWidth; | ||
|
|
||
| // create a raster that can be used by OpenGL as a source | ||
| // for a texture | ||
| boolean useAlpha = image.getColorModel().hasAlpha() || forceAlpha; | ||
|
|
||
| if (useAlpha) { | ||
| depth = 32; | ||
| raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,texWidth,texHeight,4,null); | ||
| texImage = new BufferedImage(glAlphaColorModel,raster,false,new Hashtable<String, Object>()); | ||
| } else { | ||
| depth = 24; | ||
| raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,texWidth,texHeight,3,null); | ||
| texImage = new BufferedImage(glColorModel,raster,false,new Hashtable<String, Object>()); | ||
| } | ||
|
|
||
| // copy the source image into the produced image | ||
| Graphics2D g = (Graphics2D) texImage.getGraphics(); | ||
|
|
||
| // only need to blank the image for mac compatibility if we're using alpha | ||
| if (useAlpha) { | ||
| g.setColor(new Color(0f,0f,0f,0f)); | ||
| g.fillRect(0,0,texWidth,texHeight); | ||
| } | ||
|
|
||
| if (flipped) { | ||
| g.scale(1,-1); | ||
| g.drawImage(image,0,-height,null); | ||
| } else { | ||
| g.drawImage(image,0,0,null); | ||
| } | ||
|
|
||
| if (edging) { | ||
| if (height < texHeight - 1) { | ||
| copyArea(texImage, 0, 0, width, 1, 0, texHeight-1); | ||
| copyArea(texImage, 0, height-1, width, 1, 0, 1); | ||
| } | ||
| if (width < texWidth - 1) { | ||
| copyArea(texImage, 0,0,1,height,texWidth-1,0); | ||
| copyArea(texImage, width-1,0,1,height,1,0); | ||
| } | ||
| } | ||
|
|
||
| // build a byte buffer from the temporary image | ||
| // that be used by OpenGL to produce a texture. | ||
| byte[] data = ((DataBufferByte) texImage.getRaster().getDataBuffer()).getData(); | ||
|
|
||
| if (transparent != null) { | ||
| for (int i=0;i<data.length;i+=4) { | ||
| boolean match = true; | ||
| for (int c=0;c<3;c++) { | ||
| int value = data[i+c] < 0 ? 256 + data[i+c] : data[i+c]; | ||
| if (value != transparent[c]) { | ||
| match = false; | ||
| } | ||
| } | ||
|
|
||
| if (match) { | ||
| data[i+3] = 0; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| imageBuffer = ByteBuffer.allocateDirect(data.length); | ||
| imageBuffer.order(ByteOrder.nativeOrder()); | ||
| imageBuffer.put(data, 0, data.length); | ||
| imageBuffer.flip(); | ||
| g.dispose(); | ||
|
|
||
| return imageBuffer; | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.ImageData#getImageBufferData() | ||
| */ | ||
| public ByteBuffer getImageBufferData() { | ||
| throw new RuntimeException("ImageIOImageData doesn't store it's image."); | ||
| } | ||
|
|
||
| /** | ||
| * Implement of transform copy area for 1.4 | ||
| * | ||
| * @param image The image to copy | ||
| * @param x The x position to copy to | ||
| * @param y The y position to copy to | ||
| * @param width The width of the image | ||
| * @param height The height of the image | ||
| * @param dx The transform on the x axis | ||
| * @param dy The transform on the y axis | ||
| */ | ||
| private void copyArea(BufferedImage image, int x, int y, int width, int height, int dx, int dy) { | ||
| Graphics2D g = (Graphics2D) image.getGraphics(); | ||
|
|
||
| g.drawImage(image.getSubimage(x, y, width, height),x+dx,y+dy,null); | ||
| } | ||
|
|
||
| /** | ||
| * @see org.newdawn.slick.opengl.LoadableImageData#configureEdging(boolean) | ||
| */ | ||
| public void configureEdging(boolean edging) { | ||
| this.edging = edging; | ||
| } | ||
| } |