Java tutorial
/* * Copyright LWJGL. All rights reserved. * License terms: http://lwjgl.org/license.php */ import org.lwjgl.BufferUtils; import org.lwjgl.glfw.*; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLUtil; import org.lwjgl.stb.STBTTAlignedQuad; import org.lwjgl.stb.STBTTPackContext; import org.lwjgl.stb.STBTTPackedchar; import org.lwjgl.system.libffi.Closure; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import static org.lwjgl.glfw.Callbacks.*; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL30.*; import static org.lwjgl.stb.STBTruetype.*; import static org.lwjgl.system.MemoryUtil.*; /** * STB Truetype oversampling demo. * * <p>This is a Java port of <a href="https://github.com/nothings/stb/blob/master/tests/oversample/main.c">https://github * .com/nothings/stb/blob/master/tests/oversample/main.c</a>.</p> */ public class TruetypeOversample { private static int BITMAP_W = 512; private static int BITMAP_H = 512; private static float[] scale = { 24.0f, 14.0f }; private static int[] sf = { 0, 1, 2, 0, 1, 2 }; private STBTTAlignedQuad q = STBTTAlignedQuad.malloc(); private FloatBuffer xb = memAllocFloat(1); private FloatBuffer yb = memAllocFloat(1); private GLFWErrorCallback errorfun; private GLFWWindowSizeCallback windowSizefun; private GLFWFramebufferSizeCallback framebufferSizefun; private GLFWKeyCallback keyfun; private long window; private Closure debugProc; private int ww = 1024; private int wh = 768; private int fbw = ww; private int fbh = wh; private int font_tex; private STBTTPackedchar.Buffer chardata; private int font = 3; private boolean black_on_white; private boolean integer_align; private boolean translating; private boolean rotating; private boolean supportsSRGB; private boolean srgb; private float rotate_t, translate_t; private boolean show_tex; public TruetypeOversample() { errorfun = GLFWErrorCallback.createPrint(); windowSizefun = new GLFWWindowSizeCallback() { @Override public void invoke(long window, int width, int height) { TruetypeOversample.this.ww = width; TruetypeOversample.this.wh = height; } }; framebufferSizefun = new GLFWFramebufferSizeCallback() { @Override public void invoke(long window, int width, int height) { TruetypeOversample.this.fbw = width; TruetypeOversample.this.fbh = height; } }; keyfun = new GLFWKeyCallback() { @Override public void invoke(long window, int key, int scancode, int action, int mods) { if (action == GLFW_RELEASE) return; switch (key) { case GLFW_KEY_ESCAPE: glfwSetWindowShouldClose(window, GLFW_TRUE); break; case GLFW_KEY_O: font = (font + 1) % 3 + (font / 3) * 3; break; case GLFW_KEY_S: font = (font + 3) % 6; break; case GLFW_KEY_T: translating = !translating; translate_t = 0.0f; break; case GLFW_KEY_R: rotating = !rotating; rotate_t = 0.0f; break; case GLFW_KEY_P: integer_align = !integer_align; break; case GLFW_KEY_G: if (!supportsSRGB) break; srgb = !srgb; if (srgb) glEnable(GL_FRAMEBUFFER_SRGB); else glDisable(GL_FRAMEBUFFER_SRGB); break; case GLFW_KEY_V: show_tex = !show_tex; break; case GLFW_KEY_B: black_on_white = !black_on_white; break; } } }; } public static void main(String[] args) { System.out.println("Running LWJGL native libraries."); System.setProperty("org.lwjgl.librarypath", "lib/lwjgl/native"); new TruetypeOversample().run("STB Truetype Oversample Demo"); } private void load_fonts() { font_tex = glGenTextures(); chardata = STBTTPackedchar.mallocBuffer(6 * 128); try { ByteBuffer ttf = IOUtil.ioResourceToByteBuffer("assets/fonts/Monospace.ttf", 160 * 1024); ByteBuffer bitmap = BufferUtils.createByteBuffer(BITMAP_W * BITMAP_H); STBTTPackContext pc = STBTTPackContext.malloc(); stbtt_PackBegin(pc, bitmap, BITMAP_W, BITMAP_H, 0, 1, null); for (int i = 0; i < 2; i++) { chardata.position((i * 3 + 0) * 128 + 32); stbtt_PackSetOversampling(pc, 1, 1); stbtt_PackFontRange(pc, ttf, 0, scale[i], 32, 95, chardata); chardata.position((i * 3 + 1) * 128 + 32); stbtt_PackSetOversampling(pc, 2, 2); stbtt_PackFontRange(pc, ttf, 0, scale[i], 32, 95, chardata); chardata.position((i * 3 + 2) * 128 + 32); stbtt_PackSetOversampling(pc, 3, 1); stbtt_PackFontRange(pc, ttf, 0, scale[i], 32, 95, chardata); } stbtt_PackEnd(pc); pc.free(); glBindTexture(GL_TEXTURE_2D, font_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, BITMAP_W, BITMAP_H, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bitmap); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } catch (IOException e) { throw new RuntimeException(e); } } private void draw_init() { glDisable(GL_CULL_FACE); glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); glViewport(0, 0, fbw, fbh); if (black_on_white) glClearColor(1.0f, 1.0f, 1.0f, 0.0f); else glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, ww, wh, 0.0, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } private static void drawBoxTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1) { glTexCoord2f(s0, t0); glVertex2f(x0, y0); glTexCoord2f(s1, t0); glVertex2f(x1, y0); glTexCoord2f(s1, t1); glVertex2f(x1, y1); glTexCoord2f(s0, t1); glVertex2f(x0, y1); } private void print(float x, float y, int font, String text) { xb.put(0, x); yb.put(0, y); chardata.position(font * 128); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, font_tex); glBegin(GL_QUADS); for (int i = 0; i < text.length(); i++) { stbtt_GetPackedQuad(chardata, BITMAP_W, BITMAP_H, text.charAt(i), xb, yb, q, font == 0 && integer_align ? 1 : 0); drawBoxTC(q.x0(), q.y0(), q.x1(), q.y1(), q.s0(), q.t0(), q.s1(), q.t1()); } glEnd(); } private void draw_world() { int sfont = sf[font]; float x = 20; glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if (black_on_white) glColor3f(0.0f, 0.0f, 0.0f); else glColor3f(1.0f, 1.0f, 1.0f); print(80, 30, sfont, "Controls:"); print(100, 60, sfont, "S: toggle font size"); print(100, 85, sfont, "O: toggle oversampling"); print(100, 110, sfont, "T: toggle translation"); print(100, 135, sfont, "R: toggle rotation"); print(100, 160, sfont, "P: toggle pixel-snap (only non-oversampled)"); if (supportsSRGB) print(100, 185, sfont, "G: toggle srgb gamma-correction"); if (black_on_white) print(100, 210, sfont, "B: toggle to white-on-black"); else print(100, 210, sfont, "B: toggle to black-on-white"); print(100, 235, sfont, "V: view font texture | Main Menu:"); print(80, 300, sfont, "Current font:"); if (!show_tex) { if (font < 3) print(100, 350, sfont, "Font height: 24 pixels"); else print(100, 350, sfont, "Font height: 14 pixels"); } if (font % 3 == 1) print(100, 325, sfont, "2x2 oversampled text at 1:1"); else if (font % 3 == 2) print(100, 325, sfont, "3x1 oversampled text at 1:1"); else if (integer_align) print(100, 325, sfont, "1:1 text, one texel = one pixel, snapped to integer coordinates"); else print(100, 325, sfont, "1:1 text, one texel = one pixel"); if (show_tex) { glBegin(GL_QUADS); drawBoxTC(200, 400, 200 + BITMAP_W, 300 + BITMAP_H, 0, 0, 1, 1); glEnd(); } else { glMatrixMode(GL_MODELVIEW); glTranslatef(200, 350, 0); if (translating) x += translate_t * 8 % 30; if (rotating) { glTranslatef(100, 150, 0); glRotatef(rotate_t * 2, 0, 0, 1); glTranslatef(-100, -150, 0); } print(x, 100, font, "This is a test"); print(x, 130, font, "Now is the time for all good men to come to the aid of their country."); print(x, 160, font, "The quick brown fox jumps over the lazy dog."); print(x, 190, font, "0123456789"); } } private void draw() { draw_init(); draw_world(); glfwSwapBuffers(window); } private void loopmode(float dt) { if (dt > 0.25f) dt = 0.25f; if (dt < 0.01f) dt = 0.01f; rotate_t += dt; translate_t += dt; draw(); } private void createWindow(String title) { glfwSetErrorCallback(errorfun); if (glfwInit() != GLFW_TRUE) throw new IllegalStateException("Unable to initialize GLFW"); glfwDefaultWindowHints(); glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); this.window = glfwCreateWindow(ww, wh, title, NULL, NULL); if (window == NULL) throw new RuntimeException("Failed to create the GLFW window"); windowSizefun.set(window); framebufferSizefun.set(window); keyfun.set(window); GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); glfwSetWindowPos(window, (vidmode.width() - ww) / 2, (vidmode.height() - wh) / 2); glfwMakeContextCurrent(window); GL.createCapabilities(); debugProc = GLUtil.setupDebugMessageCallback(); glfwSwapInterval(1); glfwShowWindow(window); glfwInvoke(window, windowSizefun, framebufferSizefun); // Detect sRGB support GLCapabilities caps = GL.getCapabilities(); supportsSRGB = caps.OpenGL30 || caps.GL_ARB_framebuffer_sRGB || caps.GL_EXT_framebuffer_sRGB; } private void run(String title) { try { createWindow(title); load_fonts(); long time = System.nanoTime(); while (glfwWindowShouldClose(window) == GLFW_FALSE) { glfwPollEvents(); long t = System.nanoTime(); float dt = (float) ((t - time) / 1000000000.0); time = t; loopmode(dt); } } finally { try { destroy(); } catch (Exception e) { e.printStackTrace(); } } } private void destroy() { memFree(chardata); if (debugProc != null) debugProc.release(); keyfun.release(); framebufferSizefun.release(); windowSizefun.release(); glfwTerminate(); errorfun.release(); memFree(yb); memFree(xb); q.free(); } }