Example #1
0
  private static Mesh getMeshFromGrid(final ResultGrid resultGrid, float worldScale) {
    Vector3f translate =
        new Vector3f(resultGrid.minLocation.x, resultGrid.minLocation.y, resultGrid.minLocation.z);
    float scale = 1.0f / worldScale;
    Mesh mesh = MarchingCubesMesher.createMesh(resultGrid.dataGrid, scale, translate);

    // Setup bone weight buffer
    FloatBuffer weights = BufferUtils.createFloatBuffer(mesh.getVertexCount() * 4);
    VertexBuffer weightsBuf = new VertexBuffer(VertexBuffer.Type.HWBoneWeight);
    weightsBuf.setupData(VertexBuffer.Usage.Static, 4, VertexBuffer.Format.Float, weights);
    mesh.setBuffer(weightsBuf);

    // Setup bone index buffer
    ByteBuffer indices = BufferUtils.createByteBuffer(mesh.getVertexCount() * 4);
    VertexBuffer indicesBuf = new VertexBuffer(VertexBuffer.Type.HWBoneIndex);
    indicesBuf.setupData(VertexBuffer.Usage.Static, 4, VertexBuffer.Format.UnsignedByte, indices);
    mesh.setBuffer(indicesBuf);

    Vector3f v1 = new Vector3f();
    Vector3f v2 = new Vector3f();
    Vector3f v3 = new Vector3f();
    for (int i = 0; i < mesh.getTriangleCount(); i++) {
      mesh.getTriangle(i, v1, v2, v3);
      putBoneData(weights, indices, v1, resultGrid, scale, translate);
      putBoneData(weights, indices, v2, resultGrid, scale, translate);
      putBoneData(weights, indices, v3, resultGrid, scale, translate);
    }

    return mesh;
  }
Example #2
0
  public RenderDeviceJme(NiftyJmeDisplay display) {
    this.display = display;

    quadColor = new VertexBuffer(Type.Color);
    quadColor.setNormalized(true);
    ByteBuffer bb = BufferUtils.createByteBuffer(4 * 4);
    quadColor.setupData(Usage.Stream, 4, Format.UnsignedByte, bb);
    quad.setBuffer(quadColor);

    quadModTC.setUsage(Usage.Stream);

    // Load the 3 material types separately to avoid
    // reloading the shader when the defines change.

    // Material with a single color (no texture or vertex color)
    colorMaterial = new Material(display.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");

    // Material with a texture and a color (no vertex color)
    textureColorMaterial =
        new Material(display.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");

    // Material with vertex color, used for gradients (no texture)
    vertexColorMaterial =
        new Material(display.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
    vertexColorMaterial.setBoolean("VertexColor", true);

    // Shared render state for all materials
    renderState.setDepthTest(false);
    renderState.setDepthWrite(false);
  }
 public void clearVertexAttribs() {
   for (int i = 0; i < 16; i++) {
     VertexBuffer vb = context.boundAttribs[i];
     if (vb != null) {
       int arrayType = convertArrayType(vb.getBufferType());
       glDisableClientState(arrayType);
       context.boundAttribs[vb.getBufferType().ordinal()] = null;
     }
   }
 }
Example #4
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));
    }
  }
Example #5
0
  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());
  }
  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);
      }
    }
  }
  public static void printMesh(Mesh mesh) {
    for (int bufType = 0; bufType < Type.values().length; bufType++) {
      VertexBuffer outBuf = mesh.getBuffer(Type.values()[bufType]);
      if (outBuf == null) {
        continue;
      }

      System.out.println(outBuf.getBufferType() + ": ");
      for (int vert = 0; vert < outBuf.getNumElements(); vert++) {
        String str = "[";
        for (int comp = 0; comp < outBuf.getNumComponents(); comp++) {
          Object val = outBuf.getElementComponent(vert, comp);
          outBuf.setElementComponent(vert, comp, val);
          val = outBuf.getElementComponent(vert, comp);
          str += val;
          if (comp != outBuf.getNumComponents() - 1) {
            str += ", ";
          }
        }
        str += "]";
        System.out.println(str);
      }
      System.out.println("------");
    }
  }
