public PolygonPipeline(PolygonRasterizer renderer, boolean sortAll) {
   super();
   this.sortOpaque = sortAll;
   this.queueOpaque = sortAll;
   this.renderer = renderer;
   zNormal[0] = 0;
   zNormal[1] = 0;
   zNormal[2] = 1;
   for (int i = 0; i < 8; i++) {
     int pos = 3 * i;
     pointVertices[pos] = zNormal[pos + 3] = Math.cos(2. * i * Math.PI / 8.);
     pointVertices[pos + 1] = zNormal[pos + 4] = Math.sin(2. * i * Math.PI / 8.);
     zNormal[pos + 5] = 0.01;
   }
 }
/**
 * A render pass will consit of the following: 1. calls to processPolygon for each polygon to
 * render. This will transform the polygon by using the state of matrix. and give it the current
 * shader.
 *
 * <p>2. optionally sort the polygon list.<br>
 * 3. shade all Polygons using the shader given by the polygon<br>
 * 4. clip all polygons using clipPolygons()<br>
 * 5. call perspective for all polygons<br>
 * 6. render the polygons.
 *
 * <p>In fact all the calls in 3. to 6. can be done in one pass. However we have to delay the
 * shading to after the traversal, to allow the environment to gather all the lights in the scene,
 * if we want to have lights to act globally. THIS IS NOT IMPLEMENTED YET.
 *
 * @version 1.0
 * @author <a href="mailto:[email protected]">Tim Hoffmann</a>
 */
public class PolygonPipeline implements PolygonProcessor, PointProcessor, LineProcessor {

  private int vertexColorLength;
  private DataList vertexColor;
  private boolean vertexColors;
  private final boolean queueOpaque;
  private final boolean sortOpaque;
  private final boolean triangulate = false;
  private final boolean useTexCoords = true;

  private double[] matrix;
  // private boolean itmComputed = false;
  private double[] inverseTransposeMatrix = new double[16];
  int polygonCount = 0;
  Polygon polygons[] = new Polygon[0];
  private int vertexCount = 0;
  double[] vertexData = new double[100 * Polygon.VERTEX_LENGTH];

  PolygonComparator comp = new PolygonComparator();
  private Perspective perspective = new DefaultPerspective();
  private Environment environment;

  private PolygonShader faceShader; // = new DefaultPolygonShader();
  private LineShader lineShader; // = new ConstantPolygonShader(.2,.1,.1);
  // private PolygonShader pointOutlineShader;
  // = new ConstantPolygonShader(.0,.0,.0);
  private PointShader pointShader;
  // = new ConstantPolygonShader(0.8,0.2,0.2);

  private PolygonShader shader;
  private double[] pointVertices = new double[48];
  private static int[] pointIndices = {0, 3, 6, 9, 12, 15, 18, 21};
  private static int[][] pointOutlineIndices = {
    {0, 24, 27, 3},
    {3, 27, 30, 6},
    {6, 30, 33, 9},
    {9, 33, 36, 12},
    {12, 36, 39, 15},
    {15, 39, 42, 18},
    {18, 42, 45, 21},
    {21, 45, 24, 0}
  };
  private static double[][] pointColors = new double[16][4];
  private DataList pointColorDataList =
      StorageModel.DOUBLE_ARRAY_ARRAY.createWritableDataList(pointColors);
  private static int[] pointNormals = {0, 0, 0, 0, 0, 0, 0, 0};
  private static int[][] pointOutlineNormals = {
    {0, 3, 6, 0},
    {0, 6, 9, 0},
    {0, 9, 12, 0},
    {0, 12, 15, 0},
    {0, 15, 18, 0},
    {0, 18, 21, 0},
    {0, 21, 24, 0},
    {0, 24, 3, 0}
  };
  // private static double[] zNormal = new double[] { 0, 0, 1 };
  private static double[] zNormal = new double[3 * 9];

  private static int[] lineIndices = {0, 3, 6, 9};

  private double pointRadius = 0.025;
  private double outlineFraction = 1 / 3.;
  private double lineWidth = .01;

  private final PolygonRasterizer renderer;
  //	static {
  //	}

  public PolygonPipeline(PolygonRasterizer renderer) {
    this(renderer, false);
  }

  public PolygonPipeline(PolygonRasterizer renderer, boolean sortAll) {
    super();
    this.sortOpaque = sortAll;
    this.queueOpaque = sortAll;
    this.renderer = renderer;
    zNormal[0] = 0;
    zNormal[1] = 0;
    zNormal[2] = 1;
    for (int i = 0; i < 8; i++) {
      int pos = 3 * i;
      pointVertices[pos] = zNormal[pos + 3] = Math.cos(2. * i * Math.PI / 8.);
      pointVertices[pos + 1] = zNormal[pos + 4] = Math.sin(2. * i * Math.PI / 8.);
      zNormal[pos + 5] = 0.01;
    }
  }

  public final void clearPipeline() {
    polygonCount = 0;
    vertexCount = 0;
    faceShader = null;
    lineShader = null;
    pointShader = null;
    // pointOutlineShader = null;
  }

