package gl4; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.WindowAdapter; import com.jogamp.newt.event.WindowEvent; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.*; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.GLBuffers; import com.jogamp.opengl.util.texture.TextureData; import com.jogamp.opengl.util.texture.TextureIO; import framework.Semantic; import glm.mat.Mat4x4; import glm.vec._2.Vec2; import glm.vec._3.Vec3; import uno.debug.GlDebugOutput; import uno.glsl.Program; import java.io.IOException; import java.net.URL; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; import java.util.logging.Level; import java.util.logging.Logger; import static com.jogamp.opengl.GL.GL_DONT_CARE; import static com.jogamp.opengl.GL.GL_FLOAT; import static com.jogamp.opengl.GL.GL_MAP_INVALIDATE_BUFFER_BIT; import static com.jogamp.opengl.GL.GL_MAP_WRITE_BIT; import static com.jogamp.opengl.GL2ES2.GL_DEBUG_SEVERITY_HIGH; import static com.jogamp.opengl.GL2ES2.GL_DEBUG_SEVERITY_MEDIUM; import static com.jogamp.opengl.GL2ES3.GL_UNIFORM_BUFFER; import static com.jogamp.opengl.GL2ES3.GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT; import static com.jogamp.opengl.GL4.GL_ARRAY_BUFFER; import static com.jogamp.opengl.GL4.GL_CLAMP_TO_EDGE; import static com.jogamp.opengl.GL4.*; import static com.jogamp.opengl.GL4.GL_DEPTH_TEST; import static com.jogamp.opengl.GL4.GL_ELEMENT_ARRAY_BUFFER; import static com.jogamp.opengl.GL4.GL_NEAREST; import static com.jogamp.opengl.GL4.GL_STATIC_DRAW; import static com.jogamp.opengl.GL4.GL_TEXTURE_2D; import static com.jogamp.opengl.GL4.GL_TEXTURE_MAG_FILTER; import static com.jogamp.opengl.GL4.GL_TEXTURE_MIN_FILTER; import static com.jogamp.opengl.GL4.GL_TEXTURE_WRAP_S; import static com.jogamp.opengl.GL4.GL_TEXTURE_WRAP_T; import static com.jogamp.opengl.GL4.GL_TRIANGLES; import static com.jogamp.opengl.GL4.GL_UNSIGNED_SHORT; import static glm.GlmKt.glm; import static uno.buffer.UtilKt.destroyBuffer; import static uno.buffer.UtilKt.destroyBuffers; /** * @author elect */ public class HelloGlobe implements GLEventListener, KeyListener { private static GLWindow window; private static Animator animator; public static void main(String[] args) { new HelloGlobe().setup(); } private interface Buffer { int VERTEX = 0; int ELEMENT = 1; int GLOBAL_MATRICES = 2; int MODEL_MATRIX = 3; int MAX = 4; } private IntBuffer bufferName = GLBuffers.newDirectIntBuffer(Buffer.MAX); private IntBuffer vertexArrayName = GLBuffers.newDirectIntBuffer(1); private IntBuffer textureName = GLBuffers.newDirectIntBuffer(1); private IntBuffer samplerName = GLBuffers.newDirectIntBuffer(1); private FloatBuffer clearColor = GLBuffers.newDirectFloatBuffer(4); private FloatBuffer clearDepth = GLBuffers.newDirectFloatBuffer(1); private ByteBuffer globalMatricesPointer, modelMatrixPointer; // https://jogamp.org/bugzilla/show_bug.cgi?id=1287 private boolean bug1287 = true; private Program program; private long start; private int elementCount; private void setup() { GLProfile glProfile = GLProfile.get(GLProfile.GL3); GLCapabilities glCapabilities = new GLCapabilities(glProfile); window = GLWindow.create(glCapabilities); window.setTitle("Hello Globe"); window.setSize(1024, 768); window.setContextCreationFlags(GLContext.CTX_OPTION_DEBUG); window.setVisible(true); window.addGLEventListener(this); window.addKeyListener(this); animator = new Animator(window); animator.start(); window.addWindowListener(new WindowAdapter() { @Override public void windowDestroyed(WindowEvent e) { animator.stop(); System.exit(1); } }); } @Override public void init(GLAutoDrawable drawable) { GL4 gl = drawable.getGL().getGL4(); initDebug(gl); initBuffers(gl); initTexture(gl); initSampler(gl); initVertexArray(gl); program = new Program(gl, getClass(), "shaders/gl4", "hello-globe.vert", "hello-globe.frag"); gl.glEnable(GL_DEPTH_TEST); start = System.currentTimeMillis(); } private void initDebug(GL4 gl) { window.getContext().addGLDebugListener(new GlDebugOutput()); gl.glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, null, false); gl.glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, null, true); gl.glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, null, true); } private void initBuffers(GL4 gl) { float radius = 1f; short rings = 100; short sectors = 100; FloatBuffer vertexBuffer = getVertexBuffer(radius, rings, sectors); ShortBuffer elementBuffer = getElementBuffer(radius, rings, sectors); elementCount = elementBuffer.capacity(); gl.glCreateBuffers(Buffer.MAX, bufferName); if (!bug1287) { gl.glNamedBufferStorage(bufferName.get(Buffer.VERTEX), vertexBuffer.capacity() * Float.BYTES, vertexBuffer, GL_STATIC_DRAW); gl.glNamedBufferStorage(bufferName.get(Buffer.ELEMENT), elementBuffer.capacity() * Short.BYTES, elementBuffer, GL_STATIC_DRAW); gl.glNamedBufferStorage(bufferName.get(Buffer.GLOBAL_MATRICES), Mat4x4.SIZE * 2, null, GL_MAP_WRITE_BIT); gl.glNamedBufferStorage(bufferName.get(Buffer.MODEL_MATRIX), Mat4x4.SIZE, null, GL_MAP_WRITE_BIT); } else { gl.glBindBuffer(GL_ARRAY_BUFFER, bufferName.get(Buffer.VERTEX)); gl.glBufferStorage(GL_ARRAY_BUFFER, vertexBuffer.capacity() * Float.BYTES, vertexBuffer, 0); gl.glBindBuffer(GL_ARRAY_BUFFER, 0); gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferName.get(Buffer.ELEMENT)); gl.glBufferStorage(GL_ELEMENT_ARRAY_BUFFER, elementBuffer.capacity() * Short.BYTES, elementBuffer, 0); gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); IntBuffer uniformBufferOffset = GLBuffers.newDirectIntBuffer(1); gl.glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, uniformBufferOffset); int globalBlockSize = glm.max(Mat4x4.SIZE * 2, uniformBufferOffset.get(0)); int modelBlockSize = glm.max(Mat4x4.SIZE, uniformBufferOffset.get(0)); gl.glBindBuffer(GL_UNIFORM_BUFFER, bufferName.get(Buffer.GLOBAL_MATRICES)); gl.glBufferStorage(GL_UNIFORM_BUFFER, globalBlockSize, null, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); gl.glBindBuffer(GL_UNIFORM_BUFFER, 0); gl.glBindBuffer(GL_UNIFORM_BUFFER, bufferName.get(Buffer.MODEL_MATRIX)); gl.glBufferStorage(GL_UNIFORM_BUFFER, modelBlockSize, null, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); gl.glBindBuffer(GL_UNIFORM_BUFFER, 0); destroyBuffer(uniformBufferOffset); } destroyBuffers(vertexBuffer, elementBuffer); // map the transform buffers and keep them mapped globalMatricesPointer = gl.glMapNamedBufferRange( bufferName.get(Buffer.GLOBAL_MATRICES), 0, Mat4x4.SIZE * 2, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); modelMatrixPointer = gl.glMapNamedBufferRange( bufferName.get(Buffer.MODEL_MATRIX), 0, Mat4x4.SIZE, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); } private FloatBuffer getVertexBuffer(float radius, short rings, short sectors) { float R = 1f / (float) (rings - 1); float S = 1f / (float) (sectors - 1); short r, s; float x, y, z; FloatBuffer vertexBuffer = GLBuffers.newDirectFloatBuffer(rings * sectors * (3 + 2)); for (r = 0; r < rings; r++) { for (s = 0; s < sectors; s++) { x = glm.cos(2 * (float) glm.pi * s * S) * glm.sin((float) glm.pi * r * R); y = glm.sin(-(float) glm.pi / 2 + (float) glm.pi * r * R); z = glm.sin(2 * (float) glm.pi * s * S) * glm.sin((float) glm.pi * r * R); // positions vertexBuffer.put(x * radius); vertexBuffer.put(y * radius); vertexBuffer.put(z * radius); // texture coordinates vertexBuffer.put(1 - s * S); vertexBuffer.put(r * R); } } vertexBuffer.position(0); return vertexBuffer; } private ShortBuffer getElementBuffer(float radius, short rings, short sectors) { float R = 1f / (float) (rings - 1); float S = 1f / (float) (sectors - 1); short r, s; float x, y, z; ShortBuffer elementBuffer = GLBuffers.newDirectShortBuffer(rings * sectors * 6); for (r = 0; r < rings - 1; r++) { for (s = 0; s < sectors - 1; s++) { elementBuffer.put((short) (r * sectors + s)); elementBuffer.put((short) (r * sectors + (s + 1))); elementBuffer.put((short) ((r + 1) * sectors + (s + 1))); elementBuffer.put((short) ((r + 1) * sectors + (s + 1))); elementBuffer.put((short) (r * sectors + s)); // elementBuffer.put((short) (r * sectors + (s + 1))); elementBuffer.put((short) ((r + 1) * sectors + s)); } } elementBuffer.position(0); return elementBuffer; } private void initVertexArray(GL4 gl) { gl.glCreateVertexArrays(1, vertexArrayName); gl.glVertexArrayAttribBinding(vertexArrayName.get(0), Semantic.Attr.POSITION, Semantic.Stream.A); gl.glVertexArrayAttribBinding(vertexArrayName.get(0), Semantic.Attr.TEXCOORD, Semantic.Stream.A); gl.glVertexArrayAttribFormat(vertexArrayName.get(0), Semantic.Attr.POSITION, Vec3.length, GL_FLOAT, false, 0); gl.glVertexArrayAttribFormat(vertexArrayName.get(0), Semantic.Attr.TEXCOORD, Vec2.length, GL_FLOAT, false, Vec3.SIZE); gl.glEnableVertexArrayAttrib(vertexArrayName.get(0), Semantic.Attr.POSITION); gl.glEnableVertexArrayAttrib(vertexArrayName.get(0), Semantic.Attr.TEXCOORD); gl.glVertexArrayElementBuffer(vertexArrayName.get(0), bufferName.get(Buffer.ELEMENT)); gl.glVertexArrayVertexBuffer(vertexArrayName.get(0), Semantic.Stream.A, bufferName.get(Buffer.VERTEX), 0, Vec2.SIZE + Vec3.SIZE); } private void initTexture(GL4 gl) { try { URL texture = getClass().getClassLoader().getResource("images/globe.png"); TextureData textureData = TextureIO.newTextureData(gl.getGLProfile(), texture, false, TextureIO.PNG); gl.glCreateTextures(GL_TEXTURE_2D, 1, textureName); gl.glTextureParameteri(textureName.get(0), GL_TEXTURE_BASE_LEVEL, 0); gl.glTextureParameteri(textureName.get(0), GL_TEXTURE_MAX_LEVEL, 0); gl.glTextureStorage2D(textureName.get(0), 1, // level textureData.getInternalFormat(), textureData.getWidth(), textureData.getHeight()); gl.glTextureSubImage2D(textureName.get(0), 0, // level 0, 0, // offset textureData.getWidth(), textureData.getHeight(), textureData.getPixelFormat(), textureData.getPixelType(), textureData.getBuffer()); } catch (IOException ex) { Logger.getLogger(HelloGlobe.class.getName()).log(Level.SEVERE, null, ex); } } private void initSampler(GL4 gl) { gl.glGenSamplers(1, samplerName); gl.glSamplerParameteri(samplerName.get(0), GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.glSamplerParameteri(samplerName.get(0), GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.glSamplerParameteri(samplerName.get(0), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl.glSamplerParameteri(samplerName.get(0), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } @Override public void display(GLAutoDrawable drawable) { GL4 gl = drawable.getGL().getGL4(); // view matrix { Mat4x4 view = glm.lookAt(new Vec3(0, 0, 3), new Vec3(), new Vec3(0, 1, 0)); view.to(globalMatricesPointer, Mat4x4.SIZE); } gl.glClearBufferfv(GL_COLOR, 0, clearColor.put(0, 1f).put(1, .5f).put(2, 0f).put(3, 1f)); gl.glClearBufferfv(GL_DEPTH, 0, clearDepth.put(0, 1f)); // model matrix { long now = System.currentTimeMillis(); float diff = (float) (now - start) / 1_000f; Mat4x4 model = new Mat4x4().rotate_(-diff, 0f, 1f, 0f); model.to(modelMatrixPointer); } gl.glUseProgram(program.name); gl.glBindVertexArray(vertexArrayName.get(0)); gl.glBindBufferBase( GL_UNIFORM_BUFFER, Semantic.Uniform.TRANSFORM0, bufferName.get(Buffer.GLOBAL_MATRICES)); gl.glBindBufferBase( GL_UNIFORM_BUFFER, Semantic.Uniform.TRANSFORM1, bufferName.get(Buffer.MODEL_MATRIX)); gl.glBindTextureUnit( Semantic.Sampler.DIFFUSE, textureName.get(0)); gl.glBindSampler(Semantic.Sampler.DIFFUSE, samplerName.get(0)); gl.glDrawElements( GL_TRIANGLES, elementCount, GL_UNSIGNED_SHORT, 0); } @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { GL4 gl = drawable.getGL().getGL4(); float aspect = (float) width / height; Mat4x4 proj = glm.perspective((float) glm.pi * 0.25f, aspect, 0.1f, 100f); proj.to(globalMatricesPointer); } @Override public void dispose(GLAutoDrawable drawable) { GL4 gl = drawable.getGL().getGL4(); gl.glUnmapNamedBuffer(bufferName.get(Buffer.GLOBAL_MATRICES)); gl.glUnmapNamedBuffer(bufferName.get(Buffer.MODEL_MATRIX)); gl.glDeleteProgram(program.name); gl.glDeleteVertexArrays(1, vertexArrayName); gl.glDeleteBuffers(Buffer.MAX, bufferName); gl.glDeleteTextures(1, textureName); gl.glDeleteSamplers(1, samplerName); destroyBuffers(vertexArrayName, bufferName, textureName, samplerName, clearColor, clearDepth); } @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { new Thread(() -> { window.destroy(); }).start(); } } @Override public void keyReleased(KeyEvent e) { } }