private static List<VertexData> processTriangles(
      Mesh mesh, int[] index, Vector3f[] v, Vector2f[] t, boolean splitMirrored) {
    IndexBuffer indexBuffer = mesh.getIndexBuffer();
    FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
    if (mesh.getBuffer(Type.TexCoord) == null) {
      throw new IllegalArgumentException(
          "Can only generate tangents for " + "meshes with texture coordinates");
    }

    FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData();

    List<VertexData> vertices = initVertexData(vertexBuffer.limit() / 3);

    for (int i = 0; i < indexBuffer.size() / 3; i++) {
      for (int j = 0; j < 3; j++) {
        index[j] = indexBuffer.get(i * 3 + j);
        populateFromBuffer(v[j], vertexBuffer, index[j]);
        populateFromBuffer(t[j], textureBuffer, index[j]);
      }

      TriangleData triData = processTriangle(index, v, t);
      if (splitMirrored) {
        triData.setIndex(index);
        triData.triangleOffset = i * 3;
      }
      if (triData != null) {
        vertices.get(index[0]).triangles.add(triData);
        vertices.get(index[1]).triangles.add(triData);
        vertices.get(index[2]).triangles.add(triData);
      }
    }

    return vertices;
  }
  private static List<VertexData> processTriangleStrip(
      Mesh mesh, int[] index, Vector3f[] v, Vector2f[] t) {
    IndexBuffer indexBuffer = mesh.getIndexBuffer();
    FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
    FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData();

    List<VertexData> vertices = initVertexData(vertexBuffer.limit() / 3);

    index[0] = indexBuffer.get(0);
    index[1] = indexBuffer.get(1);

    populateFromBuffer(v[0], vertexBuffer, index[0]);
    populateFromBuffer(v[1], vertexBuffer, index[1]);

    populateFromBuffer(t[0], textureBuffer, index[0]);
    populateFromBuffer(t[1], textureBuffer, index[1]);

    for (int i = 2; i < indexBuffer.size(); i++) {
      index[2] = indexBuffer.get(i);
      BufferUtils.populateFromBuffer(v[2], vertexBuffer, index[2]);
      BufferUtils.populateFromBuffer(t[2], textureBuffer, index[2]);

      boolean isDegenerate = isDegenerateTriangle(v[0], v[1], v[2]);
      TriangleData triData = processTriangle(index, v, t);

      if (triData != null && !isDegenerate) {
        vertices.get(index[0]).triangles.add(triData);
        vertices.get(index[1]).triangles.add(triData);
        vertices.get(index[2]).triangles.add(triData);
      }

      Vector3f vTemp = v[0];
      v[0] = v[1];
      v[1] = v[2];
      v[2] = vTemp;

      Vector2f tTemp = t[0];
      t[0] = t[1];
      t[1] = t[2];
      t[2] = tTemp;

      index[0] = index[1];
      index[1] = index[2];
    }

    return vertices;
  }
