1/*
2 * Copyright (c) 2009-2010 jMonkeyEngine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in the
14 *   documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 *   may be used to endorse or promote products derived from this software
18 *   without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32package com.jme3.niftygui;
33
34import com.jme3.font.BitmapText;
35import com.jme3.material.Material;
36import com.jme3.material.RenderState;
37import com.jme3.math.ColorRGBA;
38import com.jme3.math.Matrix4f;
39import com.jme3.renderer.RenderManager;
40import com.jme3.renderer.Renderer;
41import com.jme3.scene.Geometry;
42import com.jme3.scene.VertexBuffer;
43import com.jme3.scene.VertexBuffer.Format;
44import com.jme3.scene.VertexBuffer.Type;
45import com.jme3.scene.VertexBuffer.Usage;
46import com.jme3.scene.shape.Quad;
47import com.jme3.texture.Texture2D;
48import com.jme3.util.BufferUtils;
49import de.lessvoid.nifty.elements.render.TextRenderer.RenderFontNull;
50import de.lessvoid.nifty.render.BlendMode;
51import de.lessvoid.nifty.spi.render.MouseCursor;
52import de.lessvoid.nifty.spi.render.RenderDevice;
53import de.lessvoid.nifty.spi.render.RenderFont;
54import de.lessvoid.nifty.spi.render.RenderImage;
55import de.lessvoid.nifty.tools.Color;
56import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader;
57import java.nio.ByteBuffer;
58import java.nio.FloatBuffer;
59import java.util.HashMap;
60
61public class RenderDeviceJme implements RenderDevice {
62
63    private NiftyJmeDisplay display;
64    private RenderManager rm;
65    private Renderer r;
66    private HashMap<String, BitmapText> textCacheLastFrame = new HashMap<String, BitmapText>();
67    private HashMap<String, BitmapText> textCacheCurrentFrame = new HashMap<String, BitmapText>();
68    private final Quad quad = new Quad(1, -1, true);
69    private final Geometry quadGeom = new Geometry("nifty-quad", quad);
70    private final Material niftyMat;
71    private final Material niftyQuadMat;
72    private final Material niftyQuadGradMat;
73    private boolean clipWasSet = false;
74    private BlendMode blendMode = null;
75    private VertexBuffer quadDefaultTC = quad.getBuffer(Type.TexCoord);
76    private VertexBuffer quadModTC = quadDefaultTC.clone();
77    private VertexBuffer quadColor;
78    private Matrix4f tempMat = new Matrix4f();
79    private ColorRGBA tempColor = new ColorRGBA();
80
81    public RenderDeviceJme(NiftyJmeDisplay display) {
82        this.display = display;
83
84        quadColor = new VertexBuffer(Type.Color);
85        quadColor.setNormalized(true);
86        ByteBuffer bb = BufferUtils.createByteBuffer(4 * 4);
87        quadColor.setupData(Usage.Stream, 4, Format.UnsignedByte, bb);
88        quad.setBuffer(quadColor);
89
90        quadModTC.setUsage(Usage.Stream);
91
92        //Color + texture color material for text and images
93        niftyMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/NiftyTex.j3md");
94        niftyMat.getAdditionalRenderState().setDepthTest(false);
95        //Color material for uniform colored quads
96        niftyQuadMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/NiftyQuad.j3md");
97        niftyQuadMat.getAdditionalRenderState().setDepthTest(false);
98
99        //vertex color only for gradient quads (although i didn't find a way in nifty to make a gradient using vertex color)
100        niftyQuadGradMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/NiftyQuadGrad.j3md");
101        niftyQuadGradMat.getAdditionalRenderState().setDepthTest(false);
102
103    }
104
105    public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) {
106    }
107
108    public void setRenderManager(RenderManager rm) {
109        this.rm = rm;
110        this.r = rm.getRenderer();
111    }
112
113    // TODO: Cursor support
114    public MouseCursor createMouseCursor(String str, int x, int y) {
115        return new MouseCursor() {
116
117            public void dispose() {
118            }
119        };
120    }
121
122    public void enableMouseCursor(MouseCursor cursor) {
123    }
124
125    public void disableMouseCursor() {
126    }
127
128    public RenderImage createImage(String filename, boolean linear) {
129        return new RenderImageJme(filename, linear, display);
130    }
131
132    public RenderFont createFont(String filename) {
133        return new RenderFontJme(filename, display);
134    }
135
136    public void beginFrame() {
137    }
138
139    public void endFrame() {
140        HashMap<String, BitmapText> temp = textCacheLastFrame;
141        textCacheLastFrame = textCacheCurrentFrame;
142        textCacheCurrentFrame = temp;
143        textCacheCurrentFrame.clear();
144
145//        System.exit(1);
146    }
147
148    public int getWidth() {
149        return display.getWidth();
150    }
151
152    public int getHeight() {
153        return display.getHeight();
154    }
155
156    public void clear() {
157    }
158
159    public void setBlendMode(BlendMode blendMode) {
160        if (this.blendMode != blendMode) {
161            this.blendMode = blendMode;
162        }
163    }
164
165    private RenderState.BlendMode convertBlend() {
166        if (blendMode == null) {
167            return RenderState.BlendMode.Off;
168        } else if (blendMode == BlendMode.BLEND) {
169            return RenderState.BlendMode.Alpha;
170        } else if (blendMode == BlendMode.MULIPLY) {
171            return RenderState.BlendMode.Modulate;
172        } else {
173            throw new UnsupportedOperationException();
174        }
175    }
176
177    private int convertColor(Color color) {
178        int color2 = 0;
179        color2 |= ((int) (255.0 * color.getAlpha())) << 24;
180        color2 |= ((int) (255.0 * color.getBlue())) << 16;
181        color2 |= ((int) (255.0 * color.getGreen())) << 8;
182        color2 |= ((int) (255.0 * color.getRed()));
183        return color2;
184    }
185
186    private ColorRGBA convertColor(Color inColor, ColorRGBA outColor) {
187        return outColor.set(inColor.getRed(), inColor.getGreen(), inColor.getBlue(), inColor.getAlpha());
188    }
189
190//    private void setColor(Color color) {
191//        ByteBuffer buf = (ByteBuffer) quadColor.getData();
192//        buf.rewind();
193//
194//        int color2 = convertColor(color);
195//        buf.putInt(color2);
196//        buf.putInt(color2);
197//        buf.putInt(color2);
198//        buf.putInt(color2);
199//
200//        buf.flip();
201//        quadColor.updateData(buf);
202//    }
203
204    /**
205     *
206     * @param font
207     * @param str
208     * @param x
209     * @param y
210     * @param color
211     * @param size
212     * @deprecated use renderFont(RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY) instead
213     */
214    @Deprecated
215    public void renderFont(RenderFont font, String str, int x, int y, Color color, float size) {
216        renderFont(font, str, x, y, color, size, size);
217    }
218
219    @Override
220    public void renderFont(RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY) {
221        if (str.length() == 0) {
222            return;
223        }
224
225        if (font instanceof RenderFontNull) {
226            return;
227        }
228
229        RenderFontJme jmeFont = (RenderFontJme) font;
230
231        String key = font + str + color.getColorString();
232        BitmapText text = textCacheLastFrame.get(key);
233        if (text == null) {
234            text = jmeFont.createText();
235            text.setText(str);
236            text.updateLogicalState(0);
237        }
238        textCacheCurrentFrame.put(key, text);
239
240        niftyMat.setColor("Color", convertColor(color, tempColor));
241        niftyMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
242//        niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());
243        text.setMaterial(niftyMat);
244
245        tempMat.loadIdentity();
246        tempMat.setTranslation(x, getHeight() - y, 0);
247        tempMat.setScale(sizeX, sizeY, 0);
248
249        rm.setWorldMatrix(tempMat);
250        text.render(rm);
251
252//        System.out.println("renderFont");
253    }
254
255    public void renderImage(RenderImage image, int x, int y, int w, int h,
256            int srcX, int srcY, int srcW, int srcH,
257            Color color, float scale,
258            int centerX, int centerY) {
259        RenderImageJme jmeImage = (RenderImageJme) image;
260        Texture2D texture = jmeImage.getTexture();
261
262        niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());
263        niftyMat.setColor("Color", convertColor(color, tempColor));
264        niftyMat.setTexture("Texture", texture);
265        //setColor(color);
266
267        float imageWidth = jmeImage.getWidth();
268        float imageHeight = jmeImage.getHeight();
269        FloatBuffer texCoords = (FloatBuffer) quadModTC.getData();
270
271        float startX = srcX / imageWidth;
272        float startY = srcY / imageHeight;
273        float endX = startX + (srcW / imageWidth);
274        float endY = startY + (srcH / imageHeight);
275
276        startY = 1f - startY;
277        endY = 1f - endY;
278
279        texCoords.rewind();
280        texCoords.put(startX).put(startY);
281        texCoords.put(endX).put(startY);
282        texCoords.put(endX).put(endY);
283        texCoords.put(startX).put(endY);
284        texCoords.flip();
285        quadModTC.updateData(texCoords);
286
287        quad.clearBuffer(Type.TexCoord);
288        quad.setBuffer(quadModTC);
289
290        float x0 = centerX + (x - centerX) * scale;
291        float y0 = centerY + (y - centerY) * scale;
292
293        tempMat.loadIdentity();
294        tempMat.setTranslation(x0, getHeight() - y0, 0);
295        tempMat.setScale(w * scale, h * scale, 0);
296
297        rm.setWorldMatrix(tempMat);
298        niftyMat.render(quadGeom, rm);
299//
300//        System.out.println("renderImage (Sub)");
301    }
302
303    public void renderImage(RenderImage image, int x, int y, int width, int height,
304            Color color, float imageScale) {
305
306        RenderImageJme jmeImage = (RenderImageJme) image;
307
308        niftyMat.getAdditionalRenderState().setBlendMode(convertBlend());
309        niftyMat.setColor("Color", convertColor(color, tempColor));
310        niftyMat.setTexture("Texture", jmeImage.getTexture());
311        //setColor(color);
312
313        quad.clearBuffer(Type.TexCoord);
314        quad.setBuffer(quadDefaultTC);
315
316        float x0 = x + 0.5f * width * (1f - imageScale);
317        float y0 = y + 0.5f * height * (1f - imageScale);
318
319        tempMat.loadIdentity();
320        tempMat.setTranslation(x0, getHeight() - y0, 0);
321        tempMat.setScale(width * imageScale, height * imageScale, 0);
322
323        rm.setWorldMatrix(tempMat);
324        niftyMat.render(quadGeom, rm);
325//
326//        System.out.println("renderImage");
327    }
328
329    public void renderQuad(int x, int y, int width, int height, Color color) {
330        if (color.getAlpha() > 0) {
331            niftyQuadMat.getAdditionalRenderState().setBlendMode(convertBlend());
332            niftyQuadMat.setColor("Color", convertColor(color, tempColor));
333
334            tempMat.loadIdentity();
335            tempMat.setTranslation(x, getHeight() - y, 0);
336            tempMat.setScale(width, height, 0);
337
338            rm.setWorldMatrix(tempMat);
339            niftyQuadMat.render(quadGeom, rm);
340        }
341//        System.out.println("renderQuad (Solid)");
342    }
343
344    public void renderQuad(int x, int y, int width, int height,
345            Color topLeft, Color topRight, Color bottomRight, Color bottomLeft) {
346
347        ByteBuffer buf = (ByteBuffer) quadColor.getData();
348        buf.rewind();
349
350        buf.putInt(convertColor(topRight));
351        buf.putInt(convertColor(topLeft));
352
353        buf.putInt(convertColor(bottomLeft));
354        buf.putInt(convertColor(bottomRight));
355
356        buf.flip();
357        quadColor.updateData(buf);
358
359        niftyQuadGradMat.getAdditionalRenderState().setBlendMode(convertBlend());
360
361        tempMat.loadIdentity();
362        tempMat.setTranslation(x, getHeight() - y, 0);
363        tempMat.setScale(width, height, 0);
364
365        rm.setWorldMatrix(tempMat);
366        niftyQuadGradMat.render(quadGeom, rm);
367//
368//        System.out.println("renderQuad (Grad)");
369    }
370
371    public void enableClip(int x0, int y0, int x1, int y1) {
372//        System.out.println("enableClip");
373        clipWasSet = true;
374        r.setClipRect(x0, getHeight() - y1, x1 - x0, y1 - y0);
375    }
376
377    public void disableClip() {
378//        System.out.println("disableClip");
379        if (clipWasSet) {
380            r.clearClipRect();
381            clipWasSet = false;
382        }
383    }
384}
385