All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.xpenatan.gdx.backends.teavm.TeaGraphics Maven / Gradle / Ivy

There is a newer version: 1.0.4
Show newest version
package com.github.xpenatan.gdx.backends.teavm;

import com.badlogic.gdx.Application;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Graphics;
import com.badlogic.gdx.graphics.Cursor;
import com.badlogic.gdx.graphics.Cursor.SystemCursor;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.GL30;
import com.badlogic.gdx.graphics.GL31;
import com.badlogic.gdx.graphics.GL32;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.glutils.GLVersion;
import com.badlogic.gdx.utils.IntMap;
import com.github.xpenatan.gdx.backends.teavm.dom.DocumentWrapper;
import com.github.xpenatan.gdx.backends.teavm.dom.HTMLCanvasElementWrapper;
import com.github.xpenatan.gdx.backends.teavm.dom.HTMLElementWrapper;
import com.github.xpenatan.gdx.backends.teavm.dom.StyleWrapper;
import com.github.xpenatan.gdx.backends.teavm.dom.impl.TeaWindow;
import com.github.xpenatan.gdx.backends.teavm.gl.WebGL2RenderingContextWrapper;
import com.github.xpenatan.gdx.backends.teavm.gl.WebGLContextAttributesWrapper;
import com.github.xpenatan.gdx.backends.teavm.gl.WebGLRenderingContextWrapper;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.browser.Window;
import org.teavm.jso.dom.html.HTMLCanvasElement;
import org.teavm.jso.webgl.WebGLContextAttributes;

/**
 * @author xpenatan
 */
public class TeaGraphics implements Graphics {
    private WebGLRenderingContextWrapper context;
    protected HTMLCanvasElementWrapper canvas;
    protected TeaApplicationConfiguration config;
    protected GL20 gl20;
    protected GL30 gl30;
    protected GLVersion glVersion;

    float fps = 0;
    long lastTimeStamp = System.currentTimeMillis();
    long frameId = -1;
    float deltaTime = 0;
    float time = 0;
    int frames;

    public TeaGraphics(TeaApplicationConfiguration config) {
        this.config = config;
        TeaWindow window = new TeaWindow();
        DocumentWrapper document = window.getDocument();
        HTMLElementWrapper elementID = document.getElementById(config.canvasID);
        this.canvas = (HTMLCanvasElementWrapper)elementID;

        WebGLContextAttributesWrapper attr = WebGLContextAttributesWrapper.create();
        attr.setAlpha(config.alpha);
        attr.setAntialias(config.antialiasing);
        attr.setStencil(config.stencil);
        attr.setPremultipliedAlpha(config.premultipliedAlpha);
        attr.setPreserveDrawingBuffer(config.preserveDrawingBuffer);
        attr.setPowerPreference(config.powerPreference);
        HTMLCanvasElement canvas1 = (HTMLCanvasElement)canvas;

        if (config.useGL30) {
            context = (WebGLRenderingContextWrapper)canvas1.getContext("webgl2", attr);
        }

        if (config.useGL30 && context != null) {
            // WebGL2 supported
            this.gl30 = config.useDebugGL ? new TeaGL30Debug((WebGL2RenderingContextWrapper)context)
                    : new TeaGL30((WebGL2RenderingContextWrapper)context);
            this.gl20 = gl30;
        } else {
            context = (WebGLRenderingContextWrapper)canvas1.getContext("webgl", attr);
            this.gl20 = config.useDebugGL ? new TeaGL20Debug(context) : new TeaGL20(context);
        }

        String versionString = gl20.glGetString(GL20.GL_VERSION);
        String vendorString = gl20.glGetString(GL20.GL_VENDOR);
        String rendererString = gl20.glGetString(GL20.GL_RENDERER);
        glVersion = new GLVersion(Application.ApplicationType.WebGL, versionString, vendorString, rendererString);

        if(config.width >= 0 || config.height >= 0) {
            if(config.isFixedSizeApplication()) {
                setCanvasSize(config.width, config.height);
            }
            else {
                TeaWindow currentWindow = TeaWindow.get();
                int width = currentWindow.getClientWidth() - config.padHorizontal;
                int height = currentWindow.getClientHeight() - config.padVertical;
                double density = config.usePhysicalPixels ? getNativeScreenDensity() : 1;
                setCanvasSize((int)(density * width), (int)(density * height));
            }
        }

        // listen to fullscreen changes
        addFullscreenChangeListener(canvas, new FullscreenChanged() {
          @Override
          public void fullscreenChanged() {
              // listening to fullscreen mode changes
          }
        });
    }

