package demos.hdr;
import java.io.*;
import java.nio.*;
import com.jogamp.opengl.*;
public class HDRTexture {
private RGBE.Header header;
private byte[] m_data;
private float[] m_floatdata;
private int m_width, m_height;
private float m_max_r, m_max_g, m_max_b;
private float m_min_r, m_min_g, m_min_b;
private float m_max;
private int m_target;
public HDRTexture(String filename) throws IOException {
this(new FileInputStream(filename));
}
public HDRTexture(InputStream in) throws IOException {
DataInputStream datain = new DataInputStream(new BufferedInputStream(in));
header = RGBE.readHeader(datain);
m_width = header.getWidth();
m_height = header.getHeight();
m_data = new byte[m_width * m_height * 4];
RGBE.readPixelsRawRLE(datain, m_data, 0, m_width, m_height);
System.err.println("Loaded HDR image " + m_width + " x " + m_height);
}
public byte[] getData() { return m_data; }
public int getPixelIndex(int x, int y) {
return ((m_width * (m_height - 1 - y)) + x) * 4;
}
public float[] getFloatData() { return m_floatdata; }
public int getPixelFloatIndex(int x, int y) {
return ((m_width * (m_height - 1 - y)) + x) * 3;
}
public void analyze() {
m_max_r = m_max_g = m_max_b = 0.0f;
m_min_r = m_min_g = m_min_b = 1e10f;
int mine = 255;
int maxe = 0;
int ptr = 0;
float[] rgb = new float[3];
for(int i=0; i<m_width*m_height; i++) {
int e = m_data[ptr+3] & 0xFF;
if (e < mine) mine = e;
if (e > maxe) maxe = e;
RGBE.rgbe2float(rgb, m_data, ptr);
float r = rgb[0];
float g = rgb[1];
float b = rgb[2];
if (r > m_max_r) m_max_r = r;
if (g > m_max_g) m_max_g = g;
if (b > m_max_b) m_max_b = b;
if (r < m_min_r) m_min_r = r;
if (g < m_min_g) m_min_g = g;
if (b < m_min_b) m_min_b = b;
ptr += 4;
}
System.err.println("max intensity: " + m_max_r + " " + m_max_g + " " + m_max_b);
System.err.println("min intensity: " + m_min_r + " " + m_min_g + " " + m_min_b);
System.err.println("max e: " + maxe + " = " + RGBE.ldexp(1.0, maxe-128));
System.err.println("min e: " + mine + " = " + RGBE.ldexp(1.0, mine-128));
m_max = m_max_r;
if (m_max_g > m_max) m_max = m_max_g;
if (m_max_b > m_max) m_max = m_max_b;
System.err.println("max: " + m_max);
}
/** Converts from RGBE to floating-point RGB data. */
public void convert() {
m_floatdata = new float [m_width*m_height*3];
int src = 0;
int dest = 0;
float[] rgb = new float[3];
for(int i=0; i<m_width*m_height; i++) {
RGBE.rgbe2float(rgb, m_data, src);
m_floatdata[dest++] = remap(rgb[0], m_max);
m_floatdata[dest++] = remap(rgb[1], m_max);
m_floatdata[dest++] = remap(rgb[2], m_max);
src += 4;
}
}
public int create2DTextureRGBE(GL gl, int targetTextureType) {
m_target = targetTextureType;
int[] tmp = new int[1];
gl.glGenTextures(1, tmp, 0);
int texid = tmp[1];
gl.glBindTexture(m_target, texid);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
gl.glTexParameteri(m_target, GL2ES1.GL_GENERATE_MIPMAP, GL.GL_TRUE);
gl.glTexImage2D(m_target, 0, GL.GL_RGBA, m_width, m_height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap(m_data));
return texid;
}
public int create2DTextureHILO(GL gl, int targetTextureType, boolean rg) {
m_target = targetTextureType;
int[] tmp = new int[1];
gl.glGenTextures(1, tmp, 0);
int texid = tmp[0];
gl.glBindTexture(m_target, texid);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
gl.glTexParameteri(m_target, GL2ES1.GL_GENERATE_MIPMAP, GL.GL_TRUE);
float[] img = new float [m_width * m_height * 2];
int src = 0;
int dest = 0;
for (int j=0; j<m_height; j++) {
for (int i=0; i<m_width; i++) {
if (rg) {
img[dest++] = m_floatdata[src + 0];
img[dest++] = m_floatdata[src + 1];
} else {
img[dest++] = m_floatdata[src + 2];
img[dest++] = 0;
}
src+=3;
}
}
gl.glTexImage2D(m_target, 0, GL2.GL_HILO16_NV, m_width, m_height, 0, GL2.GL_HILO_NV, GL.GL_FLOAT, FloatBuffer.wrap(img));
return texid;
}
// create a cubemap texture from a 2D image in cross format (thanks to Jonathon McGee)
public int createCubemapRGBE(GL gl) {
// cross is 3 faces wide, 4 faces high
int face_width = m_width / 3;
int face_height = m_height / 4;
byte[] face = new byte[face_width * face_height * 4];
m_target = GL.GL_TEXTURE_CUBE_MAP;
int[] tmp = new int[1];
gl.glGenTextures(1, tmp, 0);
int texid = tmp[0];
gl.glBindTexture(m_target, texid);
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
gl.glTexParameteri(m_target, GL2ES1.GL_GENERATE_MIPMAP, GL.GL_TRUE);
// gl.glTexParameteri(m_target, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
// gl.glTexParameteri(m_target, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
// extract 6 faces
// positive Y
int ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelIndex(2 * face_width - (i + 1), 3 * face_height + j);
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL.GL_RGBA, face_width, face_height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap(face));
// positive X
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelIndex(i, m_height - (face_height + j + 1));
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL.GL_RGBA, face_width, face_height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap(face));
// negative Z
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelIndex(face_width + i, m_height - (face_height + j + 1));
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL.GL_RGBA, face_width, face_height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap(face));
// negative X
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelIndex(2 * face_width + i, m_height - (face_height + j + 1));
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL.GL_RGBA, face_width, face_height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap(face));
// negative Y
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelIndex(2 * face_width - (i + 1), face_height + j);
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL.GL_RGBA, face_width, face_height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap(face));
// positive Z
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelIndex(2 * face_width - (i + 1), j);
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
face[ptr++] = m_data[src++];
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL.GL_RGBA, face_width, face_height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap(face));
return texid;
}
public int createCubemapHILO(GL gl, boolean rg) {
// cross is 3 faces wide, 4 faces high
int face_width = m_width / 3;
int face_height = m_height / 4;
float[] face = new float [face_width * face_height * 2];
m_target = GL.GL_TEXTURE_CUBE_MAP;
int[] tmp = new int[1];
gl.glGenTextures(1, tmp, 0);
int texid = tmp[0];
gl.glBindTexture(m_target, texid);
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
gl.glTexParameteri(m_target, GL2ES1.GL_GENERATE_MIPMAP, GL.GL_TRUE);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
// extract 6 faces
// positive Y
int ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelFloatIndex(2 * face_width - (i + 1), 3 * face_height + j);
if (rg) {
face[ptr++] = m_floatdata[src + 0];
face[ptr++] = m_floatdata[src + 1];
} else {
face[ptr++] = m_floatdata[src + 2];
face[ptr++] = 0;
}
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL2.GL_HILO16_NV, face_width, face_height, 0, GL2.GL_HILO_NV, GL.GL_FLOAT, FloatBuffer.wrap(face));
// positive X
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelFloatIndex(i, m_height - (face_height + j + 1));
if (rg) {
face[ptr++] = m_floatdata[src + 0];
face[ptr++] = m_floatdata[src + 1];
} else {
face[ptr++] = m_floatdata[src + 2];
face[ptr++] = 0;
}
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL2.GL_HILO16_NV, face_width, face_height, 0, GL2.GL_HILO_NV, GL.GL_FLOAT, FloatBuffer.wrap(face));
// negative Z
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelFloatIndex(face_width + i, m_height - (face_height + j + 1));
if (rg) {
face[ptr++] = m_floatdata[src + 0];
face[ptr++] = m_floatdata[src + 1];
} else {
face[ptr++] = m_floatdata[src + 2];
face[ptr++] = 0;
}
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL2.GL_HILO16_NV, face_width, face_height, 0, GL2.GL_HILO_NV, GL.GL_FLOAT, FloatBuffer.wrap(face));
// negative X
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelFloatIndex(2 * face_width + i, m_height - (face_height + j + 1));
if (rg) {
face[ptr++] = m_floatdata[src + 0];
face[ptr++] = m_floatdata[src + 1];
} else {
face[ptr++] = m_floatdata[src + 2];
face[ptr++] = 0;
}
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL2.GL_HILO16_NV, face_width, face_height, 0, GL2.GL_HILO_NV, GL.GL_FLOAT, FloatBuffer.wrap(face));
// negative Y
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelFloatIndex(2 * face_width - (i + 1), face_height + j);
if (rg) {
face[ptr++] = m_floatdata[src + 0];
face[ptr++] = m_floatdata[src + 1];
} else {
face[ptr++] = m_floatdata[src + 2];
face[ptr++] = 0;
}
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL2.GL_HILO16_NV, face_width, face_height, 0, GL2.GL_HILO_NV, GL.GL_FLOAT, FloatBuffer.wrap(face));
// positive Z
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelFloatIndex(2 * face_width - (i + 1), j);
if (rg) {
face[ptr++] = m_floatdata[src + 0];
face[ptr++] = m_floatdata[src + 1];
} else {
face[ptr++] = m_floatdata[src + 2];
face[ptr++] = 0;
}
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL2.GL_HILO16_NV, face_width, face_height, 0, GL2.GL_HILO_NV, GL.GL_FLOAT, FloatBuffer.wrap(face));
return texid;
}
public int createCubemap(GL gl, int format) {
// cross is 3 faces wide, 4 faces high
int face_width = m_width / 3;
int face_height = m_height / 4;
float[] face = new float [face_width * face_height * 3];
m_target = GL.GL_TEXTURE_CUBE_MAP;
int[] tmp = new int[1];
gl.glGenTextures(1, tmp, 0);
int texid = tmp[0];
gl.glBindTexture(m_target, texid);
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
gl.glTexParameteri(m_target, GL2ES1.GL_GENERATE_MIPMAP, GL.GL_TRUE);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(m_target, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
// extract 6 faces
// positive Y
int ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelFloatIndex(2 * face_width - (i + 1), 3 * face_height + j);
face[ptr++] = m_floatdata[src + 0];
face[ptr++] = m_floatdata[src + 1];
face[ptr++] = m_floatdata[src + 2];
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, format, face_width, face_height, 0, GL.GL_RGB, GL.GL_FLOAT, FloatBuffer.wrap(face));
// positive X
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelFloatIndex(i, m_height - (face_height + j + 1));
face[ptr++] = m_floatdata[src + 0];
face[ptr++] = m_floatdata[src + 1];
face[ptr++] = m_floatdata[src + 2];
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, format, face_width, face_height, 0, GL.GL_RGB, GL.GL_FLOAT, FloatBuffer.wrap(face));
// negative Z
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelFloatIndex(face_width + i, m_height - (face_height + j + 1));
face[ptr++] = m_floatdata[src + 0];
face[ptr++] = m_floatdata[src + 1];
face[ptr++] = m_floatdata[src + 2];
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, format, face_width, face_height, 0, GL.GL_RGB, GL.GL_FLOAT, FloatBuffer.wrap(face));
// negative X
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelFloatIndex(2 * face_width + i, m_height - (face_height + j + 1));
face[ptr++] = m_floatdata[src + 0];
face[ptr++] = m_floatdata[src + 1];
face[ptr++] = m_floatdata[src + 2];
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, format, face_width, face_height, 0, GL.GL_RGB, GL.GL_FLOAT, FloatBuffer.wrap(face));
// negative Y
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelFloatIndex(2 * face_width - (i + 1), face_height + j);
face[ptr++] = m_floatdata[src + 0];
face[ptr++] = m_floatdata[src + 1];
face[ptr++] = m_floatdata[src + 2];
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, format, face_width, face_height, 0, GL.GL_RGB, GL.GL_FLOAT, FloatBuffer.wrap(face));
// positive Z
ptr = 0;
for (int j=0; j<face_height; j++) {
for (int i=0; i<face_width; i++) {
int src = getPixelFloatIndex(2 * face_width - (i + 1), j);
face[ptr++] = m_floatdata[src + 0];
face[ptr++] = m_floatdata[src + 1];
face[ptr++] = m_floatdata[src + 2];
}
}
gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, format, face_width, face_height, 0, GL.GL_RGB, GL.GL_FLOAT, FloatBuffer.wrap(face));
return texid;
}
//----------------------------------------------------------------------
// Internals only below this point
//
private static float remap(float x, float max) {
if (x > max) x = max;
return (float) Math.sqrt(x / max);
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
try {
HDRTexture tex = new HDRTexture(args[i]);
tex.analyze();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}