Example #3
0
  public static void compressIndexBuffer(Mesh mesh) {
    int vertCount = mesh.getVertexCount();
    VertexBuffer vb = mesh.getBuffer(Type.Index);
    Format targetFmt;
    if (vb.getFormat() == Format.UnsignedInt && vertCount <= 0xffff) {
      if (vertCount <= 256) targetFmt = Format.UnsignedByte;
      else targetFmt = Format.UnsignedShort;
    } else if (vb.getFormat() == Format.UnsignedShort && vertCount <= 0xff) {
      targetFmt = Format.UnsignedByte;
    } else {
      return;
    }

    IndexBuffer src = mesh.getIndexBuffer();
    Buffer newBuf = VertexBuffer.createBuffer(targetFmt, vb.getNumComponents(), src.size());

    VertexBuffer newVb = new VertexBuffer(Type.Index);
    newVb.setupData(vb.getUsage(), vb.getNumComponents(), targetFmt, newBuf);
    mesh.clearBuffer(Type.Index);
    mesh.setBuffer(newVb);

    IndexBuffer dst = mesh.getIndexBuffer();
    for (int i = 0; i < src.size(); i++) {
      dst.put(i, src.get(i));
    }
  }
  @Override
  public void initParticleData(Emitter emitter, int numParticles) {
    setMode(Mode.Triangles);

    this.emitter = emitter;

    this.finVerts = BufferUtils.createFloatBuffer(templateVerts.capacity() * numParticles);
    try {
      this.finCoords = BufferUtils.createFloatBuffer(templateCoords.capacity() * numParticles);
    } catch (Exception e) {
    }
    this.finIndexes = BufferUtils.createShortBuffer(templateIndexes.size() * numParticles);
    this.finNormals = BufferUtils.createFloatBuffer(templateNormals.capacity() * numParticles);
    this.finColors = BufferUtils.createFloatBuffer(templateVerts.capacity() / 3 * 4 * numParticles);

    int index = 0, index2 = 0, index3 = 0, index4 = 0, index5 = 0;
    int indexOffset = 0;

    for (int i = 0; i < numParticles; i++) {
      templateVerts.rewind();
      for (int v = 0; v < templateVerts.capacity(); v += 3) {
        tempV3.set(templateVerts.get(v), templateVerts.get(v + 1), templateVerts.get(v + 2));
        finVerts.put(index, tempV3.getX());
        index++;
        finVerts.put(index, tempV3.getY());
        index++;
        finVerts.put(index, tempV3.getZ());
        index++;
      }
      try {
        templateCoords.rewind();
        for (int v = 0; v < templateCoords.capacity(); v++) {
          finCoords.put(index2, templateCoords.get(v));
          index2++;
        }
      } catch (Exception e) {
      }
      for (int v = 0; v < templateIndexes.size(); v++) {
        finIndexes.put(index3, (short) (templateIndexes.get(v) + indexOffset));
        index3++;
      }
      indexOffset += templateVerts.capacity() / 3;

      templateNormals.rewind();
      for (int v = 0; v < templateNormals.capacity(); v++) {
        finNormals.put(index4, templateNormals.get(v));
        index4++;
      }

      for (int v = 0; v < finColors.capacity(); v++) {
        finColors.put(v, 1.0f);
      }
    }

    // Help GC
    //	tempV3 = null;
    //	templateVerts = null;
    //	templateCoords = null;
    //	templateIndexes = null;
    //	templateNormals = null;

    // Clear & ssign buffers
    this.clearBuffer(VertexBuffer.Type.Position);
    this.setBuffer(VertexBuffer.Type.Position, 3, finVerts);
    this.clearBuffer(VertexBuffer.Type.TexCoord);
    try {
      this.setBuffer(VertexBuffer.Type.TexCoord, 2, finCoords);
    } catch (Exception e) {
    }
    this.clearBuffer(VertexBuffer.Type.Index);
    this.setBuffer(VertexBuffer.Type.Index, 3, finIndexes);
    this.clearBuffer(VertexBuffer.Type.Normal);
    this.setBuffer(VertexBuffer.Type.Normal, 3, finNormals);
    this.clearBuffer(VertexBuffer.Type.Color);
    this.setBuffer(VertexBuffer.Type.Color, 4, finColors);
    this.updateBound();
  }
  /**
   * Merges all geometries in the collection into the output mesh. Creates a new material using the
   * TextureAtlas.
   *
   * @param geometries
   * @param outMesh
   */
  public static void mergeGeometries(Collection<Geometry> geometries, Mesh outMesh) {
    int[] compsForBuf = new int[VertexBuffer.Type.values().length];
    Format[] formatForBuf = new Format[compsForBuf.length];

    int totalVerts = 0;
    int totalTris = 0;
    int totalLodLevels = 0;
    int maxWeights = -1;

    Mode mode = null;
    for (Geometry geom : geometries) {
      totalVerts += geom.getVertexCount();
      totalTris += geom.getTriangleCount();
      totalLodLevels = Math.min(totalLodLevels, geom.getMesh().getNumLodLevels());

      Mode listMode;
      int components;
      switch (geom.getMesh().getMode()) {
        case Points:
          listMode = Mode.Points;
          components = 1;
          break;
        case LineLoop:
        case LineStrip:
        case Lines:
          listMode = Mode.Lines;
          components = 2;
          break;
        case TriangleFan:
        case TriangleStrip:
        case Triangles:
          listMode = Mode.Triangles;
          components = 3;
          break;
        default:
          throw new UnsupportedOperationException();
      }

      for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) {
        int currentCompsForBuf = compsForBuf[vb.getBufferType().ordinal()];
        if (vb.getBufferType() != Type.Index
            && currentCompsForBuf != 0
            && currentCompsForBuf != vb.getNumComponents()) {
          throw new UnsupportedOperationException(
              "The geometry "
                  + geom
                  + " buffer "
                  + vb.getBufferType()
                  + " has different number of components than the rest of the meshes "
                  + "(this: "
                  + vb.getNumComponents()
                  + ", expected: "
                  + currentCompsForBuf
                  + ")");
        }
        compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents();
        formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat();
      }

      maxWeights = Math.max(maxWeights, geom.getMesh().getMaxNumWeights());

      if (mode != null && mode != listMode) {
        throw new UnsupportedOperationException(
            "Cannot combine different" + " primitive types: " + mode + " != " + listMode);
      }
      mode = listMode;
      compsForBuf[Type.Index.ordinal()] = components;
    }

    outMesh.setMaxNumWeights(maxWeights);
    outMesh.setMode(mode);
    if (totalVerts >= 65536) {
      // make sure we create an UnsignedInt buffer so
      // we can fit all of the meshes
      formatForBuf[Type.Index.ordinal()] = Format.UnsignedInt;
    } else {
      formatForBuf[Type.Index.ordinal()] = Format.UnsignedShort;
    }

    // generate output buffers based on retrieved info
    for (int i = 0; i < compsForBuf.length; i++) {
      if (compsForBuf[i] == 0) {
        continue;
      }

      Buffer data;
      if (i == Type.Index.ordinal()) {
        data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris);
      } else {
        data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts);
      }

      VertexBuffer vb = new VertexBuffer(Type.values()[i]);
      vb.setupData(Usage.Static, compsForBuf[i], formatForBuf[i], data);
      outMesh.setBuffer(vb);
    }

    int globalVertIndex = 0;
    int globalTriIndex = 0;

    for (Geometry geom : geometries) {
      Mesh inMesh = geom.getMesh();
      geom.computeWorldMatrix();
      Matrix4f worldMatrix = geom.getWorldMatrix();

      int geomVertCount = inMesh.getVertexCount();
      int geomTriCount = inMesh.getTriangleCount();

      for (int bufType = 0; bufType < compsForBuf.length; bufType++) {
        VertexBuffer inBuf = inMesh.getBuffer(Type.values()[bufType]);
        VertexBuffer outBuf = outMesh.getBuffer(Type.values()[bufType]);

        if (inBuf == null || outBuf == null) {
          continue;
        }

        if (Type.Index.ordinal() == bufType) {
          int components = compsForBuf[bufType];

          IndexBuffer inIdx = inMesh.getIndicesAsList();
          IndexBuffer outIdx = outMesh.getIndexBuffer();

          for (int tri = 0; tri < geomTriCount; tri++) {
            for (int comp = 0; comp < components; comp++) {
              int idx = inIdx.get(tri * components + comp) + globalVertIndex;
              outIdx.put((globalTriIndex + tri) * components + comp, idx);
            }
          }
        } else if (Type.Position.ordinal() == bufType) {
          FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
          FloatBuffer outPos = (FloatBuffer) outBuf.getData();
          doTransformVerts(inPos, globalVertIndex, outPos, worldMatrix);
        } else if (Type.Normal.ordinal() == bufType) {
          FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
          FloatBuffer outPos = (FloatBuffer) outBuf.getData();
          doTransformNorms(inPos, globalVertIndex, outPos, worldMatrix);
        } else if (Type.Tangent.ordinal() == bufType) {
          FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
          FloatBuffer outPos = (FloatBuffer) outBuf.getData();
          int components = inBuf.getNumComponents();
          doTransformTangents(inPos, globalVertIndex, components, outPos, worldMatrix);
        } else {
          inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount);
        }
      }

      globalVertIndex += geomVertCount;
      globalTriIndex += geomTriCount;
    }
  }
