From ce027cf5287d008f83c70303d38f125430195b16 Mon Sep 17 00:00:00 2001 From: Rami Santina Date: Tue, 9 Aug 2011 21:10:12 +0300 Subject: Graph UI: Added scene controller and general scenegraph behavior. User can implement onClick, onPressed, onReleased for selected UIShape called by the controller. Selection done using color coding indexes. Controller also provides a default generic InputEventListener and GlEventListener --- .../opengl/test/junit/graph/demos/ui/Label.java | 14 +- .../opengl/test/junit/graph/demos/ui/RIButton.java | 139 +++++++++--- .../junit/graph/demos/ui/SceneUIController.java | 245 +++++++++++++++++++++ .../test/junit/graph/demos/ui/UIGLListener01.java | 13 +- .../opengl/test/junit/graph/demos/ui/UIShape.java | 54 +++++ 5 files changed, 431 insertions(+), 34 deletions(-) create mode 100644 src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java (limited to 'src/test/com') diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java index 70dd489b0..83fc08b86 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java @@ -27,13 +27,17 @@ */ package com.jogamp.opengl.test.junit.graph.demos.ui; +import javax.media.opengl.GL2ES2; + import jogamp.graph.curve.text.GlyphString; +import com.jogamp.graph.curve.opengl.RegionRenderer; +import com.jogamp.graph.curve.opengl.RenderState; import com.jogamp.graph.font.Font; import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.geom.Vertex.Factory; -public class Label extends UIShape implements UITextShape { +public abstract class Label extends UIShape implements UITextShape { protected Font font; protected int size; protected String text; @@ -50,7 +54,7 @@ public class Label extends UIShape implements UITextShape { return glyphString; } - public String getText(){ + public String getText() { return text; } @@ -93,4 +97,10 @@ public class Label extends UIShape implements UITextShape { clearImpl(); glyphString = GlyphString.createString(shape, getVertexFactory(), font, size, text); } + + @Override + public void render(GL2ES2 gl, RenderState rs, RegionRenderer renderer, + boolean selection) { + + } } diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/RIButton.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/RIButton.java index e9296752e..60f79e2d7 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/RIButton.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/RIButton.java @@ -27,32 +27,42 @@ */ package com.jogamp.opengl.test.junit.graph.demos.ui; +import javax.media.opengl.GL2ES2; + +import com.jogamp.graph.curve.opengl.RegionRenderer; +import com.jogamp.graph.curve.opengl.RenderState; import com.jogamp.graph.font.Font; import com.jogamp.graph.geom.AABBox; import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.geom.Vertex.Factory; +import com.jogamp.opengl.test.junit.graph.demos.ui.opengl.UIRegion; /** GPU based resolution independent Button impl */ -public class RIButton extends UIShape { +public abstract class RIButton extends UIShape { private float width, height; private Label label; - private float spacing = 2.0f; - private float[] scale = new float[]{1.0f,1.0f}; + private float spacing = 4.0f; private float corner = 1.0f; private float labelZOffset = -0.05f; private float[] buttonColor = {0.6f, 0.6f, 0.6f}; + private float[] buttonSelectedColor = {0.8f,0.8f,0.8f}; private float[] labelColor = {1.0f, 1.0f, 1.0f}; + private float[] labelSelectedColor = {0.1f, 0.1f, 0.1f}; + public RIButton(Factory factory, Font labelFont, String labelText, float width, float height) { - // w 4.0f, h 3.0f - // FontFactory.get(FontFactory.UBUNTU).getDefault() super(factory); // FIXME: Determine font size - PMV Matrix relation ? // this.label = new Label(factory, labelFont, (int)(height - 2f * spacing), labelText); - this.label = new Label(factory, labelFont, 10, labelText); + this.label = new Label(factory, labelFont, 10, labelText){ + public void onClick() { } + public void onPressed() { } + public void onRelease() { } + }; + this.width = width; this.height = height; } @@ -60,7 +70,6 @@ public class RIButton extends UIShape { public final float getWidth() { return width; } public final float getHeight() { return height; } public float getCorner() { return corner; } - public float[] getScale() { return scale; } public Label getLabel() { return label; } public void setDimension(int width, int height) { @@ -68,7 +77,7 @@ public class RIButton extends UIShape { this.height = height; dirty |= DIRTY_SHAPE; } - + @Override protected void clearImpl() { label.clear(); @@ -88,21 +97,26 @@ public class RIButton extends UIShape { } else { createCurvedOutline(lbox); } - scale[0] = getWidth() / ( 2f*spacing + lbox.getWidth() ); - scale[1] = getHeight() / ( 2f*spacing + lbox.getHeight() ); + float sx = getWidth() / ( 2f*spacing + lbox.getWidth() ); + float sy = getHeight() / ( 2f*spacing + lbox.getHeight() ); + + setScale(sx, sy, 1); } private void createSharpOutline(AABBox lbox) { float th = (2f*spacing) + lbox.getHeight(); float tw = (2f*spacing) + lbox.getWidth(); + + float[] pos = getPosition(); float minX = lbox.getMinX()-spacing; float minY = lbox.getMinY()-spacing; + float minZ = labelZOffset; - shape.addVertex(minX, minY, labelZOffset, true); - shape.addVertex(minX+tw, minY, labelZOffset, true); - shape.addVertex(minX+tw, minY + th, labelZOffset, true); - shape.addVertex(minX, minY + th, labelZOffset, true); + shape.addVertex(minX, minY, minZ, true); + shape.addVertex(minX+tw, minY, minZ, true); + shape.addVertex(minX+tw, minY + th, minZ, true); + shape.addVertex(minX, minY + th, minZ, true); shape.closeLastOutline(); } @@ -113,25 +127,25 @@ public class RIButton extends UIShape { float cw = 0.5f*corner*Math.min(tw, th); float ch = 0.5f*corner*Math.min(tw, th); + float[] pos = getPosition(); float minX = lbox.getMinX()-spacing; float minY = lbox.getMinY()-spacing; - - shape.addVertex(minX, minY + ch, labelZOffset, true); - shape.addVertex(minX, minY, labelZOffset, false); - shape.addVertex(minX + cw, minY, labelZOffset, true); - shape.addVertex(minX + tw - cw, minY, labelZOffset, true); - shape.addVertex(minX + tw, minY, labelZOffset, false); - shape.addVertex(minX + tw, minY + ch, labelZOffset, true); - shape.addVertex(minX + tw, minY + th- ch, labelZOffset, true); - shape.addVertex(minX + tw, minY + th, labelZOffset, false); - shape.addVertex(minX + tw - cw, minY + th, labelZOffset, true); - shape.addVertex(minX + cw, minY + th, labelZOffset, true); - shape.addVertex(minX, minY + th, labelZOffset, false); - shape.addVertex(minX, minY + th - ch, labelZOffset, true); + float minZ = labelZOffset; + shape.addVertex(minX, minY + ch, minZ, true); + shape.addVertex(minX, minY, minZ, false); + shape.addVertex(minX + cw, minY, minZ, true); + shape.addVertex(minX + tw - cw, minY, minZ, true); + shape.addVertex(minX + tw, minY, minZ, false); + shape.addVertex(minX + tw, minY + ch, minZ, true); + shape.addVertex(minX + tw, minY + th- ch, minZ, true); + shape.addVertex(minX + tw, minY + th, minZ, false); + shape.addVertex(minX + tw - cw, minY + th, minZ, true); + shape.addVertex(minX + cw, minY + th, minZ, true); + shape.addVertex(minX, minY + th, minZ, false); + shape.addVertex(minX, minY + th - ch, minZ, true); shape.closeLastOutline(); } - - + public void setCorner(float corner) { if(corner > 1.0f){ this.corner = 1.0f; @@ -172,6 +186,7 @@ public class RIButton extends UIShape { } public void setButtonColor(float r, float g, float b) { + this.buttonColor = new float[3]; this.buttonColor[0] = r; this.buttonColor[1] = g; this.buttonColor[2] = b; @@ -180,16 +195,80 @@ public class RIButton extends UIShape { public float[] getLabelColor() { return labelColor; } + + private UIRegion buttonRegion = null; + private UIRegion labelRegion = null; + private boolean toggle =false; + private boolean toggleable = false; + + public void render(GL2ES2 gl, RenderState rs, RegionRenderer renderer, boolean selection) { + if(null == buttonRegion) { + buttonRegion = new UIRegion(this); + labelRegion = new UIRegion(getLabel()); + } + + gl.glEnable(GL2ES2.GL_POLYGON_OFFSET_FILL); + gl.glPolygonOffset(0.0f, 1f); + + float[] bColor = buttonColor; + if(isPressed() || toggle){ + bColor = buttonSelectedColor; + } + if(!selection){ + renderer.setColorStatic(gl, bColor[0], bColor[1], bColor[2]); + } + renderer.draw(gl, buttonRegion.getRegion(gl, rs, 0), getPosition(), 0); + gl.glDisable(GL2ES2.GL_POLYGON_OFFSET_FILL); + + float[] lColor = labelColor; + if(isPressed() || toggle ){ + lColor = labelSelectedColor; + } + if(!selection){ + renderer.setColorStatic(gl, lColor[0], lColor[1], lColor[2]); + } + renderer.draw(gl, labelRegion.getRegion(gl, rs, 0), getPosition(), 0); + } + public void setPressed(boolean b) { + super.setPressed(b); + if(isToggleable() && b) { + toggle = !toggle; + } + } + public void setLabelColor(float r, float g, float b) { + this.labelColor = new float[3]; this.labelColor[0] = r; this.labelColor[1] = g; this.labelColor[2] = b; } + public void setButtonSelectedColor(float r, float g, float b){ + this.buttonSelectedColor = new float[3]; + this.buttonSelectedColor[0] = r; + this.buttonSelectedColor[1] = g; + this.buttonSelectedColor[2] = b; + } + + public void setLabelSelectedColor(float r, float g, float b){ + this.labelSelectedColor = new float[3]; + this.labelSelectedColor[0] = r; + this.labelSelectedColor[1] = g; + this.labelSelectedColor[2] = b; + } + + public boolean isToggleable() { + return toggleable; + } + + public void setToggleable(boolean toggleable) { + this.toggleable = toggleable; + } + public String toString() { return "RIButton [" + getWidth() + "x" + getHeight() + ", " - + getLabel() + "," + "spacing: " + spacing + + getLabel() + ", " + "spacing: " + spacing + ", " + "corner: " + corner + ", " + "shapeOffset: " + labelZOffset + " ]"; } } diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java new file mode 100644 index 000000000..d3b1de827 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java @@ -0,0 +1,245 @@ +package com.jogamp.opengl.test.junit.graph.demos.ui; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.util.ArrayList; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLRunnable; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.graph.curve.opengl.RegionRenderer; +import com.jogamp.graph.curve.opengl.RenderState; +import com.jogamp.newt.event.MouseEvent; +import com.jogamp.newt.event.MouseListener; +import com.jogamp.newt.opengl.GLWindow; + +public class SceneUIController implements GLEventListener{ + private ArrayList shapes = new ArrayList(); + + private int count = 0; + private RegionRenderer renderer = null; + private RenderState rs = null; + + private float[] translate = new float[3]; + private float[] scale = new float[3]; + private float[] rotation = new float[3]; + + private float[] sceneClearColor = new float[]{0,0,0,1}; + + private int activeId = -1; + + private SBCMouseListener sbcMouseListener = null; + + private GLAutoDrawable cDrawable = null; + + public SceneUIController() { + + } + + public void setRenderer(RegionRenderer renderer, RenderState rs) { + this.renderer = renderer; + this.rs = rs; + } + + public SceneUIController(RegionRenderer renderer, RenderState rs) { + this.renderer = renderer; + this.rs = rs; + } + + public void attachInputListenerTo(GLWindow window) { + sbcMouseListener = new SBCMouseListener();; + window.addMouseListener(sbcMouseListener); + } + + public ArrayList getShapes() { + return shapes; + } + public void addShape(UIShape b) { + shapes.add(b); + count++; + } + + public void removeShape(UIShape b) { + boolean found = shapes.remove(b); + if(found) { + count--; + } + } + + public void init(GLAutoDrawable drawable) { + cDrawable = drawable; + } + public void display(GLAutoDrawable drawable) { + final int width = drawable.getWidth(); + final int height = drawable.getHeight(); + GL2ES2 gl = drawable.getGL().getGL2ES2(); + + render(gl, width, height,false); + } + + public void dispose(GLAutoDrawable drawable) { + + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, + int height) { + GL2ES2 gl = drawable.getGL().getGL2ES2(); + renderer.reshapePerspective(gl, 45.0f, width, height, 5f, 70.0f); + } + + public UIShape getShape(GLAutoDrawable drawable,int x, int y) { + final int width = drawable.getWidth(); + final int height = drawable.getHeight(); + GL2ES2 gl = drawable.getGL().getGL2ES2(); + + int index = checkSelection(gl, x, y, width, height); + if(index == -1) + return null; + return shapes.get(index); + } + + public UIShape getActiveUI() { + if(activeId == -1) + return null; + return shapes.get(activeId); + } + + public void release() { + activeId = -1; + } + + private int checkSelection(GL2ES2 gl,int x, int y, int width, int height) { + gl.glPixelStorei(GL2ES2.GL_PACK_ALIGNMENT, 4); + gl.glPixelStorei(GL2ES2.GL_UNPACK_ALIGNMENT, 4); + gl.glClearColor(sceneClearColor[0], sceneClearColor[1], sceneClearColor[2], sceneClearColor[3]); + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + + render(gl, width, height,true); + ByteBuffer pixel = Buffers.newDirectByteBuffer(4); + pixel.order(ByteOrder.nativeOrder()); + IntBuffer viewport = IntBuffer.allocate(4); + gl.glGetIntegerv(GL2ES2.GL_VIEWPORT, viewport); + gl.glReadPixels(x, viewport.get(3) - y, 1, 1, GL2ES2.GL_RGBA, + GL2ES2.GL_UNSIGNED_BYTE, pixel); + + int qp = pixel.get(0) & 0xFF; + int index = Math.round(((qp/255.0f)*(count+2))-1); + if(index < 0 || index >= count) + return -1; + return index; + } + + private void render(GL2ES2 gl, int width, int height, boolean select) { + renderer.reshapePerspective(null, 45.0f, width, height, 0.1f, 7000.0f); + + for(int index=0; index < count;index++){ + if(select) { + float color= index+1; + renderer.setColorStatic(gl, color/(count+2), color/(count+2), color/(count+2)); + } + float[] s = shapes.get(index).getScale(); + float[] p = shapes.get(index).getPosition(); + renderer.resetModelview(null); + renderer.translate(null, translate[0]+p[0], translate[1]+p[1], translate[2]+p[2]); + renderer.scale(gl, s[0], s[1], s[2]); + renderer.rotate(gl, rotation[0], 1, 0, 0); + renderer.rotate(gl, rotation[1], 0, 1, 0); + renderer.rotate(gl, rotation[2], 0, 0, 1); + + shapes.get(index).render(gl, rs, renderer,select); + renderer.rotate(gl, -rotation[0], 1, 0, 0); + renderer.rotate(gl, -rotation[1], 0, 1, 0); + renderer.rotate(gl, -rotation[2], 0, 0, 1); + } + } + + public void setTranslate(float x, float y, float z) { + this.translate[0] = x; + this.translate[1] = y; + this.translate[2] = z; + } + + public void setScale(float x, float y, float z) { + this.scale[0] = x; + this.scale[1] = y; + this.scale[2] = z; + } + + public void setRotation(float x, float y, float z) { + this.rotation[0] = x; + this.rotation[1] = y; + this.rotation[2] = z; + } + public float[] getSceneClearColor() { + return sceneClearColor; + } + + public void setSceneClearColor(float r, float g, float b, float a) { + this.sceneClearColor[0] = r; + this.sceneClearColor[1] = g; + this.sceneClearColor[2] = b; + this.sceneClearColor[3] = a; + } + + private class SBCMouseListener implements MouseListener { + int lx = 0; + int ly = 0; + boolean selection = false; + int mouseX = -1; + int mouseY = -1; + + public void mouseClicked(MouseEvent e) { + UIShape uiShape = getActiveUI(); + if(uiShape != null){ + uiShape.onClick(); + } + } + + public void mousePressed(MouseEvent e) { + selection = true; + mouseX = e.getX(); + mouseY = e.getY(); + + GLRunnable runnable = new GLRunnable() { + public boolean run(GLAutoDrawable drawable) { + UIShape s = getShape(drawable, mouseX, mouseY); + if(null != s) { + activeId = getShapes().indexOf(s); + } + else { + activeId = -1; + } + return false; + } + }; + cDrawable.invoke(true, runnable); + + UIShape uiShape = getActiveUI(); + + if(uiShape != null) { + uiShape.setPressed(true); + uiShape.onPressed(); + } + } + + public void mouseReleased(MouseEvent e) { + UIShape uiShape = getActiveUI(); + if(uiShape != null){ + uiShape.setPressed(false); + uiShape.onRelease(); + } + } + + public void mouseMoved(MouseEvent e) { } + public void mouseEntered(MouseEvent e) { } + public void mouseExited(MouseEvent e) { } + public void mouseDragged(MouseEvent e) { } + public void mouseWheelMoved(MouseEvent e) { } + + } +} \ No newline at end of file diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIGLListener01.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIGLListener01.java index 2ae7d1f30..6768eccca 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIGLListener01.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIGLListener01.java @@ -51,7 +51,16 @@ public class UIGLListener01 extends UIListenerBase01 { super(RegionRenderer.create(rs, 0), debug, trace); setMatrix(-20, 00, 0f, -50); final Font font = FontFactory.get(FontFactory.UBUNTU).getDefault(); - button = new RIButton(SVertex.factory(), font, "Click me!", 4f, 3f); + button = new RIButton(SVertex.factory(), font, "Click me!", 4f, 3f){ + public void onClick() { + } + public void onPressed() { + } + public void onRelease() { + } + + }; + button.setPosition(2,1,0); /** Button defaults ! button.setLabelColor(1.0f,1.0f,1.0f); button.setButtonColor(0.6f,0.6f,0.6f); @@ -99,7 +108,7 @@ public class UIGLListener01 extends UIListenerBase01 { regionRenderer.setColorStatic(gl, bColor[0], bColor[1], bColor[2]); regionRenderer.draw(gl, regionButton.getRegion(gl, rs, 0), getPosition(), 0); - +// regionRenderer.translate(gl, button.getPosition()[0], button.getPosition()[1], button.getPosition()[2]); regionRenderer.setColorStatic(gl, lColor[0], lColor[1], lColor[2]); regionRenderer.draw(gl, regionLabel.getRegion(gl, rs, 0), getPosition(), 0); } diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java index 7e3d26775..ebed0f7aa 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java @@ -27,7 +27,11 @@ */ package com.jogamp.opengl.test.junit.graph.demos.ui; +import javax.media.opengl.GL2ES2; + import com.jogamp.graph.curve.OutlineShape; +import com.jogamp.graph.curve.opengl.RegionRenderer; +import com.jogamp.graph.curve.opengl.RenderState; import com.jogamp.graph.geom.AABBox; import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.geom.Vertex.Factory; @@ -38,6 +42,8 @@ public abstract class UIShape { protected static final int DIRTY_SHAPE = 1 << 0 ; protected int dirty = DIRTY_SHAPE; + + private boolean down = false; public UIShape(Factory factory) { this.vertexFactory = factory; @@ -48,6 +54,39 @@ public abstract class UIShape { clearImpl(); shape.clear(); } + + public abstract void render(GL2ES2 gl, RenderState rs, RegionRenderer renderer, boolean selection); + + protected boolean positionDirty = false; + + private float[] position = new float[]{0,0,0}; + private float[] scale = new float[]{1.0f,1.0f,1.0f}; + public void setScale(float x, float y, float z){ + scale[0] = x; + scale[1] = y; + scale[2] = z; + } + + public void setPosition(float x, float y, float z) { + this.position[0] = x; + this.position[1] = y; + this.position[2] = z; + positionDirty = true; + } + + private void updatePosition () { + float minX = shape.getBounds().getLow()[0]; + float minY = shape.getBounds().getLow()[1]; + float minZ = shape.getBounds().getLow()[2]; + System.out.println("Position was: " + (position[0]) + " " + (position[1]) + " " + (position[2])); + System.out.println("Position became: " + (position[0] - minX) + " " + (position[1] - minY) + " " + (position[2] - minZ)); + setPosition(position[0] - minX, position[1] - minY, position[2] - minZ); + positionDirty = false; + } + + public float[] getScale() { return scale; } + public float[] getPosition() { return position; } + protected abstract void clearImpl(); protected abstract void createShape(); @@ -56,6 +95,9 @@ public abstract class UIShape { if( isShapeDirty() ) { shape.clear(); createShape(); + if(positionDirty){ + updatePosition(); + } dirty &= ~DIRTY_SHAPE; return true; } @@ -73,4 +115,16 @@ public abstract class UIShape { public boolean isShapeDirty() { return 0 != ( dirty & DIRTY_SHAPE ) ; } + + public void setPressed(boolean b) { + this.down = b; + } + + public boolean isPressed() { + return this.down; + } + + public abstract void onClick(); + public abstract void onPressed(); + public abstract void onRelease(); } -- cgit v1.2.3