package de.lessvoid.nifty.renderer.lwjgl.render; import de.lessvoid.nifty.render.BlendMode; import de.lessvoid.nifty.spi.render.MouseCursor; import de.lessvoid.nifty.spi.render.RenderDevice; import de.lessvoid.nifty.spi.render.RenderFont; import de.lessvoid.nifty.spi.render.RenderImage; import de.lessvoid.nifty.spi.time.TimeProvider; import de.lessvoid.nifty.spi.time.impl.AccurateTimeProvider; import de.lessvoid.nifty.tools.Color; import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.util.glu.GLU; import java.io.IOException; import java.nio.IntBuffer; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * Lwjgl RenderDevice Implementation. * * @author void * * @deprecated Use {@link de.lessvoid.nifty.render.batch.BatchRenderDevice} with either * {@link LwjglBatchRenderBackendFactory#create()} or {@link LwjglBatchRenderBackendCoreProfileFactory#create()}. */ @Deprecated public class LwjglRenderDevice implements RenderDevice { private static final Logger log = Logger.getLogger(LwjglRenderDevice.class.getName()); private static final IntBuffer viewportBuffer = BufferUtils.createIntBuffer(4 * 4); private final TimeProvider timeProvider = new AccurateTimeProvider(); private NiftyResourceLoader resourceLoader; private int viewportWidth = -1; private int viewportHeight = -1; private long time; private long frames; private boolean displayFPS = false; private boolean logFPS = false; private RenderFont fpsFont; // we keep track of which GL states we've already set to make sure we don't set // the same state twice. private boolean currentTexturing = true; @Nullable private BlendMode currentBlendMode = null; private boolean currentClipping = false; private int currentClippingX0 = 0; private int currentClippingY0 = 0; private int currentClippingX1 = 0; private int currentClippingY1 = 0; @Nonnull private StringBuilder buffer = new StringBuilder(); private int quadCount; private int glyphCount; @Nullable private MouseCursor mouseCursor; /** * The standard constructor. You'll use this in production code. Using this * constructor will configure the RenderDevice to not log FPS on System.out. */ public LwjglRenderDevice() { time = timeProvider.getMsTime(); frames = 0; } /** * The development mode constructor allows to display the FPS on screen when * the given flag is set to true. Note that setting displayFPS to false will * still log the FPS on System.out every couple of frames. */ public LwjglRenderDevice(final boolean displayFPS) { this(); this.logFPS = true; this.displayFPS = displayFPS; } @Override public void setResourceLoader(@Nonnull final NiftyResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; if (this.displayFPS) { fpsFont = createFont("fps.fnt"); } } /** * Get Width. * * @return width of display mode */ @Override public int getWidth() { if (viewportWidth == -1) { getViewport(); } return viewportWidth; } /** * Get Height. * * @return height of display mode */ @Override public int getHeight() { if (viewportHeight == -1) { getViewport(); } return viewportHeight; } private void getViewport() { GL11.glGetInteger(GL11.GL_VIEWPORT, viewportBuffer); viewportWidth = viewportBuffer.get(2); viewportHeight = viewportBuffer.get(3); if (log.isLoggable(Level.FINE)) { log.fine("Viewport: " + viewportWidth + ", " + viewportHeight); } } @Override public void beginFrame() { log.fine("beginFrame()"); // set inital states for each frame GL11.glEnable(GL11.GL_BLEND); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); setBlendMode(BlendMode.BLEND); GL11.glEnable(GL11.GL_TEXTURE_2D); currentTexturing = true; GL11.glDisable(GL11.GL_SCISSOR_TEST); currentClipping = false; currentClippingX0 = 0; currentClippingY0 = 0; currentClippingX1 = 0; currentClippingY1 = 0; quadCount = 0; glyphCount = 0; } @Override public void endFrame() { log.fine("endFrame"); frames++; long diff = timeProvider.getMsTime() - time; if (diff >= 1000) { time += diff; long lastFrames = frames; buffer.setLength(0); buffer.append("FPS: "); buffer.append(lastFrames); buffer.append(" ("); buffer.append(String.format("%f", 1000.f / lastFrames)); buffer.append(" ms)"); buffer.append(", Total Tri: "); buffer.append(quadCount * 2); buffer.append(" (Text: "); buffer.append(glyphCount * 2); buffer.append(")"); buffer.append(", Total Vert: "); buffer.append(quadCount * 4); buffer.append(" (Text: "); buffer.append(glyphCount * 4); buffer.append(")"); if (logFPS) { System.out.println(buffer.toString()); } frames = 0; } if (displayFPS) { renderFont(fpsFont, buffer.toString(), 10, getHeight() - fpsFont.getHeight() - 10, Color.WHITE, 1.0f, 1.0f); } // currently the RenderDevice interface does not support a way to be notified when the resolution is changed // so we reset the viewportWidth and viewportHeight here so that we only call getViewport() once per frame and // not each time someone calls getWidth() or getHeight(). viewportWidth = -1; viewportHeight = -1; checkGLError(); } @Override public void clear() { log.fine("clear()"); GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); GL11.glClear(GL11.GL_COLOR_BUFFER_BIT); } /** * Create a new RenderImage. * * @param filename filename * @param filterLinear linear filter the image * @return RenderImage */ @Override @Nonnull public RenderImage createImage(@Nonnull final String filename, final boolean filterLinear) { return new LwjglRenderImage(filename, filterLinear, resourceLoader); } /** * Create a new RenderFont. * * @param filename filename * @return RenderFont */ @Override @Nonnull public RenderFont createFont(@Nonnull final String filename) { return new LwjglRenderFont(filename, resourceLoader); } /** * Render a quad. * * @param x x * @param y y * @param width width * @param height height * @param color color */ @Override public void renderQuad(final int x, final int y, final int width, final int height, @Nonnull final Color color) { log.fine("renderQuad()"); if (currentTexturing) { GL11.glDisable(GL11.GL_TEXTURE_2D); currentTexturing = false; } GL11.glColor4f(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); GL11.glBegin(GL11.GL_QUADS); GL11.glVertex2i(x, y); GL11.glVertex2i(x + width, y); GL11.glVertex2i(x + width, y + height); GL11.glVertex2i(x, y + height); GL11.glEnd(); quadCount++; } @Override public void renderQuad( final int x, final int y, final int width, final int height, @Nonnull final Color topLeft, @Nonnull final Color topRight, @Nonnull final Color bottomRight, @Nonnull final Color bottomLeft) { log.fine("renderQuad2()"); if (currentTexturing) { GL11.glDisable(GL11.GL_TEXTURE_2D); currentTexturing = false; } GL11.glBegin(GL11.GL_QUADS); GL11.glColor4f(topLeft.getRed(), topLeft.getGreen(), topLeft.getBlue(), topLeft.getAlpha()); GL11.glVertex2i(x, y); GL11.glColor4f(topRight.getRed(), topRight.getGreen(), topRight.getBlue(), topRight.getAlpha()); GL11.glVertex2i(x + width, y); GL11.glColor4f(bottomRight.getRed(), bottomRight.getGreen(), bottomRight.getBlue(), bottomRight.getAlpha()); GL11.glVertex2i(x + width, y + height); GL11.glColor4f(bottomLeft.getRed(), bottomLeft.getGreen(), bottomLeft.getBlue(), bottomLeft.getAlpha()); GL11.glVertex2i(x, y + height); GL11.glEnd(); quadCount++; } /** * Render the image using the given Box to specify the render attributes. * * @param x x * @param y y * @param width width * @param height height * @param color color * @param scale scale */ @Override public void renderImage( @Nonnull final RenderImage image, final int x, final int y, final int width, final int height, @Nonnull final Color color, final float scale) { log.fine("renderImage()"); if (width < 0) { log.warning("Attempt to render image with negative width"); return; } if (height < 0) { log.warning("Attempt to render image with negative height"); return; } if (!currentTexturing) { GL11.glEnable(GL11.GL_TEXTURE_2D); currentTexturing = true; } GL11.glPushMatrix(); GL11.glTranslatef(x + width / 2, y + height / 2, 0.0f); GL11.glScalef(scale, scale, 1.0f); GL11.glTranslatef(-(x + width / 2), -(y + height / 2), 0.0f); LwjglRenderImage internalImage = (LwjglRenderImage) image; internalImage.bind(); float textureWidth = (float) internalImage.getTextureWidth(); float textureHeight = (float) internalImage.getTextureHeight(); float imageWidth = (float) internalImage.getWidth(); float imageHeight = (float) internalImage.getHeight(); float u1 = imageWidth / textureWidth; float v1 = imageHeight / textureHeight; GL11.glColor4f(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); GL11.glBegin(GL11.GL_QUADS); GL11.glTexCoord2f(0.0f, 0.0f); GL11.glVertex2i(x, y); GL11.glTexCoord2f(u1, 0.0f); GL11.glVertex2i(x + width, y); GL11.glTexCoord2f(u1, v1); GL11.glVertex2i(x + width, y + height); GL11.glTexCoord2f(0.0f, v1); GL11.glVertex2i(x, y + height); GL11.glEnd(); GL11.glPopMatrix(); quadCount++; } /** * Render sub image. * * @param x x * @param y y * @param w w * @param h h * @param srcX x * @param srcY y * @param srcW w * @param srcH h * @param color color */ @Override public void renderImage( @Nonnull final RenderImage image, final int x, final int y, final int w, final int h, final int srcX, final int srcY, final int srcW, final int srcH, @Nonnull final Color color, final float scale, final int centerX, final int centerY) { log.fine("renderImage2()"); if (w < 0) { log.warning("Attempt to render image with negative width"); return; } if (h < 0) { log.warning("Attempt to render image with negative height"); return; } if (!currentTexturing) { GL11.glEnable(GL11.GL_TEXTURE_2D); currentTexturing = true; } GL11.glPushMatrix(); GL11.glTranslatef(centerX, centerY, 0.0f); GL11.glScalef(scale, scale, 1.0f); GL11.glTranslatef(-(centerX), -(centerY), 0.0f); LwjglRenderImage internalImage = (LwjglRenderImage) image; internalImage.bind(); float textureWidth = (float) internalImage.getTextureWidth(); float textureHeight = (float) internalImage.getTextureHeight(); float u0 = srcX / textureWidth; float v0 = srcY / textureHeight; float u1 = (srcX + srcW) / textureWidth; float v1 = (srcY + srcH) / textureHeight; GL11.glColor4f(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); GL11.glBegin(GL11.GL_QUADS); GL11.glTexCoord2f(u0, v0); GL11.glVertex2i(x, y); GL11.glTexCoord2f(u1, v0); GL11.glVertex2i(x + w, y); GL11.glTexCoord2f(u1, v1); GL11.glVertex2i(x + w, y + h); GL11.glTexCoord2f(u0, v1); GL11.glVertex2i(x, y + h); GL11.glEnd(); GL11.glPopMatrix(); quadCount++; } /** * render the text. */ @Override public void renderFont( @Nonnull final RenderFont font, @Nonnull final String text, final int x, final int y, @Nonnull final Color color, final float fontSizeX, final float fontSizeY) { log.fine("renderFont()"); if (!currentTexturing) { GL11.glEnable(GL11.GL_TEXTURE_2D); currentTexturing = true; } setBlendMode(BlendMode.BLEND); int count = ((LwjglRenderFont) font).getFont().renderWithSizeAndColor(x, y, text, fontSizeX, fontSizeY, color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); glyphCount += count; quadCount += count; } private void checkGLError() { int error = GL11.glGetError(); if (error != GL11.GL_NO_ERROR) { String glerrmsg = GLU.gluErrorString(error); log.warning("Error: (" + error + ") " + glerrmsg); try { throw new Exception(); } catch (Exception e) { e.printStackTrace(); } } } /** * Enable clipping to the given region. * * @param x0 x0 * @param y0 y0 * @param x1 x1 * @param y1 y1 */ @Override public void enableClip(final int x0, final int y0, final int x1, final int y1) { log.fine("enableClip()"); if (currentClipping && currentClippingX0 == x0 && currentClippingY0 == y0 && currentClippingX1 == x1 && currentClippingY1 == y1) { return; } currentClipping = true; currentClippingX0 = x0; currentClippingY0 = y0; currentClippingX1 = x1; currentClippingY1 = y1; GL11.glScissor(x0, getHeight() - y1, x1 - x0, y1 - y0); GL11.glEnable(GL11.GL_SCISSOR_TEST); } /** * Disable Clip. */ @Override public void disableClip() { log.fine("disableClip()"); if (!currentClipping) { return; } GL11.glDisable(GL11.GL_SCISSOR_TEST); currentClipping = false; currentClippingX0 = 0; currentClippingY0 = 0; currentClippingX1 = 0; currentClippingY1 = 0; } @Override public void setBlendMode(@Nonnull final BlendMode renderMode) { log.fine("setBlendMode()"); if (renderMode.equals(currentBlendMode)) { return; } currentBlendMode = renderMode; if (currentBlendMode.equals(BlendMode.BLEND)) { GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); } else if (currentBlendMode.equals(BlendMode.MULIPLY)) { GL11.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO); } } @Override @Nonnull public MouseCursor createMouseCursor( @Nonnull final String filename, final int hotspotX, final int hotspotY) throws IOException { return new LwjglMouseCursor(filename, hotspotX, hotspotY, resourceLoader); } @Override public void enableMouseCursor(@Nonnull final MouseCursor mouseCursor) { this.mouseCursor = mouseCursor; mouseCursor.enable(); } @Override public void disableMouseCursor() { if (mouseCursor != null) { mouseCursor.disable(); } } }