content: /*
   * Copyright 2016 Andrey Novikov
   * Copyright 2016 devemux86
   * Copyright 2017 schedul-xor
   * Copyright 2018-2019 Gustl22
   *
   * This program is free software: you can redistribute it and/or modify it under the
   * terms of the GNU Lesser General Public License as published by the Free Software
   * Foundation, either version 3 of the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful, but WITHOUT ANY
   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
   * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public License along with
   * this program. If not, see <http://www.gnu.org/licenses/>.
   */
  package org.oscim.renderer.bucket;
  
  import org.oscim.backend.GL;
  import org.oscim.backend.GLAdapter;
  import org.oscim.core.GeometryBuffer;
  import org.oscim.renderer.GLShader;
  import org.oscim.renderer.GLState;
  import org.oscim.renderer.GLUtils;
  import org.oscim.renderer.GLViewport;
  import org.oscim.theme.styles.CircleStyle;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import static org.oscim.backend.GLAdapter.gl;
  import static org.oscim.renderer.MapRenderer.COORD_SCALE;
  
  public class CircleBucket extends RenderBucket {
  
      private static final Logger log = LoggerFactory.getLogger(CircleBucket.class);
  
      public CircleStyle circle;
  
      public CircleBucket(int level) {
          super(RenderBucket.CIRCLE, true, GLAdapter.CIRCLE_QUADS);
          this.level = level;
      }
  
      public void addCircle(GeometryBuffer geom) {
          if (!geom.isPoint()) {
              log.error("Circle style applied to non-point geometry");
              return;
          }
  
          float x = geom.getPointX(0);
          float y = geom.getPointY(0);
  
          if (GLAdapter.CIRCLE_QUADS) {
              // Create quad
              vertexItems.add((short) ((x + circle.radius) * COORD_SCALE), (short) ((y - circle.radius) * COORD_SCALE));
              int ne = numVertices++;
              vertexItems.add((short) ((x - circle.radius) * COORD_SCALE), (short) ((y - circle.radius) * COORD_SCALE));
              int nw = numVertices++;
              vertexItems.add((short) ((x - circle.radius) * COORD_SCALE), (short) ((y + circle.radius) * COORD_SCALE));
              int sw = numVertices++;
              vertexItems.add((short) ((x + circle.radius) * COORD_SCALE), (short) ((y + circle.radius) * COORD_SCALE));
              int se = numVertices++;
  
              indiceItems.add((short) ne);
              numIndices++;
              indiceItems.add((short) nw);
              numIndices++;
              indiceItems.add((short) sw);
              numIndices++;
  
              indiceItems.add((short) sw);
              numIndices++;
              indiceItems.add((short) se);
              numIndices++;
              indiceItems.add((short) ne);
              numIndices++;
          } else {
              // Use point
              vertexItems.add((short) (x * COORD_SCALE), (short) (y * COORD_SCALE));
              indiceItems.add((short) numVertices++);
              numIndices++;
          }
      }
  
      public static class Renderer {
          static Shader shader;
  
          static boolean init() {
              shader = new Shader(GLAdapter.CIRCLE_QUADS ? "circle_quad" : "circle_point");
              return true;
          }
  
          public static class Shader extends GLShader {
              int uMVP, uFill, uRadius, uStroke, uWidth, aPos;
  
              Shader(String shaderFile) {
                  if (!GLAdapter.CIRCLE_QUADS && !GLAdapter.ANDROID_QUIRKS && !GLAdapter.GDX_WEBGL_QUIRKS)
                      gl.enable(GL.VERTEX_PROGRAM_POINT_SIZE);
  
                  String version = null;
                  if (!GLAdapter.CIRCLE_QUADS && GLAdapter.GDX_DESKTOP_QUIRKS) {
                      // Point sprite sometimes isn't enabled by default, see #268
                      gl.enable(0x8861); // GL.POINT_SPRITE
  
                      // OpenGL requires GLSL version 120 for gl_PointCoord
                      version = "120";
                  }
  
                  if (!createVersioned(shaderFile, version))
                      return;
  
                  uMVP = getUniform("u_mvp");
                  uFill = getUniform("u_fill");
                  uRadius = getUniform("u_radius");
                  uStroke = getUniform("u_stroke");
                  uWidth = getUniform("u_width");
                  aPos = getAttrib("a_pos");
              }
  
              public void set(GLViewport v) {
                  useProgram();
                  GLState.enableVertexArrays(aPos, GLState.DISABLED);
  
                  v.mvp.setAsUniform(uMVP);
              }
          }
  
          public static RenderBucket draw(RenderBucket b, GLViewport v) {
              GLState.blend(true);
  
              Shader s = shader;
  
              s.set(v);
  
              for (; b != null && b.type == CIRCLE; b = b.next) {
                  CircleBucket cb = (CircleBucket) b;
                  CircleStyle circle = cb.circle.current();
  
                  GLUtils.setColor(s.uFill, circle.fillColor, 1);
                  gl.uniform1f(s.uRadius, circle.radius);
                  GLUtils.setColor(s.uStroke, circle.strokeColor, 1);
                  gl.uniform1f(s.uWidth, circle.strokeWidth);
  
                  gl.vertexAttribPointer(s.aPos, 2, GL.SHORT,
                          false, 0, cb.vertexOffset);
  
                  if (GLAdapter.CIRCLE_QUADS)
                      gl.drawElements(GL.TRIANGLES,
                              cb.numIndices,
                              GL.UNSIGNED_SHORT,
                              cb.indiceOffset);
                  else
                      gl.drawElements(GL.POINTS,
                              cb.numIndices,
                              GL.UNSIGNED_SHORT,
                              cb.indiceOffset);
              }
  
              return b;
          }
      }
  }
  