  private static final int POLYGON_INCR = 100;

  private final void increasePolygonCapacity() {
    Polygon np[] = new Polygon[polygons.length + POLYGON_INCR];
    System.arraycopy(polygons, 0, np, 0, polygons.length);
    for (int i = polygons.length; i < np.length; i++) {
      np[i] = new Polygon();
    }
    polygons = np;
  }

  //	private final void increaseTransparentPolygonCapacity() {
  //		Polygon np[] = new Polygon[transparentPolygons.length+POLYGON_INCR];
  //		System.arraycopy(transparentPolygons,0,np,0,transparentPolygons.length);
  //		for (int i = transparentPolygons.length; i < np.length; i++) {
  //			np[i] =
  //			 new Polygon();
  //		}
  //		transparentPolygons = np;
  //	}

  private static final int VERTEX_INCR = 1200;

  private final void increaseVertexCapacity(int amount) {
    int a = (amount > VERTEX_INCR ? amount : VERTEX_INCR);
    double nv[] = new double[vertexData.length + Polygon.VERTEX_LENGTH * a];
    System.arraycopy(vertexData, 0, nv, 0, vertexData.length);
    vertexData = nv;
    // System.out.println("vertex capacity "+vertexData.length);
  }

  /* (non-Javadoc)
   * @see de.jreality.soft.PolygonProcessor#processPolygon(double[], int[], double[], int[])
   */
  public final void processPolygon(
      final double[] vd, final int[] vertices, final double[] nd, final int[] normals) {
    if (faceShader == null) return;
    shader = faceShader;
    //			process(vd,vertices,nd,normals);
    computeArray(vd, vertices, nd, normals);

    // we do outlining polygons in the renderer at the moment...
    //        if (false) {
    //            for (int i = 0; i < vertices.length - 1;)
    //                processLine(vd, vertices[i++], vertices[i]);
    //            processLine(vd, vertices[vertices.length - 1], vertices[0]);
    //        }
  }

  final void startGeometry(Geometry geom) {
    if (lineShader != null) lineShader.startGeometry(geom);
    if (faceShader != null) faceShader.startGeometry(geom);
    if (pointShader != null) pointShader.startGeometry(geom);
    if (geom instanceof PointSet) {
      vertexColor = ((PointSet) geom).getVertexAttributes(Attribute.COLORS);
      vertexColors = vertexColor != null;
      if (vertexColors) vertexColorLength = vertexColor.getStorageModel().getDimensions()[1];
      //            System.out.println("PolygonPipeline.startGeometry("+geom.getName()
      //              +"): colors: "+vertexColors);
    } else {
      vertexColor = null;
      vertexColors = false;
    }
  }

  public final void processPolygon(
      final DoubleArrayArray vd,
      final IntArray vertices,
      final DoubleArrayArray nd,
      final IntArray normals,
      final DataList texCoords) {
    if (faceShader == null) return;
    shader = faceShader;
    computeArray(vd, vertices, nd, normals, texCoords);
  }

  private final void computeArray(
      final double[] vd, final int[] vertices, final double[] nd, final int[] normals) {
    if (2 * vertices.length + 6 >= (vertexData.length - vertexCount) / Polygon.VERTEX_LENGTH)
      increaseVertexCapacity(vertices.length / 3);

    int vc = vertexCount;
    for (int i = 0; i < vertices.length; i++) {
      int vi = vertices[i];
      VecMat.transform(matrix, vd[vi++], vd[vi++], vd[vi], vertexData, vc + Polygon.SX);
      vertexData[vc + Polygon.SW] = 1;
      int ni = normals[i];
      VecMat.transformNormal(
          inverseTransposeMatrix, nd[ni++], nd[ni++], nd[ni], vertexData, vc + Polygon.NX);
      vc += Polygon.VERTEX_LENGTH;
      if (vertexColors) {
        DoubleArray color = vertexColor.item(vi / 3).toDoubleArray();
        vertexData[vc + Polygon.R] = color.getValueAt(0);
        vertexData[vc + Polygon.G] = color.getValueAt(1);
        vertexData[vc + Polygon.B] = color.getValueAt(2);
        vertexData[vc + Polygon.A] =
            (vertexColorLength == 4 || (vertexColorLength == -1 && color.getLength() > 3))
                ? color.getValueAt(3)
                : 1.;
      }
    }
    compute(vertices.length);
  }

  private final void computeArrayNoTransform(
      final double[] vd, final int[] vertices, final double[] nd, final int[] normals) {
    if (2 * vertices.length + 6 >= (vertexData.length - vertexCount) / Polygon.VERTEX_LENGTH)
      increaseVertexCapacity(vertices.length / 3);

    int vc = vertexCount;
    for (int i = 0; i < vertices.length; i++) {
      int vi = vertices[i];

      vertexData[vc + Polygon.SX] = vd[vi++];
      vertexData[vc + Polygon.SY] = vd[vi++];
      vertexData[vc + Polygon.SZ] = vd[vi];
      vertexData[vc + Polygon.SW] = 1;
      int ni = normals[i];

      vertexData[vc + Polygon.NX] = nd[ni++];
      vertexData[vc + Polygon.NY] = nd[ni++];
      vertexData[vc + Polygon.NZ] = nd[ni];
      // VecMat.normalize(vertexData, vc + Polygon.NX);

      vc += Polygon.VERTEX_LENGTH;
    }
    compute(vertices.length);
  }

