@@ -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;
}
}