package org.geogebra.common.geogebra3D.euclidian3D.openGL; import java.util.ArrayList; import org.geogebra.common.geogebra3D.euclidian3D.EuclidianView3D; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.discrete.PolygonTriangulation.TriangleFan; import org.geogebra.common.util.debug.Log; /** * manager using shaders and binding to gpu buffers * * @author mathieu * */ public class ManagerShadersElementsGlobalBuffer extends ManagerShadersNoTriangleFan { private GLBufferIndices curvesIndices, fanDirectIndices, fanIndirectIndices; private int curvesIndicesSize, fanDirectIndicesSize, fanIndirectIndicesSize; final static boolean DEBUG = false; /** * debug if debug mode * * @param message * message */ final static void debug(String message) { if (DEBUG) { Log.debug(message); } } /** * debug if debug mode * * @param message * message * @param val * value */ final static void debug(String message, double val) { if (DEBUG) { Log.debug(message + val); } } /** * * @param r * renderer * @param size * sections size * @return GPU buffer for curve indices, update it if current is not big * enough */ public final GLBufferIndices getBufferIndicesForCurve( RendererShadersInterface r, int size) { if (size > curvesIndicesSize) { debug("NEW curvesIndicesSize : ", size); // creates indices buffer if (curvesIndices == null) { curvesIndices = GLFactory.getPrototype().newBufferIndices(); } curvesIndices.allocate(3 * 2 * size * PlotterBrush.LATITUDES); for (int k = 0; k < size; k++) { for (int i = 0; i < PlotterBrush.LATITUDES; i++) { int iNext = (i + 1) % PlotterBrush.LATITUDES; // first triangle curvesIndices.put((short) (i + k * PlotterBrush.LATITUDES)); curvesIndices.put( (short) (i + (k + 1) * PlotterBrush.LATITUDES)); curvesIndices.put( (short) (iNext + (k + 1) * PlotterBrush.LATITUDES)); // second triangle curvesIndices.put((short) (i + k * PlotterBrush.LATITUDES)); curvesIndices.put( (short) (iNext + (k + 1) * PlotterBrush.LATITUDES)); curvesIndices .put((short) (iNext + k * PlotterBrush.LATITUDES)); } } curvesIndices.rewind(); curvesIndicesSize = size; } return curvesIndices; } /** * * @param r * renderer * @param size * sections size * @return GPU buffer for direct fan indices, update it if current is not * big enough */ public final GLBufferIndices getBufferIndicesForFanDirect( RendererShadersInterface r, int size) { if (size > fanDirectIndicesSize) { debug("NEW fanDirectIndicesSize : ", size); // creates indices buffer if (fanDirectIndices == null) { fanDirectIndices = GLFactory.getPrototype().newBufferIndices(); } fanDirectIndices.allocate(3 * (size - 2)); short k = 1; short zero = 0; while (k < size - 1) { fanDirectIndices.put(zero); fanDirectIndices.put(k); k++; fanDirectIndices.put(k); } fanDirectIndices.rewind(); fanDirectIndicesSize = size; } return fanDirectIndices; } /** * * @param r * renderer * @param size * sections size * @return GPU buffer for indirect fan indices, update it if current is not * big enough */ public final GLBufferIndices getBufferIndicesForFanIndirect( RendererShadersInterface r, int size) { if (size > fanIndirectIndicesSize) { debug("NEW fanIndirectIndicesSize : ", size); // creates indices buffer if (fanIndirectIndices == null) { fanIndirectIndices = GLFactory.getPrototype() .newBufferIndices(); } fanIndirectIndices.allocate(3 * (size - 2)); short k2 = 2; short k = 1; short zero = 0; while (k < size - 1) { fanIndirectIndices.put(zero); fanIndirectIndices.put(k2); fanIndirectIndices.put(k); k++; k2++; } fanIndirectIndices.rewind(); fanIndirectIndicesSize = size; } return fanIndirectIndices; } @SuppressWarnings("serial") protected class GeometriesSetElementsGlobalBuffer extends GeometriesSet { @Override protected Geometry newGeometry(Type type) { return new GeometryElementsGlobalBuffer(type); } @Override public void bindGeometry(int size, TypeElement type) { ((GeometryElementsGlobalBuffer) currentGeometry) .bind((RendererShadersInterface) renderer, size, type); } /** * remove GL buffers */ public void removeBuffers() { for (int i = 0; i < getGeometriesLength(); i++) { ((GeometryElementsGlobalBuffer) get(i)) .removeBuffers((RendererShadersInterface) renderer); } } } public class GeometryElementsGlobalBuffer extends Geometry { private GLBufferIndices arrayI = null; private int indicesLength; private boolean hasSharedIndexBuffer = false; public GeometryElementsGlobalBuffer(Type type) { super(type); } /** * remove buffers * * @param r * GL renderer */ public void removeBuffers(RendererShadersInterface r) { // TODO not needed? } private TypeElement oldType = TypeElement.NONE; /** * bind the geometry to its GL buffer * * @param r * renderer * @param size * indices size * @param type * type for elements indices */ public void bind(RendererShadersInterface r, int size, TypeElement type) { switch (type) { case NONE: if (hasSharedIndexBuffer) { // need specific index if was sharing one arrayI = null; } if (arrayI == null) { arrayI = GLFactory.getPrototype().newBufferIndices(); } indicesLength = getLength(); if (!indicesDone || type != oldType || arrayI.capacity() < indicesLength) { debug("NEW index buffer"); arrayI.allocate(indicesLength); for (short i = 0; i < indicesLength; i++) { arrayI.put(i); } arrayI.rewind(); indicesDone = true; } else { debug("keep same index buffer"); } hasSharedIndexBuffer = false; break; case CURVE: debug("curve: shared index buffer"); arrayI = getBufferIndicesForCurve(r, size); indicesLength = 3 * 2 * size * PlotterBrush.LATITUDES; hasSharedIndexBuffer = true; // debug("curve: NOT shared index buffer"); // bufferI = getBufferIndicesForCurve(bufferI, r, size, // indicesLength / (3 * 2 * PlotterBrush.LATITUDES)); // indicesLength = 3 * 2 * size * PlotterBrush.LATITUDES; // hasSharedIndexBuffer = false; break; case SURFACE: debug("surface -- keep same index buffer"); indicesLength = size; hasSharedIndexBuffer = false; break; case FAN_DIRECT: debug("fan direct: shared index buffer"); arrayI = getBufferIndicesForFanDirect(r, size); indicesLength = 3 * (size - 2); hasSharedIndexBuffer = true; break; case FAN_INDIRECT: debug("fan indirect: shared index buffer"); arrayI = getBufferIndicesForFanIndirect(r, size); indicesLength = 3 * (size - 2); hasSharedIndexBuffer = true; break; } oldType = type; } @Override public void draw(RendererShadersInterface r) { if (arrayI == null) { return; } r.loadVertexBuffer(getVertices(), getLength()); r.loadNormalBuffer(getNormals(), getLength()); r.loadColorBuffer(getColors(), getLength()); if (r.areTexturesEnabled()) { r.loadTextureBuffer(getTextures(), getLength()); } r.loadIndicesBuffer(arrayI, indicesLength); r.draw(getType(), indicesLength); } @Override public void drawLabel(RendererShadersInterface r) { if (arrayI == null) { return; } r.loadVertexBuffer(getVertices(), getLength()); if (r.areTexturesEnabled()) { r.loadTextureBuffer(getTextures(), getLength()); } r.loadIndicesBuffer(arrayI, indicesLength); r.draw(getType(), indicesLength); } private boolean indicesDone = false; /** * * @param size * size * @return indices buffer with correct size */ public GLBufferIndices getBufferI(int size) { if (arrayI == null || hasSharedIndexBuffer) { arrayI = GLFactory.getPrototype().newBufferIndices(); } arrayI.allocate(size); return arrayI; } /** * * @return current indices buffer */ public GLBufferIndices getCurrentBufferI() { return arrayI; } /** * * @return indices length */ public int getIndicesLength() { return indicesLength; } } /** * constructor * * @param renderer * renderer * @param view3d * 3D view */ public ManagerShadersElementsGlobalBuffer(Renderer renderer, EuclidianView3D view3d) { super(renderer, view3d); } @Override protected void initGeometriesList() { curvesIndicesSize = -1; fanDirectIndicesSize = -1; fanIndirectIndicesSize = -1; super.initGeometriesList(); } @Override protected GeometriesSet newGeometriesSet() { return new GeometriesSetElementsGlobalBuffer(); } @Override protected PlotterBrush newPlotterBrush() { return new PlotterBrushElements(this); } @Override protected PlotterSurface newPlotterSurface() { return new PlotterSurfaceElements(this); } @Override public GLBufferIndices getCurrentGeometryIndices(int size) { return ((GeometryElementsGlobalBuffer) currentGeometriesSet.currentGeometry) .getBufferI(size); } @Override protected void removeGeometrySet(int index) { GeometriesSetElementsGlobalBuffer set = (GeometriesSetElementsGlobalBuffer) geometriesSetList .remove(index); if (set != null) { set.removeBuffers(); } // Log.debug("removeGeometrySet : " + index); } @Override public void drawPolygonConvex(Coords n, Coords[] v, int length, boolean reverse) { startGeometry(Type.TRIANGLES); // set texture setDummyTexture(); // set normal normalToScale(n); // set vertices for (int i = 0; i < length; i++) { vertexToScale(v[i]); } if (reverse) { endGeometry(length, TypeElement.FAN_INDIRECT); } else { endGeometry(length, TypeElement.FAN_DIRECT); } } @Override public void drawTriangleFans(Coords n, Coords[] verticesWithIntersections, int length, ArrayList<TriangleFan> triFanList) { startGeometry(Type.TRIANGLES); // set texture setDummyTexture(); // set normal normalToScale(n); // set vertices for (int i = 0; i < length; i++) { vertexToScale(verticesWithIntersections[i]); } // indices int size = 0; for (TriangleFan triFan : triFanList) { size += triFan.size() - 1; } GLBufferIndices arrayI = getCurrentGeometryIndices(size * 3); for (TriangleFan triFan : triFanList) { short apex = (short) triFan.getApexPoint(); short current = (short) triFan.getVertexIndex(0); for (int i = 1; i < triFan.size(); i++) { arrayI.put(apex); arrayI.put(current); current = (short) triFan.getVertexIndex(i); arrayI.put(current); } } arrayI.rewind(); // end endGeometry(3 * size, TypeElement.SURFACE); } }