  private final void computeArray(
      final DoubleArrayArray vd,
      final IntArray vertices,
      final DoubleArrayArray nd,
      final IntArray normals,
      DataList texCoords) {
    if (2 * vertices.getLength() + 6 >= (vertexData.length - vertexCount) / Polygon.VERTEX_LENGTH)
      increaseVertexCapacity(vertices.getLength() / 3);

    int vc = vertexCount;
    if (vd.getLengthAt(0) == 3)
      for (int i = 0; i < vertices.getLength(); i++) {
        int vi = vertices.getValueAt(i);
        DoubleArray vertex = vd.item(vi).toDoubleArray();
        VecMat.transform(
            matrix,
            vertex.getValueAt(0),
            vertex.getValueAt(1),
            vertex.getValueAt(2),
            vertexData,
            vc + Polygon.SX);
        vertexData[vc + Polygon.SW] = 1;
        int ni = normals.getValueAt(i);
        DoubleArray normal = nd.item(ni).toDoubleArray();
        VecMat.transformNormal(
            inverseTransposeMatrix,
            normal.getValueAt(0),
            normal.getValueAt(1),
            normal.getValueAt(2),
            vertexData,
            vc + Polygon.NX);
        if (vertexColors) {
          DoubleArray color = vertexColor.item(vi).toDoubleArray();
          vertexData[vc + Polygon.R] = color.getValueAt(0);
          vertexData[vc + Polygon.G] = color.getValueAt(1);
          vertexData[vc + Polygon.B] = color.getValueAt(2);
          vertexData[vc + Polygon.A] =
              (vertexColorLength == 4 || (vertexColorLength == -1 && color.getLength() > 3))
                  ? color.getValueAt(3)
                  : 1.;
        }
        if (useTexCoords && texCoords != null) {
          DoubleArray tc = texCoords.item(vi).toDoubleArray();
          vertexData[vc + Polygon.U] = tc.getValueAt(0);
          vertexData[vc + Polygon.V] = tc.getValueAt(1);
        }
        vc += Polygon.VERTEX_LENGTH;
      }
    else // vertices are 4 tupel
    for (int i = 0; i < vertices.getLength(); i++) {
        int vi = vertices.getValueAt(i);
        DoubleArray vertex = vd.item(vi).toDoubleArray();
        VecMat.transform(
            matrix,
            vertex.getValueAt(0),
            vertex.getValueAt(1),
            vertex.getValueAt(2),
            vertex.getValueAt(3),
            vertexData,
            vc + Polygon.SX);
        if (vertexData[vc + Polygon.SW] != 0) {
          final double d = vertexData[vc + Polygon.SW];
          vertexData[vc + Polygon.SX] /= d;
          vertexData[vc + Polygon.SY] /= d;
          vertexData[vc + Polygon.SZ] /= d;
          vertexData[vc + Polygon.SW] = 1;
        }
        int ni = normals.getValueAt(i);
        DoubleArray normal = nd.item(ni).toDoubleArray();
        VecMat.transformNormal(
            inverseTransposeMatrix,
            normal.getValueAt(0),
            normal.getValueAt(1),
            normal.getValueAt(2),
            // normal.getValueAt(3),
            vertexData,
            vc + Polygon.NX);
        if (vertexColors) {
          DoubleArray color = vertexColor.item(vi).toDoubleArray();
          vertexData[vc + Polygon.R] = color.getValueAt(0);
          vertexData[vc + Polygon.G] = color.getValueAt(1);
          vertexData[vc + Polygon.B] = color.getValueAt(2);
          vertexData[vc + Polygon.A] =
              (vertexColorLength == 4 || (vertexColorLength == -1 && color.getLength() > 3))
                  ? color.getValueAt(3)
                  : 1.;
        }
        if (useTexCoords && texCoords != null) {
          DoubleArray tc = texCoords.item(vi).toDoubleArray();
          vertexData[vc + Polygon.U] = tc.getValueAt(0);
          vertexData[vc + Polygon.V] = tc.getValueAt(1);
        }
        vc += Polygon.VERTEX_LENGTH;
      }
    compute(vertices.getLength());
  }