    public void update() {
        long currTimeStamp = System.currentTimeMillis();
        deltaTime = (currTimeStamp - lastTimeStamp) / 1000.0f;
        lastTimeStamp = currTimeStamp;
        time += deltaTime;
        frames++;
        if(time > 1) {
            this.fps = frames;
            time = 0;
            frames = 0;
        }
    }

    @Override
    public boolean isGL30Available() {
        return gl30 != null;
    }

    @Override
    public boolean isGL31Available() {
        return false;
    }

    @Override
    public boolean isGL32Available() {
        return false;
    }

    @Override
    public GL20 getGL20() {
        return gl20;
    }

    @Override
    public GL30 getGL30() {
        return gl30;
    }

    @Override
    public GL31 getGL31() {
        return null;
    }

    @Override
    public GL32 getGL32() {
        return null;
    }

    @Override
    public void setGL20(GL20 gl20) {
        this.gl20 = gl20;
        Gdx.gl = gl20;
        Gdx.gl20 = gl20;
    }

    @Override
    public void setGL30(GL30 gl30) {
        this.gl30 = gl30;
        if (gl30 != null) {
            this.gl20 = gl30;
            Gdx.gl = gl20;
            Gdx.gl20 = gl20;
            Gdx.gl30 = gl30;
        }
    }

    @Override
    public void setGL31(GL31 gl31) {
    }

    @Override
    public void setGL32(GL32 gl32) {
    }

    @Override
    public int getWidth() {
        return canvas.getWidth();
    }

    @Override
    public int getHeight() {
        return canvas.getHeight();
    }

    @Override
    public int getBackBufferWidth() {
        return canvas.getWidth();
    }

    @Override
    public int getBackBufferHeight() {
        return canvas.getHeight();
    }

    @Override
    public int getSafeInsetLeft() {
        return 0;
    }

    @Override
    public int getSafeInsetTop() {
        return 0;
    }

    @Override
    public int getSafeInsetBottom() {
        return 0;
    }

    @Override
    public int getSafeInsetRight() {
        return 0;
    }

    @Override
    public long getFrameId() {
        return frameId;
    }

    @Override
    public float getDeltaTime() {
        return deltaTime;
    }

    @Override
    public float getRawDeltaTime() {
        return deltaTime;
    }

    @Override
    public int getFramesPerSecond() {
        return (int)fps;
    }

    @Override
    public GraphicsType getType() {
        return GraphicsType.WebGL;
    }

    @Override
    public GLVersion getGLVersion() {
        return glVersion;
    }

    @Override
    public float getPpiX () {
        return 96f * (float)getNativeScreenDensity();
    }

    @Override
    public float getPpiY () {
        return 96f * (float)getNativeScreenDensity();
    }

    @Override
    public float getPpcX () {
        return getPpiX() / 2.54f;
    }

    @Override
    public float getPpcY () {
        return getPpiY() / 2.54f;
    }

    @Override
    public float getDensity() {
        float ppiX = getPpiX();
        return (ppiX > 0 && ppiX <= Float.MAX_VALUE) ? ppiX / 160f : 1f;
    }

    @Override
    public boolean supportsDisplayModeChange() {
        return true;
    }

    static class TeaMonitor extends Monitor {
      protected TeaMonitor (int virtualX, int virtualY, String name) {
        super(virtualX, virtualY, name);
      }
    }

    @Override
    public Monitor getPrimaryMonitor() {
        return new TeaMonitor(0, 0, "Primary Monitor");
    }

    @Override
    public Monitor getMonitor() {
        return getPrimaryMonitor();
    }

    @Override
    public Monitor[] getMonitors() {
      return new Monitor[] {getPrimaryMonitor()};
    }

    @Override
    public DisplayMode[] getDisplayModes() {
        return new DisplayMode[]{getDisplayMode()};
    }

