private void applyFrame(Mesh target, int frameIndex, float weight) {
    PoseFrame frame = frames[frameIndex];
    VertexBuffer pb = target.getBuffer(Type.Position);
    for (int i = 0; i < frame.poses.length; i++) {
      Pose pose = frame.poses[i];
      float poseWeight = frame.weights[i] * weight;

      pose.apply(poseWeight, (FloatBuffer) pb.getData());
    }

    // force to re-upload data to gpu
    pb.updateData(pb.getData());
  }
Beispiel #2
0
  public static void convertToFixed(Geometry geom, Format posFmt, Format nmFmt, Format tcFmt) {
    geom.updateModelBound();
    BoundingBox bbox = (BoundingBox) geom.getModelBound();
    Mesh mesh = geom.getMesh();

    VertexBuffer positions = mesh.getBuffer(Type.Position);
    VertexBuffer normals = mesh.getBuffer(Type.Normal);
    VertexBuffer texcoords = mesh.getBuffer(Type.TexCoord);
    VertexBuffer indices = mesh.getBuffer(Type.Index);

    // positions
    FloatBuffer fb = (FloatBuffer) positions.getData();
    if (posFmt != Format.Float) {
      Buffer newBuf =
          VertexBuffer.createBuffer(posFmt, positions.getNumComponents(), mesh.getVertexCount());
      Transform t = convertPositions(fb, bbox, newBuf);
      t.combineWithParent(geom.getLocalTransform());
      geom.setLocalTransform(t);

      VertexBuffer newPosVb = new VertexBuffer(Type.Position);
      newPosVb.setupData(positions.getUsage(), positions.getNumComponents(), posFmt, newBuf);
      mesh.clearBuffer(Type.Position);
      mesh.setBuffer(newPosVb);
    }

    // normals, automatically convert to signed byte
    fb = (FloatBuffer) normals.getData();

    ByteBuffer bb = BufferUtils.createByteBuffer(fb.capacity());
    convertNormals(fb, bb);

    normals = new VertexBuffer(Type.Normal);
    normals.setupData(Usage.Static, 3, Format.Byte, bb);
    normals.setNormalized(true);
    mesh.clearBuffer(Type.Normal);
    mesh.setBuffer(normals);

    // texcoords
    fb = (FloatBuffer) texcoords.getData();
    if (tcFmt != Format.Float) {
      Buffer newBuf =
          VertexBuffer.createBuffer(tcFmt, texcoords.getNumComponents(), mesh.getVertexCount());
      convertTexCoords2D(fb, newBuf);

      VertexBuffer newTcVb = new VertexBuffer(Type.TexCoord);
      newTcVb.setupData(texcoords.getUsage(), texcoords.getNumComponents(), tcFmt, newBuf);
      mesh.clearBuffer(Type.TexCoord);
      mesh.setBuffer(newTcVb);
    }
  }
Beispiel #3
0
  public static VertexBuffer convertToUByte(VertexBuffer vb) {
    FloatBuffer fb = (FloatBuffer) vb.getData();
    ByteBuffer bb = BufferUtils.createByteBuffer(fb.capacity());
    convertToUByte(fb, bb);

    VertexBuffer newVb = new VertexBuffer(vb.getBufferType());
    newVb.setupData(vb.getUsage(), vb.getNumComponents(), Format.UnsignedByte, bb);
    newVb.setNormalized(true);
    return newVb;
  }
