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 simpleInitApp() {
    renderManager.setAlphaToCoverage(true);
    cam.setLocation(new Vector3f(0.14914267f, 0.58147097f, 4.7686534f));
    cam.setRotation(new Quaternion(-0.0044764364f, 0.9767943f, 0.21314798f, 0.020512417f));

    //        cam.setLocation(new Vector3f(2.0606942f, 3.20342f, 6.7860126f));
    //        cam.setRotation(new Quaternion(-0.017481906f, 0.98241085f, -0.12393151f,
    // -0.13857932f));

    viewPort.setBackgroundColor(ColorRGBA.DarkGray);

    Quad q = new Quad(20, 20);
    q.scaleTextureCoordinates(Vector2f.UNIT_XY.mult(5));
    Geometry geom = new Geometry("floor", q);
    Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
    geom.setMaterial(mat);

    geom.rotate(-FastMath.HALF_PI, 0, 0);
    geom.center();
    geom.setShadowMode(ShadowMode.Receive);
    rootNode.attachChild(geom);

    // create the geometry and attach it
    Spatial teaGeom = assetManager.loadModel("Models/Tree/Tree.mesh.j3o");
    teaGeom.setQueueBucket(Bucket.Transparent);
    teaGeom.setShadowMode(ShadowMode.Cast);
    makeToonish(teaGeom);

    AmbientLight al = new AmbientLight();
    al.setColor(ColorRGBA.White.mult(2));
    rootNode.addLight(al);

    DirectionalLight dl1 = new DirectionalLight();
    dl1.setDirection(new Vector3f(1, -1, 1).normalizeLocal());
    dl1.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f));
    rootNode.addLight(dl1);

    DirectionalLight dl = new DirectionalLight();
    dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
    dl.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f));
    rootNode.addLight(dl);

    rootNode.attachChild(teaGeom);

    FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
    CartoonEdgeFilter toon = new CartoonEdgeFilter();
    toon.setEdgeWidth(0.5f);
    toon.setEdgeIntensity(1.0f);
    toon.setNormalThreshold(0.8f);
    fpp.addFilter(toon);
    viewPort.addProcessor(fpp);
  }
  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 DDSPreview(ProjectAssetManager assetManager) {
    this.assetManager = assetManager;

    Quad quadMesh = new Quad(4.5f, 4.5f);
    Quad quadMesh3D = new Quad(4.5f, 4.5f);
    quadMesh3D.scaleTextureCoordinates(new Vector2f(4, 4));
    quad = new Geometry("previewQuad", quadMesh);
    quad.setLocalTranslation(new Vector3f(-2.25f, -2.25f, 0));
    quad3D = new Geometry("previewQuad", quadMesh3D);
    quad3D.setLocalTranslation(new Vector3f(-2.25f, -2.25f, 0));
    material3D = new Material(assetManager, "com/jme3/gde/core/properties/preview/tex3DThumb.j3md");
    material3D.setFloat("InvDepth", 1f / 16f);
    material3D.setInt("Rows", 4);
    material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    SceneApplication.getApplication().addSceneListener(this);
  }
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;
    }
  }
}
  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);
  }