Example #8
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;
  }
  public void renderMesh(Mesh mesh, int lod, int count) {
    if (mesh.getVertexCount() == 0) {
      return;
    }

    if (context.pointSize != mesh.getPointSize()) {
      glPointSize(mesh.getPointSize());
      context.pointSize = mesh.getPointSize();
    }
    if (context.lineWidth != mesh.getLineWidth()) {
      glLineWidth(mesh.getLineWidth());
      context.lineWidth = mesh.getLineWidth();
    }

    boolean dynamic = false;
    if (mesh.getBuffer(Type.InterleavedData) != null) {
      throw new UnsupportedOperationException("Interleaved meshes are not supported");
    }

    if (mesh.getNumLodLevels() == 0) {
      for (VertexBuffer vb : mesh.getBufferList().getArray()) {
        if (vb.getUsage() != VertexBuffer.Usage.Static) {
          dynamic = true;
          break;
        }
      }
    } else {
      dynamic = true;
    }

    statistics.onMeshDrawn(mesh, lod);

    //        if (!dynamic) {
    // dealing with a static object, generate display list
    //            renderMeshDisplayList(mesh);
    //        } else {
    renderMeshDefault(mesh, lod, count);
    //        }

  }
Example #10
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());
  }
Example #11
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 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);
    }
  }
  private void renderMeshDefault(Mesh mesh, int lod, int count) {
    VertexBuffer indices;

    VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
    if (interleavedData != null && interleavedData.isUpdateNeeded()) {
      updateBufferData(interleavedData);
    }

    if (mesh.getNumLodLevels() > 0) {
      indices = mesh.getLodLevel(lod);
    } else {
      indices = mesh.getBuffer(Type.Index);
    }
    for (VertexBuffer vb : mesh.getBufferList().getArray()) {
      if (vb.getBufferType() == Type.InterleavedData
          || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
          || vb.getBufferType() == Type.Index) {
        continue;
      }

      if (vb.getStride() == 0) {
        // not interleaved
        setVertexAttrib(vb);
      } else {
        // interleaved
        setVertexAttrib(vb, interleavedData);
      }
    }

    if (indices != null) {
      drawTriangleList(indices, mesh, count);
    } else {
      GL gl = GLContext.getCurrentGL();
      gl.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
    }

    // TODO: Fix these to use IDList??
    clearVertexAttribs();
    clearTextureUnits();
    resetFixedFuncBindings();
  }
  // 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;
  }
  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;
    }
  }
Example #16
0
  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();
      }
    }
  }
  /**
   * 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 #18
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);
  }
Example #19
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);
  }
  /**
   * This method returns an array of size 2. The first element is a vertex buffer holding bone
   * weights for every vertex in the model. The second element is a vertex buffer holding bone
   * indices for vertices (the indices of bones the vertices are assigned to).
   *
   * @param meshStructure the mesh structure object
   * @param vertexListSize a number of vertices in the model
   * @param bonesGroups this is an output parameter, it should be a one-sized array; the maximum
   *     amount of weights per vertex (up to MAXIMUM_WEIGHTS_PER_VERTEX) is stored there
   * @param vertexReferenceMap this reference map allows to map the original vertices read from
   *     blender to vertices that are really in the model; one vertex may appear several times in
   *     the result model
   * @param groupToBoneIndexMap this object maps the group index (to which a vertices in blender
   *     belong) to bone index of the model
   * @param blenderContext the blender context
   * @return arrays of vertices weights and their bone indices and (as an output parameter) the
   *     maximum amount of weights for a vertex
   * @throws BlenderFileException this exception is thrown when the blend file structure is somehow
   *     invalid or corrupted
   */
  private VertexBuffer[] getBoneWeightAndIndexBuffer(
      Structure meshStructure,
      int vertexListSize,
      int[] bonesGroups,
      Map<Integer, List<Integer>> vertexReferenceMap,
      Map<Integer, Integer> groupToBoneIndexMap,
      BlenderContext blenderContext)
      throws BlenderFileException {
    Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert"); // dvert = DeformVERTices
    FloatBuffer weightsFloatData =
        BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
    ByteBuffer indicesData =
        BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
    if (pDvert.isNotNull()) { // assigning weights and bone indices
      List<Structure> dverts =
          pDvert.fetchData(
              blenderContext.getInputStream()); // dverts.size() == verticesAmount (one dvert per
      // vertex in blender)
      int vertexIndex = 0;
      for (Structure dvert : dverts) {
        int totweight =
            ((Number) dvert.getFieldValue("totweight"))
                .intValue(); // total amount of weights assignet to the vertex
        // (max. 4 in JME)
        Pointer pDW = (Pointer) dvert.getFieldValue("dw");
        List<Integer> vertexIndices =
            vertexReferenceMap.get(
                Integer.valueOf(vertexIndex)); // we fetch the referenced vertices here
        if (totweight > 0
            && pDW.isNotNull()
            && groupToBoneIndexMap
                != null) { // pDW should never be null here, but I check it just in case :)
          int weightIndex = 0;
          List<Structure> dw = pDW.fetchData(blenderContext.getInputStream());
          for (Structure deformWeight : dw) {
            Integer boneIndex =
                groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());

            // Remove this code if 4 weights limitation is removed
            if (weightIndex == 4) {
              LOGGER.log(
                  Level.WARNING,
                  "{0} has more than 4 weight on bone index {1}",
                  new Object[] {meshStructure.getName(), boneIndex});
              break;
            }

            if (boneIndex
                != null) { // null here means that we came accross group that has no bone attached
                           // to
              float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
              if (weight == 0.0f) {
                weight = 1;
                boneIndex = Integer.valueOf(0);
              }
              // we apply the weight to all referenced vertices
              for (Integer index : vertexIndices) {
                weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
                indicesData.put(
                    index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
              }
            }
            ++weightIndex;
          }
        } else {
          for (Integer index : vertexIndices) {
            weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
            indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
          }
        }
        ++vertexIndex;
      }
    } else {
      // always bind all vertices to 0-indexed bone
      // this bone makes the model look normally if vertices have no bone assigned
      // and it is used in object animation, so if we come accross object animation
      // we can use the 0-indexed bone for this
      for (List<Integer> vertexIndexList : vertexReferenceMap.values()) {
        // we apply the weight to all referenced vertices
        for (Integer index : vertexIndexList) {
          weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
          indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
        }
      }
    }

    bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData);
    VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight);
    verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData);

    VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex);
    verticesWeightsIndices.setupData(
        Usage.CpuOnly, bonesGroups[0], Format.UnsignedByte, indicesData);
    return new VertexBuffer[] {verticesWeights, verticesWeightsIndices};
  }
