com.google.gapid.glviewer.gl.Renderer.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gapid.glviewer.gl.Renderer.java

Source

/*
 * Copyright (C) 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.gapid.glviewer.gl;

import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
import com.google.gapid.glviewer.Constants;
import com.google.gapid.glviewer.ShaderSource;
import com.google.gapid.glviewer.vec.MatD;
import com.google.gapid.glviewer.vec.VecD;

import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.internal.DPIUtil;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;

import java.util.List;
import java.util.Set;

/**
 * Renderer maintains the objects and state for an OpenGL context.
 */
public final class Renderer {
    /** The set of {@link GlObject}s owned by this renderer */
    private final Set<GlObject> objects = Sets.newHashSet();

    // The primitive shaders, vertex-buffers and index-buffers.
    private Shader solidShader;
    private Shader checkerShader;
    private Shader borderShader;
    private VertexBuffer quadVB;
    private VertexBuffer borderVB;
    private IndexBuffer borderIB;

    /** Width of the canvas in pixels. */
    private int physicalWidth;
    /** Height of the canvas in pixels. */
    private int physicalHeight;

    /** Width of the canvas in device-independent pixels. */
    private int dipWidth;
    /** Height of the canvas in device-independent pixels. */
    private int dipHeight;

    /**
     * Initializes the renderer for use.
     * Must be called before any other methods.
     */
    public void initialize() {
        GL30.glBindVertexArray(GL30.glGenVertexArrays());
        buildPrimitives();
    }

    /**
     * Releases the objects allocated by the renderer.
     * It is not safe to call any other methods after calling terminate.
     */
    public void terminate() {
        deleteOwnedObjects();
    }

    /**
     * Updates the size of the back-buffer.
     * @param width of the back-buffer in real pixels.
     * @param height of the back-buffer in real pixels.
     */
    public void setSize(int width, int height) {
        physicalWidth = width;
        physicalHeight = height;
        dipWidth = DPIUtil.autoScaleDown(width);
        dipHeight = DPIUtil.autoScaleDown(height);
        GL11.glViewport(0, 0, physicalWidth, physicalHeight);
    }

    /** @return the size of the viewport in device-independent pixels. */
    public VecD getViewSize() {
        return new VecD(dipWidth, dipHeight, 0);
    }

    /** @return the width of the viewport in device-independent pixels. */
    public int getViewWidth() {
        return dipWidth;
    }

    /** @return the height of the viewport in device-independent pixels. */
    public int getViewHeight() {
        return dipHeight;
    }

    /**
     * Constructs and returns a new {@link VertexBuffer} filled with the given data.
     * The returned {@link VertexBuffer} must only be used with this {@link Renderer}.
     *
     * @param data the vertex data.
     * @param elementsPerVertex number of data elements per vertex.
     */
    public VertexBuffer newVertexBuffer(float[] data, int elementsPerVertex) {
        return new VertexBuffer(this, data, elementsPerVertex);
    }

    public VertexBuffer newVertexBuffer(List<Float> data, int elementsPerVertex) {
        float[] floats = new float[data.size()];
        for (int i = 0; i < floats.length; i++) {
            floats[i] = data.get(i);
        }
        return new VertexBuffer(this, floats, elementsPerVertex);
    }

    /**
     * Constructs and returns a new {@link IndexBuffer} filled with the given data.
     * The returned {@link IndexBuffer} must only be used with this {@link Renderer}.
     *
     * @param data the index data.
     */
    public IndexBuffer newIndexBuffer(int[] data) {
        return new IndexBuffer(this, data);
    }

    /**
     * Constructs and returns a new {@link Texture}.
     * The returned {@link Texture} must only be used with this {@link Renderer}.
     */
    public Texture newTexture(int target) {
        return new Texture(this, target);
    }

    /**
     * Loads a new {@link Shader} from the shader resource of the given name.
     * The returned {@link Shader} must only be used with this {@link Renderer}.
     */
    public Shader loadShader(String name) {
        ShaderSource source = ShaderSource.load(name);
        Shader shader = new Shader(this);
        if (!shader.link(source.vertex, source.fragment)) {
            shader.delete();
        }
        return shader;
    }

    /** Clears the viewport with the given solid color */
    public static void clear(Color c) {
        GL11.glClearColor(c.getRed() / 255f, c.getGreen() / 255f, c.getBlue() / 255f, 1f);
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
    }

    /** Draws primitives using the vertex data bound to the given shader. */
    public static void draw(Shader shader, int primitive, int vertexCount) {
        shader.bind();
        GL11.glDrawArrays(primitive, 0, vertexCount);
    }

    /**
     * Draws primitives using the vertex data bound to the given shader, indexed from the
     * given index buffer.
     */
    public static void draw(Shader shader, int primitive, IndexBuffer indices) {
        shader.bind();
        indices.bind();
        GL11.glDrawElements(primitive, indices.count, indices.type, 0);
    }

    /**
     * Draws a normalized (-1 to 1) 2D quad transformed by the given transform with the given shader.
     * The shader is expected to use a {@code uniform mat4 uTransform} for transforming the positions
     * passed in the {@link Constants#POSITION_ATTRIBUTE}.
     */
    public void drawQuad(MatD transform, Shader shader) {
        shader.setUniform("uTransform", transform.toFloatArray());
        shader.setAttribute(Constants.POSITION_ATTRIBUTE, quadVB);
        draw(shader, GL11.GL_TRIANGLE_FAN, 4);
    }