    @Override
    public DisplayMode[] getDisplayModes(Monitor monitor) {
        return getDisplayModes();
    }

    @Override
    public DisplayMode getDisplayMode() {
        double density = config.usePhysicalPixels ? getNativeScreenDensity() : 1;
        return new DisplayMode((int)(getScreenWidthNATIVE() * density), (int)(getScreenHeightNATIVE() * density), 60, 8) {
        };
    }

    @Override
    public DisplayMode getDisplayMode(Monitor monitor) {
        return getDisplayMode();
    }

    @Override
    public boolean setFullscreenMode(DisplayMode displayMode) {
        DisplayMode supportedMode = getDisplayMode();
        if(displayMode.width != supportedMode.width && displayMode.height != supportedMode.height) return false;
        return enterFullscreen(canvas, displayMode.width, displayMode.height);
    }

    @Override
    public boolean setWindowedMode(int width, int height) {
        if(isFullscreen()) exitFullscreen();
        // don't set canvas for resizable applications, resize handler will do it
        if(!config.isAutoSizeApplication()) {
            setCanvasSize(width, height);
        }
        return true;
    }

    void setCanvasSize(int width, int height) {
        canvas.setWidth(width);
        canvas.setHeight(height);
        if(config.usePhysicalPixels) {
            //TODO Not tested
            double density = getNativeScreenDensity();
            StyleWrapper style = canvas.getStyle();
            style.setProperty("width", width / density + StyleWrapper.Unit.PX.getType());
            style.setProperty("height", height / density + StyleWrapper.Unit.PX.getType());
        }
    }

    @Override
    public void setTitle(String title) {
        Window.current().getDocument().setTitle(title);
    }

    @Override
    public void setUndecorated(boolean undecorated) {
        // not available
    }

    @Override
    public void setResizable(boolean resizable) {
        // not available
    }

    @Override
    public void setVSync(boolean vsync) {
        // not available
    }

    @Override
    public BufferFormat getBufferFormat() {
        return new BufferFormat(8, 8, 8, 0, 16, config.stencil ? 8 : 0, 0, false);
    }

    @Override
    public boolean supportsExtension(String extensionName) {
        // Contrary to regular OpenGL, WebGL extensions need to be explicitly enabled before they can be used. See
        // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Using_Extensions
        // Thus, it is not safe to use an extension just because context.getSupportedExtensions() tells you it is available.
        // We need to call getExtension() to enable it.
        return context.getExtension(extensionName) != null;
    }

    @Override
    public void setContinuousRendering(boolean isContinuous) {
        // not available
    }

    @Override
    public boolean isContinuousRendering() {
        return true;
    }

    @Override
    public void requestRendering() {
        // not available
    }

    @Override
    public Cursor newCursor(Pixmap pixmap, int xHotspot, int yHotspot) {
        return new TeaCursor(pixmap, xHotspot, yHotspot);
    }

    @Override
    public void setCursor(Cursor cursor) {
      canvas.getStyle().setProperty("cursor", ((TeaCursor)cursor).cssCursorProperty);
    }

    @Override
    public void setSystemCursor(SystemCursor systemCursor) {
      canvas.getStyle().setProperty("cursor", TeaCursor.getNameForSystemCursor(systemCursor));
    }

    @Override
    public float getBackBufferScale() {
        return getBackBufferWidth() / (float)getWidth();
    }

    @Override
    public void setForegroundFPS(int fps) {
        // not available
    }

    // ##################### NATIVE CALLS #####################

    public double getNativeScreenDensity() {
        return getNativeScreenDensityNATIVE();
    }

    @JSBody(script = "return devicePixelRatio || 1;")
    private static native int getNativeScreenDensityNATIVE();

    @JSBody(script = "return screen.width;")
    private static native int getScreenWidthNATIVE();

    @JSBody(script = "return screen.height;")
    private static native int getScreenHeightNATIVE();