Example #21
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);
    }
  }
Example #22
0
public class RenderDeviceJme implements RenderDevice {

  private NiftyJmeDisplay display;
  private RenderManager rm;
  private Renderer r;
  private HashMap<CachedTextKey, BitmapText> textCacheLastFrame =
      new HashMap<CachedTextKey, BitmapText>();
  private HashMap<CachedTextKey, BitmapText> textCacheCurrentFrame =
      new HashMap<CachedTextKey, BitmapText>();
  private final Quad quad = new Quad(1, -1, true);
  private final Geometry quadGeom = new Geometry("nifty-quad", quad);
  private boolean clipWasSet = false;
  private VertexBuffer quadDefaultTC = quad.getBuffer(Type.TexCoord);
  private VertexBuffer quadModTC = quadDefaultTC.clone();
  private VertexBuffer quadColor;
  private Matrix4f tempMat = new Matrix4f();
  private ColorRGBA tempColor = new ColorRGBA();
  private RenderState renderState = new RenderState();

  private Material colorMaterial;
  private Material textureColorMaterial;
  private Material vertexColorMaterial;

  private static class CachedTextKey {

    BitmapFont font;
    String text;
    //        ColorRGBA color;

    public CachedTextKey(BitmapFont font, String text /*, ColorRGBA color*/) {
      this.font = font;
      this.text = text;
      //            this.color = color;
    }

    @Override
    public boolean equals(Object other) {
      CachedTextKey otherKey = (CachedTextKey) other;
      return font.equals(otherKey.font) && text.equals(otherKey.text) /* &&
                   color.equals(otherKey.color)*/;
    }

    @Override
    public int hashCode() {
      int hash = 5;
      hash = 53 * hash + font.hashCode();
      hash = 53 * hash + text.hashCode();
      //            hash = 53 * hash + color.hashCode();
      return hash;
    }
  }

