I'm using the LWJGL 3 version of assimp, and I've stumbled my way through loading a model. The issue I'm running into is loading actual pixel data for textures. What is the process of loading these textures by use of a AIMaterial object?
When I did this for a quick demo test, I just used the regular Image IO from Java. It's not as fancy but it works and might get you going:
public static ByteBuffer decodePng( BufferedImage image )
throws IOException
{
int width = image.getWidth();
int height = image.getHeight();
// Load texture contents into a byte buffer
ByteBuffer buf = ByteBuffer.allocateDirect(
4 * width * height );
// decode image
// ARGB format to -> RGBA
for( int h = 0; h < height; h++ )
for( int w = 0; w < width; w++ ) {
int argb = image.getRGB( w, h );
buf.put( (byte) ( 0xFF & ( argb >> 16 ) ) );
buf.put( (byte) ( 0xFF & ( argb >> 8 ) ) );
buf.put( (byte) ( 0xFF & ( argb ) ) );
buf.put( (byte) ( 0xFF & ( argb >> 24 ) ) );
}
buf.flip();
return buf;
}
Where the image was loaded as part of another routine:
public Texture(InputStream is) throws Exception {
try {
// Load Texture file
BufferedImage image = ImageIO.read(is);
this.width = image.getWidth();
this.height = image.getHeight();
// Load texture contents into a byte buffer
ByteBuffer buf = xogl.utils.TextureUtils.decodePng(image);
// Create a new OpenGL texture
this.id = glGenTextures();
// Bind the texture
glBindTexture(GL_TEXTURE_2D, this.id);
// Tell OpenGL how to unpack the RGBA bytes. Each component is 1 byte size
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Upload the texture data
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this.width, this.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
Related
I'm trying to create a texture from a loaded Buffered image like this:
public static long loadTexture(String img) throws IOException{
File imgPath = new File(img);
BufferedImage bufferedImage = ImageIO.read(imgPath);
byte[] pixels = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData();
int id = glGenTextures();
glBindTexture(GL_TEXTURE_2D, id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferedImage.getWidth(), bufferedImage.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, ByteBuffer.wrap(pixels));
glGenerateMipmap(GL_TEXTURE_2D);
return id;
}
But this code gives me a java sigsegv error. I am using a Java.nio.Bytebuffer because the sun one isn't supported in java 11.
So what am I doing wrong? The Image is loaded correctly, with 4bpp:
//last 2 digits are lenght
FF-AD-6F-CB-FF-FF-FF-00-FF-FF-FF-00-FF-FF-FF-00-FF-FF-FF-00-FF-00-00-FF-FF-00-00-FF-FF-FF-FF-00-FF-FF-FF-00-FF-00-00-FF-FF-00-00-FF-FF-FF-FF-00-FF-FF-FF-00-FF-FF-FF-00-FF-FF-FF-00-FF-FF-FF-00-64
Heres the image:
its pretty small of course but the data is correct.
So why am I getting a sigsegv? the log is pretty useless and long so I can't post it.
And how do I create an opengl texture from a 4bpp byte array?
As suggested in Java Code Examples for org.lwjgl.opengl.GL11.glTexImage2D() (Example 5) you have to copy the data to a ByteBuffer in loops:
public static long loadTexture(String img) throws IOException{
File imgPath = new File(img);
BufferedImage image = ImageIO.read(imgPath);
int[] pixels = new int[image.getWidth() * image.getHeight()];
image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
ByteBuffer buffer = ByteBuffer.allocateDirect(image.getWidth() * image.getHeight() * 4);
for(int h = 0; h < image.getHeight(); h++) {
for(int w = 0; w < image.getWidth(); w++) {
int pixel = pixels[h * image.getWidth() + w];
buffer.put((byte) ((pixel >> 16) & 0xFF));
buffer.put((byte) ((pixel >> 8) & 0xFF));
buffer.put((byte) (pixel & 0xFF));
buffer.put((byte) ((pixel >> 24) & 0xFF));
}
}
buffer.flip();
int id = glGenTextures();
glBindTexture(GL_TEXTURE_2D, id);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.getWidth(), image.getHeight(),
0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
glGenerateMipmap(GL_TEXTURE_2D);
return id;
}
Note, glPixelStorei(GL_UNPACK_ALIGNMENT, 1) is unnecessary in that case, because the size of a RGBA pixel is 4 bytes, so each row is aligned to 4 bytes and GL_UNPACK_ALIGNMENT by default is 4.
Furthermore, if you want to use Mipmaps (glGenerateMipmap), then the minifying function (GL_TEXTURE_MIN_FILTER) has to be GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR or GL_LINEAR_MIPMAP_LINEAR. (See glTexParameter)
I've been trying to load in bufferedImages in java as IntBuffers. However, one problem I've come across is getting the pixel data from an image with semi or complete transparency. Java only seems to allow you to get the RGB value, which in my case is a problem because any pixels that should be transparent are rendered completely opaque. After about a few hours of searching I came across this way of getting the RGBA values...
Color color = new Color(image.getRGB(x, y), true);
Although it does work, it can't possibly be the best way of doing this. Does anyone know of a more efficient way to complete the same task, one that does not require an instance of a color object for EVERY pixel. You can see how this would be bad if you're trying to load in a fairly large image. Here is my code just in case you need a reference...
public static IntBuffer getImageBuffer(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
int[] pixels = new int[width * height];
for (int i = 0; i < pixels.length; i++) {
Color color = new Color(image.getRGB(i % width, i / width), true);
int a = color.getAlpha();
int r = color.getRed();
int g = color.getGreen();
int b = color.getBlue();
pixels[i] = a << 24 | b << 16 | g << 8 | r;
}
return BufferUtils.toIntBuffer(pixels);
}
public static IntBuffer toIntBuffer(int[] elements) {
IntBuffer buffer = ByteBuffer.allocateDirect(elements.length << 2).order(ByteOrder.nativeOrder()).asIntBuffer();
buffer.put(elements).flip();
return buffer;
}
*Edit: The bufferedImage passed into the parameter is loaded from the disk
Here's some old code I have that converts images to OpenGL for LWJGL. Since the byte order has to be swapped, it isn't useful (I think) to load the image as for example integers.
public static ByteBuffer decodePng( BufferedImage image )
throws IOException
{
int width = image.getWidth();
int height = image.getHeight();
// Load texture contents into a byte buffer
ByteBuffer buf = ByteBuffer.allocateDirect( 4 * width * height );
// decode image
// ARGB format to -> RGBA
for( int h = 0; h < height; h++ )
for( int w = 0; w < width; w++ ) {
int argb = image.getRGB( w, h );
buf.put( (byte) ( 0xFF & ( argb >> 16 ) ) );
buf.put( (byte) ( 0xFF & ( argb >> 8 ) ) );
buf.put( (byte) ( 0xFF & ( argb ) ) );
buf.put( (byte) ( 0xFF & ( argb >> 24 ) ) );
}
buf.flip();
return buf;
}
Example usage:
BufferedImage image = ImageIO.read( getClass().getResourceAsStream(heightMapFile) );
int height = image.getHeight();
int width = image.getWidth();
ByteBuffer buf = TextureUtils.decodePng(image);
If interested, I did a jvm port of gli that deals with these stuff so that you don't have to worry about.
An example of texture loading:
public static int createTexture(String filename) {
Texture texture = gli.load(filename);
if (texture.empty())
return 0;
gli_.gli.gl.setProfile(gl.Profile.GL33);
gl.Format format = gli_.gli.gl.translate(texture.getFormat(), texture.getSwizzles());
gl.Target target = gli_.gli.gl.translate(texture.getTarget());
assert (texture.getFormat().isCompressed() && target == gl.Target._2D);
IntBuffer textureName = intBufferBig(1);
glGenTextures(textureName);
glBindTexture(target.getI(), textureName.get(0));
glTexParameteri(target.getI(), GL12.GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(target.getI(), GL12.GL_TEXTURE_MAX_LEVEL, texture.levels() - 1);
IntBuffer swizzles = intBufferBig(4);
texture.getSwizzles().to(swizzles);
glTexParameteriv(target.getI(), GL33.GL_TEXTURE_SWIZZLE_RGBA, swizzles);
Vec3i extent = texture.extent(0);
glTexStorage2D(target.getI(), texture.levels(), format.getInternal().getI(), extent.x, extent.y);
for (int level = 0; level < texture.levels(); level++) {
extent = texture.extent(level);
glCompressedTexSubImage2D(
target.getI(), level, 0, 0, extent.x, extent.y,
format.getInternal().getI(), texture.data(0, 0, level));
}
return textureName.get(0);
}
I want to dynamically create an image, and the created image must meet some requirements.
The created image should be a png, and it must have the same behavior as if it's a loaded png from a file.
It's for creating a texture to use in LWJGL.
When I load a png image as a file and have a BufferedImage, I can use the following code for my texture:
(The Texture constructor is designed for using with loaded images)
public class Texture {
public Texture(BufferedImage bi) {
width = bi.getWidth();
height = bi.getHeight();
System.out.println(bi.toString());
int[] pixels_raw = new int[width * height];
pixels_raw = bi.getRGB(0, 0, width, height, null, 0, width);
ByteBuffer pixels = BufferUtils.createByteBuffer(width * height * 4);
for(int i = 0; i < width; i++) {
for(int j = 0; j < height; j++) {
int pixel = pixels_raw[i * width + j]; // This is the error line.
pixels.put((byte)((pixel >> 16) & 0xFF)); // red
pixels.put((byte)((pixel >> 8) & 0xFF)); // green
pixels.put((byte)(pixel & 0xFF)); // blue
pixels.put((byte)((pixel >> 24) & 0xFF)); // alpha
}
}
pixels.flip();
id = glGenTextures();
glBindTexture(GL_TEXTURE_2D, id);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
}
}
But when I try to create an image dynamically, without loading anything from a file, then I get an ArrayIndexOutOfBoundsException on line 18 of the above code (see comment in code).
Of course it has something to do with the bits per pixel of the created BufferedImage. I tried changing the image type for my BufferedImage, and changing the array size when initializing the pixels_raw array. But I still get array exceptions. So, the above constructor method does only works when I pass a BufferedImage instance which comes from a loaded png. When I pass in a BurfferedImage I created dynamically with the code below, it gives me the exceptions I mentioned before.
public class TextDrawer {
public BufferedImage drawText(String text, Font font, Color color) {
BufferedImage graphicsGetterBi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics g = graphicsGetterBi.getGraphics();
Graphics2D g2 = (Graphics2D) g;
Rectangle2D bounds = font.getStringBounds(text, 0, text.length(), g2.getFontRenderContext());
BufferedImage bi = new BufferedImage((int) bounds.getWidth(), (int) bounds.getHeight(), BufferedImage.TYPE_INT_ARGB);
System.out.println("Created the image. \n");
g2.setColor(color);
g2.setFont(font);
g2.drawString(text, 0, 0);
return bi;
}
}
instead of int pixel = pixels_raw[i * width + j]; it should be int pixel = pixels_raw[i * height + j]; or int pixel = pixels_raw[j * width + i];. Consider you have image of width = 2x and height = x. Then the array size is 2x^2, while the maximum index you request for is (2x - 1) * 2x + x - 1 = 4x^2 - x - 1, which is more than 2x^2 for x > 2
I use lwjgl to render Images in OpenGL, now i want to store the content of the Framebuffer as RGB in an OpenCV Matrix. To make shure everything runs fine, im showing the captured image on Panel of a jFrame.
But heres the problem: While showing stored jpegs everything looks fine but if im trying to show the captured Framebuffer i only see stripes!
Here is the code for a screenshot:
public Mat takeMatScreenshot()
{
int width = m_iResolutionX;
int height = m_iResolutionY;
int pixelCount = width * height;
byte[] pixelValues = new byte[ pixelCount * 3 ];
ByteBuffer pixelBuffer = BufferUtils.createByteBuffer( width * height * 3 );
glBindFramebuffer( GL_FRAMEBUFFER, m_iFramebuffer );
glReadPixels( 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixelBuffer );
for( int i = 0; i < pixelCount; i++ )
{
int line = height - 1 - (i / width); // flipping the image upside down
int column = i % width;
int bufferIndex = ( line * width + column ) * 3;
pixelValues[bufferIndex + 0 ] = (byte)(pixelBuffer.get(bufferIndex + 0) & 0xFF) ;
pixelValues[bufferIndex + 1 ] = (byte)(pixelBuffer.get(bufferIndex + 1) & 0xFF);
pixelValues[bufferIndex + 2 ] = (byte)(pixelBuffer.get(bufferIndex + 2) & 0xFF);
}
Mat image = new Mat(width, height, CvType.CV_8UC3);
image.put(0, 0, pixelValues);
new ImageFrame(image);
return image;
}
And here the code for displaying a Mat:
public static Image toBufferedImage(Mat m)
{
int type = BufferedImage.TYPE_BYTE_GRAY;
if ( m.channels() == 3 )
type = BufferedImage.TYPE_3BYTE_BGR;
if( m.channels() == 4 )
type = BufferedImage.TYPE_4BYTE_ABGR;
int bufferSize = m.channels()*m.cols()*m.rows();
byte [] b = new byte[bufferSize];
m.get( 0, 0, b ); // get all the pixels
BufferedImage image = new BufferedImage( m.cols(), m.rows(), type );
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.arraycopy(b, 0, targetPixels, 0, b.length);
return image;
}
It would be great if anyoune could help me!
Cheers!
Oh no! Facepalm!
The constructor of a OpenCV Mat Object is: Mat(rows, cols)!
So the right solution is:
Mat image = new Mat(height, width, CvType.CV_8UC3);
image.put(0, 0, pixelValues);
I am making a game with LWJGL and by using openGL, I believe my best option is to use Textures and render them with quads. However, I can only seem to find information on loading a texture from an image where the entire image is only ONE texture. What I would like to do is read an entire spritesheet in and be able to separate it into different textures. Is there a somewhat simple way to do this?
You could load the image, from e.g. a .png file to a BufferedImage with
public static BufferedImage loadImage(String location)
{
try {
BufferedImage image = ImageIO.read(new File(location));
return image;
} catch (IOException e) {
System.out.println("Could not load texture: " + location);
}
return null;
}
Now you are able to call getSubimage(int x, int y, int w, int h) on that resulting BufferedImage, giving you the seperated part. You now just need to create a Texture of the BufferedImage. This code should do the work:
public static int loadTexture(BufferedImage image){
if (image == null) {
return 0;
}
int[] pixels = new int[image.getWidth() * image.getHeight()];
image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
ByteBuffer buffer = BufferUtils.createByteBuffer(image.getWidth() * image.getHeight() * BYTES_PER_PIXEL); //4 for RGBA, 3 for RGB
for(int y = 0; y < image.getHeight(); y++){
for(int x = 0; x < image.getWidth(); x++){
int pixel = pixels[y * image.getWidth() + x];
buffer.put((byte) ((pixel >> 16) & 0xFF)); // Red component
buffer.put((byte) ((pixel >> 8) & 0xFF)); // Green component
buffer.put((byte) (pixel & 0xFF)); // Blue component
buffer.put((byte) ((pixel >> 24) & 0xFF)); // Alpha component. Only for RGBA
}
}
buffer.flip(); //FOR THE LOVE OF GOD DO NOT FORGET THIS
// You now have a ByteBuffer filled with the color data of each pixel.
// Now just create a texture ID and bind it. Then you can load it using
// whatever OpenGL method you want, for example:
int textureID = glGenTextures();
glBindTexture(GL_TEXTURE_2D, textureID);
//setup wrap mode
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
//setup texture scaling filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//Send texel data to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.getWidth(), image.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); //GL_RGBA8 was GL_RGB8A
return textureID;
}
You are now able to bind the returned textureID with glBindTexture(GL_TEXTURE_2D, textureID); if you need the texture.
This way you only have to split the BufferedImage in the desired parts.
I recommend reading this: LWJGL Textures and Strings