  private final void compute(int verticesLength) {

    //        boolean isTransparent = shader.getVertexShader().getTransparency() != 0.;

    if (polygonCount + 1 >= this.polygons.length) increasePolygonCapacity();

    Polygon p = polygons[polygonCount++];
    p.length = verticesLength;

    // We store the current shader for later shading
    p.setShader(shader);

    for (int i = 0; i < verticesLength; i++) {
      p.vertices[i] = vertexCount;
      vertexCount += Polygon.VERTEX_LENGTH;
    }

    // sky box shaders need the matrix...
    environment.setMatrix(matrix);
    // ****
    shader.shadePolygon(p, vertexData, environment);

    int numClip = environment.getNumClippingPlanes();
    if (numClip > 0) {
      ClippingPlaneSoft[] cp = environment.getClippingPlanes();
      // Intersector.dehomogenize(p,vertexData);
      for (int k = 0; k < numClip; k++) {
        double d = VecMat.dot(cp[k].getNormal(), cp[k].getPoint());
        // System.out.println("d "+d);
        int res = Intersector.clipToHalfspace(p, cp[k].getNormal(), -1, d, this);
        if (res == -1) { // clipped out
          p.setShader(null);
          vertexCount -= p.length * Polygon.VERTEX_LENGTH;
          polygonCount--;
          return;
        }
      }
    }
    for (int i = 0; i < p.length; i++) {
      perspective.perspective(vertexData, p.vertices[i]);
    }

    boolean clippedAway = clipPolygon();

    if (!clippedAway) {
      // if (p.length == 0)
      //    System.out.println("ZEROLENGTH!");
      // TODO: check wether we sort before computing this.
      // boolean isTransparent = (shader.getVertexShader().getTransparency() !=
      // 0.)||shader.hasTexture()||shader.interpolateAlpha();
      // shader might have changed after lighting:
      boolean isTransparent = p.getShader().needsSorting();
      ////            isTransparent = false;
      if (triangulate) {
        PolygonShader ps = p.getShader();
        if (polygonCount + 1 >= this.polygons.length) increasePolygonCapacity();
        polygonCount--;
        for (int i = 0, pl = p.length - 2; i < pl; i++) {
          Polygon pi = polygons[polygonCount++];
          pi.length = 3;
          pi.vertices[0] = p.vertices[0];
          pi.vertices[1] = p.vertices[i + 1];
          pi.vertices[2] = p.vertices[i + 2];
          pi.setShader(ps);
          if (queueOpaque || isTransparent) {
            if (sortOpaque || isTransparent) pi.computeCenterZ(vertexData);
            // pi.computeMaxZ(vertexData);
            // return;
          } else {
            renderer.renderPolygon(pi, vertexData, ps.isOutline());
            pi.setShader(null);
            // TODO:check wether this is save
            // vertexCount -= p.length * Polygon.VERTEX_LENGTH;
            polygonCount--;
          }
        }
        return;
      } else {

        if (queueOpaque || isTransparent) {
          if (sortOpaque || isTransparent) p.computeCenterZ(vertexData);
          // p.computeMaxZ(vertexData);
          return;
        } else {
          renderer.renderPolygon(p, vertexData, p.getShader().isOutline());
        }
      }
    }
    // if the polygon was clipped completely or if the polygon was opaque and therefore
    // rendered already, we can free its resources:
    p.setShader(null);
    vertexCount -= p.length * Polygon.VERTEX_LENGTH;
    polygonCount--;
  }

  public int getFreeVertex() {
    //        if(vertexCount*Polygon.VERTEX_LENGTH>=vertexData.length) increaseVertexCapacity(1);
    if (vertexCount >= vertexData.length) increaseVertexCapacity(1);
    int vc = vertexCount;
    vertexCount += Polygon.VERTEX_LENGTH;
    return vc;
  }

  public void freePolygon(int idx) {
    // polygons[idx].setShader(null);
    if (!(idx == polygonCount - 1)) {
      Polygon p = polygons[idx];
      polygons[idx] = polygons[polygonCount - 1];
      polygons[polygonCount - 1] = p;
    }
    polygonCount--;
  }

  public int getFreePolygon() {
    if (polygonCount >= polygons.length - 1) increasePolygonCapacity();
    int pc = polygonCount;
    polygonCount++;
    return pc;
  }