    @JSBody(params = {"element", "fullscreenChanged"}, script = "" +
            "if (element.requestFullscreen) {\n" +
            "   document.addEventListener(\"fullscreenchange\", fullscreenChanged, false);\n" +
            "}\n" +
            "// Attempt to the vendor specific variants of the API\n" +
            "if (element.webkitRequestFullScreen) {\n" +
            "   document.addEventListener(\"webkitfullscreenchange\", fullscreenChanged, false);\n" +
            "}\n" +
            "if (element.mozRequestFullScreen) {\n" +
            "   document.addEventListener(\"mozfullscreenchange\", fullscreenChanged, false);\n" +
            "}\n" +
            "if (element.msRequestFullscreen) {\n" +
            "   document.addEventListener(\"msfullscreenchange\", fullscreenChanged, false);\n" +
            "}")
    private static native void addFullscreenChangeListener(HTMLCanvasElementWrapper element, FullscreenChanged fullscreenChanged);

    @JSFunctor
    public interface FullscreenChanged extends org.teavm.jso.JSObject {
        void fullscreenChanged();
    }

    public boolean enterFullscreen(HTMLCanvasElementWrapper element, int screenWidth, int screenHeight) {
        return enterFullscreenNATIVE(element, screenWidth, screenHeight);
    }

    @JSBody(params = {"element", "screenWidth", "screenHeight"}, script = "" +
            "if (element.requestFullscreen) {\n" +
            "   element.width = screenWidth;\n" +
            "   element.height = screenHeight;\n" +
            "   element.requestFullscreen();\n" +
            "   return true;\n" +
            "}\n" +
            "// Attempt to the vendor specific variants of the API\n" +
            "if (element.webkitRequestFullScreen) {\n" +
            "   element.width = screenWidth;\n" +
            "   element.height = screenHeight;\n" +
            "   element.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);\n" +
            "   return true;\n" +
            "}\n" +
            "if (element.mozRequestFullScreen) {\n" +
            "   element.width = screenWidth;\n" +
            "   element.height = screenHeight;\n" +
            "   element.mozRequestFullScreen();\n" +
            "   return true;\n" +
            "}\n" +
            "if (element.msRequestFullscreen) {\n" +
            "   element.width = screenWidth;\n" +
            "   element.height = screenHeight;\n" +
            "   element.msRequestFullscreen();\n" +
            "   return true;\n" +
            "}\n" +
            "\n" +
            "return false;")
    private static native boolean enterFullscreenNATIVE(HTMLCanvasElementWrapper element, int screenWidth, int screenHeight);

    public void exitFullscreen() {
        exitFullscreenNATIVE();
    }

    @JSBody(script = "" +
            "if (document.exitFullscreen)\n" +
            "  document.exitFullscreen();\n" +
            "if (document.msExitFullscreen)\n" +
            "  document.msExitFullscreen();\n" +
            "if (document.webkitExitFullscreen)\n" +
            "  document.webkitExitFullscreen();\n" +
            "if (document.mozExitFullscreen)\n" +
            "  document.mozExitFullscreen();\n" +
            "if (document.webkitCancelFullScreen) // Old WebKit\n" +
            "  document.webkitCancelFullScreen();")
    private static native boolean exitFullscreenNATIVE();

    @Override
    public boolean isFullscreen() {
        return isFullscreenNATIVE();
    }

    @JSBody(script = "" +
            "// Standards compliant check for fullscreen\n" +
            "if (\"fullscreenElement\" in document) {\n" +
            "  return document.fullscreenElement != null;\n" +
            "}" +
            "// Vendor prefixed versions of standard check\n" +
            "if (\"msFullscreenElement\" in document) {\n" +
            "  return document.msFullscreenElement != null;\n" +
            "}" +
            "if (\"webkitFullscreenElement\" in document) {\n" +
            "  return document.webkitFullscreenElement != null;\n" +
            "}" +
            "if (\"mozFullScreenElement\" in document) { // Yes, with a capital 'S'\n" +
            "  return document.mozFullScreenElement != null;\n" +
            "}" +
            "// Older, non-standard ways of checking for fullscreen\n" +
            "if (\"webkitIsFullScreen\" in document) {\n" +
            "  return document.webkitIsFullScreen;\n" +
            "}" +
            "if (\"mozFullScreen\" in document) {\n" +
            "  return document.mozFullScreen;\n" +
            "}" +
            "return false")
    private static native boolean isFullscreenNATIVE();
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy