Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
524 lines
20 KiB
Java
524 lines
20 KiB
Java
package electrosphere.renderer.texture;
|
|
|
|
import electrosphere.engine.Globals;
|
|
import electrosphere.logger.LoggerInterface;
|
|
import electrosphere.renderer.OpenGLState;
|
|
import electrosphere.renderer.RenderingEngine;
|
|
import electrosphere.util.FileUtils;
|
|
import java.awt.Color;
|
|
import java.awt.image.BufferedImage;
|
|
import java.io.IOException;
|
|
import java.nio.ByteBuffer;
|
|
import javax.imageio.ImageIO;
|
|
import org.lwjgl.BufferUtils;
|
|
import org.lwjgl.opengl.GL40;
|
|
import org.lwjgl.system.MemoryUtil;
|
|
|
|
import static org.lwjgl.opengl.GL11.*;
|
|
import static org.lwjgl.opengl.GL12.GL_CLAMP_TO_EDGE;
|
|
import static org.lwjgl.opengl.GL13.GL_TEXTURE0;
|
|
import static org.lwjgl.opengl.GL14.*;
|
|
import static org.lwjgl.opengl.GL30.*;
|
|
|
|
/**
|
|
* A opengl in texture
|
|
*/
|
|
public class Texture {
|
|
|
|
//the pointer for the texture
|
|
int texturePointer = -1;
|
|
//the width of the texture
|
|
int width = -1;
|
|
//the height of the texture
|
|
int height = -1;
|
|
//whether the texture has transparency or not
|
|
boolean hasTransparency;
|
|
//the path to the texture
|
|
String path = "";
|
|
//the border color
|
|
float[] borderColor = null;
|
|
|
|
//the min and max filter
|
|
int minFilter = -1;
|
|
int maxFilter = -1;
|
|
|
|
//the pixel format (ie RGB, ARGB, etc)
|
|
int pixelFormat = -1;
|
|
|
|
//the data type of a single component of a pixel (IE UNSIGNED_INT, BYTE, etc)
|
|
int datatype = -1;
|
|
|
|
/**
|
|
* Constructs a texture from a pointer
|
|
* @param pointer the pointer
|
|
*/
|
|
public Texture(int pointer){
|
|
this.texturePointer = pointer;
|
|
}
|
|
|
|
/**
|
|
* Creates a texture with a new opengl texture object
|
|
*/
|
|
public Texture(){
|
|
this.texturePointer = glGenTextures();
|
|
}
|
|
|
|
/**
|
|
* Creates an in engine texture object from a java bufferedimage object
|
|
* @param bufferedImage The java bufferedimage object
|
|
*/
|
|
public Texture(OpenGLState openGlState, BufferedImage bufferedImage){
|
|
this.texturePointer = glGenTextures();
|
|
//bind the new texture
|
|
openGlState.glBindTexture(GL_TEXTURE_2D, texturePointer);
|
|
//how are we gonna wrap the texture??
|
|
this.setWrap(openGlState, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
|
|
this.setWrap(openGlState, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
|
|
//set the border color to black
|
|
this.setBorderColor(openGlState, new float[]{ 0.0f, 0.0f, 0.0f, 1.0f });
|
|
//set magnification and minification operation sampling strategies
|
|
this.setMinFilter(openGlState, GL_LINEAR);
|
|
this.setMagFilter(openGlState, GL_LINEAR);
|
|
//load the image here
|
|
ByteBuffer data;
|
|
width = 1;
|
|
height = 1;
|
|
BufferedImage image_data = bufferedImage;
|
|
if (
|
|
image_data.getType() == BufferedImage.TYPE_3BYTE_BGR ||
|
|
image_data.getType() == BufferedImage.TYPE_INT_RGB
|
|
){
|
|
hasTransparency = false;
|
|
} else if(
|
|
image_data.getType() == BufferedImage.TYPE_4BYTE_ABGR ||
|
|
image_data.getType() == BufferedImage.TYPE_INT_ARGB
|
|
){
|
|
hasTransparency = true;
|
|
}
|
|
width = image_data.getWidth();
|
|
height = image_data.getHeight();
|
|
if(hasTransparency){
|
|
data = BufferUtils.createByteBuffer(width * height * 4);
|
|
} else {
|
|
data = BufferUtils.createByteBuffer(width * height * 3);
|
|
}
|
|
for(int y = height - 1; y > -1; y--){
|
|
for(int x = 0; x < width; x++){
|
|
Color temp = new Color(image_data.getRGB(x, y), true);
|
|
|
|
data.put((byte)temp.getRed());
|
|
data.put((byte)temp.getGreen());
|
|
data.put((byte)temp.getBlue());
|
|
if(hasTransparency){
|
|
data.put((byte)temp.getAlpha());
|
|
}
|
|
}
|
|
}
|
|
data.flip();
|
|
//call if width != height so opengl figures out how to unpack it properly
|
|
if(width != height){
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
}
|
|
//buffer the texture information
|
|
if(hasTransparency){
|
|
this.pixelFormat = GL_RGBA;
|
|
this.datatype = GL_UNSIGNED_BYTE;
|
|
this.glTexImage2D(openGlState, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
|
} else {
|
|
this.pixelFormat = GL_RGB;
|
|
this.datatype = GL_UNSIGNED_BYTE;
|
|
this.glTexImage2D(openGlState, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);
|
|
}
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
//check build status
|
|
String errorMessage = RenderingEngine.getErrorInEnglish(Globals.renderingEngine.getError());
|
|
if(errorMessage != null){
|
|
LoggerInterface.loggerRenderer.ERROR(new IllegalStateException("Texture Constructor[from bufferedimage]: " + errorMessage));
|
|
}
|
|
openGlState.glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
/**
|
|
* Creates a texture from an existing file
|
|
* @param path The path to the image file
|
|
*/
|
|
public Texture(OpenGLState openGlState, String path){
|
|
LoggerInterface.loggerRenderer.DEBUG("Create texture " + path);
|
|
this.path = path;
|
|
if(!Globals.HEADLESS){
|
|
LoggerInterface.loggerRenderer.DEBUG("Setup texture object");
|
|
//generate the texture object on gpu
|
|
this.texturePointer = glGenTextures();
|
|
//bind the new texture
|
|
openGlState.glBindTexture(GL_TEXTURE_2D, texturePointer);
|
|
//how are we gonna wrap the texture??
|
|
this.setWrap(openGlState, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
|
|
this.setWrap(openGlState, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
|
|
//set the border color to black
|
|
this.setBorderColor(openGlState, new float[]{ 0.0f, 0.0f, 0.0f, 1.0f });
|
|
//set magnification and minification operation sampling strategies
|
|
this.setMinFilter(openGlState, GL_LINEAR);
|
|
this.setMagFilter(openGlState, GL_LINEAR);
|
|
|
|
LoggerInterface.loggerRenderer.DEBUG("Create texture data buffers");
|
|
//load the image here
|
|
ByteBuffer data;
|
|
width = 1;
|
|
height = 1;
|
|
try {
|
|
BufferedImage image_data = ImageIO.read(FileUtils.getAssetFile(path));
|
|
|
|
//
|
|
//transparency check
|
|
if (
|
|
image_data.getType() == BufferedImage.TYPE_3BYTE_BGR ||
|
|
image_data.getType() == BufferedImage.TYPE_INT_RGB
|
|
){
|
|
hasTransparency = false;
|
|
} else if(
|
|
image_data.getType() == BufferedImage.TYPE_4BYTE_ABGR ||
|
|
image_data.getType() == BufferedImage.TYPE_INT_ARGB
|
|
){
|
|
hasTransparency = true;
|
|
}
|
|
|
|
//
|
|
//create buffer
|
|
width = image_data.getWidth();
|
|
height = image_data.getHeight();
|
|
if(hasTransparency){
|
|
data = BufferUtils.createByteBuffer(width * height * 4);
|
|
} else {
|
|
data = BufferUtils.createByteBuffer(width * height * 3);
|
|
}
|
|
|
|
//
|
|
//error check
|
|
if(data == null){
|
|
throw new IllegalStateException("Failed to allocate buffer for texture");
|
|
}
|
|
|
|
//
|
|
//buffer data
|
|
for(int y = height - 1; y > -1; y--){
|
|
for(int x = 0; x < width; x++){
|
|
Color temp = new Color(image_data.getRGB(x, y), hasTransparency);
|
|
|
|
if(data.position() + 3 > data.limit() + 1){
|
|
throw new IllegalStateException("Hit buffer limit!");
|
|
}
|
|
data.put((byte)temp.getRed());
|
|
data.put((byte)temp.getGreen());
|
|
data.put((byte)temp.getBlue());
|
|
if(hasTransparency){
|
|
if(data.position() + 1 > data.limit() + 1){
|
|
throw new IllegalStateException("Hit buffer limit!");
|
|
}
|
|
data.put((byte)temp.getAlpha());
|
|
}
|
|
}
|
|
}
|
|
} catch (IOException ex) {
|
|
ex.printStackTrace();
|
|
hasTransparency = false;
|
|
data = BufferUtils.createByteBuffer(3);
|
|
data.put((byte)0);
|
|
data.put((byte)0);
|
|
data.put((byte)0);
|
|
}
|
|
|
|
LoggerInterface.loggerRenderer.DEBUG("Flip buffer");
|
|
data.flip();
|
|
|
|
//call if width != height so opengl figures out how to unpack it properly
|
|
if(width != height){
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
}
|
|
|
|
LoggerInterface.loggerRenderer.DEBUG("Upload texture buffer");
|
|
//buffer the texture information
|
|
if(hasTransparency){
|
|
this.pixelFormat = GL_RGBA;
|
|
this.datatype = GL_UNSIGNED_BYTE;
|
|
this.glTexImage2D(openGlState, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
|
} else {
|
|
this.pixelFormat = GL_RGB;
|
|
this.datatype = GL_UNSIGNED_BYTE;
|
|
this.glTexImage2D(openGlState, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);
|
|
}
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
//OPTIONAL free the original image data now that it's on the gpu
|
|
// System.gc();
|
|
//check build status
|
|
String errorMessage = RenderingEngine.getErrorInEnglish(Globals.renderingEngine.getError());
|
|
if(errorMessage != null){
|
|
LoggerInterface.loggerRenderer.ERROR(new IllegalStateException("Texture Constructor[from bufferedimage]: " + errorMessage));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a texture based on a buffer (for use passing data to gpu)
|
|
* @param buffer The buffer of data
|
|
* @param width the 'width' of the 'texture'
|
|
* @param height the 'height' of the 'texture'
|
|
*/
|
|
public Texture(OpenGLState openGlState, ByteBuffer buffer, int width, int height){
|
|
if(!Globals.HEADLESS){
|
|
//generate the texture object on gpu
|
|
this.texturePointer = glGenTextures();
|
|
//bind the new texture
|
|
openGlState.glBindTexture(GL_TEXTURE_2D, texturePointer);
|
|
//how are we gonna wrap the texture??
|
|
this.setWrap(openGlState, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
this.setWrap(openGlState, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
//disable mipmap
|
|
this.setMinFilter(openGlState, GL_LINEAR);
|
|
//call if width != height so opengl figures out how to unpack it properly
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
|
//GL_RED = 32bit r value
|
|
//buffer the texture information
|
|
this.pixelFormat = GL_RED;
|
|
this.datatype = GL_FLOAT;
|
|
GL40.glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, width, height, 0, GL_RED, GL_FLOAT, buffer);
|
|
//check build status
|
|
String errorMessage = RenderingEngine.getErrorInEnglish(Globals.renderingEngine.getError());
|
|
if(errorMessage != null){
|
|
LoggerInterface.loggerRenderer.ERROR(new IllegalStateException("Texture Constructor[from bytebuffer]: " + errorMessage));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Binds the texture to unit 0
|
|
* @param openGLState The opengl state
|
|
*/
|
|
public void bind(OpenGLState openGLState){
|
|
if(texturePointer == -1){
|
|
LoggerInterface.loggerRenderer.ERROR(new IllegalStateException("Tring to bind a texture object that has not been initialized yet"));
|
|
}
|
|
if(texturePointer == 0){
|
|
LoggerInterface.loggerRenderer.ERROR(new IllegalStateException("Trying to bind texture object that has texturepointer of 0"));
|
|
}
|
|
// openGLState.glActiveTexture(GL_TEXTURE0);
|
|
// openGLState.glBindTexture(GL_TEXTURE_2D, texturePointer);
|
|
openGLState.glBindTextureUnit(GL_TEXTURE0,this.texturePointer,GL_TEXTURE_2D);
|
|
}
|
|
|
|
/**
|
|
* Binds the texture
|
|
* @param openGLState The opengl state
|
|
* @param attrib_val The texture unit number
|
|
*/
|
|
public void bind(OpenGLState openGLState, int attrib_val){
|
|
if(texturePointer == -1){
|
|
LoggerInterface.loggerRenderer.ERROR(new IllegalStateException("Tring to bind a texture object that has not been initialized yet"));
|
|
}
|
|
if(texturePointer == 0){
|
|
LoggerInterface.loggerRenderer.ERROR(new IllegalStateException("Trying to bind texture object that has texturepointer of 0"));
|
|
}
|
|
openGLState.glBindTextureUnit(GL_TEXTURE0 + attrib_val,this.texturePointer,GL_TEXTURE_2D);
|
|
Globals.renderingEngine.checkError();
|
|
// openGLState.glActiveTexture(GL_TEXTURE0 + attrib_val);
|
|
// openGLState.glBindTexture(GL_TEXTURE_2D, texturePointer);
|
|
}
|
|
|
|
/**
|
|
* Checks if the texture has transparency or not
|
|
* @return true if transparent, false otherwise
|
|
*/
|
|
public boolean isTransparent(){
|
|
return hasTransparency;
|
|
}
|
|
|
|
/**
|
|
* Gets the path of the texture
|
|
* @return The path
|
|
*/
|
|
public String getPath(){
|
|
return path;
|
|
}
|
|
|
|
/**
|
|
* Gets the pointer of the texture
|
|
* @return The pointer
|
|
*/
|
|
public int getTexturePointer(){
|
|
return texturePointer;
|
|
}
|
|
|
|
/**
|
|
* Sets the wrap strategy of the texture
|
|
* @param wrapDir The direction to wrap
|
|
* @param wrapType The type of wrapping to perform
|
|
*/
|
|
public void setWrap(OpenGLState openGlState, int wrapDir, int wrapType){
|
|
//TODO: store wrap type for the direction in this object
|
|
openGlState.glBindTexture(GL_TEXTURE_2D,texturePointer);
|
|
glTexParameteri(GL_TEXTURE_2D, wrapDir, wrapType);
|
|
}
|
|
|
|
/**
|
|
* Sets the border color
|
|
* @param borderColor The color (must be 4 floats)
|
|
*/
|
|
public void setBorderColor(OpenGLState openGlState, float borderColor[]){
|
|
this.borderColor = borderColor;
|
|
openGlState.glBindTexture(GL_TEXTURE_2D,texturePointer);
|
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
|
|
}
|
|
|
|
/**
|
|
* Sets the min filter
|
|
* @param minFilter The min filter
|
|
*/
|
|
public void setMinFilter(OpenGLState openGlState, int minFilter){
|
|
this.minFilter = minFilter;
|
|
openGlState.glBindTexture(GL_TEXTURE_2D,texturePointer);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
|
|
}
|
|
|
|
/**
|
|
* Sets the max filter
|
|
* @param maxFilter The max filter
|
|
*/
|
|
public void setMagFilter(OpenGLState openGlState, int maxFilter){
|
|
this.maxFilter = maxFilter;
|
|
openGlState.glBindTexture(GL_TEXTURE_2D,texturePointer);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, maxFilter);
|
|
}
|
|
|
|
|
|
/**
|
|
* Specifies a 2d image
|
|
* @param width The width of the image
|
|
* @param height The height of the image
|
|
* @param format The format of the pixels (ie GL_RGB, GL_RGBA, etc)
|
|
* @param datatype The data type of a single component of a pixel (ie GL_BYTE, GL_UNSIGNED_INT, etc)
|
|
*/
|
|
public void glTexImage2D(OpenGLState openGLState, int width, int height, int format, int datatype){
|
|
//store provided values
|
|
this.width = width;
|
|
this.height = height;
|
|
this.pixelFormat = format;
|
|
this.datatype = datatype;
|
|
//static values going into call
|
|
int level = 0;
|
|
int border = 0; //this must be 0 according to docs
|
|
openGLState.glBindTexture(GL_TEXTURE_2D,texturePointer);
|
|
GL40.glTexImage2D(GL_TEXTURE_2D, level, format, width, height, border, format, datatype, MemoryUtil.NULL);
|
|
}
|
|
|
|
/**
|
|
* Specifies a 2d image
|
|
* @param width The width of the image
|
|
* @param height The height of the image
|
|
* @param format The format of the pixels (ie GL_RGB, GL_RGBA, etc)
|
|
* @param datatype The data type of a single component of a pixel (ie GL_BYTE, GL_UNSIGNED_INT, etc)
|
|
* @param data The data to populate the image with
|
|
*/
|
|
public void glTexImage2D(OpenGLState openGLState, int width, int height, int format, int datatype, ByteBuffer data){
|
|
//store provided values
|
|
this.width = width;
|
|
this.height = height;
|
|
this.pixelFormat = format;
|
|
this.datatype = datatype;
|
|
//static values going into call
|
|
int level = 0;
|
|
int border = 0; //this must be 0 according to docs
|
|
openGLState.glBindTexture(GL_TEXTURE_2D,texturePointer);
|
|
GL40.glTexImage2D(GL_TEXTURE_2D, level, format, width, height, border, format, datatype, data);
|
|
}
|
|
|
|
/**
|
|
* Gets the width of the texture
|
|
* @return The width
|
|
*/
|
|
public int getWidth(){
|
|
if(pixelFormat == -1){
|
|
throw new IllegalStateException(
|
|
"The width of the texture you are trying to query from has not been set yet." +
|
|
" The texture was likely constructed by passing the opengl texture pointer into the texture object."
|
|
);
|
|
}
|
|
return width;
|
|
}
|
|
|
|
/**
|
|
* Gets the height of the texture
|
|
* @return The height
|
|
*/
|
|
public int getHeight(){
|
|
if(pixelFormat == -1){
|
|
throw new IllegalStateException(
|
|
"The height of the texture you are trying to query from has not been set yet." +
|
|
" The texture was likely constructed by passing the opengl texture pointer into the texture object."
|
|
);
|
|
}
|
|
return height;
|
|
}
|
|
|
|
/**
|
|
* Gets the format of the pixels
|
|
* @return The format of the pixels (ie GL_RGBA, GL_RGB, etc)
|
|
*/
|
|
public int getFormat(){
|
|
if(pixelFormat == -1){
|
|
throw new IllegalStateException(
|
|
"The pixel format of the texture you are trying to query from has not been set yet." +
|
|
" The texture was likely constructed by passing the opengl texture pointer into the texture object."
|
|
);
|
|
}
|
|
return pixelFormat;
|
|
}
|
|
|
|
/**
|
|
* Gets the datatype of the pixels
|
|
* @return The datatype (IE GL_FLOAT, GL_BYTE, etc)
|
|
*/
|
|
public int getDataType(){
|
|
if(datatype == -1){
|
|
throw new IllegalStateException(
|
|
"The datatype of the texture you are trying to query from has not been set yet." +
|
|
" The texture was likely constructed by passing the opengl texture pointer into the texture object."
|
|
);
|
|
}
|
|
return datatype;
|
|
}
|
|
|
|
/**
|
|
* Gets for errors with the texture
|
|
* @param state The opengl state
|
|
*/
|
|
public void checkStatus(OpenGLState state){
|
|
this.bind(state);
|
|
int errorCode = Globals.renderingEngine.getError();
|
|
if(errorCode != GL40.GL_NO_ERROR){
|
|
switch(errorCode){
|
|
case GL40.GL_INVALID_VALUE: {
|
|
if(this.width < 0){
|
|
LoggerInterface.loggerRenderer.ERROR("Texture has width less than 0", new IllegalStateException("Texture has width less than 0"));
|
|
}
|
|
if(this.width > state.getMAX_TEXTURE_WIDTH()){
|
|
LoggerInterface.loggerRenderer.ERROR("Texture is greater width than environment allows", new IllegalStateException("Texture is greater width than environment allows"));
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String toString(){
|
|
String rVal = "" +
|
|
"Texture[" +
|
|
"path=\"" + path + "\", " +
|
|
"texturePointer=\"" + texturePointer + "\", " +
|
|
"width=\"" + width + "\", " +
|
|
"height=\"" + height + "\", " +
|
|
"]"
|
|
;
|
|
return rVal;
|
|
}
|
|
|
|
}
|