  /** @param p */
  private final boolean clipPolygon() {
    int polPos = polygonCount - 1;
    Polygon p1 = polygons[polPos];
    ClippingBox box = perspective.getFrustum();
    int x0out = 0;
    int x1out = 0;
    int y0out = 0;
    int y1out = 0;
    int z0out = 0;
    int z1out = 0;

    int n = p1.length;
    // TODO check maximal polygon vertex num here:...if applicable...

    // after clipping at the clipping plane vertices need not be in order anymore!
    // for (int v = p1.vertices[0], i = n;
    // i > 0;
    // i--, v += Polygon.VERTEX_LENGTH) {
    for (int v = p1.vertices[0], i = 0; i < n; i++) {
      v = p1.vertices[i];
      double vsw = vertexData[v + Polygon.SW];
      if (vertexData[v + Polygon.SX] < box.x0 * vsw) x0out++;
      if (vertexData[v + Polygon.SX] > box.x1 * vsw) x1out++;

      if (vertexData[v + Polygon.SY] < box.y0 * vsw) y0out++;
      if (vertexData[v + Polygon.SY] > box.y1 * vsw) y1out++;

      if (vertexData[v + Polygon.SZ] < box.z0 * vsw) z0out++;
      if (vertexData[v + Polygon.SZ] > box.z1 * vsw) z1out++;
    }
    if (x0out + x1out + y0out + y1out + z0out + z1out == 0) {
      // shader.shadePolygon(p1, vertexData, environment);
      return false; /*POLY_CLIP_IN*/
    }
    if (x0out == n || x1out == n || y0out == n || y1out == n || z0out == n || z1out == n) {
      // p1->n = 0;
      // System.out.println("OUT "+(n*Polygon.VERTEX_LENGTH));
      return true; /*POLY_CLIP_OUT*/
    }
    // shader.shadePolygon(p1, vertexData, environment);
    // now clip:
    // if (x0out) CLIP_AND_SWAP(sx, -1., box->x0, p, q, r);
    if (x0out != 0) clipToHalfspace(polPos, polygons, Polygon.SX, -1, -box.x0);
    if (x1out != 0) clipToHalfspace(polPos, polygons, Polygon.SX, 1, box.x1);

    if (y0out != 0) clipToHalfspace(polPos, polygons, Polygon.SY, -1, -box.y0);
    if (y1out != 0) clipToHalfspace(polPos, polygons, Polygon.SY, 1, box.y1);

    if (z0out != 0) clipToHalfspace(polPos, polygons, Polygon.SZ, -1, -box.z0);
    if (z1out != 0) clipToHalfspace(polPos, polygons, Polygon.SZ, 1, box.z1);
    if (p1.length == 0) return true;
    else return false;
  }

  /**
   * This helper method assumes that the polygon following the one at polPos is free.
   *
   * @param polPos the position of the polygon in thepolygons array
   * @param index The index to compare --- e.g. Polyggn.SX
   * @param sign +/-1 which side of the halfspace.
   * @param k the location of the halfspace --- e.g. ClippingBox.x0
   */
  private void clipToHalfspace(int polPos, Polygon polygons[], int index, int sign, double k) {

    Polygon p = polygons[polPos];
    if (p.length == 0) return;
    // int vc = vertexCount;
    int u = p.vertices[p.length - 1];
    int v = p.vertices[0];
    double tu = sign * vertexData[u + index] - k * vertexData[u + Polygon.SW];
    // sign * vertexData[u + index] / vertexData[u+Polygon.SW] -k;
    double tv = 0;
    // HERE!!!!!
    Polygon newP = polygons[polPos + 1];
    newP.length = 0;
    for (int i = 0; i < p.length; i++, u = v, tu = tv, v = p.vertices[i]) {
      tv = sign * vertexData[v + index] - k * vertexData[v + Polygon.SW];
      // sign * vertexData[v + index] / vertexData[v+Polygon.SW] - k;
      if (tu <= 0. ^ tv <= 0.) { // edge crosses plane...
        double t = tu / (tu - tv);

        for (int j = 0; j < Polygon.VERTEX_LENGTH; j++) {
          vertexData[vertexCount + j] =
              vertexData[u + j] + t * (vertexData[v + j] - vertexData[u + j]);
        }
        newP.vertices[newP.length++] = vertexCount;
        vertexCount += Polygon.VERTEX_LENGTH;
      }
      if (tv <= 0.) { // vertex v is in ...
        //				for(int j = 0;j<Polygon.VERTEX_LENGTH;j++) {
        //					vertexData[vc+j] = vertexData[v+j];
        //				}
        newP.vertices[newP.length++] = v;
        //				vc+= Polygon.VERTEX_LENGTH;
      }
    }
    // It is left to swap:
    int vp[] = p.vertices;
    p.vertices = newP.vertices;
    p.length = newP.length;
    newP.vertices = vp;
  }

  /**
   * Sets the current matrix for the @link processPolygon method.
   *
   * @param matrix The matrix to set
   */
  public final void setMatrix(final double[] matrix) {
    this.matrix = matrix;
    Rn.transpose(inverseTransposeMatrix, matrix);
    Rn.inverse(inverseTransposeMatrix, inverseTransposeMatrix);
  }

  public final void sortPolygons() {
    // eSystem.out.println("scheduled polys "+polygonCount);
    // it might be better to sort the non transparent polygons too
    // since the setPixel call is one of the most speed relevant. If the
    // polygons are drawn front to back (the reverse oder compared to the
    // painter's algorithm) the setPixel calls return immediately:
    Arrays.sort(polygons, 0, polygonCount, comp);
    // Arrays.sort(transparentPolygons,0,transparentPolygonCount,comp);
  }

  /** @return Perspective */
  public final Perspective getPerspective() {
    return perspective;
  }

  /**
   * Sets the perspective.
   *
   * @param perspective The perspective to set
   */
  public final void setPerspective(final Perspective perspective) {
    this.perspective = perspective;
  }

  /** @return Environment */
  public final Environment getEnvironment() {
    return environment;
  }