Beispiel #4
0
  public static VertexBuffer convertToFloat(VertexBuffer vb) {
    if (vb.getFormat() == Format.Float) return vb;

    IntBuffer ib = (IntBuffer) vb.getData();
    FloatBuffer fb = BufferUtils.createFloatBuffer(ib.capacity());
    convertToFloat(ib, fb);

    VertexBuffer newVb = new VertexBuffer(vb.getBufferType());
    newVb.setupData(vb.getUsage(), vb.getNumComponents(), Format.Float, fb);
    return newVb;
  }
  public static void generate(Mesh mesh, boolean approxTangents, boolean splitMirrored) {
    int[] index = new int[3];
    Vector3f[] v = new Vector3f[3];
    Vector2f[] t = new Vector2f[3];
    for (int i = 0; i < 3; i++) {
      v[i] = new Vector3f();
      t[i] = new Vector2f();
    }

    if (mesh.getBuffer(Type.Normal) == null) {
      throw new IllegalArgumentException("The given mesh has no normal data!");
    }

    List<VertexData> vertices;
    switch (mesh.getMode()) {
      case Triangles:
        vertices = processTriangles(mesh, index, v, t, splitMirrored);
        if (splitMirrored) {
          splitVertices(mesh, vertices, splitMirrored);
        }
        break;
      case TriangleStrip:
        vertices = processTriangleStrip(mesh, index, v, t);
        break;
      case TriangleFan:
        vertices = processTriangleFan(mesh, index, v, t);
        break;
      default:
        throw new UnsupportedOperationException(mesh.getMode() + " is not supported.");
    }

    processTriangleData(mesh, vertices, approxTangents, splitMirrored);

    // if the mesh has a bind pose, we need to generate the bind pose for the tangent buffer
    if (mesh.getBuffer(Type.BindPosePosition) != null) {

      VertexBuffer tangents = mesh.getBuffer(Type.Tangent);
      if (tangents != null) {
        VertexBuffer bindTangents = new VertexBuffer(Type.BindPoseTangent);
        bindTangents.setupData(
            Usage.CpuOnly, 4, Format.Float, BufferUtils.clone(tangents.getData()));

        if (mesh.getBuffer(Type.BindPoseTangent) != null) {
          mesh.clearBuffer(Type.BindPoseTangent);
        }
        mesh.setBuffer(bindTangents);
        tangents.setUsage(Usage.Stream);
      }
    }
  }