    /**
     * Draws a 2D quad using the device-independent coordinates with the given shader.
     * The shader is expected to use a {@code uniform mat4 uTransform} for transforming the positions
     * passed in the {@link Constants#POSITION_ATTRIBUTE}.
     */
    public void drawQuad(int x, int y, int w, int h, Shader shader) {
        drawQuad(rectTransform(x, y, w, h), shader);
    }

    public void drawSolid(MatD transform, Color color) {
        solidShader.setUniform("uTransform", transform.toFloatArray());
        solidShader.setUniform("uColor", color);
        solidShader.setAttribute(Constants.POSITION_ATTRIBUTE, quadVB);
        drawQuad(transform, solidShader);
    }

    public void drawSolid(MatD transform, Color color, VertexBuffer vertexBuffer, int primitiveMode) {
        solidShader.setUniform("uTransform", transform.toFloatArray());
        solidShader.setUniform("uColor", color);
        solidShader.setAttribute(Constants.POSITION_ATTRIBUTE, vertexBuffer);
        draw(solidShader, primitiveMode, vertexBuffer.vertexCount);
    }

    /** draws a 2D solid color quad using the device-independent coordinates. */
    public void drawSolid(int x, int y, int w, int h, Color color) {
        drawSolid(rectTransform(x, y, w, h), color);
    }

    /** draws a 2D checkered-color quad using the device-independent coordinates. */
    public void drawChecker(MatD transform, Color colorA, Color colorB, int blockSize) {
        checkerShader.setUniform("uTransform", transform.toFloatArray());
        checkerShader.setUniform("uColorA", colorA);
        checkerShader.setUniform("uColorB", colorB);
        checkerShader.setUniform("uCheckerSize", new float[] { blockSize, blockSize });
        checkerShader.setAttribute(Constants.POSITION_ATTRIBUTE, quadVB);
        drawQuad(transform, checkerShader);
    }

    /**
     * draws a normalized (-1 to 1) 2D border transformed by the given transform and shader.
     * The border is extruded by the given width in pixels outward.
     * The shader is expected to use a {@code uniform mat4 uTransform} for transforming the positions
     * passed in the {@link Constants#POSITION_ATTRIBUTE}.
     */
    public void drawBorder(MatD transform, Color color, int width) {
        borderShader.setUniform("uTransform", transform.toFloatArray());
        borderShader.setUniform("uColor", color);
        borderShader.setUniform("uBorderWidth",
                new float[] { 2 * width / (float) dipWidth, 2 * width / (float) dipHeight });
        borderShader.setAttribute(Constants.POSITION_ATTRIBUTE, borderVB);
        draw(borderShader, GL11.GL_TRIANGLES, borderIB);
    }

    /** draws a 2D solid color border quad using the device-independent coordinates. */
    public void drawBorder(int x, int y, int w, int h, Color color, int width) {
        drawBorder(rectTransform(x, y, w, h), color, width);
    }

    /**
     * returns a matrix that will transform an identity quad ([-1, -1] [1, 1]) to the specified
     * DIP coordinates.
     */
    public MatD rectTransform(int x, int y, int w, int h) {
        double dx = (x + 0.5 * w) * (2. / dipWidth) - 1;
        double dy = (y + 0.5 * h) * (2. / dipHeight) - 1;
        return MatD.translation(dx, dy, 0).scale(w / (double) dipWidth, h / (double) dipHeight, 1);
    }

    /**
     * registers the object with this renderer.
     * The object will be freed when the renderer is destroyed.
     */
    void register(GlObject object) {
        Preconditions.checkState(objects.add(object), "Object was already owned by this renderer");
    }

    /**
     * unregisters the object with this renderer.
     * Should only be called by the object when it is destroyed.
     */
    void unregister(GlObject object) {
        Preconditions.checkState(objects.remove(object), "Object was not owned by this renderer");
    }

    private void buildPrimitives() {
        solidShader = loadShader("solid");
        checkerShader = loadShader("checker");
        borderShader = loadShader("border");

        quadVB = newVertexBuffer(new float[] { -1f, -1f, 1f, -1f, 1f, 1f, -1f, 1f }, 2);

        borderVB = newVertexBuffer(new float[] {
                // x, y, extrude
                -1, -1, 0, /**/ 1, -1, 0, /**/ 1, 1, 0, /**/ -1, 1, 0, -1, -1, 1, /**/ 1, -1, 1, /**/ 1, 1, 1,
                /**/ -1, 1, 1, }, 3);

        borderIB = newIndexBuffer(new int[] { 0, 4, 1, /**/ 1, 4, 5, 1, 5, 2, /**/ 2, 5, 6, 2, 6, 3, /**/ 3, 6, 7,
                3, 7, 0, /**/ 0, 7, 4, });
    }

    private void deleteOwnedObjects() {
        for (GlObject object : objects.toArray(new GlObject[objects.size()])) {
            object.delete();
        }
        assert (objects.isEmpty());
    }
}