  /**
   * Sets the environment.
   *
   * @param environment The environment to set
   */
  public final void setEnvironment(Environment environment) {
    this.environment = environment;
  }

  /** @param renderer */
  public final void renderRemaining(
      final PolygonRasterizer
          renderer /*,final int xmin, final int xmax, final int ymin, final int ymax*/) {
    // renderer.setWindow(xmin,xmax,ymin,ymax);
    //		for (int i = polygonCount; i > 0;) {
    //			renderer.renderPolygon(polygons[--i],vertexData,polygons[i].getShader().isOutline());
    //			polygons[i].setShader(null);
    //		}
    //		for (int i = 0; i < transparentPolygonCount; i++) {
    //
    //	renderer.renderPolygon(transparentPolygons[i],vertexData,transparentPolygons[i].getShader().isOutline());
    //			transparentPolygons[i].setShader(null);
    //		}
    // System.out.println("polys "+ polygonCount);

    // we render the polygons front to back in order to have less to do in setPixel. transparent
    // polys are
    // sorted in reverse order and at the beginning, so we know, that they are rendered last and in
    // back to front order.
    //		for (int i = polygonCount; i > 0;) {
    //			renderer.renderPolygon(polygons[--i],vertexData,polygons[i].getShader().isOutline());
    //			polygons[i].setShader(null);
    //		}

    for (int i = 0; i < (polygonCount); i++) {
      Polygon p = polygons[i];
      renderer.renderPolygon(p, vertexData, p.getShader().isOutline());
      p.setShader(null);
      // System.out.println("centerZ "+p.getCenterZ() );
    }
  }

  private double[] point0 = new double[4];
  private double[] normal0 = new double[3];
  private double[] point1 = new double[4];
  private double[] normal1 = new double[3];
  private double[] substMatrix = new double[16];

  /* (non-Javadoc)
   * @see de.jreality.soft.PointProcessor#processPoint(double[], int)
   */
  public final void processPoint(final double[] data, int index, int length) {
    if (pointShader == null) return;
    if (length == 4) processPoint(data[index], data[index + 1], data[index + 2], data[index + 3]);
    else processPoint(data[index], data[index + 1], data[index + 2], 1);
  }

  public final void processPoint(final DoubleArrayArray data, int index) {
    DoubleArray da = data.item(index).toDoubleArray();
    double w = 1;
    if (da.size() == 4) w = da.getValueAt(3);
    if (vertexColors) {
      DoubleArray color = vertexColor.item(index).toDoubleArray();
      for (int i = 0; i < 16; i++) {
        pointColors[i][0] = color.getValueAt(0);
        pointColors[i][1] = color.getValueAt(1);
        pointColors[i][2] = color.getValueAt(2);
        pointColors[i][3] =
            (vertexColorLength == 4 || (vertexColorLength == -1 && color.getLength() > 3))
                ? color.getValueAt(3)
                : 1.;
      }
      vertexColor = pointColorDataList;
    }
    processPoint(da.getValueAt(0), da.getValueAt(1), da.getValueAt(2), w);
  }

  public final void processPoint(final double x, final double y, final double z, final double w) {
    if (pointShader == null) return;
    VecMat.transform(matrix, x, y, z, w, point0, 0);
    VecMat.transformUnNormalized(matrix, 0, 0, pointRadius, normal0, 0);
    double[] mat = matrix;
    double[] tmat = inverseTransposeMatrix;
    matrix = substMatrix;
    double d = 1 - outlineFraction;
    // inner point :
    double l = VecMat.norm(normal0);
    VecMat.assignScale(matrix, l * d);
    matrix[4 * 0 + 3] = point0[0];
    matrix[4 * 1 + 3] = point0[1];
    matrix[4 * 2 + 3] = point0[2] + pointRadius;
    Rn.transpose(inverseTransposeMatrix, matrix);
    Rn.inverse(inverseTransposeMatrix, inverseTransposeMatrix);
    shader = pointShader.getCoreShader();
    computeArray(pointVertices, pointIndices, zNormal, pointNormals);

    // outline :
    if (outlineFraction > 0 /*&& pointShader!= null*/) {
      shader = pointShader.getOutlineShader();
      d = 1 / d;

      for (int i = 0; i < 24; i += 3) {
        pointVertices[24 + i] = pointVertices[i] * d;
        pointVertices[25 + i] = pointVertices[i + 1] * d;
        pointVertices[26 + i] = -d;
      }
      for (int i = 0; i < 8; i++) {
        computeArray(pointVertices, pointOutlineIndices[i], zNormal, pointOutlineNormals[i]);
      }
    }

    // reset the matrix:
    matrix = mat;
    inverseTransposeMatrix = tmat;
  }