Beispiel #6
0
  public void renderQuad(
      int x,
      int y,
      int width,
      int height,
      Color topLeft,
      Color topRight,
      Color bottomRight,
      Color bottomLeft) {

    ByteBuffer buf = (ByteBuffer) quadColor.getData();
    buf.rewind();

    buf.putInt(convertColor(topRight));
    buf.putInt(convertColor(topLeft));

    buf.putInt(convertColor(bottomLeft));
    buf.putInt(convertColor(bottomRight));

    buf.flip();
    quadColor.updateData(buf);

    tempMat.loadIdentity();
    tempMat.setTranslation(x, getHeight() - y, 0);
    tempMat.setScale(width, height, 0);

    rm.setWorldMatrix(tempMat);
    rm.setForcedRenderState(renderState);
    vertexColorMaterial.render(quadGeom, rm);

    // System.out.format("renderQuad2(%d, %d, %d, %d, %s, %s, %s, %s)\n", x, y, width, height,
    // topLeft.toString(),
    //
    // topRight.toString(),
    //
    // bottomRight.toString(),
    //
    // bottomLeft.toString());
  }
  public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) {
    Mesh.Mode mode = mesh.getMode();

    Buffer indexData = indexBuf.getData();
    indexData.rewind();

    if (mesh.getMode() == Mode.Hybrid) {
      throw new UnsupportedOperationException();
      /*
      int[] modeStart = mesh.getModeStart();
      int[] elementLengths = mesh.getElementLengths();

      int elMode = convertElementMode(Mode.Triangles);
      int fmt = convertVertexFormat(indexBuf.getFormat());
      //            int elSize = indexBuf.getFormat().getComponentSize();
      //            int listStart = modeStart[0];
      int stripStart = modeStart[1];
      int fanStart = modeStart[2];
      int curOffset = 0;
      for (int i = 0; i < elementLengths.length; i++) {
      if (i == stripStart) {
      elMode = convertElementMode(Mode.TriangleStrip);
      } else if (i == fanStart) {
      elMode = convertElementMode(Mode.TriangleStrip);
      }
      int elementLength = elementLengths[i];
      indexData.position(curOffset);

      drawElements(elMode,
      fmt,
      indexData);

      curOffset += elementLength;
      }*/
    } else {
      drawElements(convertElementMode(mode), convertVertexFormat(indexBuf.getFormat()), indexData);
    }
  }
  void resetToBind() {
    for (Mesh mesh : targets) {
      if (isMeshAnimated(mesh)) {
        FloatBuffer bwBuff = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
        ByteBuffer biBuff = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
        if (!biBuff.hasArray() || !bwBuff.hasArray()) {
          mesh.prepareForAnim(true); // prepare for software animation
        }
        VertexBuffer bindPos = mesh.getBuffer(Type.BindPosePosition);
        VertexBuffer bindNorm = mesh.getBuffer(Type.BindPoseNormal);
        VertexBuffer pos = mesh.getBuffer(Type.Position);
        VertexBuffer norm = mesh.getBuffer(Type.Normal);
        FloatBuffer pb = (FloatBuffer) pos.getData();
        FloatBuffer nb = (FloatBuffer) norm.getData();
        FloatBuffer bpb = (FloatBuffer) bindPos.getData();
        FloatBuffer bnb = (FloatBuffer) bindNorm.getData();
        pb.clear();
        nb.clear();
        bpb.clear();
        bnb.clear();

        // reseting bind tangents if there is a bind tangent buffer
        VertexBuffer bindTangents = mesh.getBuffer(Type.BindPoseTangent);
        if (bindTangents != null) {
          VertexBuffer tangents = mesh.getBuffer(Type.Tangent);
          FloatBuffer tb = (FloatBuffer) tangents.getData();
          FloatBuffer btb = (FloatBuffer) bindTangents.getData();
          tb.clear();
          btb.clear();
          tb.put(btb).clear();
        }

        pb.put(bpb).clear();
        nb.put(bnb).clear();
      }
    }
  }
  public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) {
    if (vb.getBufferType() == VertexBuffer.Type.Color && !context.useVertexColor) {
      // Ignore vertex color buffer if vertex color is disabled.
      return;
    }

    int arrayType = convertArrayType(vb.getBufferType());
    if (arrayType == -1) {
      return; // unsupported
    }
    glEnableClientState(arrayType);
    context.boundAttribs[vb.getBufferType().ordinal()] = vb;

    if (vb.getBufferType() == Type.Normal) {
      // normalize if requested
      if (vb.isNormalized() && !context.normalizeEnabled) {
        glEnable(GL_NORMALIZE);
        context.normalizeEnabled = true;
      } else if (!vb.isNormalized() && context.normalizeEnabled) {
        glDisable(GL_NORMALIZE);
        context.normalizeEnabled = false;
      }
    }

    // NOTE: Use data from interleaved buffer if specified
    Buffer data = idb != null ? idb.getData() : vb.getData();
    int comps = vb.getNumComponents();
    int type = convertVertexFormat(vb.getFormat());

    data.rewind();

    switch (vb.getBufferType()) {
      case Position:
        if (!(data instanceof FloatBuffer)) {
          throw new UnsupportedOperationException();
        }

        glVertexPointer(comps, vb.getStride(), (FloatBuffer) data);
        break;
      case Normal:
        if (!(data instanceof FloatBuffer)) {
          throw new UnsupportedOperationException();
        }

        glNormalPointer(vb.getStride(), (FloatBuffer) data);
        break;
      case Color:
        if (data instanceof FloatBuffer) {
          glColorPointer(comps, vb.getStride(), (FloatBuffer) data);
        } else if (data instanceof ByteBuffer) {
          glColorPointer(comps, true, vb.getStride(), (ByteBuffer) data);
        } else {
          throw new UnsupportedOperationException();
        }
        break;
      case TexCoord:
        if (!(data instanceof FloatBuffer)) {
          throw new UnsupportedOperationException();
        }

        glTexCoordPointer(comps, vb.getStride(), (FloatBuffer) data);
        break;
      default:
        // Ignore, this is an unsupported attribute for OpenGL1.
        break;
    }
  }
  /**
   * 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;
    }
  }
Beispiel #11
0
  public void renderImage(
      RenderImage image,
      int x,
      int y,
      int w,
      int h,
      int srcX,
      int srcY,
      int srcW,
      int srcH,
      Color color,
      float scale,
      int centerX,
      int centerY) {

    RenderImageJme jmeImage = (RenderImageJme) image;
    Texture2D texture = jmeImage.getTexture();

    textureColorMaterial.setColor("Color", convertColor(color, tempColor));
    textureColorMaterial.setTexture("ColorMap", texture);

    float imageWidth = jmeImage.getWidth();
    float imageHeight = jmeImage.getHeight();
    FloatBuffer texCoords = (FloatBuffer) quadModTC.getData();

    float startX = srcX / imageWidth;
    float startY = srcY / imageHeight;
    float endX = startX + (srcW / imageWidth);
    float endY = startY + (srcH / imageHeight);

    startY = 1f - startY;
    endY = 1f - endY;

    texCoords.rewind();
    texCoords.put(startX).put(startY);
    texCoords.put(endX).put(startY);
    texCoords.put(endX).put(endY);
    texCoords.put(startX).put(endY);
    texCoords.flip();
    quadModTC.updateData(texCoords);

    quad.clearBuffer(Type.TexCoord);
    quad.setBuffer(quadModTC);

    float x0 = centerX + (x - centerX) * scale;
    float y0 = centerY + (y - centerY) * scale;

    tempMat.loadIdentity();
    tempMat.setTranslation(x0, getHeight() - y0, 0);
    tempMat.setScale(w * scale, h * scale, 0);

    rm.setWorldMatrix(tempMat);
    rm.setForcedRenderState(renderState);
    textureColorMaterial.render(quadGeom, rm);

    // System.out.format("renderImage2(%s, %d, %d, %d, %d, %d, %d, %d, %d, %s, %f, %d, %d)\n",
    // texture.getKey().toString(),
    //                                                                                       x, y,
    // w, h, srcX, srcY, srcW, srcH,
    //
    // color.toString(), scale, centerX, centerY);
  }
Beispiel #12
0
  /**
   * Specific method for skinning with tangents to avoid cluttering the classic skinning calculation
   * with null checks that would slow down the process even if tangents don't have to be computed.
   * Also the iteration has additional indexes since tangent has 4 components instead of 3 for pos
   * and norm
   *
   * @param maxWeightsPerVert maximum number of weights per vertex
   * @param mesh the mesh
   * @param offsetMatrices the offsetMaytrices to apply
   * @param tb the tangent vertexBuffer
   */
  private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexBuffer tb) {
    int maxWeightsPerVert = mesh.getMaxNumWeights();

    if (maxWeightsPerVert <= 0) {
      throw new IllegalStateException("Max weights per vert is incorrectly set!");
    }

    int fourMinusMaxWeights = 4 - maxWeightsPerVert;

    // NOTE: This code assumes the vertex buffer is in bind pose
    // resetToBind() has been called this frame
    VertexBuffer vb = mesh.getBuffer(Type.Position);
    FloatBuffer fvb = (FloatBuffer) vb.getData();
    fvb.rewind();

    VertexBuffer nb = mesh.getBuffer(Type.Normal);

    FloatBuffer fnb = (FloatBuffer) nb.getData();
    fnb.rewind();

    FloatBuffer ftb = (FloatBuffer) tb.getData();
    ftb.rewind();

    // get boneIndexes and weights for mesh
    ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
    FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();

    ib.rewind();
    wb.rewind();

    float[] weights = wb.array();
    byte[] indices = ib.array();
    int idxWeights = 0;

    TempVars vars = TempVars.get();

    float[] posBuf = vars.skinPositions;
    float[] normBuf = vars.skinNormals;
    float[] tanBuf = vars.skinTangents;

    int iterations = (int) FastMath.ceil(fvb.capacity() / ((float) posBuf.length));
    int bufLength = 0;
    int tanLength = 0;
    for (int i = iterations - 1; i >= 0; i--) {
      // read next set of positions and normals from native buffer
      bufLength = Math.min(posBuf.length, fvb.remaining());
      tanLength = Math.min(tanBuf.length, ftb.remaining());
      fvb.get(posBuf, 0, bufLength);
      fnb.get(normBuf, 0, bufLength);
      ftb.get(tanBuf, 0, tanLength);
      int verts = bufLength / 3;
      int idxPositions = 0;
      // tangents has their own index because of the 4 components
      int idxTangents = 0;

      // iterate vertices and apply skinning transform for each effecting bone
      for (int vert = verts - 1; vert >= 0; vert--) {
        float nmx = normBuf[idxPositions];
        float vtx = posBuf[idxPositions++];
        float nmy = normBuf[idxPositions];
        float vty = posBuf[idxPositions++];
        float nmz = normBuf[idxPositions];
        float vtz = posBuf[idxPositions++];

        float tnx = tanBuf[idxTangents++];
        float tny = tanBuf[idxTangents++];
        float tnz = tanBuf[idxTangents++];

        // skipping the 4th component of the tangent since it doesn't have to be transformed
        idxTangents++;

        float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0, rtx = 0, rty = 0, rtz = 0;

        for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
          float weight = weights[idxWeights];
          Matrix4f mat = offsetMatrices[indices[idxWeights++]];

          rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
          ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
          rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight;

          rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight;
          rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight;
          rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight;

          rtx += (tnx * mat.m00 + tny * mat.m01 + tnz * mat.m02) * weight;
          rty += (tnx * mat.m10 + tny * mat.m11 + tnz * mat.m12) * weight;
          rtz += (tnx * mat.m20 + tny * mat.m21 + tnz * mat.m22) * weight;
        }

        idxWeights += fourMinusMaxWeights;

        idxPositions -= 3;

        normBuf[idxPositions] = rnx;
        posBuf[idxPositions++] = rx;
        normBuf[idxPositions] = rny;
        posBuf[idxPositions++] = ry;
        normBuf[idxPositions] = rnz;
        posBuf[idxPositions++] = rz;

        idxTangents -= 4;

        tanBuf[idxTangents++] = rtx;
        tanBuf[idxTangents++] = rty;
        tanBuf[idxTangents++] = rtz;

        // once again skipping the 4th component of the tangent
        idxTangents++;
      }

      fvb.position(fvb.position() - bufLength);
      fvb.put(posBuf, 0, bufLength);
      fnb.position(fnb.position() - bufLength);
      fnb.put(normBuf, 0, bufLength);
      ftb.position(ftb.position() - tanLength);
      ftb.put(tanBuf, 0, tanLength);
    }

    vars.release();

    vb.updateData(fvb);
    nb.updateData(fnb);
    tb.updateData(ftb);
  }
  // Don't remove splitmirorred boolean,It's not used right now, but i intend to
  // make this method also split vertice with rotated tangent space and I'll
  // add another splitRotated boolean
  private static List<VertexData> splitVertices(
      Mesh mesh, List<VertexData> vertexData, boolean splitMirorred) {
    int nbVertices = mesh.getBuffer(Type.Position).getNumElements();
    List<VertexData> newVertices = new ArrayList<VertexData>();
    Map<Integer, Integer> indiceMap = new HashMap<Integer, Integer>();
    FloatBuffer normalBuffer = mesh.getFloatBuffer(Type.Normal);

    for (int i = 0; i < vertexData.size(); i++) {
      ArrayList<TriangleData> triangles = vertexData.get(i).triangles;
      Vector3f givenNormal = new Vector3f();
      populateFromBuffer(givenNormal, normalBuffer, i);

      ArrayList<TriangleData> trianglesUp = new ArrayList<TriangleData>();
      ArrayList<TriangleData> trianglesDown = new ArrayList<TriangleData>();
      for (int j = 0; j < triangles.size(); j++) {
        TriangleData triangleData = triangles.get(j);
        if (parity(givenNormal, triangleData.normal) > 0) {
          trianglesUp.add(triangleData);
        } else {
          trianglesDown.add(triangleData);
        }
      }

      // if the vertex has triangles with opposite parity it has to be split
      if (!trianglesUp.isEmpty() && !trianglesDown.isEmpty()) {
        log.log(Level.FINE, "Splitting vertex {0}", i);
        // assigning triangle with the same parity to the original vertex
        vertexData.get(i).triangles.clear();
        vertexData.get(i).triangles.addAll(trianglesUp);

        // creating a new vertex
        VertexData newVert = new VertexData();
        // assigning triangles with opposite parity to it
        newVert.triangles.addAll(trianglesDown);

        newVertices.add(newVert);
        // keep vertex index to fix the index buffers later
        indiceMap.put(nbVertices, i);
        for (TriangleData tri : newVert.triangles) {
          for (int j = 0; j < tri.index.length; j++) {
            if (tri.index[j] == i) {
              tri.index[j] = nbVertices;
            }
          }
        }
        nbVertices++;
      }
    }

    if (!newVertices.isEmpty()) {

      // we have new vertices, we need to update the mesh's buffers.
      for (Type type : VertexBuffer.Type.values()) {
        // skip tangent buffer as we're gonna overwrite it later
        if (type == Type.Tangent || type == Type.BindPoseTangent) {
          continue;
        }
        VertexBuffer vb = mesh.getBuffer(type);
        // Some buffer (hardware skinning ones) can be there but not
        // initialized, they must be skipped.
        // They'll be initialized when Hardware Skinning is engaged
        if (vb == null || vb.getNumComponents() == 0) {
          continue;
        }

        Buffer buffer = vb.getData();
        // IndexBuffer has special treatement, only swapping the vertex indices is needed
        if (type == Type.Index) {
          boolean isShortBuffer = vb.getFormat() == VertexBuffer.Format.UnsignedShort;
          for (VertexData vertex : newVertices) {
            for (TriangleData tri : vertex.triangles) {
              for (int i = 0; i < tri.index.length; i++) {
                if (isShortBuffer) {
                  ((ShortBuffer) buffer).put(tri.triangleOffset + i, (short) tri.index[i]);
                } else {
                  ((IntBuffer) buffer).put(tri.triangleOffset + i, tri.index[i]);
                }
              }
            }
          }
          vb.setUpdateNeeded();
        } else {
          // copy the buffer in a bigger one and append nex vertices to the end
          Buffer newVerts =
              VertexBuffer.createBuffer(vb.getFormat(), vb.getNumComponents(), nbVertices);
          if (buffer != null) {
            buffer.rewind();
            bulkPut(vb.getFormat(), newVerts, buffer);

            int index = vertexData.size();
            newVerts.position(vertexData.size() * vb.getNumComponents());
            for (int j = 0; j < newVertices.size(); j++) {
              int oldInd = indiceMap.get(index);
              for (int i = 0; i < vb.getNumComponents(); i++) {
                putValue(vb.getFormat(), newVerts, buffer, oldInd * vb.getNumComponents() + i);
              }
              index++;
            }
            vb.updateData(newVerts);
            // destroy previous buffer as it's no longer needed
            destroyDirectBuffer(buffer);
          }
        }
      }
      vertexData.addAll(newVertices);

      mesh.updateCounts();
    }

    return vertexData;
  }