Example #6
0
  /**
   * Rebuilds the cylinder based on a new set of parameters.
   *
   * @param axisSamples the number of samples along the axis.
   * @param radialSamples the number of samples around the radial.
   * @param radius the radius of the bottom of the cylinder.
   * @param radius2 the radius of the top of the cylinder.
   * @param height the cylinder's height.
   * @param closed should the cylinder have top and bottom surfaces.
   * @param inverted is the cylinder is meant to be viewed from the inside.
   */
  public void updateGeometry(
      int axisSamples,
      int radialSamples,
      float radius,
      float radius2,
      float height,
      boolean closed,
      boolean inverted) {
    this.axisSamples = axisSamples + (closed ? 2 : 0);
    this.radialSamples = radialSamples;
    this.radius = radius;
    this.radius2 = radius2;
    this.height = height;
    this.closed = closed;
    this.inverted = inverted;

    //        VertexBuffer pvb = getBuffer(Type.Position);
    //        VertexBuffer nvb = getBuffer(Type.Normal);
    //        VertexBuffer tvb = getBuffer(Type.TexCoord);

    // Vertices
    int vertCount = axisSamples * (radialSamples + 1) + (closed ? 2 : 0);

    setBuffer(Type.Position, 3, createVector3Buffer(getFloatBuffer(Type.Position), vertCount));

    // Normals
    setBuffer(Type.Normal, 3, createVector3Buffer(getFloatBuffer(Type.Normal), vertCount));

    // Texture co-ordinates
    setBuffer(Type.TexCoord, 2, createVector2Buffer(vertCount));

    int triCount = ((closed ? 2 : 0) + 2 * (axisSamples - 1)) * radialSamples;

    setBuffer(Type.Index, 3, createShortBuffer(getShortBuffer(Type.Index), 3 * triCount));

    // generate geometry
    float inverseRadial = 1.0f / radialSamples;
    float inverseAxisLess = 1.0f / (closed ? axisSamples - 3 : axisSamples - 1);
    float inverseAxisLessTexture = 1.0f / (axisSamples - 1);
    float halfHeight = 0.5f * height;

    // Generate points on the unit circle to be used in computing the mesh
    // points on a cylinder slice.
    float[] sin = new float[radialSamples + 1];
    float[] cos = new float[radialSamples + 1];

    for (int radialCount = 0; radialCount < radialSamples; radialCount++) {
      float angle = FastMath.TWO_PI * inverseRadial * radialCount;
      cos[radialCount] = FastMath.cos(angle);
      sin[radialCount] = FastMath.sin(angle);
    }
    sin[radialSamples] = sin[0];
    cos[radialSamples] = cos[0];

    // calculate normals
    Vector3f[] vNormals = null;
    Vector3f vNormal = Vector3f.UNIT_Z;

    if ((height != 0.0f) && (radius != radius2)) {
      vNormals = new Vector3f[radialSamples];
      Vector3f vHeight = Vector3f.UNIT_Z.mult(height);
      Vector3f vRadial = new Vector3f();

      for (int radialCount = 0; radialCount < radialSamples; radialCount++) {
        vRadial.set(cos[radialCount], sin[radialCount], 0.0f);
        Vector3f vRadius = vRadial.mult(radius);
        Vector3f vRadius2 = vRadial.mult(radius2);
        Vector3f vMantle = vHeight.subtract(vRadius2.subtract(vRadius));
        Vector3f vTangent = vRadial.cross(Vector3f.UNIT_Z);
        vNormals[radialCount] = vMantle.cross(vTangent).normalize();
      }
    }

    FloatBuffer nb = getFloatBuffer(Type.Normal);
    FloatBuffer pb = getFloatBuffer(Type.Position);
    FloatBuffer tb = getFloatBuffer(Type.TexCoord);

    // generate the cylinder itself
    Vector3f tempNormal = new Vector3f();
    for (int axisCount = 0, i = 0; axisCount < axisSamples; axisCount++, i++) {
      float axisFraction;
      float axisFractionTexture;
      int topBottom = 0;
      if (!closed) {
        axisFraction = axisCount * inverseAxisLess; // in [0,1]
        axisFractionTexture = axisFraction;
      } else {
        if (axisCount == 0) {
          topBottom = -1; // bottom
          axisFraction = 0;
          axisFractionTexture = inverseAxisLessTexture;
        } else if (axisCount == axisSamples - 1) {
          topBottom = 1; // top
          axisFraction = 1;
          axisFractionTexture = 1 - inverseAxisLessTexture;
        } else {
          axisFraction = (axisCount - 1) * inverseAxisLess;
          axisFractionTexture = axisCount * inverseAxisLessTexture;
        }
      }

      // compute center of slice
      float z = -halfHeight + height * axisFraction;
      Vector3f sliceCenter = new Vector3f(0, 0, z);

      // compute slice vertices with duplication at end point
      int save = i;
      for (int radialCount = 0; radialCount < radialSamples; radialCount++, i++) {
        float radialFraction = radialCount * inverseRadial; // in [0,1)
        tempNormal.set(cos[radialCount], sin[radialCount], 0.0f);

        if (vNormals != null) {
          vNormal = vNormals[radialCount];
        } else if (radius == radius2) {
          vNormal = tempNormal;
        }

        if (topBottom == 0) {
          if (!inverted) nb.put(vNormal.x).put(vNormal.y).put(vNormal.z);
          else nb.put(-vNormal.x).put(-vNormal.y).put(-vNormal.z);
        } else {
          nb.put(0).put(0).put(topBottom * (inverted ? -1 : 1));
        }

        tempNormal.multLocal((radius - radius2) * axisFraction + radius2).addLocal(sliceCenter);
        pb.put(tempNormal.x).put(tempNormal.y).put(tempNormal.z);

        tb.put((inverted ? 1 - radialFraction : radialFraction)).put(axisFractionTexture);
      }

      BufferUtils.copyInternalVector3(pb, save, i);
      BufferUtils.copyInternalVector3(nb, save, i);

      tb.put((inverted ? 0.0f : 1.0f)).put(axisFractionTexture);
    }

    if (closed) {
      pb.put(0).put(0).put(-halfHeight); // bottom center
      nb.put(0).put(0).put(-1 * (inverted ? -1 : 1));
      tb.put(0.5f).put(0);
      pb.put(0).put(0).put(halfHeight); // top center
      nb.put(0).put(0).put(1 * (inverted ? -1 : 1));
      tb.put(0.5f).put(1);
    }

    IndexBuffer ib = getIndexBuffer();
    int index = 0;
    // Connectivity
    for (int axisCount = 0, axisStart = 0; axisCount < axisSamples - 1; axisCount++) {
      int i0 = axisStart;
      int i1 = i0 + 1;
      axisStart += radialSamples + 1;
      int i2 = axisStart;
      int i3 = i2 + 1;
      for (int i = 0; i < radialSamples; i++) {
        if (closed && axisCount == 0) {
          if (!inverted) {
            ib.put(index++, i0++);
            ib.put(index++, vertCount - 2);
            ib.put(index++, i1++);
          } else {
            ib.put(index++, i0++);
            ib.put(index++, i1++);
            ib.put(index++, vertCount - 2);
          }
        } else if (closed && axisCount == axisSamples - 2) {
          ib.put(index++, i2++);
          ib.put(index++, inverted ? vertCount - 1 : i3++);
          ib.put(index++, inverted ? i3++ : vertCount - 1);
        } else {
          ib.put(index++, i0++);
          ib.put(index++, inverted ? i2 : i1);
          ib.put(index++, inverted ? i1 : i2);
          ib.put(index++, i1++);
          ib.put(index++, inverted ? i2++ : i3++);
          ib.put(index++, inverted ? i3++ : i2++);
        }
      }
    }

    updateBound();
  }