  private static final double[] identity =
      new double[] {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
  private double[] line = new double[12];
  private double[] direction = new double[3];
  /* (non-Javadoc)
   * @see de.jreality.soft.LineProcessor#processLine(double[], int, int)
   */
  public final void processLine(DoubleArray from, DoubleArray to) {
    if (lineShader == null) return;
    shader = lineShader.getPolygonShader();
    double w = 1;
    if (from.size() == 4) w = from.getValueAt(3);
    VecMat.transform(
        matrix, from.getValueAt(0), from.getValueAt(1), from.getValueAt(2), 1, point0, 0);

    VecMat.transformUnNormalized(matrix, 0, 0, lineWidth, normal0, 0);

    w = 1;
    if (to.size() == 4) w = from.getValueAt(3);
    VecMat.transform(matrix, to.getValueAt(0), to.getValueAt(1), to.getValueAt(2), 1, point1, 0);
    // VecMat.transformUnNormalized(matrix,0,0,.2,normal0,0);

    double[] mat = matrix;
    double[] tmat = inverseTransposeMatrix;
    matrix = identity;
    inverseTransposeMatrix = identity;
    // VecMat.assignIdentity(matrix);

    direction[0] = point1[0] - point0[0];
    direction[1] = point1[1] - point0[1];
    direction[2] = point1[2] - point0[2];
    // TODO make only one call to norm...
    if (VecMat.norm(direction) == 0) {
      matrix = mat;
      inverseTransposeMatrix = tmat;
      return;
    }
    VecMat.normalize(direction);
    double length = VecMat.norm(normal0);
    VecMat.cross(direction, zNormal, normal0);
    VecMat.normalize(normal0);
    normal0[0] *= length;
    normal0[1] *= length;
    normal0[2] *= length;

    VecMat.cross(normal0, direction, normal1);
    VecMat.normalize(normal1);

    line[0] = point0[0] - normal0[0];
    line[1] = point0[1] - normal0[1];
    line[2] = point0[2] + length; // - normal0[2];	

    line[3] = point0[0] + normal0[0];
    line[4] = point0[1] + normal0[1];
    line[5] = point0[2] + length; // + normal0[2];

    line[6] = point1[0] + normal0[0];
    line[7] = point1[1] + normal0[1];
    line[8] = point1[2] + length; // + normal0[2];

    line[9] = point1[0] - normal0[0];
    line[10] = point1[1] - normal0[1];
    line[11] = point1[2] + length; // + normal0[2];

    computeArray(line, lineIndices, normal1, pointNormals);
    matrix = mat;
    inverseTransposeMatrix = tmat;
  }

  private double[] normal2 = new double[6];
  private static int[] nIndices = {0, 3, 3, 0};
  private static final double cs = Math.cos(0.2);
  private static final double ss = Math.sin(0.2);

  private double test1[] = new double[3];
  private double test2[] = new double[3];
  private DoubleArray tda1 = new DoubleArray(test1);
  private DoubleArray tda2 = new DoubleArray(test2);

  private double test3[] = new double[3];
  private double test4[] = new double[3];
  private DoubleArray tda3 = new DoubleArray(test3);
  private DoubleArray tda4 = new DoubleArray(test4);
  private double[] zzNormal = new double[3];

  public final void processPseudoTube(DoubleArray from, DoubleArray to) {
    if (lineShader == null) return;
    shader = lineShader.getPolygonShader();
    double w = 1;
    if (from.size() == 4) w = from.getValueAt(3);
    VecMat.transform(
        matrix, from.getValueAt(0), from.getValueAt(1), from.getValueAt(2), w, point0, 0);

    VecMat.transformUnNormalized(matrix, 0, 0, lineWidth, normal0, 0);
    w = 1;
    if (to.size() == 4) w = to.getValueAt(3);
    VecMat.transform(matrix, to.getValueAt(0), to.getValueAt(1), to.getValueAt(2), w, point1, 0);

    double[] mat = matrix;
    double[] tmat = inverseTransposeMatrix;
    matrix = identity;
    inverseTransposeMatrix = identity;

    direction[0] = point1[0] - point0[0];
    direction[1] = point1[1] - point0[1];
    direction[2] = point1[2] - point0[2];

    // TODO: decide: better but slower:
    zzNormal[0] = point1[0] + point0[0];
    zzNormal[1] = point1[1] + point0[1];
    zzNormal[2] = point1[2] + point0[2];
    VecMat.normalize(zzNormal);

    // TODO make only one call to norm...
    if (VecMat.norm(direction) == 0) {
      matrix = mat;
      inverseTransposeMatrix = tmat;

      return;
    }
    VecMat.normalize(direction);
    double length = VecMat.norm(normal0);
    VecMat.cross(direction, zzNormal, normal0);
    VecMat.normalize(normal0);
    VecMat.cross(normal0, direction, normal1);

    normal0[0] *= length;
    normal0[1] *= length;
    // allways zero:
    normal0[2] *= length;

    VecMat.normalize(normal1);

    if (VecMat.dot(normal1, zzNormal) < 0) {
      normal1[0] *= -1;
      normal1[1] *= -1;
      normal1[2] *= -1;
      System.out.println("flip");
    }

    line[0] = point0[0] - normal0[0];
    line[1] = point0[1] - normal0[1];
    line[2] = point0[2];

    line[3] = point0[0];
    line[4] = point0[1];
    line[5] = point0[2] + length;

    line[6] = point1[0];
    line[7] = point1[1];
    line[8] = point1[2] + length;

    line[9] = point1[0] - normal0[0];
    line[10] = point1[1] - normal0[1];
    line[11] = point1[2];

    normal2[0] = +cs / length * normal0[0] + ss * normal1[0];
    normal2[1] = +cs / length * normal0[1] + ss * normal1[1];
    normal2[2] = ss * normal1[2];

    normal2[3] = normal1[0];
    normal2[4] = normal1[1];
    normal2[5] = normal1[2];

    computeArrayNoTransform(line, lineIndices, normal2, nIndices);

    /////

    line[0] = point0[0];
    line[1] = point0[1];
    line[2] = point0[2] + length;

    line[3] = point0[0] + normal0[0];
    line[4] = point0[1] + normal0[1];
    line[5] = point0[2];

    line[6] = point1[0] + normal0[0];
    line[7] = point1[1] + normal0[1];
    line[8] = point1[2];

    line[9] = point1[0];
    line[10] = point1[1];
    line[11] = point1[2] + length;

    normal2[0] = normal1[0];
    normal2[1] = normal1[1];
    normal2[2] = normal1[2];

    normal2[3] = -cs / length * normal0[0] + ss * normal1[0];
    normal2[4] = -cs / length * normal0[1] + ss * normal1[1];
    normal2[5] = ss * normal1[2];

    computeArrayNoTransform(line, lineIndices, normal2, nIndices);

    /*
    test1[0] = point1[0];
    test1[1] = point1[1];
    test1[2] = point1[2];
    test2[0] = point1[0]+normal1[0];
    test2[1] = point1[1]+normal1[1];
    test2[2] = point1[2]+normal1[2];




    test3[0] = point0[0];
    test3[1] = point0[1];
    test3[2] = point0[2];
    test4[0] = point0[0]+normal1[0];
    test4[1] = point0[1]+normal1[1];
    test4[2] = point0[2]+normal1[2];

    processLine(tda1,tda2);
    processLine(tda3,tda4);

    */

    matrix = mat;
    inverseTransposeMatrix = tmat;
  }

  /** @return PolygonShader */
  public PolygonShader getFaceShader() {
    return faceShader;
  }

  /**
   * Sets the faceShader.
   *
   * @param faceShader The faceShader to set
   */
  public void setFaceShader(PolygonShader faceShader) {
    this.faceShader = faceShader;
  }

  /** @return double */
  public double getLineWidth() {
    return lineWidth;
  }

  /** @return double */
  public double getPointRadius() {
    return pointRadius;
  }

  /**
   * Sets the lineWidth.
   *
   * @param lineWidth The lineWidth to set
   */
  public void setLineWidth(double lineWidth) {
    this.lineWidth = lineWidth;
  }

  /**
   * Sets the pointRadius.
   *
   * @param pointRadius The pointRadius to set
   */
  public void setPointRadius(double pointRadius) {
    this.pointRadius = pointRadius;
  }

  /** @return PolygonShader */
  public LineShader getLineShader() {
    return lineShader;
  }

  /** @return PolygonShader */
  public PointShader getPointShader() {
    return pointShader;
  }

  /**
   * Sets the lineShader.
   *
   * @param lineShader The lineShader to set
   */
  public void setLineShader(LineShader lineShader) {
    this.lineShader = lineShader;
    if (lineShader != null) lineWidth = lineShader.getLineWidth();
  }

  /**
   * Sets the pointShader.
   *
   * @param pointShader The pointShader to set
   */
  public void setPointShader(PointShader pointShader) {
    this.pointShader = pointShader;
    if (pointShader != null) {
      pointRadius = pointShader.getPointRadius();
      outlineFraction = pointShader.getOutlineFraction();
    }
  }

  /** @return PolygonShader */
  //    public PolygonShader getPointOutlineShader() {
  //        return pointOutlineShader;
  //    }

  /**
   * Sets the pointOutlineShader.
   *
   * @param pointOutlineShader The pointOutlineShader to set
   */
  //    public void setPointOutlineShader(PolygonShader pointOutlineShader) {
  //        this.pointOutlineShader = pointOutlineShader;
  //    }

  /** @return double */
  public double getOutlineFraction() {
    return outlineFraction;
  }

  /**
   * Sets the outlineFraction.
   *
   * @param outlineFraction The outlineFraction to set
   */
  public void setOutlineFraction(double outlineFraction) {
    this.outlineFraction = outlineFraction;
  }

  public int copyPolygon(Polygon p) {
    int newP = getFreePolygon();
    Polygon np = polygons[newP];
    np.length = p.length;
    for (int i = 0; i < p.length; i++) {
      np.vertices[i] = p.vertices[i];
      np.setShader(p.getShader());
      np.setCenterZ(p.getCenterZ());
    }
    return newP;
  }
}