com.cyphercove.doublehelix.points.BillboardDecalBatch.java Source code

Java tutorial

Introduction

Here is the source code for com.cyphercove.doublehelix.points.BillboardDecalBatch.java

Source

/*******************************************************************************
 * Copyright 2015 Cypher Cove, LLC
 *
 * 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.cyphercove.doublehelix.points;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.VertexAttributes;
import com.badlogic.gdx.graphics.g3d.decals.GroupStrategy;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.Pool;
import com.badlogic.gdx.utils.SortedIntList;

/**
 * Created by Darren on 9/20/2015.
 */
public class BillboardDecalBatch implements Disposable {
    public static final String SIZE_ATTRIBUTE = "a_size";
    public static final String ROTATION_ATTRIBUTE = "a_rotation";

    private static final int DEFAULT_SIZE = 1000;
    private float[] vertices;
    private Mesh mesh;

    private final SortedIntList<Array<BillboardDecal>> groupList = new SortedIntList<Array<BillboardDecal>>();
    private BillboardGroupStrategy groupStrategy;
    private final Pool<Array<BillboardDecal>> groupPool = new Pool<Array<BillboardDecal>>(16) {
        @Override
        protected Array<BillboardDecal> newObject() {
            return new Array<BillboardDecal>(false, 100);
        }
    };
    private final Array<Array<BillboardDecal>> usedGroups = new Array<Array<BillboardDecal>>(16);

    public BillboardDecalBatch(BillboardGroupStrategy groupStrategy) {
        this(DEFAULT_SIZE, groupStrategy);
    }

    public BillboardDecalBatch(int size, BillboardGroupStrategy groupStrategy) {
        initialize(size);
        setGroupStrategy(groupStrategy);
    }

    /** Sets the {@link GroupStrategy} used
     * @param groupStrategy Group strategy to use */
    public void setGroupStrategy(BillboardGroupStrategy groupStrategy) {
        this.groupStrategy = groupStrategy;
    }

    public void initialize(int size) {
        vertices = new float[size * BillboardDecal.VERTEX_SIZE];

        Mesh.VertexDataType vertexDataType = Mesh.VertexDataType.VertexArray;
        if (Gdx.gl30 != null) {
            vertexDataType = Mesh.VertexDataType.VertexBufferObjectWithVAO;
        }
        mesh = new Mesh(vertexDataType, false, size, 0,
                new VertexAttribute(VertexAttributes.Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE),
                new VertexAttribute(VertexAttributes.Usage.ColorPacked, 4, ShaderProgram.COLOR_ATTRIBUTE),
                new VertexAttribute(VertexAttributes.Usage.Generic, 1, SIZE_ATTRIBUTE),
                new VertexAttribute(VertexAttributes.Usage.Generic, 1, ROTATION_ATTRIBUTE));
    }

    public int getSize() {
        return vertices.length / BillboardDecal.VERTEX_SIZE;
    }

    public void add(BillboardDecal decal) {
        int groupIndex = groupStrategy.decideBillboardGroup(decal);
        Array<BillboardDecal> targetGroup = groupList.get(groupIndex);
        if (targetGroup == null) {
            targetGroup = groupPool.obtain();
            targetGroup.clear();
            usedGroups.add(targetGroup);
            groupList.insert(groupIndex, targetGroup);
        }
        targetGroup.add(decal);
    }

    public void flush() {
        render();
        clear();
    }

    /** Renders all decals to the buffer and flushes the buffer to the GL when full/done */
    protected void render() {
        groupStrategy.beforeBillboardGroups();
        for (SortedIntList.Node<Array<BillboardDecal>> group : groupList) {
            groupStrategy.beforeBillboardGroup(group.index, group.value);
            ShaderProgram shader = groupStrategy.getBillboardGroupShader(group.index);
            render(shader, group.value);
            groupStrategy.afterBillboardGroup(group.index);
        }
        groupStrategy.afterBillboardGroups();
    }

    /** Renders a group of vertices to the buffer, flushing them to GL when done/full
     *
     * @param decals Decals to render */
    private void render(ShaderProgram shader, Array<BillboardDecal> decals) {
        // batch vertices
        BillboardDecalMaterial lastMaterial = null;
        int idx = 0;
        for (BillboardDecal decal : decals) {
            if (lastMaterial == null || !lastMaterial.equals(decal.getMaterial())) {
                if (idx > 0) {
                    flush(shader, idx);
                    idx = 0;
                }
                decal.material.set();
                lastMaterial = decal.material;
            }
            decal.update();
            System.arraycopy(decal.vertices, 0, vertices, idx, decal.vertices.length);
            idx += decal.vertices.length;
            // if our batch is full we have to flush it
            if (idx == vertices.length) {
                flush(shader, idx);
                idx = 0;
            }
        }
        // at the end if there is stuff left in the batch we render that
        if (idx > 0) {
            flush(shader, idx);
        }
    }

    /** Flushes vertices[0,verticesPosition[ to GL verticesPosition % Decal.SIZE must equal 0
     *
     * @param verticesPosition Amount of elements from the vertices array to flush */
    protected void flush(ShaderProgram shader, int verticesPosition) {
        mesh.setVertices(vertices, 0, verticesPosition);
        mesh.render(shader, GL20.GL_POINTS, 0, verticesPosition / BillboardDecal.VERTEX_SIZE);
    }

    /** Remove all decals from batch */
    protected void clear() {
        groupList.clear();
        groupPool.freeAll(usedGroups);
        usedGroups.clear();
    }

    @Override
    public void dispose() {
        clear();
        vertices = null;
        mesh.dispose();
    }
}