Example #7
0
  /**
   * Merges all geometries in the collection into the output mesh. Does not take into account
   * materials.
   *
   * @param geometries
   * @param outMesh
   */
  private void mergeGeometries(Mesh outMesh, List<Geometry> geometries) {
    int[] compsForBuf = new int[VertexBuffer.Type.values().length];
    VertexBuffer.Format[] formatForBuf = new VertexBuffer.Format[compsForBuf.length];

    int totalVerts = 0;
    int totalTris = 0;
    int totalLodLevels = 0;
    int maxWeights = -1;

    Mesh.Mode mode = null;
    for (Geometry geom : geometries) {
      totalVerts += geom.getVertexCount();
      totalTris += geom.getTriangleCount();
      totalLodLevels = Math.min(totalLodLevels, geom.getMesh().getNumLodLevels());
      if (maxVertCount < geom.getVertexCount()) {
        maxVertCount = geom.getVertexCount();
      }
      Mesh.Mode listMode;
      int components;
      switch (geom.getMesh().getMode()) {
        case Points:
          listMode = Mesh.Mode.Points;
          components = 1;
          break;
        case LineLoop:
        case LineStrip:
        case Lines:
          listMode = Mesh.Mode.Lines;
          components = 2;
          break;
        case TriangleFan:
        case TriangleStrip:
        case Triangles:
          listMode = Mesh.Mode.Triangles;
          components = 3;
          break;
        default:
          throw new UnsupportedOperationException();
      }

      for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) {
        compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents();
        formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat();
      }

      maxWeights = Math.max(maxWeights, geom.getMesh().getMaxNumWeights());

      if (mode != null && mode != listMode) {
        throw new UnsupportedOperationException(
            "Cannot combine different" + " primitive types: " + mode + " != " + listMode);
      }
      mode = listMode;
      compsForBuf[VertexBuffer.Type.Index.ordinal()] = components;
    }

    outMesh.setMaxNumWeights(maxWeights);
    outMesh.setMode(mode);
    if (totalVerts >= 65536) {
      // make sure we create an UnsignedInt buffer so
      // we can fit all of the meshes
      formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedInt;
    } else {
      formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedShort;
    }

    // generate output buffers based on retrieved info
    for (int i = 0; i < compsForBuf.length; i++) {
      if (compsForBuf[i] == 0) {
        continue;
      }

      Buffer data;
      if (i == VertexBuffer.Type.Index.ordinal()) {
        data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris);
      } else {
        data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts);
      }

      VertexBuffer vb = new VertexBuffer(VertexBuffer.Type.values()[i]);
      vb.setupData(VertexBuffer.Usage.Dynamic, compsForBuf[i], formatForBuf[i], data);
      outMesh.setBuffer(vb);
    }

    int globalVertIndex = 0;
    int globalTriIndex = 0;

    for (Geometry geom : geometries) {
      Mesh inMesh = geom.getMesh();
      if (!isBatch(geom)) {
        geom.batch(this, globalVertIndex);
      }

      int geomVertCount = inMesh.getVertexCount();
      int geomTriCount = inMesh.getTriangleCount();

      for (int bufType = 0; bufType < compsForBuf.length; bufType++) {
        VertexBuffer inBuf = inMesh.getBuffer(VertexBuffer.Type.values()[bufType]);

        VertexBuffer outBuf = outMesh.getBuffer(VertexBuffer.Type.values()[bufType]);

        if (outBuf == null) {
          continue;
        }

        if (VertexBuffer.Type.Index.ordinal() == bufType) {
          int components = compsForBuf[bufType];

          IndexBuffer inIdx = inMesh.getIndicesAsList();
          IndexBuffer outIdx = outMesh.getIndexBuffer();

          for (int tri = 0; tri < geomTriCount; tri++) {
            for (int comp = 0; comp < components; comp++) {
              int idx = inIdx.get(tri * components + comp) + globalVertIndex;
              outIdx.put((globalTriIndex + tri) * components + comp, idx);
            }
          }
        } else if (VertexBuffer.Type.Position.ordinal() == bufType) {
          FloatBuffer inPos = (FloatBuffer) inBuf.getData();
          FloatBuffer outPos = (FloatBuffer) outBuf.getData();
          doCopyBuffer(inPos, globalVertIndex, outPos, 3);
        } else if (VertexBuffer.Type.Normal.ordinal() == bufType
            || VertexBuffer.Type.Tangent.ordinal() == bufType) {
          FloatBuffer inPos = (FloatBuffer) inBuf.getData();
          FloatBuffer outPos = (FloatBuffer) outBuf.getData();
          doCopyBuffer(inPos, globalVertIndex, outPos, compsForBuf[bufType]);
          if (VertexBuffer.Type.Tangent.ordinal() == bufType) {
            useTangents = true;
          }
        } else {
          inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount);
          //                    for (int vert = 0; vert < geomVertCount; vert++) {
          //                        int curGlobalVertIndex = globalVertIndex + vert;
          //                        inBuf.copyElement(vert, outBuf, curGlobalVertIndex);
          //                    }
        }
      }

      globalVertIndex += geomVertCount;
      globalTriIndex += geomTriCount;
    }
  }