  public RenderDeviceJme(NiftyJmeDisplay display) {
    this.display = display;

    quadColor = new VertexBuffer(Type.Color);
    quadColor.setNormalized(true);
    ByteBuffer bb = BufferUtils.createByteBuffer(4 * 4);
    quadColor.setupData(Usage.Stream, 4, Format.UnsignedByte, bb);
    quad.setBuffer(quadColor);

    quadModTC.setUsage(Usage.Stream);

    // Load the 3 material types separately to avoid
    // reloading the shader when the defines change.

    // Material with a single color (no texture or vertex color)
    colorMaterial = new Material(display.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");

    // Material with a texture and a color (no vertex color)
    textureColorMaterial =
        new Material(display.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");

    // Material with vertex color, used for gradients (no texture)
    vertexColorMaterial =
        new Material(display.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
    vertexColorMaterial.setBoolean("VertexColor", true);

    // Shared render state for all materials
    renderState.setDepthTest(false);
    renderState.setDepthWrite(false);
  }

  public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) {}

  public void setRenderManager(RenderManager rm) {
    this.rm = rm;
    this.r = rm.getRenderer();
  }

  // TODO: Cursor support
  public MouseCursor createMouseCursor(String str, int x, int y) {
    return new MouseCursor() {
      public void dispose() {}
    };
  }

  public void enableMouseCursor(MouseCursor cursor) {}

  public void disableMouseCursor() {}

  public RenderImage createImage(String filename, boolean linear) {
    // System.out.println("createImage(" + filename + ", " + linear + ")");
    return new RenderImageJme(filename, linear, display);
  }

  public RenderFont createFont(String filename) {
    return new RenderFontJme(filename, display);
  }

  public void beginFrame() {}

  public void endFrame() {
    HashMap<CachedTextKey, BitmapText> temp = textCacheLastFrame;
    textCacheLastFrame = textCacheCurrentFrame;
    textCacheCurrentFrame = temp;
    textCacheCurrentFrame.clear();
    rm.setForcedRenderState(null);
  }

  public int getWidth() {
    return display.getWidth();
  }

  public int getHeight() {
    return display.getHeight();
  }

  public void clear() {}

  public void setBlendMode(BlendMode blendMode) {
    renderState.setBlendMode(convertBlend(blendMode));
  }

  private RenderState.BlendMode convertBlend(BlendMode blendMode) {
    if (blendMode == null) {
      return RenderState.BlendMode.Off;
    } else if (blendMode == BlendMode.BLEND) {
      return RenderState.BlendMode.Alpha;
    } else if (blendMode == BlendMode.MULIPLY) {
      return RenderState.BlendMode.Modulate;
    } else {
      throw new UnsupportedOperationException();
    }
  }

  private int convertColor(Color color) {
    int color2 = 0;
    color2 |= ((int) (255.0 * color.getAlpha())) << 24;
    color2 |= ((int) (255.0 * color.getBlue())) << 16;
    color2 |= ((int) (255.0 * color.getGreen())) << 8;
    color2 |= ((int) (255.0 * color.getRed()));
    return color2;
  }

  private ColorRGBA convertColor(Color inColor, ColorRGBA outColor) {
    return outColor.set(
        inColor.getRed(), inColor.getGreen(), inColor.getBlue(), inColor.getAlpha());
  }

  @Override
  public void renderFont(
      RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY) {
    if (str.length() == 0 || font instanceof RenderFontNull) {
      return;
    }

    RenderFontJme jmeFont = (RenderFontJme) font;

    ColorRGBA colorRgba = convertColor(color, tempColor);
    CachedTextKey key = new CachedTextKey(jmeFont.getFont(), str /*, colorRgba*/);
    BitmapText text = textCacheLastFrame.get(key);
    if (text == null) {
      text = jmeFont.createText();
      text.setText(str);
      text.updateLogicalState(0);
    }
    textCacheCurrentFrame.put(key, text);

    //        float width = text.getLineWidth();
    //        float height = text.getLineHeight();
    float x0 = x; // + 0.5f * width * (1f - sizeX);
    float y0 = y; // + 0.5f * height * (1f - sizeY);

    tempMat.loadIdentity();
    tempMat.setTranslation(x0, getHeight() - y0, 0);
    tempMat.setScale(sizeX, sizeY, 0);

    rm.setWorldMatrix(tempMat);
    rm.setForcedRenderState(renderState);
    text.setColor(colorRgba);
    text.updateLogicalState(0);
    text.render(rm, colorRgba);

    //        System.out.format("renderFont(%s, %s, %d, %d, %s, %f, %f)\n", jmeFont.getFont(), str,
    // x, y, color.toString(), sizeX, sizeY);
  }

  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);
  }

  public void renderImage(
      RenderImage image, int x, int y, int width, int height, Color color, float imageScale) {

    RenderImageJme jmeImage = (RenderImageJme) image;

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

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

    float x0 = x + 0.5f * width * (1f - imageScale);
    float y0 = y + 0.5f * height * (1f - imageScale);

    tempMat.loadIdentity();
    tempMat.setTranslation(x0, getHeight() - y0, 0);
    tempMat.setScale(width * imageScale, height * imageScale, 0);

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

    // System.out.format("renderImage1(%s, %d, %d, %d, %d, %s, %f)\n",
    // jmeImage.getTexture().getKey().toString(), x, y, width, height, color.toString(),
    // imageScale);
  }

  public void renderQuad(int x, int y, int width, int height, Color color) {
    colorMaterial.setColor("Color", convertColor(color, tempColor));

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

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

    // System.out.format("renderQuad1(%d, %d, %d, %d, %s)\n", x, y, width, height,
    // color.toString());
  }

  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 enableClip(int x0, int y0, int x1, int y1) {
    clipWasSet = true;
    r.setClipRect(x0, getHeight() - y1, x1 - x0, y1 - y0);
  }

  public void disableClip() {
    if (clipWasSet) {
      r.clearClipRect();
      clipWasSet = false;
    }
  }
}