From 7f745c32c5734bc9549a16a98d158cdc01215bf5 Mon Sep 17 00:00:00 2001 From: Rami Santina Date: Fri, 25 Mar 2011 03:58:05 +0100 Subject: Add initial GPU based curve rendering implementation, utilizing TTF fonts and manual shapes --- src/com/jogamp/graph/curve/HwRegionRenderer.java | 385 +++++++++++++++++++++ src/com/jogamp/graph/curve/OutlineShape.java | 244 +++++++++++++ src/com/jogamp/graph/curve/Region.java | 125 +++++++ src/com/jogamp/graph/curve/RegionFactory.java | 58 ++++ src/com/jogamp/graph/curve/shader/curverenderer.fp | 99 ++++++ src/com/jogamp/graph/curve/shader/curverenderer.vp | 13 + .../jogamp/graph/curve/tess/CDTriangulator2D.java | 213 ++++++++++++ .../jogamp/graph/curve/text/HwTextRenderer.java | 367 ++++++++++++++++++++ src/com/jogamp/graph/math/Quaternion.java | 382 ++++++++++++++++++++ src/demo/GPURegionNewtDemo01.java | 230 ++++++++++++ src/demo/GPURegionNewtDemo02.java | 227 ++++++++++++ src/demo/GPUTextNewtDemo01.java | 198 +++++++++++ src/demo/GPUTextNewtDemo02.java | 215 ++++++++++++ src/jogamp/graph/curve/opengl/VBORegion2PGL3.java | 363 +++++++++++++++++++ src/jogamp/graph/curve/opengl/VBORegionSPES2.java | 185 ++++++++++ src/jogamp/graph/curve/tess/GraphOutline.java | 81 +++++ src/jogamp/graph/curve/tess/GraphPoint.java | 120 +++++++ src/jogamp/graph/curve/tess/HEdge.java | 131 +++++++ src/jogamp/graph/curve/tess/Loop.java | 376 ++++++++++++++++++++ src/jogamp/graph/curve/text/GlyphShape.java | 169 +++++++++ src/jogamp/graph/curve/text/GlyphString.java | 163 +++++++++ src/jogamp/graph/math/VectorFloatUtil.java | 293 ++++++++++++++++ 22 files changed, 4637 insertions(+) create mode 100755 src/com/jogamp/graph/curve/HwRegionRenderer.java create mode 100755 src/com/jogamp/graph/curve/OutlineShape.java create mode 100755 src/com/jogamp/graph/curve/Region.java create mode 100755 src/com/jogamp/graph/curve/RegionFactory.java create mode 100644 src/com/jogamp/graph/curve/shader/curverenderer.fp create mode 100644 src/com/jogamp/graph/curve/shader/curverenderer.vp create mode 100644 src/com/jogamp/graph/curve/tess/CDTriangulator2D.java create mode 100644 src/com/jogamp/graph/curve/text/HwTextRenderer.java create mode 100755 src/com/jogamp/graph/math/Quaternion.java create mode 100755 src/demo/GPURegionNewtDemo01.java create mode 100755 src/demo/GPURegionNewtDemo02.java create mode 100644 src/demo/GPUTextNewtDemo01.java create mode 100644 src/demo/GPUTextNewtDemo02.java create mode 100644 src/jogamp/graph/curve/opengl/VBORegion2PGL3.java create mode 100644 src/jogamp/graph/curve/opengl/VBORegionSPES2.java create mode 100644 src/jogamp/graph/curve/tess/GraphOutline.java create mode 100644 src/jogamp/graph/curve/tess/GraphPoint.java create mode 100644 src/jogamp/graph/curve/tess/HEdge.java create mode 100644 src/jogamp/graph/curve/tess/Loop.java create mode 100644 src/jogamp/graph/curve/text/GlyphShape.java create mode 100644 src/jogamp/graph/curve/text/GlyphString.java create mode 100755 src/jogamp/graph/math/VectorFloatUtil.java (limited to 'src') diff --git a/src/com/jogamp/graph/curve/HwRegionRenderer.java b/src/com/jogamp/graph/curve/HwRegionRenderer.java new file mode 100755 index 000000000..3c3142061 --- /dev/null +++ b/src/com/jogamp/graph/curve/HwRegionRenderer.java @@ -0,0 +1,385 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.graph.curve; + +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLException; +import javax.media.opengl.GLUniformData; +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +import com.jogamp.graph.geom.Triangle; +import com.jogamp.graph.geom.PointTex; + +import jogamp.opengl.Debug; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.glsl.ShaderCode; +import com.jogamp.opengl.util.glsl.ShaderProgram; +import com.jogamp.opengl.util.glsl.ShaderState; + +public class HwRegionRenderer { + protected static final boolean DEBUG = Debug.debug("RegionRenderer"); + + private ShaderState st; + private PMVMatrix pmvMatrix = new PMVMatrix(); + + /**Sharpness is equivalent to the value of t value of texture coord + * on the off-curve vertex. The high value of sharpness will + * result in high curvature. + */ + private float sharpness = 0.5f; + private float alpha = 1.0f; + private float strength = 3.0f; + private boolean initialized = false; + + private int regionType = Region.SINGLE_PASS; + + private GLContext context; + private FloatBuffer color = FloatBuffer.allocate(3); + private HashMap regions = new HashMap(); + + /** Create a Hardware accelerated Region Renderer + * @param context OpenGL rendering context + * @param factory optional Point.Factory for PointTex construction. Default is Vertex.Factory. + */ + public HwRegionRenderer(GLContext context) { + this.context = context; + init(context, 0.5f); + } + /** Create a Hardware accelerated Region Renderer + * @param context OpenGL rendering context + * @param type region type (single or multipass) + */ + public HwRegionRenderer(GLContext context, int type) { + this.context = context; + this.regionType = type; + init(context, 0.5f); + } + + private boolean init(GLContext context, float sharpvalue){ + if(initialized){ + if(DEBUG) { + System.err.println("HWRegionRenderer: Already initialized!"); + } + return true; + } + sharpness = sharpvalue; + + GL2ES2 gl = context.getGL().getGL2ES2(); + + boolean VBOsupported = gl.isFunctionAvailable("glGenBuffers") && + gl.isFunctionAvailable("glBindBuffer") && + gl.isFunctionAvailable("glBufferData") && + gl.isFunctionAvailable("glDrawElements") && + gl.isFunctionAvailable("glVertexAttribPointer") && + gl.isFunctionAvailable("glDeleteBuffers"); + + if(DEBUG) { + System.err.println("HWRegionRenderer: VBO Supported = " + VBOsupported); + } + + if(!VBOsupported){ + return false; + } + + gl.setSwapInterval(1); + + gl.glEnable(GL2ES2.GL_BLEND); + gl.glBlendFunc(GL2ES2.GL_SRC_ALPHA, GL2ES2.GL_ONE_MINUS_SRC_ALPHA); + + initShader(gl); + + st.glUseProgram(gl, true); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + resetMatrix(); + + if(!st.glUniform(gl, new GLUniformData("mgl_PMVMatrix", 4, 4, pmvMatrix.glGetPMvMatrixf()))) { + if(DEBUG){ + System.err.println("Error setting PMVMatrix in shader: "+st); + } + return false; + } + if(!st.glUniform(gl, new GLUniformData("p1y", sharpness))) { + if(DEBUG){ + System.err.println("Error setting sharpness in shader: "+st); + } + return false; + } + if(!st.glUniform(gl, new GLUniformData("g_alpha", alpha))) { + if(DEBUG){ + System.err.println("Error setting global alpha in shader: "+st); + } + return false; + } + if(!st.glUniform(gl, new GLUniformData("g_color", 3, color))) { + if(DEBUG){ + System.err.println("Error setting global color in shader: "+st); + } + return false; + } + if(!st.glUniform(gl, new GLUniformData("a_strength", strength))) { + System.err.println("Error setting antialias strength in shader: "+st); + } + st.glUseProgram(gl, false); + + if(DEBUG) { + System.err.println("HWRegionRenderer initialized: " + Thread.currentThread()+" "+st); + } + initialized = true; + return true; + } + + public float getAlpha() { + return alpha; + } + public void setAlpha(float alpha_t) { + alpha = alpha_t; + } + + public void setColor(float r, float g, float b){ + color.put(r); + color.put(g); + color.put(b); + color.rewind(); + } + + public void rotate(float angle, float x, float y, float z){ + pmvMatrix.glRotatef(angle, x, y, z); + } + public void translate(float x, float y, float z){ + pmvMatrix.glTranslatef(x, y, z); + } + + public void resetMatrix(){ + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + } + + /** + * @param drawable + * @param angle + * @param ratio + * @param near + * @param far + * @return + */ + public boolean reshape(GLAutoDrawable drawable, float angle, float ratio, float near, float far){ + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + pmvMatrix.gluPerspective(angle, ratio, near, far); + + if(null==st) { + if(DEBUG){ + System.err.println("HWRegionRenderer: Shader State is null, or not"); + } + return false; + } + GL2ES2 gl = drawable.getGL().getGL2ES2(); + + st.glUseProgram(gl, true); + GLUniformData ud = st.getUniform("mgl_PMVMatrix"); + if(null!=ud) { + st.glUniform(gl, ud); + } + st.glUseProgram(gl, false); + return true; + } + + private void initShader(GL2ES2 gl) { + ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, 1, HwRegionRenderer.class, + "shader", "shader/bin", "curverenderer"); + ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, 1, HwRegionRenderer.class, + "shader", "shader/bin", "curverenderer"); + + ShaderProgram sp = new ShaderProgram(); + sp.add(rsVp); + sp.add(rsFp); + + if(!sp.link(gl, System.err)) { + throw new GLException("HWRegionRenderer: Couldn't link program: "+sp); + } + + st = new ShaderState(); + st.attachShaderProgram(gl, sp); + gl.glBindAttribLocation(sp.id(), 0, "v_position"); + gl.glBindAttribLocation(sp.id(), 1, "texCoord"); + } + + private Region createRegion(OutlineShape outlineShape) { + Region region = RegionFactory.create(context, st, regionType); + + outlineShape.transformOutlines(OutlineShape.QUADRATIC_NURBS); + + ArrayList> triangles = (ArrayList>) outlineShape.triangulate(sharpness); + ArrayList vertices = (ArrayList) outlineShape.getVertices(); + region.addVertices(vertices); + region.addTriangles(triangles); + + region.update(); + return region; + } + + private Region createRegion(OutlineShape[] outlineShapes) { + Region region = RegionFactory.create(context, st, regionType); + + int numVertices = region.getNumVertices(); + + for(OutlineShape outlineShape:outlineShapes){ + outlineShape.transformOutlines(OutlineShape.QUADRATIC_NURBS); + + ArrayList> triangles = outlineShape.triangulate(sharpness); + region.addTriangles(triangles); + + ArrayList vertices = outlineShape.getVertices(); + for(PointTex vert:vertices){ + vert.setId(numVertices++); + } + region.addVertices(vertices); + } + + region.update(); + return region; + } + + + /** Render outline in 3D space at the position provided + * the triangles of the shapes will be generated, if not yet generated + * @param outlineShape the OutlineShape to Render. + * @param position the initial translation of the outlineShape. + * @throws Exception if HwRegionRenderer not initialized + */ + public void renderOutlineShape(OutlineShape outlineShape, float[] position) throws Exception{ + if(!initialized){ + throw new Exception("HWRegionRenderer: not initialized!"); + } + int hashCode = getHashCode(outlineShape); + Region region = regions.get(hashCode); + + if(null == region) { + region = createRegion(outlineShape); + regions.put(hashCode, region); + } + + GL2ES2 gl = context.getGL().getGL2ES2(); + st.glUseProgram(gl, true); + GLUniformData ud = st.getUniform("mgl_PMVMatrix"); + if(null!=ud) { + st.glUniform(gl, ud); + } + if(!st.glUniform(gl, new GLUniformData("g_alpha", alpha))) { + System.err.println("Error setting global alpha in shader: "+st); + } + GLUniformData gcolorUD = st.getUniform("g_color"); + if(null!=gcolorUD) { + st.glUniform(gl, gcolorUD); + } + if(!st.glUniform(gl, new GLUniformData("a_strength", strength))) { + System.err.println("Error setting antialias strength in shader: "+st); + } + + region.render(null, 0, 0, 0); + st.glUseProgram(gl, false); + } + + /** Render a list of Outline shapes combined in one region + * at the position provided the triangles of the + * shapes will be generated, if not yet generated + * @param outlineShapes the list of OutlineShapes to Render. + * @param position the initial translation of the outlineShapes. + * @throws Exception if HwRegionRenderer not initialized + */ + public void renderOutlineShapes(OutlineShape[] outlineShapes, float[] position) throws Exception{ + if(!initialized){ + throw new Exception("HWRegionRenderer: not initialized!"); + } + + int hashCode = getHashCode(outlineShapes); + Region region = regions.get(hashCode); + + if(null == region) { + region = createRegion(outlineShapes); + regions.put(hashCode, region); + } + + GL2ES2 gl = context.getGL().getGL2ES2(); + st.glUseProgram(gl, true); + GLUniformData ud = st.getUniform("mgl_PMVMatrix"); + if(null!=ud) { + st.glUniform(gl, ud); + } + if(!st.glUniform(gl, new GLUniformData("g_alpha", alpha))) { + System.err.println("Error setting global alpha in shader: "+st); + } + GLUniformData gcolorUD = st.getUniform("g_color"); + if(null!=gcolorUD) { + st.glUniform(gl, gcolorUD); + } + if(!st.glUniform(gl, new GLUniformData("a_strength", strength))) { + System.err.println("Error setting antialias strength in shader: "+st); + } + region.render(null, 0, 0, 0); + st.glUseProgram(gl, false); + } + + private int getHashCode(OutlineShape outlineShape){ + return outlineShape.hashCode(); + } + + private int getHashCode(OutlineShape[] outlineShapes){ + int hashcode = 0; + for(OutlineShape outlineShape:outlineShapes){ + hashcode += getHashCode(outlineShape); + } + return hashcode; + } + + /** Clears the cached string curves + * and destorys underlying buffers + */ + public void clearCached() { + Iterator iterator = regions.values().iterator(); + while(iterator.hasNext()){ + Region region = iterator.next(); + region.destroy(); + } + regions.clear(); + } +} diff --git a/src/com/jogamp/graph/curve/OutlineShape.java b/src/com/jogamp/graph/curve/OutlineShape.java new file mode 100755 index 000000000..d939d7427 --- /dev/null +++ b/src/com/jogamp/graph/curve/OutlineShape.java @@ -0,0 +1,244 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.graph.curve; + +import java.util.ArrayList; +import java.util.Collections; + +import jogamp.graph.math.VectorFloatUtil; + +import com.jogamp.graph.geom.Outline; +import com.jogamp.graph.geom.Line; +import com.jogamp.graph.geom.Triangle; +import com.jogamp.graph.geom.Point; +import com.jogamp.graph.geom.PointTex; + +import com.jogamp.graph.curve.tess.CDTriangulator2D; + +/** A Generic shape objects which is defined by a list of Outlines. + * This Shape can be transformed to Triangulations. + * The list of triangles generated are render-able by a Region object. + * The triangulation produced by this Shape will define the + * closed region defined by the outlines. + * + * One or more OutlineShape Object can be associated to a region + * this is left high-level representation of the Objects. For + * possible Optimizations. + * + * @see Region + */ +public class OutlineShape { + public static final int QUADRATIC_NURBS = 10; + private final Point.Factory pointFactory; + private ArrayList> outlines = new ArrayList>(3); + + /** Create a new Outline based Shape + */ + public OutlineShape(Point.Factory factory) { + pointFactory = factory; + outlines.add(new Outline()); + } + + public final Point.Factory pointFactory() { return pointFactory; } + + /** Add a new empty outline + * to the shape, this new outline will + * be placed at the end of the outline list. + */ + public void addEmptyOutline(){ + outlines.add(new Outline()); + } + + /** Adds an outline to the OutlineShape object + * if last outline of the shape is empty, it will replace + * that last Outline with the new one. If outline is empty, + * it will do nothing. + * @param outline an Outline object + */ + public void addOutline(Outline outline){ + if(outline.isEmpty()){ + return; + } + if(getLastOutline().isEmpty()){ + outlines.remove(getLastOutline()); + } + outlines.add(outline); + } + + /** Adds a vertex to the last open outline in the + * shape + * @param point + */ + public final void addVertex(PointTex point){ + getLastOutline().addVertex(point); + } + + public final void addVertex(float x, float y, boolean onCurve) { + getLastOutline().addVertex(pointFactory, x, y, onCurve); + } + + public final void addVertex(float x, float y, float z, boolean onCurve) { + getLastOutline().addVertex(pointFactory, x, y, z, onCurve); + } + + public final void addVertex(float[] coordsBuffer, int offset, int length, boolean onCurve) { + getLastOutline().addVertex(pointFactory, coordsBuffer, offset, length, onCurve); + } + + /** Closes the last outline in the shape + * if last vertex is not equal to first vertex. + * A new temp vertex is added at the end which + * is equal to the first. + */ + public void closeLastOutline(){ + getLastOutline().setClosed(true); + } + + /** Get the last added outline to the list + * of outlines that define the shape + * @return the last outline + */ + public final Outline getLastOutline(){ + return outlines.get(outlines.size()-1); + } + /** Make sure that the outlines represent + * the specified destinationType, if not + * transform outlines to destinationType. + * @param destinationType The curve type needed + */ + public void transformOutlines(int destinationType){ + if(destinationType == QUADRATIC_NURBS){ + transformOutlinesQuadratic(); + } + } + + private void transformOutlinesQuadratic(){ + ArrayList> newOutlines = new ArrayList>(3); + + /**loop over the outlines and make sure no + * adj off-curve vertices + */ + for(Outline outline:outlines){ + Outline newOutline = new Outline(); + + ArrayList vertices = outline.getVertices(); + int size =vertices.size()-1; + for(int i=0;i outline:outlines){ + ArrayList vertices = outline.getVertices(); + for(PointTex vert:vertices){ + vert.setId(maxVertexId); + maxVertexId++; + } + } + } + + /** @return the list of vertices associated with the + * {@code Outline} list of this object + */ + public ArrayList getVertices(){ + ArrayList vertices = new ArrayList(); + for(Outline polyline:outlines){ + vertices.addAll(polyline.getVertices()); + } + return vertices; + } + + + /** Generates the lines the define the noncurved + * parts of this graph + * @return arraylist of lines + */ + public ArrayList> getLines(){ + ArrayList> lines = new ArrayList>(); + for(Outline outline:outlines){ + ArrayList outVertices = outline.getVertices(); + int size = outVertices.size(); + for(int i=0; i < size; i++) { + PointTex currentVertex = outVertices.get(i); + if(currentVertex.isOnCurve()) { + PointTex v2 = outVertices.get((i+1)%size); + if(v2.isOnCurve()){ + lines.add(new Line(currentVertex, v2)); + } + } + } + } + return lines; + } + + /** Triangluate the graph object + * @param sharpness sharpness of the curved regions default = 0.5 + */ + public ArrayList> triangulate(float sharpness){ + if(outlines.size() == 0){ + return null; + } + sortOutlines(); + generateVertexIds(); + + CDTriangulator2D triangulator2d = new CDTriangulator2D(sharpness); + + for(int index = 0; index< outlines.size();index++){ + Outline outline = outlines.get(index); + triangulator2d.addCurve(outline); + } + + ArrayList> triangles = triangulator2d.generateTriangulation(); + triangulator2d.reset(); + + return triangles; + } + + /** Sort the outlines from large + * to small depending on the AABox + */ + private void sortOutlines() { + Collections.sort(outlines); + Collections.reverse(outlines); + } +} diff --git a/src/com/jogamp/graph/curve/Region.java b/src/com/jogamp/graph/curve/Region.java new file mode 100755 index 000000000..44f426313 --- /dev/null +++ b/src/com/jogamp/graph/curve/Region.java @@ -0,0 +1,125 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.graph.curve; + +import java.util.ArrayList; + +import com.jogamp.graph.geom.Triangle; +import com.jogamp.graph.geom.PointTex; +import com.jogamp.opengl.util.PMVMatrix; + +/** A Region is the OGL binding of one or more OutlineShapes + * Defined by its vertices and generated triangles. The Region + * defines the final shape of the OutlineShape(s), which shall produced a shaded + * region on the screen. + * + * Implementations of the Region shall take care of the OGL + * binding of the depending on its context, profile. + * + * @see RegionFactory, OutlineShape + */ +public interface Region { + /** The vertices index in an OGL object + */ + public static int VERTEX_POS_INDX = 0; + + /** The Texture Coord index in an OGL object + */ + public static int TEX_COORD = 1; + + public static int SINGLE_PASS = 10; + public static int TWO_PASS = 20; + + /** Updates a graph region by updating the ogl related + * objects for use in rendering. if called for the first time + * it initialize the objects. + */ + public void update(); + + /** Renders the associated OGL objects specifying + * current width/hight of window for multi pass rendering + * of the region. + * @param matrix current pmv matrix. + * @param vp_width current screen width + * @param vp_height current screen height + * @param width texture width for mp rendering + * + * @see update() + */ + public void render(PMVMatrix matrix, int vp_width, int vp_height, int width); + + /** Adds a list of {@code Triangle} objects to the Region + * These triangles are to be binded to OGL objects + * on the next call to {@code update} + * @param tris an arraylist of triangle objects + * + * @see update() + */ + public void addTriangles(ArrayList> tris); + + /** Get the current number of vertices associated + * with this region. This number is not necessary equal to + * the OGL binded number of vertices. + * @return vertices count + * + * @see isDirty() + */ + public int getNumVertices(); + + /** Adds a list of {@code Vertex} objects to the Region + * These vertices are to be binded to OGL objects + * on the next call to {@code update} + * @param verts an arraylist of vertex objects + * + * @see update() + */ + public void addVertices(ArrayList verts); + + /** Check if this region is dirty. A region is marked dirty + * when new Vertices, Triangles, and or Lines are added after a + * call to update() + * @return true if region is Dirty, false otherwise + * + * @see update(); + */ + public boolean isDirty(); + + /** Delete and clean the associated OGL + * objects + */ + public void destroy(); + + public boolean isFlipped(); + + /** Set if the y coordinate of the region should be flipped + * {@code y=-y} used mainly for fonts since they use opposite vertex + * as origion + * @param flipped flag if the coordinate is flipped defaults to false. + */ + public void setFlipped(boolean flipped); +} diff --git a/src/com/jogamp/graph/curve/RegionFactory.java b/src/com/jogamp/graph/curve/RegionFactory.java new file mode 100755 index 000000000..158f9db5b --- /dev/null +++ b/src/com/jogamp/graph/curve/RegionFactory.java @@ -0,0 +1,58 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.graph.curve; + +import javax.media.opengl.GLContext; + +import com.jogamp.opengl.util.glsl.ShaderState; + +import jogamp.graph.curve.opengl.VBORegionSPES2; +import jogamp.graph.curve.opengl.VBORegion2PGL3; + + +/** RegionFactory to create a Context specific Region implementation. + * + * @see Region + */ +public class RegionFactory { + + /**Create a Region based on the GLContext attached + * @param context the current opengl context + * @param st the shader state object + * @param type can be one of Region.SINGLE_PASS or Region.TWO_PASS + * @return region + */ + public static Region create(GLContext context, ShaderState st, int type){ + if(Region.TWO_PASS == type && context.isGL3()){ + return new VBORegion2PGL3(context, st); + } + else{ + return new VBORegionSPES2(context); + } + } +} diff --git a/src/com/jogamp/graph/curve/shader/curverenderer.fp b/src/com/jogamp/graph/curve/shader/curverenderer.fp new file mode 100644 index 000000000..2b3a0ce1d --- /dev/null +++ b/src/com/jogamp/graph/curve/shader/curverenderer.fp @@ -0,0 +1,99 @@ +//#version 100 + +uniform float p1y; +uniform float g_alpha; +uniform vec3 g_color; +uniform float a_strength; + +varying vec2 v_texCoord; + +vec3 b_color = vec3(0.0, 0.0, 0.0); + +uniform sampler2D texture; +vec4 weights = vec4(0.075, 0.06, 0.045, 0.025); + +void main (void) +{ + vec2 rtex = vec2(abs(v_texCoord.x),abs(v_texCoord.y)); + vec3 c = g_color; + + float alpha = 0.0; + + if((v_texCoord.x == 0.0) && (v_texCoord.y == 0.0)){ + alpha = g_alpha; + } + else if((v_texCoord.x >= 5.0)){ + vec2 dfx = dFdx(v_texCoord); + vec2 dfy = dFdy(v_texCoord); + + vec2 size = 1.0/textureSize(texture,0); //version 130 + rtex -= 5.0; + vec4 t = texture2D(texture, rtex)* 0.18; + + t += texture2D(texture, rtex + size*(vec2(1, 0)))*weights.x; + t += texture2D(texture, rtex - size*(vec2(1, 0)))*weights.x; + t += texture2D(texture, rtex + size*(vec2(0, 1)))*weights.x; + t += texture2D(texture, rtex - size*(vec2(0, 1)))*weights.x; + + t += texture2D(texture, rtex + 2.0*size*(vec2(1, 0))) *weights.y; + t += texture2D(texture, rtex - 2.0*size*(vec2(1, 0)))*weights.y; + t += texture2D(texture, rtex + 2.0*size*(vec2(0, 1)))*weights.y; + t += texture2D(texture, rtex - 2.0*size*(vec2(0, 1)))*weights.y; + + t += texture2D(texture, rtex + 3.0*size*(vec2(1, 0))) *weights.z; + t += texture2D(texture, rtex - 3.0*size*(vec2(1, 0)))*weights.z; + t += texture2D(texture, rtex + 3.0*size*(vec2(0, 1)))*weights.z; + t += texture2D(texture, rtex - 3.0*size*(vec2(0, 1)))*weights.z; + + t += texture2D(texture, rtex + 4.0*size*(vec2(1, 0))) *weights.w; + t += texture2D(texture, rtex - 4.0*size*(vec2(1, 0)))*weights.w; + t += texture2D(texture, rtex + 4.0*size*(vec2(0, 1)))*weights.w; + t += texture2D(texture, rtex - 4.0*size*(vec2(0, 1)))*weights.w; + + if(t.w == 0.0){ + discard; + } + + c = t.xyz; + alpha = g_alpha* t.w; + } + /////////////////////////////////////////////////////////// + else if ((v_texCoord.x > 0.0) && (rtex.y > 0.0 || rtex.x == 1.0)){ + vec2 dtx = dFdx(rtex); + vec2 dty = dFdy(rtex); + + rtex.y -= 0.1; + if(rtex.y < 0.0) { + if(v_texCoord.y < 0.0) + discard; + else{ + rtex.y = 0.0; + } + } + + vec2 f = vec2((dtx.y - 2.0*p1y*dtx.x + 4.0*p1y*rtex.x*dtx.x), (dty.y - 2.0*p1y*dty.x + 4.0*p1y*rtex.x*dty.x)); + + float position = rtex.y - ((2.0 * rtex.x * p1y) * (1.0 - rtex.x)); + float d = position/(length(f)); + + float a = (0.5 - d * sign(v_texCoord.y)); + + + if (a >= 1.0) { + alpha = g_alpha; + // c = vec3(1.0,1.0,1.0); + } + else if (a <= 0.0) { + alpha = 0.0;//discard; + // c = vec3(0.0,0.0,0.0); + + } + else { + alpha = g_alpha*a; + // c = vec3(a,a,a); + mix(b_color,g_color, a); + } + } + + gl_FragColor = vec4(c, alpha); +} diff --git a/src/com/jogamp/graph/curve/shader/curverenderer.vp b/src/com/jogamp/graph/curve/shader/curverenderer.vp new file mode 100644 index 000000000..bc9ecb41e --- /dev/null +++ b/src/com/jogamp/graph/curve/shader/curverenderer.vp @@ -0,0 +1,13 @@ +//#version 100 + +uniform mat4 mgl_PMVMatrix[2]; +attribute vec4 v_position; +attribute vec2 texCoord; + +varying vec2 v_texCoord; + +void main(void) +{ + gl_Position = mgl_PMVMatrix[0] * mgl_PMVMatrix[1] * v_position; + v_texCoord = texCoord.st; +} \ No newline at end of file diff --git a/src/com/jogamp/graph/curve/tess/CDTriangulator2D.java b/src/com/jogamp/graph/curve/tess/CDTriangulator2D.java new file mode 100644 index 000000000..936965f0c --- /dev/null +++ b/src/com/jogamp/graph/curve/tess/CDTriangulator2D.java @@ -0,0 +1,213 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.graph.curve.tess; + +import java.util.ArrayList; + +import jogamp.graph.curve.tess.GraphOutline; +import jogamp.graph.curve.tess.GraphPoint; +import jogamp.graph.curve.tess.Loop; +import jogamp.graph.math.VectorFloatUtil; + +import com.jogamp.graph.geom.Outline; +import com.jogamp.graph.geom.Triangle; +import com.jogamp.graph.geom.PointTex; +import jogamp.opengl.Debug; + +/** Constrained Delaunay Triangulation + * implementation of a list of Outlines that define a set of + * Closed Regions with optional n holes. + * + */ +public class CDTriangulator2D { + + protected static final boolean DEBUG = Debug.debug("Triangulation"); + + private float sharpness = 0.5f; + private ArrayList> loops; + private ArrayList vertices; + + private ArrayList> triangles; + private int maxTriID = 0; + + + public CDTriangulator2D() { + this(0.5f); + } + + /** Constructor for a new Delaunay triangulator + * @param curveSharpness the curvature around + * the off-curve vertices + */ + public CDTriangulator2D(float curveSharpness) { + this.sharpness = curveSharpness; + reset(); + } + + /** Reset the triangulation to initial state + * Clearing cached data + */ + public void reset() { + maxTriID = 0; + vertices = new ArrayList(); + triangles = new ArrayList>(3); + loops = new ArrayList>(); + } + + /** Add a curve to the list of profiles provided + * @param polyline a bounding Outline + */ + public void addCurve(Outline polyline){ + Loop loop = null; + + if(!loops.isEmpty()){ + loop = getContainerLoop(polyline); + } + + if(loop == null) { + GraphOutline outline = new GraphOutline(polyline); + GraphOutline innerPoly = extractBoundaryTriangles(outline, false); + vertices.addAll(polyline.getVertices()); + loop = new Loop(innerPoly, VectorFloatUtil.CCW); + loops.add(loop); + } + else { + GraphOutline outline = new GraphOutline(polyline); + GraphOutline innerPoly = extractBoundaryTriangles(outline, true); + vertices.addAll(innerPoly.getPoints()); + loop.addConstraintCurve(innerPoly); + } + } + + /** Generate the triangulation of the provided + * List of Outlines + */ + public ArrayList> generateTriangulation(){ + for(int i=0;i loop = loops.get(i); + int numTries = 0; + int size = loop.computeLoopSize(); + while(!loop.isSimplex()){ + Triangle tri = null; + if(numTries > size){ + tri = loop.cut(false); + } + else{ + tri = loop.cut(true); + } + numTries++; + + if(tri != null) { + numTries = 0; + size--; + tri.setId(maxTriID++); + triangles.add(tri); + if(DEBUG){ + System.err.println(tri); + } + } + if(numTries > size*2){ + if(DEBUG){ + System.err.println("Triangulation not complete!"); + } + break; + } + } + Triangle tri = loop.cut(true); + if(tri != null) + triangles.add(tri); + } + return triangles; + } + + @SuppressWarnings("unchecked") + private GraphOutline extractBoundaryTriangles(GraphOutline outline, boolean hole){ + GraphOutline innerOutline = new GraphOutline(); + ArrayList> outVertices = outline.getGraphPoint(); + int size = outVertices.size(); + for(int i=0; i < size; i++) { + GraphPoint currentVertex = outVertices.get(i); + GraphPoint gv0 = outVertices.get((i+size-1)%size); + GraphPoint gv2 = outVertices.get((i+1)%size); + GraphPoint gv1 = currentVertex; + + if(!currentVertex.getPoint().isOnCurve()) { + T v0 = (T) gv0.getPoint().clone(); + T v2 = (T) gv2.getPoint().clone(); + T v1 = (T) gv1.getPoint().clone(); + + gv0.setBoundaryContained(true); + gv1.setBoundaryContained(true); + gv2.setBoundaryContained(true); + + Triangle t= null; + boolean holeLike = false; + if(VectorFloatUtil.ccw(v0,v1,v2)){ + t = new Triangle(v0, v1, v2); + } + else { + holeLike = true; + t = new Triangle(v2, v1, v0); + } + t.setId(maxTriID++); + triangles.add(t); + + if(hole || holeLike) { + v0.setTexCoord(0, -0.1f); + v2.setTexCoord(1, -0.1f); + v1.setTexCoord(0.5f, -1*sharpness -0.1f); + innerOutline.addVertex(currentVertex); + } + else { + v0.setTexCoord(0, 0.1f); + v2.setTexCoord(1, 0.1f); + v1.setTexCoord(0.5f, sharpness+0.1f); + } + } + else { + if(!gv2.getPoint().isOnCurve() || !gv0.getPoint().isOnCurve()){ + currentVertex.setBoundaryContained(true); + } + innerOutline.addVertex(currentVertex); + } + } + return innerOutline; + } + + private Loop getContainerLoop(Outline polyline){ + T v = polyline.getVertex(0); + + for (Loop loop:loops){ + if(loop.checkInside(v)){ + return loop; + } + } + return null; + } +} diff --git a/src/com/jogamp/graph/curve/text/HwTextRenderer.java b/src/com/jogamp/graph/curve/text/HwTextRenderer.java new file mode 100644 index 000000000..5813225e4 --- /dev/null +++ b/src/com/jogamp/graph/curve/text/HwTextRenderer.java @@ -0,0 +1,367 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.graph.curve.text; + +import java.nio.FloatBuffer; +import java.util.HashMap; +import java.util.Iterator; + +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLException; +import javax.media.opengl.GLUniformData; +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +import jogamp.graph.curve.text.GlyphString; +import jogamp.graph.font.typecast.TypecastFontFactory; + +import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.graph.curve.Region; +import com.jogamp.graph.font.Font; +import com.jogamp.graph.font.FontFactory; +import com.jogamp.graph.geom.plane.AffineTransform; +import com.jogamp.graph.geom.plane.Path2D; +import com.jogamp.graph.geom.Point; +import com.jogamp.graph.geom.PointTex; +import com.jogamp.graph.geom.opengl.Vertex; +import jogamp.opengl.Debug; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.glsl.ShaderCode; +import com.jogamp.opengl.util.glsl.ShaderProgram; +import com.jogamp.opengl.util.glsl.ShaderState; + +public class HwTextRenderer { + protected static final boolean DEBUG = Debug.debug("TextRenderer"); + static final boolean FONTTOOL_CUSTOM = false; + + private static FontFactory fontFactory; + + static { + FontFactory _fontFactory = null; + + if(FONTTOOL_CUSTOM) { + _fontFactory = (FontFactory) ReflectionUtil.createInstance("jogamp.graph.font.ttf.TTFFontFactory", HwTextRenderer.class.getClassLoader()); + if(null!=_fontFactory) { + System.err.println("Using custom font tool"); + } + } + if(null==_fontFactory) { + _fontFactory = new TypecastFontFactory(); + } + fontFactory = _fontFactory; + } + + + public static FontFactory getFontFactory() { + return fontFactory; + } + + private ShaderState st; + private PMVMatrix pmvMatrix = new PMVMatrix(); + + /**Sharpness is equivalent to the value of t value of texture coord + * on the off-curve vertex. The high value of sharpness will + * result in high curvature. + */ + private float sharpness = 0.5f; + private float alpha = 1.0f; + private float strength = 1.8f; + private boolean initialized = false; + + + private int regionType = Region.SINGLE_PASS; + private GLContext context; + private FloatBuffer color = FloatBuffer.allocate(3); + private HashMap strings = new HashMap(); + private final Point.Factory pointFactory; + + int win_width = 0; + int win_height = 0; + + /** Create a Hardware accelerated Text Renderer + * @param context OpenGL rendering context + * @param factory optional Point.Factory for PointTex construction. Default is Vertex.Factory. + */ + public HwTextRenderer(GLContext context, Point.Factory factory, int type) { + this.pointFactory = (null != factory) ? factory : Vertex.factory(); + this.context = context; + this.regionType = type; + init(context, 0.5f); + } + + public Font createFont(Point.Factory factory, String name, int size) { + return fontFactory.createFont(factory, name, size); + } + + + public Font createFont(Point.Factory factory, + String[] families, + String style, + String variant, + String weight, + String size) { + return fontFactory.createFont(factory, families, style, variant, weight, size); + } + + /** initialize shaders and bindings for GPU based text Rendering, should + * be called only onceangle + * @param drawable the current drawable + * @param shapvalue shaprness around the off-curve vertices + * @return true if init succeeded, false otherwise + */ + private boolean init(GLContext context, float sharpvalue){ + if(initialized){ + if(DEBUG) { + System.err.println("HWTextRenderer: Already initialized!"); + } + return true; + } + sharpness = sharpvalue; + + GL2ES2 gl = context.getGL().getGL2ES2(); + + boolean VBOsupported = gl.isFunctionAvailable("glGenBuffers") && + gl.isFunctionAvailable("glBindBuffer") && + gl.isFunctionAvailable("glBufferData") && + gl.isFunctionAvailable("glDrawElements") && + gl.isFunctionAvailable("glVertexAttribPointer") && + gl.isFunctionAvailable("glDeleteBuffers"); + + if(DEBUG) { + System.err.println("HWTextRenderer: VBO Supported = " + VBOsupported); + } + + if(!VBOsupported){ + return false; + } + + gl.setSwapInterval(1); + + gl.glEnable(GL2ES2.GL_BLEND); + gl.glBlendFunc(GL2ES2.GL_SRC_ALPHA, GL2ES2.GL_ONE_MINUS_SRC_ALPHA); + + initShader(gl); + + st.glUseProgram(gl, true); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + resetMatrix(); + + if(!st.glUniform(gl, new GLUniformData("mgl_PMVMatrix", 4, 4, pmvMatrix.glGetPMvMatrixf()))) { + if(DEBUG){ + System.err.println("Error setting PMVMatrix in shader: "+st); + } + return false; + } + if(!st.glUniform(gl, new GLUniformData("p1y", sharpness))) { + if(DEBUG){ + System.err.println("Error setting sharpness in shader: "+st); + } + return false; + } + if(!st.glUniform(gl, new GLUniformData("g_alpha", alpha))) { + if(DEBUG){ + System.err.println("Error setting global alpha in shader: "+st); + } + return false; + } + if(!st.glUniform(gl, new GLUniformData("g_color", 3, color))) { + if(DEBUG){ + System.err.println("Error setting global color in shader: "+st); + } + return false; + } + if(!st.glUniform(gl, new GLUniformData("a_strength", strength))) { + System.err.println("Error setting antialias strength in shader: "+st); + } + + st.glUseProgram(gl, false); + + if(DEBUG) { + System.err.println("HWTextRenderer initialized: " + Thread.currentThread()+" "+st); + } + initialized = true; + return true; + } + + public float getAlpha() { + return alpha; + } + public void setAlpha(float alpha_t) { + alpha = alpha_t; + } + + public void setColor(float r, float g, float b){ + color.put(r); + color.put(g); + color.put(b); + color.rewind(); + } + + public void rotate(float angle, float x, float y, float z){ + pmvMatrix.glRotatef(angle, x, y, z); + } + public void translate(float x, float y, float z){ + pmvMatrix.glTranslatef(x, y, z); + } + + public void resetMatrix(){ + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + } + + /** + * @param drawable + * @param angle + * @param ratio + * @param near + * @param far + * @return + */ + public boolean reshape(GLAutoDrawable drawable, float angle, int width, int height, float near, float far){ + win_width = width; + win_height = height; + float ratio = (float)width/(float)height; + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + pmvMatrix.gluPerspective(angle, ratio, near, far); + + if(null==st) { + if(DEBUG){ + System.err.println("HWTextRenderer: Shader State is null, or not"); + } + return false; + } + GL2ES2 gl = drawable.getGL().getGL2ES2(); + + st.glUseProgram(gl, true); + GLUniformData ud = st.getUniform("mgl_PMVMatrix"); + if(null!=ud) { + st.glUniform(gl, ud); + } + st.glUseProgram(gl, false); + return true; + } + + private void initShader(GL2ES2 gl) { + ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, 1, HwTextRenderer.class, + "../shader", "../shader/bin", "curverenderer"); + ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, 1, HwTextRenderer.class, + "../shader", "../shader/bin", "curverenderer"); + + ShaderProgram sp = new ShaderProgram(); + sp.add(rsVp); + sp.add(rsFp); + + if(!sp.link(gl, System.err)) { + throw new GLException("HWTextRenderer: Couldn't link program: "+sp); + } + + st = new ShaderState(); + st.attachShaderProgram(gl, sp); + gl.glBindAttribLocation(sp.id(), 0, "v_position"); + gl.glBindAttribLocation(sp.id(), 1, "texCoord"); + } + + private GlyphString createString(Font font, String str) { + AffineTransform affineTransform = new AffineTransform(pointFactory); + + Path2D[] paths = new Path2D[str.length()]; + font.getOutline(str, affineTransform, paths); + + GlyphString glyphString = new GlyphString(pointFactory, font.getName(), str); + glyphString.createfromFontPath(paths, affineTransform); + + glyphString.generateRegion(context, sharpness, st, regionType); + return glyphString; + } + + + /** Render the String in 3D space wrt to the font provided at the position provided + * the outlines will be generated, if not yet generated + * @param font font to be used + * @param str text to be rendered + * @param position the lower left corner of the string + * @param size texture size for multipass render + * @throws Exception if TextRenderer not initialized + */ + public void renderString3D(Font font, String str, float[] position, int size) throws Exception{ + if(!initialized){ + throw new Exception("HWTextRenderer: not initialized!"); + } + String fontStrHash = getTextHashCode(font, str); + GlyphString glyphString = strings.get(fontStrHash); + if(null == glyphString) { + glyphString = createString(font, str); + strings.put(fontStrHash, glyphString); + } + + GL2ES2 gl = context.getGL().getGL2ES2(); + st.glUseProgram(gl, true); + GLUniformData ud = st.getUniform("mgl_PMVMatrix"); + if(null!=ud) { + st.glUniform(gl, ud); + } + if(!st.glUniform(gl, new GLUniformData("g_alpha", alpha))) { + System.err.println("Error setting global alpha in shader: "+st); + } + GLUniformData gcolorUD = st.getUniform("g_color"); + if(null!=gcolorUD) { + st.glUniform(gl, gcolorUD); + } + + if(!st.glUniform(gl, new GLUniformData("a_strength", strength))) { + System.err.println("Error setting antialias strength in shader: "+st); + } + glyphString.renderString3D(pmvMatrix, win_width, win_height, size); + st.glUseProgram(gl, false); + } + + private String getTextHashCode(Font font, String str){ + return "" + str.hashCode() + font.getSize(); + } + + /** Clears the cached string curves + * and destorys underlying buffers + */ + public void clearCached() { + Iterator iterator = strings.values().iterator(); + while(iterator.hasNext()){ + GlyphString glyphString = iterator.next(); + glyphString.destroy(); + } + strings.clear(); + } +} diff --git a/src/com/jogamp/graph/math/Quaternion.java b/src/com/jogamp/graph/math/Quaternion.java new file mode 100755 index 000000000..b77a5fa08 --- /dev/null +++ b/src/com/jogamp/graph/math/Quaternion.java @@ -0,0 +1,382 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.graph.math; + +import jogamp.graph.math.MathFloat; + +public class Quaternion { + protected float x,y,z,w; + + public Quaternion(){ + + } + + public Quaternion(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /** Constructor to create a rotation based quaternion from two vectors + * @param vector1 + * @param vector2 + */ + public Quaternion(float[] vector1, float[] vector2) + { + float theta = (float)MathFloat.acos(dot(vector1, vector2)); + float[] cross = cross(vector1,vector2); + cross = normalizeVec(cross); + + this.x = (float)MathFloat.sin(theta/2)*cross[0]; + this.y = (float)MathFloat.sin(theta/2)*cross[1]; + this.z = (float)MathFloat.sin(theta/2)*cross[2]; + this.w = (float)MathFloat.cos(theta/2); + this.normalize(); + } + + /** Transform the rotational quaternion to axis based rotation angles + * @return new float[4] with ,theta,Rx,Ry,Rz + */ + public float[] toAxis() + { + float[] vec = new float[4]; + float scale = (float)MathFloat.sqrt(x * x + y * y + z * z); + vec[0] =(float) MathFloat.acos(w) * 2.0f; + vec[1] = x / scale; + vec[2] = y / scale; + vec[3] = z / scale; + return vec; + } + + /** Normalize a vector + * @param vector input vector + * @return normalized vector + */ + private float[] normalizeVec(float[] vector) + { + float[] newVector = new float[3]; + + float d = MathFloat.sqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]); + if(d> 0.0f) + { + newVector[0] = vector[0]/d; + newVector[1] = vector[1]/d; + newVector[2] = vector[2]/d; + } + return newVector; + } + /** compute the dot product of two points + * @param vec1 vector 1 + * @param vec2 vector 2 + * @return the dot product as float + */ + private float dot(float[] vec1, float[] vec2) + { + return (vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2]); + } + /** cross product vec1 x vec2 + * @param vec1 vector 1 + * @param vec2 vecttor 2 + * @return the resulting vector + */ + private float[] cross(float[] vec1, float[] vec2) + { + float[] out = new float[3]; + + out[0] = vec2[2]*vec1[1] - vec2[1]*vec1[2]; + out[1] = vec2[0]*vec1[2] - vec2[2]*vec1[0]; + out[2] = vec2[1]*vec1[0] - vec2[0]*vec1[1]; + + return out; + } + public float getW() { + return w; + } + public void setW(float w) { + this.w = w; + } + public float getX() { + return x; + } + public void setX(float x) { + this.x = x; + } + public float getY() { + return y; + } + public void setY(float y) { + this.y = y; + } + public float getZ() { + return z; + } + public void setZ(float z) { + this.z = z; + } + + /** Add a quaternion + * @param q quaternion + */ + public void add(Quaternion q) + { + x+=q.x; + y+=q.y; + z+=q.z; + } + + /** Subtract a quaternion + * @param q quaternion + */ + public void subtract(Quaternion q) + { + x-=q.x; + y-=q.y; + z-=q.z; + } + + /** Divide a quaternion by a constant + * @param n a float to divide by + */ + public void divide(float n) + { + x/=n; + y/=n; + z/=n; + } + + /** Multiply this quaternion by + * the param quaternion + * @param q a quaternion to multiply with + */ + public void mult(Quaternion q) + { + float w1 = w*q.w - (x*q.x + y*q.y + z*q.z); + + float x1 = w*q.z + q.w*z + y*q.z - z*q.y; + float y1 = w*q.x + q.w*x + z*q.x - x*q.z; + float z1 = w*q.y + q.w*y + x*q.y - y*q.x; + + w = w1; + x = x1; + y = y1; + z = z1; + } + + /** Multiply a quaternion by a constant + * @param n a float constant + */ + public void mult(float n) + { + x*=n; + y*=n; + z*=n; + } + + /** Normalize a quaternion required if + * to be used as a rotational quaternion + */ + public void normalize() + { + float norme = (float)MathFloat.sqrt(w*w + x*x + y*y + z*z); + if (norme == 0.0f) + { + w = 1.0f; + x = y = z = 0.0f; + } + else + { + float recip = 1.0f/norme; + + w *= recip; + x *= recip; + y *= recip; + z *= recip; + } + } + + /** Invert the quaternion If rotational, + * will produce a the inverse rotation + */ + public void inverse() + { + float norm = w*w + x*x + y*y + z*z; + + float recip = 1.0f/norm; + + w *= recip; + x = -1*x*recip; + y = -1*y*recip; + z = -1*z*recip; + } + + /** Transform this quaternion to a + * 4x4 column matrix representing the rotation + * @return new float[16] column matrix 4x4 + */ + public float[] toMatrix() + { + float[] matrix = new float[16]; + matrix[0] = 1.0f - 2*y*y - 2*z*z; + matrix[1] = 2*x*y + 2*w*z; + matrix[2] = 2*x*z - 2*w*y; + matrix[3] = 0; + + matrix[4] = 2*x*y - 2*w*z; + matrix[5] = 1.0f - 2*x*x - 2*z*z; + matrix[6] = 2*y*z + 2*w*x; + matrix[7] = 0; + + matrix[8] = 2*x*z + 2*w*y; + matrix[9] = 2*y*z - 2*w*x; + matrix[10] = 1.0f - 2*x*x - 2*y*y; + matrix[11] = 0; + + matrix[12] = 0; + matrix[13] = 0; + matrix[14] = 0; + matrix[15] = 1; + return matrix; + } + + /** Set this quaternion from a Sphereical interpolation + * of two param quaternion, used mostly for rotational animation + * @param a initial quaternion + * @param b target quaternion + * @param t float between 0 and 1 representing interp. + */ + public void slerp(Quaternion a,Quaternion b, float t) + { + float omega, cosom, sinom, sclp, sclq; + cosom = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; + if ((1.0f+cosom) > MathFloat.E) { + if ((1.0f-cosom) > MathFloat.E) { + omega = (float)MathFloat.acos(cosom); + sinom = (float)MathFloat.sin(omega); + sclp = (float)MathFloat.sin((1.0f-t)*omega) / sinom; + sclq = (float)MathFloat.sin(t*omega) / sinom; + } + else { + sclp = 1.0f - t; + sclq = t; + } + x = sclp*a.x + sclq*b.x; + y = sclp*a.y + sclq*b.y; + z = sclp*a.z + sclq*b.z; + w = sclp*a.w + sclq*b.w; + } + else { + x =-a.y; + y = a.x; + z =-a.w; + w = a.z; + sclp = MathFloat.sin((1.0f-t) * MathFloat.PI * 0.5f); + sclq = MathFloat.sin(t * MathFloat.PI * 0.5f); + x = sclp*a.x + sclq*b.x; + y = sclp*a.y + sclq*b.y; + z = sclp*a.z + sclq*b.z; + } + } + + /** Check if this quaternion is empty, ie (0,0,0,1) + * @return true if empty, false otherwise + */ + public boolean isEmpty() + { + if (w==1 && x==0 && y==0 && z==0) + return true; + return false; + } + + /** Check if this quaternion represents an identity + * matrix, for rotation. + * @return true if it is an identity rep., false otherwise + */ + public boolean isIdentity() + { + if (w==0 && x==0 && y==0 && z==0) + return true; + return false; + } + + /** compute the quaternion from a 3x3 column matrix + * @param m 3x3 column matrix + */ + public void setFromMatrix(float[] m) { + float T= m[0] + m[4] + m[8] + 1; + if (T>0){ + float S = 0.5f / (float)MathFloat.sqrt(T); + w = 0.25f / S; + x = ( m[5] - m[7]) * S; + y = ( m[6] - m[2]) * S; + z = ( m[1] - m[3] ) * S; + } + else{ + if ((m[0] > m[4])&(m[0] > m[8])) { + float S = MathFloat.sqrt( 1.0f + m[0] - m[4] - m[8] ) * 2f; // S=4*qx + w = (m[7] - m[5]) / S; + x = 0.25f * S; + y = (m[3] + m[1]) / S; + z = (m[6] + m[2]) / S; + } + else if (m[4] > m[8]) { + float S = MathFloat.sqrt( 1.0f + m[4] - m[0] - m[8] ) * 2f; // S=4*qy + w = (m[6] - m[2]) / S; + x = (m[3] + m[1]) / S; + y = 0.25f * S; + z = (m[7] + m[5]) / S; + } + else { + float S = MathFloat.sqrt( 1.0f + m[8] - m[0] - m[4] ) * 2f; // S=4*qz + w = (m[3] - m[1]) / S; + x = (m[6] + m[2]) / S; + y = (m[7] + m[5]) / S; + z = 0.25f * S; + } + } + } + + /** Check if the the 3x3 matrix (param) is in fact + * an affine rotational matrix + * @param m 3x3 column matrix + * @return true if representing a rotational matrix, false otherwise + */ + public boolean isRotationMatrix(float[] m) { + double epsilon = 0.01; // margin to allow for rounding errors + if (MathFloat.abs(m[0]*m[3] + m[3]*m[4] + m[6]*m[7]) > epsilon) return false; + if (MathFloat.abs(m[0]*m[2] + m[3]*m[5] + m[6]*m[8]) > epsilon) return false; + if (MathFloat.abs(m[1]*m[2] + m[4]*m[5] + m[7]*m[8]) > epsilon) return false; + if (MathFloat.abs(m[0]*m[0] + m[3]*m[3] + m[6]*m[6] - 1) > epsilon) return false; + if (MathFloat.abs(m[1]*m[1] + m[4]*m[4] + m[7]*m[7] - 1) > epsilon) return false; + if (MathFloat.abs(m[2]*m[2] + m[5]*m[5] + m[8]*m[8] - 1) > epsilon) return false; + return (MathFloat.abs(determinant(m)-1) < epsilon); + } + private float determinant(float[] m) { + return m[0]*m[4]*m[8] + m[3]*m[7]*m[2] + m[6]*m[1]*m[5] - m[0]*m[7]*m[5] - m[3]*m[1]*m[8] - m[6]*m[4]*m[2]; + } +} diff --git a/src/demo/GPURegionNewtDemo01.java b/src/demo/GPURegionNewtDemo01.java new file mode 100755 index 000000000..c728e74e5 --- /dev/null +++ b/src/demo/GPURegionNewtDemo01.java @@ -0,0 +1,230 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package demo; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLProfile; + +import com.jogamp.graph.curve.HwRegionRenderer; +import com.jogamp.graph.curve.OutlineShape; +import com.jogamp.graph.geom.opengl.Vertex; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.WindowAdapter; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.util.FPSAnimator; + +/** Demonstrate the rendering of multiple outlines into one region/OutlineShape + * These Outlines are not necessary connected or contained. + * The output of this demo shows two identical shapes but the left one + * has some vertices with off-curve flag set to true, and the right allt he vertices + * are on the curve. Demos the Res. Independent Nurbs based Curve rendering + * + */ +public class GPURegionNewtDemo01 { + private static void create(){ + new RegionNewtWindow(); + } + public static void main(String[] args) { + create(); + } +} + +class RegionNewtWindow { + RegionGLListener regionGLListener = null; + + public RegionNewtWindow(){ + createWindow(); + } + private void createWindow() { + GLProfile.initSingleton(true); + GLProfile glp = GLProfile.getGL2ES2(); + GLCapabilities caps = new GLCapabilities(glp); + caps.setDoubleBuffered(true); + caps.setSampleBuffers(true); + caps.setNumSamples(4); + System.out.println(caps); + final GLWindow window = GLWindow.create(caps); + window.setPosition(10, 10); + window.setSize(500, 500); + + window.setTitle("GPU Curve Region Newt Demo 01"); + regionGLListener = new RegionGLListener(); + window.addGLEventListener(regionGLListener); + + window.setVisible(true); + + window.addKeyListener(new KeyListener() { + public void keyPressed(KeyEvent arg0) { + if(arg0.getKeyCode() == KeyEvent.VK_1){ + regionGLListener.zoomIn(); + } + else if(arg0.getKeyCode() == KeyEvent.VK_2){ + regionGLListener.zoomOut(); + } + else if(arg0.getKeyCode() == KeyEvent.VK_UP){ + regionGLListener.move(0, -1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_DOWN){ + regionGLListener.move(0, 1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_LEFT){ + regionGLListener.move(1, 0); + } + else if(arg0.getKeyCode() == KeyEvent.VK_RIGHT){ + regionGLListener.move(-1, 0); + } + else if(arg0.getKeyCode() == KeyEvent.VK_0){ + regionGLListener.rotate(1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_9){ + regionGLListener.rotate(-1); + } + } + public void keyTyped(KeyEvent arg0) {} + public void keyReleased(KeyEvent arg0) {} + }); + + FPSAnimator animator = new FPSAnimator(60); + animator.add(window); + window.addWindowListener(new WindowAdapter() { + public void windowDestroyNotify(WindowEvent arg0) { + System.exit(0); + }; + }); + animator.start(); + } + private class RegionGLListener implements GLEventListener{ + HwRegionRenderer regionRenderer = null; + + OutlineShape outlineShape = null; + + public RegionGLListener() {} + + private void createTestOutline(){ + float offset = 0; + outlineShape = new OutlineShape(Vertex.factory()); + outlineShape.addVertex(0.0f,-10.0f, true); + outlineShape.addVertex(15.0f,-10.0f, true); + outlineShape.addVertex(10.0f,5.0f, false); + outlineShape.addVertex(15.0f,10.0f, true); + outlineShape.addVertex(6.0f,15.0f, false); + outlineShape.addVertex(5.0f,8.0f, false); + outlineShape.addVertex(0.0f,10.0f,true); + outlineShape.closeLastOutline(); + outlineShape.addEmptyOutline(); + outlineShape.addVertex(5.0f,-5.0f,true); + outlineShape.addVertex(10.0f,-5.0f, false); + outlineShape.addVertex(10.0f,0.0f, true); + outlineShape.addVertex(5.0f,0.0f, false); + outlineShape.closeLastOutline(); + + /** Same shape as above but without any off-curve vertices */ + outlineShape.addEmptyOutline(); + offset = 30; + outlineShape.addVertex(offset+0.0f,-10.0f, true); + outlineShape.addVertex(offset+17.0f,-10.0f, true); + outlineShape.addVertex(offset+11.0f,5.0f, true); + outlineShape.addVertex(offset+16.0f,10.0f, true); + outlineShape.addVertex(offset+7.0f,15.0f, true); + outlineShape.addVertex(offset+6.0f,8.0f, true); + outlineShape.addVertex(offset+0.0f,10.0f, true); + outlineShape.closeLastOutline(); + outlineShape.addEmptyOutline(); + outlineShape.addVertex(offset+5.0f,0.0f, true); + outlineShape.addVertex(offset+5.0f,-5.0f, true); + outlineShape.addVertex(offset+10.0f,-5.0f, true); + outlineShape.addVertex(offset+10.0f,0.0f, true); + outlineShape.closeLastOutline(); + } + + public void init(GLAutoDrawable drawable) { + GL2ES2 gl = drawable.getGL().getGL2ES2(); + gl.setSwapInterval(1); + gl.glEnable(GL2ES2.GL_DEPTH_TEST); + regionRenderer = new HwRegionRenderer(drawable.getContext()); + createTestOutline(); + } + + float ang = 0; + float zoom = -70; + float xTran = -20; + float yTran = 5; + + public void display(GLAutoDrawable drawable) { + GL2ES2 gl = drawable.getGL().getGL2ES2(); + + gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + + regionRenderer.resetMatrix(); + regionRenderer.translate(xTran, yTran, zoom); + regionRenderer.rotate(ang, 0, 1, 0); + + try { + regionRenderer.setAlpha(1.0f); + regionRenderer.setColor(0.0f, 0.0f, 1.0f); + + regionRenderer.renderOutlineShape(outlineShape, new float[]{0,0,0}); + } catch (Exception e) { + e.printStackTrace(); + } + } + public void reshape(GLAutoDrawable drawable, int xstart, int ystart, int width, int height){ + GL2ES2 gl = drawable.getGL().getGL2ES2(); + gl.glViewport(xstart, ystart, width, height); + + regionRenderer.reshape(drawable, 45.0f, (float)width / (float)height, 0.1f, 7000.0f); + } + + public void zoomIn(){ + zoom++; + } + public void zoomOut(){ + zoom--; + } + public void move(float x, float y){ + xTran += x; + yTran += y; + } + public void rotate(float delta){ + ang+= delta; + ang%=360; + } + public void dispose(GLAutoDrawable arg0) { + regionRenderer.clearCached(); + + } + } +} diff --git a/src/demo/GPURegionNewtDemo02.java b/src/demo/GPURegionNewtDemo02.java new file mode 100755 index 000000000..414b7942d --- /dev/null +++ b/src/demo/GPURegionNewtDemo02.java @@ -0,0 +1,227 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package demo; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLProfile; + +import com.jogamp.graph.curve.HwRegionRenderer; +import com.jogamp.graph.curve.OutlineShape; +import com.jogamp.graph.geom.opengl.Vertex; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.WindowAdapter; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.util.FPSAnimator; + +/** Demonstrate the rendering of multiple OutlineShapes + * into one region + * + */ +public class GPURegionNewtDemo02 { + private static void create(){ + new RegionsNewtWindow(); + } + public static void main(String[] args) { + create(); + } +} + +class RegionsNewtWindow { + RegionGLListener regionGLListener = null; + + public RegionsNewtWindow(){ + createWindow(); + } + private void createWindow() { + GLProfile.initSingleton(true); + GLProfile glp = GLProfile.getGL2ES2(); + GLCapabilities caps = new GLCapabilities(glp); + caps.setDoubleBuffered(true); + caps.setSampleBuffers(true); + caps.setNumSamples(4); + System.out.println(caps); + final GLWindow window = GLWindow.create(caps); + window.setPosition(10, 10); + window.setSize(500, 500); + + window.setTitle("GPU Curve Region Newt Demo 01"); + regionGLListener = new RegionGLListener(); + window.addGLEventListener(regionGLListener); + + window.setVisible(true); + + window.addKeyListener(new KeyListener() { + public void keyPressed(KeyEvent arg0) { + if(arg0.getKeyCode() == KeyEvent.VK_1){ + regionGLListener.zoomIn(); + } + else if(arg0.getKeyCode() == KeyEvent.VK_2){ + regionGLListener.zoomOut(); + } + else if(arg0.getKeyCode() == KeyEvent.VK_UP){ + regionGLListener.move(0, -1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_DOWN){ + regionGLListener.move(0, 1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_LEFT){ + regionGLListener.move(1, 0); + } + else if(arg0.getKeyCode() == KeyEvent.VK_RIGHT){ + regionGLListener.move(-1, 0); + } + else if(arg0.getKeyCode() == KeyEvent.VK_0){ + regionGLListener.rotate(1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_9){ + regionGLListener.rotate(-1); + } + } + public void keyTyped(KeyEvent arg0) {} + public void keyReleased(KeyEvent arg0) {} + }); + + FPSAnimator animator = new FPSAnimator(60); + animator.add(window); + window.addWindowListener(new WindowAdapter() { + public void windowDestroyNotify(WindowEvent arg0) { + System.exit(0); + }; + }); + animator.start(); + } + private class RegionGLListener implements GLEventListener{ + HwRegionRenderer regionRenderer = null; + + OutlineShape[] outlineShapes = new OutlineShape[2]; + + public RegionGLListener() {} + + private void createTestOutline(){ + float offset = 0; + outlineShapes[0] = new OutlineShape(Vertex.factory()); + outlineShapes[0].addVertex(0.0f,-10.0f,true); + outlineShapes[0].addVertex(15.0f,-10.0f, true); + outlineShapes[0].addVertex(10.0f,5.0f, false); + outlineShapes[0].addVertex(15.0f,10.0f, true); + outlineShapes[0].addVertex(6.0f,15.0f, false); + outlineShapes[0].addVertex(5.0f,8.0f, false); + outlineShapes[0].addVertex(0.0f,10.0f,true); + outlineShapes[0].closeLastOutline(); + outlineShapes[0].addEmptyOutline(); + outlineShapes[0].addVertex(5.0f,-5.0f,true); + outlineShapes[0].addVertex(10.0f,-5.0f, false); + outlineShapes[0].addVertex(10.0f,0.0f, true); + outlineShapes[0].addVertex(5.0f,0.0f, false); + outlineShapes[0].closeLastOutline(); + + /** Same shape as above but without any off-curve vertices */ + outlineShapes[1] = new OutlineShape(Vertex.factory()); + offset = 30; + outlineShapes[1].addVertex(offset+0.0f,-10.0f, true); + outlineShapes[1].addVertex(offset+17.0f,-10.0f, true); + outlineShapes[1].addVertex(offset+11.0f,5.0f, true); + outlineShapes[1].addVertex(offset+16.0f,10.0f, true); + outlineShapes[1].addVertex(offset+7.0f,15.0f, true); + outlineShapes[1].addVertex(offset+6.0f,8.0f, true); + outlineShapes[1].addVertex(offset+0.0f,10.0f, true); + outlineShapes[1].closeLastOutline(); + outlineShapes[1].addEmptyOutline(); + outlineShapes[1].addVertex(offset+5.0f,0.0f, true); + outlineShapes[1].addVertex(offset+5.0f,-5.0f, true); + outlineShapes[1].addVertex(offset+10.0f,-5.0f, true); + outlineShapes[1].addVertex(offset+10.0f,0.0f, true); + outlineShapes[1].closeLastOutline(); + } + + public void init(GLAutoDrawable drawable) { + GL2ES2 gl = drawable.getGL().getGL2ES2(); + gl.setSwapInterval(1); + gl.glEnable(GL2ES2.GL_DEPTH_TEST); + regionRenderer = new HwRegionRenderer(drawable.getContext()); + createTestOutline(); + } + + float ang = 0; + float zoom = -70; + float xTran = -20; + float yTran = 5; + + public void display(GLAutoDrawable drawable) { + GL2ES2 gl = drawable.getGL().getGL2ES2(); + + gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + + regionRenderer.resetMatrix(); + regionRenderer.translate(xTran, yTran, zoom); + regionRenderer.rotate(ang, 0, 1, 0); + + try { + regionRenderer.setAlpha(1.0f); + regionRenderer.setColor(0.0f, 0.0f, 1.0f); + + regionRenderer.renderOutlineShapes(outlineShapes, new float[]{0,0,0}); + } catch (Exception e) { + e.printStackTrace(); + } + } + public void reshape(GLAutoDrawable drawable, int xstart, int ystart, int width, int height){ + GL2ES2 gl = drawable.getGL().getGL2ES2(); + gl.glViewport(xstart, ystart, width, height); + + regionRenderer.reshape(drawable, 45.0f, (float)width / (float)height, 0.1f, 7000.0f); + } + + public void zoomIn(){ + zoom++; + } + public void zoomOut(){ + zoom--; + } + public void move(float x, float y){ + xTran += x; + yTran += y; + } + public void rotate(float delta){ + ang+= delta; + ang%=360; + } + public void dispose(GLAutoDrawable arg0) { + regionRenderer.clearCached(); + + } + } +} diff --git a/src/demo/GPUTextNewtDemo01.java b/src/demo/GPUTextNewtDemo01.java new file mode 100644 index 000000000..fcd2134ae --- /dev/null +++ b/src/demo/GPUTextNewtDemo01.java @@ -0,0 +1,198 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package demo; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLProfile; + +import com.jogamp.graph.curve.Region; +import com.jogamp.graph.curve.text.HwTextRenderer; +import com.jogamp.graph.font.Font; +import com.jogamp.graph.geom.Point; +import com.jogamp.graph.geom.opengl.Vertex; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.WindowAdapter; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.util.FPSAnimator; + +public class GPUTextNewtDemo01 { + private static void create(){ + new TextNewtWindow1(); + } + public static void main(String[] args) { + create(); + } +} + +class TextNewtWindow1 { + Point.Factory pointFactory = Vertex.factory(); + + TextGLListener textGLListener = null; + + public TextNewtWindow1(){ + createWindow(); + } + private void createWindow() { + GLProfile.initSingleton(true); + GLProfile glp = GLProfile.getGL2ES2(); + GLCapabilities caps = new GLCapabilities(glp); + caps.setAlphaBits(4); + caps.setSampleBuffers(true); // FIXME: sure ? + caps.setNumSamples(4); // FIXME: sure ? + System.out.println("Requested: "+caps); + + final GLWindow window = GLWindow.create(caps); + + window.setPosition(10, 10); + window.setSize(500, 500); + + window.setTitle("GPU Text Newt Demo 01"); + textGLListener = new TextGLListener(); + window.addGLEventListener(textGLListener); + + window.setVisible(true); + + window.addKeyListener(new KeyListener() { + public void keyPressed(KeyEvent arg0) { + if(arg0.getKeyCode() == KeyEvent.VK_1){ + textGLListener.zoomIn(); + } + else if(arg0.getKeyCode() == KeyEvent.VK_2){ + textGLListener.zoomOut(); + } + else if(arg0.getKeyCode() == KeyEvent.VK_UP){ + textGLListener.move(0, -1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_DOWN){ + textGLListener.move(0, 1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_LEFT){ + textGLListener.move(1, 0); + } + else if(arg0.getKeyCode() == KeyEvent.VK_RIGHT){ + textGLListener.move(-1, 0); + } + else if(arg0.getKeyCode() == KeyEvent.VK_0){ + textGLListener.rotate(1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_9){ + textGLListener.rotate(-1); + } + } + public void keyTyped(KeyEvent arg0) {} + public void keyReleased(KeyEvent arg0) {} + }); + + FPSAnimator animator = new FPSAnimator(60); + animator.add(window); + window.addWindowListener(new WindowAdapter() { + public void windowDestroyNotify(WindowEvent arg0) { + System.exit(0); + }; + }); + animator.start(); + } + private class TextGLListener implements GLEventListener{ + HwTextRenderer textRenderer = null; + + public TextGLListener(){ + + } + + public void init(GLAutoDrawable drawable) { + GL2ES2 gl = drawable.getGL().getGL2ES2(); + gl.setSwapInterval(1); + gl.glEnable(GL2ES2.GL_DEPTH_TEST); + textRenderer = new HwTextRenderer(drawable.getContext(), pointFactory, Region.SINGLE_PASS); + + System.out.println("Realised: "+drawable.getChosenGLCapabilities()); + System.out.println("MS: " + gl.glIsEnabled(GL2ES2.GL_MULTISAMPLE)); + } + + float ang = 0; + float zoom = -70; + float xTran = -10; + float yTran = 10; + + public void display(GLAutoDrawable drawable) { + GL2ES2 gl = drawable.getGL().getGL2ES2(); + + gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + + textRenderer.resetMatrix(); + textRenderer.translate(xTran, yTran, zoom); + textRenderer.rotate(ang, 0, 1, 0); + + String text1 = "abcdef\nghijklmn\nopqrstuv\nwxyz\n0123456789"; + String text2 = text1.toUpperCase(); + + Font font = textRenderer.createFont(pointFactory, "Lucida Sans Regular",40); + float[] position = new float[]{0,0,0}; + + try { + textRenderer.setAlpha(1.0f); + textRenderer.setColor(0.0f, 0.0f, 1.0f); + gl.glSampleCoverage(0.75f, false); + textRenderer.renderString3D(font, text2, position, 0); + } catch (Exception e) { + e.printStackTrace(); + } + } + public void reshape(GLAutoDrawable drawable, int xstart, int ystart, int width, int height){ + GL2ES2 gl = drawable.getGL().getGL2ES2(); + gl.glViewport(xstart, ystart, width, height); + + textRenderer.reshape(drawable, 45.0f, width, height, 0.1f, 7000.0f); + } + + public void zoomIn(){ + zoom++; + } + public void zoomOut(){ + zoom--; + } + public void move(float x, float y){ + xTran += x; + yTran += y; + } + public void rotate(float delta){ + ang+= delta; + ang%=360; + } + public void dispose(GLAutoDrawable arg0) { + textRenderer.clearCached(); + } + } +} diff --git a/src/demo/GPUTextNewtDemo02.java b/src/demo/GPUTextNewtDemo02.java new file mode 100644 index 000000000..b0c7115ec --- /dev/null +++ b/src/demo/GPUTextNewtDemo02.java @@ -0,0 +1,215 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package demo; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GL3; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLProfile; + +import com.jogamp.graph.curve.Region; +import com.jogamp.graph.curve.text.HwTextRenderer; +import com.jogamp.graph.font.Font; +import com.jogamp.graph.geom.Point; +import com.jogamp.graph.geom.opengl.Vertex; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.WindowAdapter; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.util.FPSAnimator; + +public class GPUTextNewtDemo02 { + private static void create(){ + new TextNewtWindow(); + } + public static void main(String[] args) { + create(); + } +} + +class TextNewtWindow { + Point.Factory pointFactory = Vertex.factory(); + TextGLListener textGLListener = null; + + public TextNewtWindow(){ + createWindow(); + } + private void createWindow() { + GLProfile.initSingleton(true); + GLProfile glp = GLProfile.get(GLProfile.GL3); + + GLCapabilities caps = new GLCapabilities(glp); + caps.setAlphaBits(4); + caps.setSampleBuffers(true); + caps.setNumSamples(4); + System.out.println("Requested: "+caps); + + final GLWindow window = GLWindow.create(caps); + + window.setPosition(10, 10); + window.setSize(1000, 1000); + + window.setTitle("GPU Text Newt Demo 02"); + textGLListener = new TextGLListener(); + window.addGLEventListener(textGLListener); + + window.setVisible(true); + + window.addKeyListener(new KeyListener() { + public void keyPressed(KeyEvent arg0) { + if(arg0.getKeyCode() == KeyEvent.VK_1){ + textGLListener.zoomIn(1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_2){ + textGLListener.zoomOut(1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_3){ + textGLListener.zoomIn(10); + } + else if(arg0.getKeyCode() == KeyEvent.VK_4){ + textGLListener.zoomOut(10); + } + else if(arg0.getKeyCode() == KeyEvent.VK_UP){ + textGLListener.move(0, -1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_DOWN){ + textGLListener.move(0, 1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_LEFT){ + textGLListener.move(1, 0); + } + else if(arg0.getKeyCode() == KeyEvent.VK_RIGHT){ + textGLListener.move(-1, 0); + } + else if(arg0.getKeyCode() == KeyEvent.VK_0){ + textGLListener.rotate(1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_9){ + textGLListener.rotate(-1); + } + else if(arg0.getKeyCode() == KeyEvent.VK_6){ + textGLListener.size -= 10; + } + else if(arg0.getKeyCode() == KeyEvent.VK_7){ + textGLListener.size += 10; + } + } + public void keyTyped(KeyEvent arg0) {} + public void keyReleased(KeyEvent arg0) {} + }); + + FPSAnimator animator = new FPSAnimator(60); + animator.add(window); + window.addWindowListener(new WindowAdapter() { + public void windowDestroyNotify(WindowEvent arg0) { + System.exit(0); + }; + }); + animator.start(); + } + private class TextGLListener implements GLEventListener{ + HwTextRenderer textRenderer = null; + + public TextGLListener(){ + + } + + public void init(GLAutoDrawable drawable) { + GL3 gl = drawable.getGL().getGL3(); + gl.setSwapInterval(1); + gl.glEnable(GL3.GL_DEPTH_TEST); + + textRenderer = new HwTextRenderer(drawable.getContext(), pointFactory, Region.TWO_PASS); + gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL3.GL_NICEST); + System.out.println("Realised: "+drawable.getChosenGLCapabilities()); + System.out.println("MS: " + gl.glIsEnabled(GL3.GL_MULTISAMPLE)); + } + + float ang = 0; + float zoom = -300; + float xTran = -100; + float yTran = 40; + int size = 200; + + public void display(GLAutoDrawable drawable) { + GL3 gl = drawable.getGL().getGL3(); + + gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + + textRenderer.resetMatrix(); + textRenderer.translate(xTran, yTran, zoom); + textRenderer.rotate(ang, 0, 1, 0); + + String text1 = "abcdef\nghijklmn\nopqrstuv\nwxyz\n0123456789"; + String text2 = text1.toUpperCase(); + + Font font = textRenderer.createFont(pointFactory, "Lucida Sans Regular",40); + float[] position = new float[]{0,0,0}; + + try { + textRenderer.setAlpha(1.0f); + textRenderer.setColor(0.0f, 0.0f, 0.0f); + gl.glSampleCoverage(0.75f, false); + textRenderer.renderString3D(font, text2, position, size); + } catch (Exception e) { + e.printStackTrace(); + } + } + public void reshape(GLAutoDrawable drawable, int xstart, int ystart, int width, int height){ + GL3 gl = drawable.getGL().getGL3(); + gl.glViewport(xstart, ystart, width, height); + + textRenderer.reshape(drawable, 45.0f, width , height, 0.1f, 7000.0f); + } + + public void zoomIn(float f){ + zoom+=f; + } + public void zoomOut(float f){ + zoom-=f; + System.err.println("Zoom: " + zoom); + } + public void move(float x, float y){ + xTran += x; + yTran += y; + } + public void rotate(float delta){ + ang+= delta; + ang%=360; + } + + public void dispose(GLAutoDrawable arg0) { + textRenderer.clearCached(); + } + } +} diff --git a/src/jogamp/graph/curve/opengl/VBORegion2PGL3.java b/src/jogamp/graph/curve/opengl/VBORegion2PGL3.java new file mode 100644 index 000000000..c5182059e --- /dev/null +++ b/src/jogamp/graph/curve/opengl/VBORegion2PGL3.java @@ -0,0 +1,363 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.opengl; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; + +import javax.media.opengl.GL3; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLUniformData; +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +import com.jogamp.common.nio.Buffers; + +import com.jogamp.graph.geom.AABBox; +import com.jogamp.graph.geom.Triangle; +import com.jogamp.graph.geom.PointTex; + +import com.jogamp.graph.curve.Region; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.glsl.ShaderState; + +public class VBORegion2PGL3 implements Region{ + private int numVertices = 0; + private IntBuffer vboIds; + + private IntBuffer t_vboIds; + + private ArrayList> triangles = new ArrayList>(); + private ArrayList vertices = new ArrayList(); + private GLContext context; + + private int numBuffers = 3; + + private boolean flipped = false; + + private boolean dirty = false; + + private AABBox box = null; + private IntBuffer texture = IntBuffer.allocate(1); + private IntBuffer fbo = IntBuffer.allocate(1); + private IntBuffer rbo = IntBuffer.allocate(1); + private boolean texInitialized = false; + + private int tex_width_c = 0; + private int tex_height_c = 0; + + private ShaderState st; + + public VBORegion2PGL3(GLContext context, ShaderState st){ + this.context =context; + this.st = st; + } + + public void update(){ + box = new AABBox(); + + GL3 gl = context.getGL().getGL3(); + ShortBuffer indicies = Buffers.newDirectShortBuffer(triangles.size() * 3); + + for(Triangle t:triangles){ + if(t.getVertices()[0].getId() == Integer.MAX_VALUE){ + t.getVertices()[0].setId(numVertices++); + t.getVertices()[1].setId(numVertices++); + t.getVertices()[2].setId(numVertices++); + + vertices.add(t.getVertices()[0]); + vertices.add(t.getVertices()[1]); + vertices.add(t.getVertices()[2]); + + indicies.put((short) t.getVertices()[0].getId()); + indicies.put((short) t.getVertices()[1].getId()); + indicies.put((short) t.getVertices()[2].getId()); + } + else{ + PointTex v1 = t.getVertices()[0]; + PointTex v2 = t.getVertices()[1]; + PointTex v3 = t.getVertices()[2]; + + indicies.put((short) v1.getId()); + indicies.put((short) v2.getId()); + indicies.put((short) v3.getId()); + } + } + indicies.rewind(); + + FloatBuffer verticesBuffer = Buffers.newDirectFloatBuffer(vertices.size() * 3); + for(PointTex v:vertices){ + verticesBuffer.put(v.getX()); + if(flipped){ + verticesBuffer.put(-1*v.getY()); + } + else{ + verticesBuffer.put(v.getY()); + } + verticesBuffer.put(v.getZ()); + + box.resize(v.getX(), -1*v.getY(), v.getZ()); + } + verticesBuffer.rewind(); + + FloatBuffer texCoordBuffer = Buffers.newDirectFloatBuffer(vertices.size() * 2); + for(PointTex v:vertices){ + float[] tex = v.getTexCoord(); + texCoordBuffer.put(tex[0]); + texCoordBuffer.put(tex[1]); + } + texCoordBuffer.rewind(); + + vboIds = IntBuffer.allocate(numBuffers); + gl.glGenBuffers(numBuffers, vboIds); + + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, vboIds.get(0)); // vertices + gl.glBufferData(GL3.GL_ARRAY_BUFFER, numVertices * 3 * Buffers.SIZEOF_FLOAT, verticesBuffer, GL3.GL_STATIC_DRAW); + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, 0); + + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, vboIds.get(1)); //texture + gl.glBufferData(GL3.GL_ARRAY_BUFFER, numVertices * 2 * Buffers.SIZEOF_FLOAT, texCoordBuffer, GL3.GL_STATIC_DRAW); + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, 0); + + gl.glBindBuffer(GL3.GL_ELEMENT_ARRAY_BUFFER, vboIds.get(2)); //triangles + gl.glBufferData(GL3.GL_ELEMENT_ARRAY_BUFFER, triangles.size()* 3 * Buffers.SIZEOF_SHORT, indicies, GL3.GL_STATIC_DRAW); + gl.glBindBuffer(GL3.GL_ELEMENT_ARRAY_BUFFER, 0); + + dirty = false; + } + + public void render(PMVMatrix matrix, int vp_width, int vp_height, int width){ + if(null == matrix || vp_width <=0 || vp_height <= 0 || width <= 0){ + renderRegion(); + } + else { + if(width != tex_width_c){ + texInitialized = false; + tex_width_c = width; + } + if(!texInitialized){ + initFBOTexture(matrix,vp_width, vp_height); + texInitialized = true; + } +// System.out.println("Scale: " + matrix.glGetMatrixf().get(1+4*3) +" " + matrix.glGetMatrixf().get(2+4*3)); + renderTexture(matrix, vp_width, vp_height); + } + } + + private void renderTexture(PMVMatrix matrix, int width, int hight){ + GL3 gl = context.getGL().getGL3(); + gl.glViewport(0, 0, width, hight); + if(!st.glUniform(gl, new GLUniformData("mgl_PMVMatrix", 4, 4, matrix.glGetPMvMatrixf()))){ + System.out.println("Cnt set tex based mat"); + } + gl.glEnable(GL3.GL_TEXTURE_2D); + gl.glActiveTexture(GL3.GL_TEXTURE0); + gl.glBindTexture(GL3.GL_TEXTURE_2D, texture.get(0)); + + st.glUniform(gl, new GLUniformData("texture", texture.get(0))); + int loc = gl.glGetUniformLocation(st.shaderProgram().id(), "texture"); + gl.glUniform1i(loc, 0); + + + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, t_vboIds.get(0)); + gl.glEnableVertexAttribArray(VERTEX_POS_INDX); + gl.glVertexAttribPointer(VERTEX_POS_INDX, 3, GL3.GL_FLOAT, false, 3 * Buffers.SIZEOF_FLOAT, 0); + + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, t_vboIds.get(1)); + gl.glEnableVertexAttribArray(TEX_COORD); + gl.glVertexAttribPointer(TEX_COORD, 2, GL3.GL_FLOAT, false, 2 * Buffers.SIZEOF_FLOAT, 0); + + gl.glBindBuffer(GL3.GL_ELEMENT_ARRAY_BUFFER, t_vboIds.get(2)); + gl.glDrawElements(GL3.GL_TRIANGLES, 2 * 3, GL3.GL_UNSIGNED_SHORT, 0); + + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, 0); + } + + private void setupBoundingBuffers(){ + GL3 gl = context.getGL().getGL3(); + ShortBuffer indicies = Buffers.newDirectShortBuffer(6); + indicies.put((short) 0); indicies.put((short) 1); indicies.put((short) 3); + indicies.put((short) 1); indicies.put((short) 2); indicies.put((short) 3); + indicies.rewind(); + + FloatBuffer verticesBuffer = Buffers.newDirectFloatBuffer(4 * 3); + FloatBuffer texCoordBuffer = Buffers.newDirectFloatBuffer(4 * 2); + + verticesBuffer.put(box.getLow()[0]); + verticesBuffer.put(box.getLow()[1]); + verticesBuffer.put(box.getLow()[2]); + texCoordBuffer.put(5); + texCoordBuffer.put(5); + + verticesBuffer.put(box.getLow()[0]); + verticesBuffer.put(box.getHigh()[1]); + verticesBuffer.put(box.getLow()[2]); + + texCoordBuffer.put(5); + texCoordBuffer.put(6); + + verticesBuffer.put(box.getHigh()[0]); + verticesBuffer.put(box.getHigh()[1]); + verticesBuffer.put(box.getLow()[2]); + + texCoordBuffer.put(6); + texCoordBuffer.put(6); + + verticesBuffer.put(box.getHigh()[0]); + verticesBuffer.put(box.getLow()[1]); + verticesBuffer.put(box.getLow()[2]); + + texCoordBuffer.put(6); + texCoordBuffer.put(5); + + verticesBuffer.rewind(); + texCoordBuffer.rewind(); + + t_vboIds = IntBuffer.allocate(3); + gl.glGenBuffers(numBuffers, t_vboIds); + + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, t_vboIds.get(0)); // vertices + gl.glBufferData(GL3.GL_ARRAY_BUFFER, 4 * 3 * Buffers.SIZEOF_FLOAT, verticesBuffer, GL3.GL_STATIC_DRAW); + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, 0); + + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, t_vboIds.get(1)); //texture + gl.glBufferData(GL3.GL_ARRAY_BUFFER, 4 * 2 * Buffers.SIZEOF_FLOAT, texCoordBuffer, GL3.GL_STATIC_DRAW); + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, 0); + + gl.glBindBuffer(GL3.GL_ELEMENT_ARRAY_BUFFER, t_vboIds.get(2)); //triangles + gl.glBufferData(GL3.GL_ELEMENT_ARRAY_BUFFER, 4 * 3 * Buffers.SIZEOF_SHORT, indicies, GL3.GL_STATIC_DRAW); + gl.glBindBuffer(GL3.GL_ELEMENT_ARRAY_BUFFER, 0); + } + + private void initFBOTexture(PMVMatrix m, int width, int hight){ + tex_height_c = (int)(tex_width_c*box.getHeight()/box.getWidth()); + System.out.println("Scale: " + m.glGetMatrixf().get(0) +" " + m.glGetMatrixf().get(5)); + GL3 gl = context.getGL().getGL3(); + + gl.glDeleteFramebuffers(1, fbo); + gl.glDeleteTextures(1, texture); + + gl.glGenTextures(1, texture); + gl.glBindTexture(GL3.GL_TEXTURE_2D, texture.get(0)); + gl.glTexImage2D(GL3.GL_TEXTURE_2D, 0, GL3.GL_RGBA, tex_width_c, + tex_height_c, 0, GL3.GL_RGBA, GL3.GL_UNSIGNED_BYTE, null); + + gl.glTexParameterf(GL3.GL_TEXTURE_2D, GL3.GL_TEXTURE_MIN_FILTER, GL3.GL_LINEAR); + gl.glTexParameterf(GL3.GL_TEXTURE_2D, GL3.GL_TEXTURE_MAG_FILTER, GL3.GL_LINEAR); + gl.glTexParameterf(GL3.GL_TEXTURE_2D, GL3.GL_TEXTURE_WRAP_S, GL3.GL_CLAMP_TO_EDGE); + gl.glTexParameterf(GL3.GL_TEXTURE_2D, GL3.GL_TEXTURE_WRAP_T, GL3.GL_CLAMP_TO_EDGE); + + gl.glGenRenderbuffers(1,rbo); + gl.glBindRenderbuffer(GL3.GL_RENDERBUFFER, rbo.get(0)); + gl.glRenderbufferStorage(GL3.GL_RENDERBUFFER, GL3.GL_DEPTH_COMPONENT, tex_width_c, tex_height_c); + + gl.glGenFramebuffers(1, fbo); + gl.glBindFramebuffer(GL3.GL_DRAW_FRAMEBUFFER, fbo.get(0)); + gl.glFramebufferTexture2D(GL3.GL_DRAW_FRAMEBUFFER, GL3.GL_COLOR_ATTACHMENT0, + GL3.GL_TEXTURE_2D, texture.get(0), 0); + gl.glFramebufferRenderbuffer(GL3.GL_FRAMEBUFFER, GL3.GL_DEPTH_COMPONENT, GL3.GL_RENDERBUFFER, rbo.get(0)); + + int status = gl.glCheckFramebufferStatus(GL3.GL_FRAMEBUFFER); + if(status != GL3.GL_FRAMEBUFFER_COMPLETE){ + System.out.println("FRAAAAAAAAAAAAAAME"); + } + + //render texture + PMVMatrix tex_matrix = new PMVMatrix(); + gl.glBindFramebuffer(GL3.GL_DRAW_FRAMEBUFFER, fbo.get(0)); + gl.glViewport(0, 0, tex_width_c, tex_height_c); + tex_matrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + tex_matrix.glLoadIdentity(); + tex_matrix.glOrthof(box.getLow()[0], box.getHigh()[0], box.getLow()[1], box.getHigh()[1], -1, 1); + + if(!st.glUniform(gl, new GLUniformData("mgl_PMVMatrix", 4, 4, tex_matrix.glGetPMvMatrixf()))){ + System.out.println("Cnt set tex based mat"); + } + + gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + gl.glClear(GL3.GL_COLOR_BUFFER_BIT | GL3.GL_DEPTH_BUFFER_BIT); + renderRegion(); + + gl.glBindFramebuffer(GL3.GL_FRAMEBUFFER, 0); + gl.glBindTexture(GL3.GL_TEXTURE_2D, 0); + + setupBoundingBuffers(); + } + + private void renderRegion(){ + GL3 gl = context.getGL().getGL3(); + + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, vboIds.get(0)); + gl.glEnableVertexAttribArray(VERTEX_POS_INDX); + gl.glVertexAttribPointer(VERTEX_POS_INDX, 3, GL3.GL_FLOAT, false, 3 * Buffers.SIZEOF_FLOAT, 0); + + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, vboIds.get(1)); + gl.glEnableVertexAttribArray(TEX_COORD); + gl.glVertexAttribPointer(TEX_COORD, 2, GL3.GL_FLOAT, false, 2 * Buffers.SIZEOF_FLOAT, 0); + + gl.glBindBuffer(GL3.GL_ELEMENT_ARRAY_BUFFER, vboIds.get(2)); + gl.glDrawElements(GL3.GL_TRIANGLES, triangles.size() * 3, GL3.GL_UNSIGNED_SHORT, 0); + + gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, 0); + } + + public void addTriangles(ArrayList> tris) { + triangles.addAll(tris); + dirty = true; + } + + public int getNumVertices(){ + return numVertices; + } + + public void addVertices(ArrayList verts){ + vertices.addAll(verts); + numVertices = vertices.size(); + dirty = true; + } + + public boolean isDirty(){ + return dirty; + } + + public void destroy() { + GL3 gl = context.getGL().getGL3(); + gl.glDeleteBuffers(numBuffers, vboIds); + gl.glDeleteFramebuffers(1, fbo); + gl.glDeleteTextures(1, texture); + } + + public boolean isFlipped() { + return flipped; + } + + public void setFlipped(boolean flipped) { + this.flipped = flipped; + } +} diff --git a/src/jogamp/graph/curve/opengl/VBORegionSPES2.java b/src/jogamp/graph/curve/opengl/VBORegionSPES2.java new file mode 100644 index 000000000..5ef23d4e0 --- /dev/null +++ b/src/jogamp/graph/curve/opengl/VBORegionSPES2.java @@ -0,0 +1,185 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.opengl; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; + +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLContext; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.graph.curve.Region; +import com.jogamp.graph.geom.PointTex; +import com.jogamp.graph.geom.Triangle; +import com.jogamp.opengl.util.PMVMatrix; + +public class VBORegionSPES2 implements Region{ + private int numVertices = 0; + private IntBuffer vboIds; + + private ArrayList> triangles = new ArrayList>(); + private ArrayList vertices = new ArrayList(); + + private GLContext context; + + private int numBuffers = 3; + + private boolean flipped = false; + private boolean dirty = false; + + public VBORegionSPES2(GLContext context){ + this.context =context; + } + + public void update(){ + GL2ES2 gl = context.getGL().getGL2ES2(); + ShortBuffer indicies = Buffers.newDirectShortBuffer(triangles.size() * 3); + + for(Triangle t:triangles){ + final PointTex[] t_vertices = t.getVertices(); + + if(t_vertices[0].getId() == Integer.MAX_VALUE){ + t_vertices[0].setId(numVertices++); + t_vertices[1].setId(numVertices++); + t_vertices[2].setId(numVertices++); + + vertices.add(t.getVertices()[0]); + vertices.add(t.getVertices()[1]); + vertices.add(t.getVertices()[2]); + + indicies.put((short) t.getVertices()[0].getId()); + indicies.put((short) t.getVertices()[1].getId()); + indicies.put((short) t.getVertices()[2].getId()); + } + else{ + PointTex v1 = t_vertices[0]; + PointTex v2 = t_vertices[1]; + PointTex v3 = t_vertices[2]; + + indicies.put((short) v1.getId()); + indicies.put((short) v2.getId()); + indicies.put((short) v3.getId()); + } + } + indicies.rewind(); + + FloatBuffer verticesBuffer = Buffers.newDirectFloatBuffer(vertices.size() * 3); + for(PointTex v:vertices){ + verticesBuffer.put(v.getX()); + if(flipped){ + verticesBuffer.put(-1*v.getY()); + } + else{ + verticesBuffer.put(v.getY()); + } + verticesBuffer.put(v.getZ()); + } + verticesBuffer.rewind(); + + FloatBuffer texCoordBuffer = Buffers.newDirectFloatBuffer(vertices.size() * 2); + for(PointTex v:vertices){ + float[] tex = v.getTexCoord(); + texCoordBuffer.put(tex[0]); + texCoordBuffer.put(tex[1]); + } + texCoordBuffer.rewind(); + + vboIds = IntBuffer.allocate(numBuffers); + gl.glGenBuffers(numBuffers, vboIds); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, vboIds.get(0)); // vertices + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, numVertices * 3 * Buffers.SIZEOF_FLOAT, verticesBuffer, GL2ES2.GL_STATIC_DRAW); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, 0); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, vboIds.get(1)); //texture + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, numVertices * 2 * Buffers.SIZEOF_FLOAT, texCoordBuffer, GL2ES2.GL_STATIC_DRAW); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, 0); + + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, vboIds.get(2)); //triangles + gl.glBufferData(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, triangles.size()* 3 * Buffers.SIZEOF_SHORT, indicies, GL2ES2.GL_STATIC_DRAW); + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, 0); + + dirty = false; + } + + private void render() { + GL2ES2 gl = context.getGL().getGL2ES2(); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, vboIds.get(0)); + gl.glEnableVertexAttribArray(VERTEX_POS_INDX); + gl.glVertexAttribPointer(VERTEX_POS_INDX, 3, GL2ES2.GL_FLOAT, false, 3 * Buffers.SIZEOF_FLOAT, 0); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, vboIds.get(1)); + gl.glEnableVertexAttribArray(TEX_COORD); + gl.glVertexAttribPointer(TEX_COORD, 2, GL2ES2.GL_FLOAT, false, 2 * Buffers.SIZEOF_FLOAT, 0); + + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, vboIds.get(2)); + gl.glDrawElements(GL2ES2.GL_TRIANGLES, triangles.size() * 3, GL2ES2.GL_UNSIGNED_SHORT, 0); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, 0); + } + + public void render(PMVMatrix matrix, int vp_width, int vp_height, int width){ + render(); + } + + public void addTriangles(ArrayList> tris) { + triangles.addAll(tris); + dirty = true; + } + + public int getNumVertices(){ + return numVertices; + } + + public void addVertices(ArrayList verts){ + vertices.addAll(verts); + numVertices = vertices.size(); + dirty = true; + } + + public boolean isDirty(){ + return dirty; + } + + public void destroy() { + GL2ES2 gl = context.getGL().getGL2ES2(); + gl.glDeleteBuffers(numBuffers, vboIds); + } + + public boolean isFlipped() { + return flipped; + } + + public void setFlipped(boolean flipped) { + this.flipped = flipped; + } +} diff --git a/src/jogamp/graph/curve/tess/GraphOutline.java b/src/jogamp/graph/curve/tess/GraphOutline.java new file mode 100644 index 000000000..cf73ab379 --- /dev/null +++ b/src/jogamp/graph/curve/tess/GraphOutline.java @@ -0,0 +1,81 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.tess; + +import java.util.ArrayList; + +import com.jogamp.graph.geom.Outline; +import com.jogamp.graph.geom.PointTex; + +public class GraphOutline { + final private Outline outline; + final private ArrayList> controlpoints = new ArrayList>(3); + + public GraphOutline(){ + this.outline = new Outline(); + } + + /**Create a control polyline of control vertices + * the curve pieces can be identified by onCurve flag + * of each cp the control polyline is open by default + */ + public GraphOutline(Outline ol){ + this.outline = ol; + ArrayList vertices = this.outline.getVertices(); + for(T v:vertices){ + this.controlpoints.add(new GraphPoint(v)); + } + } + + public Outline getOutline() { + return outline; + } + + /*public void setOutline(Outline outline) { + this.outline = outline; + }*/ + + + public ArrayList> getGraphPoint() { + return controlpoints; + } + + public ArrayList getPoints() { + return outline.getVertices(); + } + + /*public void setControlpoints(ArrayList> controlpoints) { + this.controlpoints = controlpoints; + }*/ + + public void addVertex(GraphPoint v) { + controlpoints.add(v); + outline.addVertex(v.getPoint()); + } + +} diff --git a/src/jogamp/graph/curve/tess/GraphPoint.java b/src/jogamp/graph/curve/tess/GraphPoint.java new file mode 100644 index 000000000..87d0b9929 --- /dev/null +++ b/src/jogamp/graph/curve/tess/GraphPoint.java @@ -0,0 +1,120 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.tess; + +import java.util.ArrayList; + +import com.jogamp.graph.geom.PointTex; + +public class GraphPoint { + private T point; + private ArrayList> edges = null; + private boolean boundaryContained = false; + + public GraphPoint(T point) { + this.point = point; + } + + public T getPoint() { + return point; + } + + public float getX(){ + return point.getX(); + } + + public float getY(){ + return point.getY(); + } + + public float getZ(){ + return point.getZ(); + } + public float[] getCoord() { + return point.getCoord(); + } + + public void setPoint(T point) { + this.point = point; + } + + public ArrayList> getEdges() { + return edges; + } + + public void setEdges(ArrayList> edges) { + this.edges = edges; + } + + public void addEdge(HEdge edge){ + if(edges == null){ + edges = new ArrayList>(); + } + edges.add(edge); + } + public void removeEdge(HEdge edge){ + if(edges == null) + return; + edges.remove(edge); + if(edges.size() == 0){ + edges = null; + } + } + public HEdge findNextEdge(GraphPoint nextVert){ + for(HEdge e:edges){ + if(e.getNext().getGraphPoint() == nextVert){ + return e; + } + } + return null; + } + public HEdge findBoundEdge(){ + for(HEdge e:edges){ + if((e.getType() == HEdge.BOUNDARY) || (e.getType() == HEdge.HOLE)){ + return e; + } + } + return null; + } + public HEdge findPrevEdge(GraphPoint prevVert){ + for(HEdge e:edges){ + if(e.getPrev().getGraphPoint() == prevVert){ + return e; + } + } + return null; + } + + public boolean isBoundaryContained() { + return boundaryContained; + } + + public void setBoundaryContained(boolean boundaryContained) { + this.boundaryContained = boundaryContained; + } +} diff --git a/src/jogamp/graph/curve/tess/HEdge.java b/src/jogamp/graph/curve/tess/HEdge.java new file mode 100644 index 000000000..acb32cb4d --- /dev/null +++ b/src/jogamp/graph/curve/tess/HEdge.java @@ -0,0 +1,131 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.tess; + +import com.jogamp.graph.geom.PointTex; +import com.jogamp.graph.geom.Triangle; + + +public class HEdge { + public static int BOUNDARY = 3; + public static int INNER = 1; + public static int HOLE = 2; + + private GraphPoint vert; + private HEdge prev = null; + private HEdge next = null; + private HEdge sibling = null; + private int type = BOUNDARY; + private Triangle triangle = null; + + public HEdge(GraphPoint vert, int type) { + this.vert = vert; + this.type = type; + } + + public HEdge(GraphPoint vert, HEdge prev, HEdge next, HEdge sibling, int type) { + this.vert = vert; + this.prev = prev; + this.next = next; + this.sibling = sibling; + this.type = type; + } + + public HEdge(GraphPoint vert, HEdge prev, HEdge next, HEdge sibling, int type, + Triangle triangle) { + this.vert = vert; + this.prev = prev; + this.next = next; + this.sibling = sibling; + this.type = type; + this.triangle = triangle; + } + + public GraphPoint getGraphPoint() { + return vert; + } + + public void setVert(GraphPoint vert) { + this.vert = vert; + } + + public HEdge getPrev() { + return prev; + } + + public void setPrev(HEdge prev) { + this.prev = prev; + } + + public HEdge getNext() { + return next; + } + + public void setNext(HEdge next) { + this.next = next; + } + + public HEdge getSibling() { + return sibling; + } + + public void setSibling(HEdge sibling) { + this.sibling = sibling; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public Triangle getTriangle() { + return triangle; + } + + public void setTriangle(Triangle triangle) { + this.triangle = triangle; + } + + public static void connect(HEdge first, HEdge next){ + first.setNext(next); + next.setPrev(first); + } + + public static void makeSiblings(HEdge first, HEdge second){ + first.setSibling(second); + second.setSibling(first); + } + + public boolean vertexOnCurveVertex(){ + return vert.getPoint().isOnCurve(); + } + +} diff --git a/src/jogamp/graph/curve/tess/Loop.java b/src/jogamp/graph/curve/tess/Loop.java new file mode 100644 index 000000000..caebd64e4 --- /dev/null +++ b/src/jogamp/graph/curve/tess/Loop.java @@ -0,0 +1,376 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.tess; + +import java.util.ArrayList; + +import jogamp.graph.math.VectorFloatUtil; + +import com.jogamp.graph.geom.AABBox; +import com.jogamp.graph.geom.PointTex; +import com.jogamp.graph.geom.Triangle; + +public class Loop { + private HEdge root = null; + private AABBox box = new AABBox(); + private GraphOutline initialOutline = null; + + public Loop(GraphOutline polyline, int direction){ + initialOutline = polyline; + this.root = initFromPolyline(initialOutline, direction); + } + + public HEdge getHEdge(){ + return root; + } + + public Triangle cut(boolean delaunay){ + if(isSimplex()){ + @SuppressWarnings("unchecked") + Triangle t = new Triangle(root.getGraphPoint().getPoint(), root.getNext().getGraphPoint().getPoint(), + root.getNext().getNext().getGraphPoint().getPoint()); + t.setVerticesBoundary(checkVerticesBoundary(root)); + return t; + } + HEdge prev = root.getPrev(); + HEdge next1 = root.getNext(); + + HEdge next2 =findClosestValidNeighbor(next1.getNext(), delaunay); + if(next2 == null){ + root = root.getNext(); + return null; + } + + GraphPoint v1 = root.getGraphPoint(); + GraphPoint v2 = next1.getGraphPoint(); + GraphPoint v3 = next2.getGraphPoint(); + + HEdge v3Edge = new HEdge(v3, HEdge.INNER); + + HEdge.connect(v3Edge, root); + HEdge.connect(next1, v3Edge); + + HEdge v3EdgeSib = v3Edge.getSibling(); + if(v3EdgeSib == null){ + v3EdgeSib = new HEdge(v3Edge.getNext().getGraphPoint(), HEdge.INNER); + HEdge.makeSiblings(v3Edge, v3EdgeSib); + } + + HEdge.connect(prev, v3EdgeSib); + HEdge.connect(v3EdgeSib, next2); + + Triangle t = createTriangle(v1.getPoint(), v2.getPoint(), v3.getPoint(), root); + this.root = next2; + return t; + } + + public boolean isSimplex(){ + return (root.getNext().getNext().getNext() == root); + } + + /**Create a connected list of half edges (loop) + * from the boundary profile + * @param direction requested winding of edges (CCW or CW) + */ + private HEdge initFromPolyline(GraphOutline outline, int direction){ + ArrayList> vertices = outline.getGraphPoint(); + + if(vertices.size()<3) { + throw new IllegalArgumentException("outline's vertices < 3: " + vertices.size()); + } + boolean isCCW = VectorFloatUtil.ccw(vertices.get(0).getPoint(), vertices.get(1).getPoint(), + vertices.get(2).getPoint()); + boolean invert = isCCW && (direction == VectorFloatUtil.CW); + + HEdge firstEdge = null; + HEdge lastEdge = null; + int index =0; + int max = vertices.size(); + + int edgeType = HEdge.BOUNDARY; + if(invert){ + index = vertices.size() -1; + max = -1; + edgeType = HEdge.HOLE; + } + + while(index != max){ + GraphPoint v1 = vertices.get(index); + box.resize(v1.getX(), v1.getY(), v1.getZ()); + + HEdge edge = new HEdge(v1, edgeType); + + v1.addEdge(edge); + if(lastEdge != null){ + lastEdge.setNext(edge); + edge.setPrev(lastEdge); + } + else{ + firstEdge = edge; + } + + if(!invert){ + if(index == vertices.size()-1){ + edge.setNext(firstEdge); + firstEdge.setPrev(edge); + } + } + else if (index == 0){ + edge.setNext(firstEdge); + firstEdge.setPrev(edge); + } + + lastEdge = edge; + + if(!invert){ + index++; + } + else{ + index--; + } + } + return firstEdge; + } + + public void addConstraintCurve(GraphOutline polyline) { + // GraphOutline outline = new GraphOutline(polyline); + /**needed to generate vertex references.*/ + initFromPolyline(polyline, VectorFloatUtil.CW); + + GraphPoint v3 = locateClosestVertex(polyline); + HEdge v3Edge = v3.findBoundEdge(); + HEdge v3EdgeP = v3Edge.getPrev(); + HEdge crossEdge = new HEdge(root.getGraphPoint(), HEdge.INNER); + + HEdge.connect(root.getPrev(), crossEdge); + HEdge.connect(crossEdge, v3Edge); + + HEdge crossEdgeSib = crossEdge.getSibling(); + if(crossEdgeSib == null) { + crossEdgeSib = new HEdge(crossEdge.getNext().getGraphPoint(), HEdge.INNER); + HEdge.makeSiblings(crossEdge, crossEdgeSib); + } + + HEdge.connect(v3EdgeP, crossEdgeSib); + HEdge.connect(crossEdgeSib, root); + } + + /** Locates the vertex and update the loops root + * to have (root + vertex) as closest pair + * @param polyline the control polyline + * to search for closestvertices + * @return the vertex that is closest to the newly set root Hedge. + */ + private GraphPoint locateClosestVertex(GraphOutline polyline) { + HEdge closestE = null; + GraphPoint closestV = null; + + float minDistance = Float.MAX_VALUE; + boolean inValid = false; + ArrayList> initVertices = initialOutline.getGraphPoint(); + ArrayList> vertices = polyline.getGraphPoint(); + + for(int i=0; i< initVertices.size()-1; i++){ + GraphPoint v = initVertices.get(i); + GraphPoint nextV = initVertices.get(i+1); + for(GraphPoint cand:vertices){ + float distance = VectorFloatUtil.computeLength(v.getCoord(), cand.getCoord()); + if(distance < minDistance){ + for (GraphPoint vert:vertices){ + if(vert == v || vert == nextV || vert == cand) + continue; + inValid = VectorFloatUtil.inCircle(v.getPoint(), nextV.getPoint(), + cand.getPoint(), vert.getPoint()); + if(inValid){ + break; + } + } + if(!inValid){ + closestV = cand; + minDistance = distance; + closestE = v.findBoundEdge(); + } + } + + } + } + + if(closestE != null){ + root = closestE; + } + + return closestV; + } + + private HEdge findClosestValidNeighbor(HEdge edge, boolean delaunay) { + HEdge next = root.getNext(); + + if(!VectorFloatUtil.ccw(root.getGraphPoint().getPoint(), next.getGraphPoint().getPoint(), + edge.getGraphPoint().getPoint())){ + return null; + } + + HEdge candEdge = edge; + boolean inValid = false; + + if(delaunay){ + T cand = candEdge.getGraphPoint().getPoint(); + HEdge e = candEdge.getNext(); + while (e != candEdge){ + if(e.getGraphPoint() == root.getGraphPoint() + || e.getGraphPoint() == next.getGraphPoint() + || e.getGraphPoint().getPoint() == cand){ + e = e.getNext(); + continue; + } + inValid = VectorFloatUtil.inCircle(root.getGraphPoint().getPoint(), next.getGraphPoint().getPoint(), + cand, e.getGraphPoint().getPoint()); + if(inValid){ + break; + } + e = e.getNext(); + } + } + if(!inValid){ + return candEdge; + } + return null; + } + + /** Create a triangle from the param vertices only if + * the triangle is valid. IE not outside region. + * @param v1 vertex 1 + * @param v2 vertex 2 + * @param v3 vertex 3 + * @param root and edge of this triangle + * @return the triangle iff it satisfies, null otherwise + */ + private Triangle createTriangle(T v1, T v2, T v3, HEdge rootT){ + @SuppressWarnings("unchecked") + Triangle t = new Triangle(v1, v2, v3); + t.setVerticesBoundary(checkVerticesBoundary(rootT)); + return t; + } + + private boolean[] checkVerticesBoundary(HEdge rootT) { + boolean[] boundary = new boolean[3]; + HEdge e1 = rootT; + HEdge e2 = rootT.getNext(); + HEdge e3 = rootT.getNext().getNext(); + + if(e1.getGraphPoint().isBoundaryContained()){ + boundary[0] = true; + } + if(e2.getGraphPoint().isBoundaryContained()){ + boundary[1] = true; + } + if(e3.getGraphPoint().isBoundaryContained()){ + boundary[2] = true; + } + return boundary; + } + + + /** Check if vertex inside the Loop + * @param vertex the Vertex + * @return true if the vertex is inside, false otherwise + */ + public boolean checkInside(T vertex) { + if(!box.contains(vertex.getX(), vertex.getY(), vertex.getZ())){ + return false; + } + + float[] center = box.getCenter(); + + int hits = 0; + HEdge current = root; + HEdge next = root.getNext(); + while(next!= root){ + if(current.getType() == HEdge.INNER || next.getType() == HEdge.INNER){ + current = next; + next = current.getNext(); + continue; + } + + T vert1 = current.getGraphPoint().getPoint(); + T vert2 = next.getGraphPoint().getPoint(); + + /** The ray is P0+s*D0, where P0 is the ray origin, D0 is a direction vector and s >= 0. + * The segment is P1+t*D1, where P1 and P1+D1 are the endpoints, and 0 <= t <= 1. + * perp(x,y) = (y,-x). + * if Dot(perp(D1),D0) is not zero, + * s = Dot(perp(D1),P1-P0)/Dot(perp(D1),D0) + * t = Dot(perp(D0),P1-P0)/Dot(perp(D1),D0) + */ + + float[] d0 = new float[]{center[0] - vertex.getX(), center[1]-vertex.getY(), + center[2]-vertex.getZ()}; + float[] d1 = {vert2.getX() - vert1.getX(), vert2.getY() - vert1.getY(), + vert2.getZ() - vert1.getZ()}; + + float[] prepD1 = {d1[1],-1*d1[0], d1[2]}; + float[] prepD0 = {d0[1],-1*d0[0], d0[2]}; + + float[] p0p1 = new float[]{vert1.getX() - vertex.getX(), vert1.getY() - vertex.getY(), + vert1.getZ() - vertex.getZ()}; + + float dotD1D0 = VectorFloatUtil.dot(prepD1, d0); + if(dotD1D0 == 0){ + /** ray parallel to segment */ + current = next; + next = current.getNext(); + continue; + } + + float s = VectorFloatUtil.dot(prepD1,p0p1)/dotD1D0; + float t = VectorFloatUtil.dot(prepD0,p0p1)/dotD1D0; + + if(s >= 0 && t >= 0 && t<= 1){ + hits++; + } + current = next; + next = current.getNext(); + } + + if(hits % 2 != 0){ + /** check if hit count is even */ + return true; + } + return false; + } + + public int computeLoopSize(){ + int size = 0; + HEdge e = root; + do{ + size++; + e = e.getNext(); + }while(e != root); + return size; + } +} diff --git a/src/jogamp/graph/curve/text/GlyphShape.java b/src/jogamp/graph/curve/text/GlyphShape.java new file mode 100644 index 000000000..9862a5407 --- /dev/null +++ b/src/jogamp/graph/curve/text/GlyphShape.java @@ -0,0 +1,169 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.text; + +import java.util.ArrayList; + +import com.jogamp.graph.geom.plane.PathIterator; +import com.jogamp.graph.geom.Line; +import com.jogamp.graph.geom.Point; +import com.jogamp.graph.geom.PointTex; +import com.jogamp.graph.geom.Triangle; + +import com.jogamp.graph.curve.OutlineShape; +import com.jogamp.graph.math.Quaternion; + +public class GlyphShape { + + private Quaternion quat= null; + private int numVertices = 0; + private OutlineShape shape = null; + + /** Create a new Glyph shape + * based on Parametric curve control polyline + */ + public GlyphShape(Point.Factory factory){ + shape = new OutlineShape(factory); + } + + /** Create a GlyphShape from a font Path Iterator + * @param pathIterator the path iterator + * + * @see PathIterator + */ + public GlyphShape(Point.Factory factory, PathIterator pathIterator){ + this(factory); + + if(null != pathIterator){ + while(!pathIterator.isDone()){ + float[] coords = new float[6]; + int segmentType = pathIterator.currentSegment(coords); + addOutlineVerticesFromGlyphVector(coords, segmentType); + + pathIterator.next(); + } + } + shape.transformOutlines(OutlineShape.QUADRATIC_NURBS); + } + + public final Point.Factory pointFactory() { return shape.pointFactory(); } + + private void addVertexToLastOutline(PointTex vertex){ + shape.addVertex(vertex); + } + + private void addOutlineVerticesFromGlyphVector(float[] coords, int segmentType){ + if(segmentType == PathIterator.SEG_MOVETO){ + if(!shape.getLastOutline().isEmpty()){ + shape.addEmptyOutline(); + } + PointTex vert = pointFactory().create(coords[0],coords[1]); + vert.setOnCurve(true); + addVertexToLastOutline(vert); + + numVertices++; + } + else if(segmentType == PathIterator.SEG_LINETO){ + PointTex vert1 = pointFactory().create(coords[0],coords[1]); + vert1.setOnCurve(true); + addVertexToLastOutline(vert1); + + numVertices++; + } + else if(segmentType == PathIterator.SEG_QUADTO){ + PointTex vert1 = pointFactory().create(coords[0],coords[1]); + vert1.setOnCurve(false); + addVertexToLastOutline(vert1); + + PointTex vert2 = pointFactory().create(coords[2],coords[3]); + vert2.setOnCurve(true); + addVertexToLastOutline(vert2); + + numVertices+=2; + } + else if(segmentType == PathIterator.SEG_CUBICTO){ + PointTex vert1 = pointFactory().create(coords[0],coords[1]); + vert1.setOnCurve(false); + addVertexToLastOutline(vert1); + + PointTex vert2 = pointFactory().create(coords[2],coords[3]); + vert2.setOnCurve(false); + addVertexToLastOutline(vert2); + + PointTex vert3 = pointFactory().create(coords[4],coords[5]); + vert3.setOnCurve(true); + addVertexToLastOutline(vert3); + + numVertices+=3; + } + else if(segmentType == PathIterator.SEG_CLOSE){ + shape.closeLastOutline(); + } + } + + public int getNumVertices() { + return numVertices; + } + + /** Get the rotational Quaternion attached to this Shape + * @return the Quaternion Object + */ + public Quaternion getQuat() { + return quat; + } + + /** Set the Quaternion that shall defien the rotation + * of this shape. + * @param quat + */ + public void setQuat(Quaternion quat) { + this.quat = quat; + } + + /** Triangluate the glyph shape + * @param sharpness sharpness of the curved regions default = 0.5 + * @return ArrayList of triangles which define this shape + */ + public ArrayList> triangulate(float sharpness){ + return shape.triangulate(sharpness); + } + + /** Get the list of Vertices of this Object + * @return arrayList of Vertices + */ + public ArrayList getVertices(){ + return shape.getVertices(); + } + + /** Get the list of AA lines defined by this object + * @return arraylist of lines + */ + public ArrayList> getLines(){ + return shape.getLines(); + } +} diff --git a/src/jogamp/graph/curve/text/GlyphString.java b/src/jogamp/graph/curve/text/GlyphString.java new file mode 100644 index 000000000..d85e59c43 --- /dev/null +++ b/src/jogamp/graph/curve/text/GlyphString.java @@ -0,0 +1,163 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.text; + +import java.util.ArrayList; + +import com.jogamp.graph.geom.plane.AffineTransform; +import com.jogamp.graph.geom.plane.Path2D; +import com.jogamp.graph.geom.plane.PathIterator; +import com.jogamp.graph.geom.Point; +import com.jogamp.graph.geom.PointTex; +import com.jogamp.graph.geom.Triangle; +import com.jogamp.graph.geom.opengl.Vertex; + +import javax.media.opengl.GLContext; + + +import com.jogamp.graph.curve.Region; +import com.jogamp.graph.curve.RegionFactory; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.glsl.ShaderState; + +public class GlyphString { + private final Point.Factory pointFactory; + private ArrayList glyphs = new ArrayList(); + private String str = ""; + private String fontname = ""; + private Region region; + + private Vertex origin = new Vertex(); + + /** Create a new GlyphString object + * @param fontname the name of the font that this String is + * associated with + * @param str the string object + */ + public GlyphString(Point.Factory factory, String fontname, String str){ + pointFactory = factory; + this.fontname = fontname; + this.str = str; + } + + public final Point.Factory pointFactory() { return pointFactory; } + + public void addGlyphShape(GlyphShape glyph){ + glyphs.add(glyph); + } + public String getString(){ + return str; + } + + /** Creates the Curve based Glyphs from a Font + * @param paths a list of FontPath2D objects that define the outline + * @param affineTransform a global affine transformation applied to the paths. + */ + public void createfromFontPath(Path2D[] paths, AffineTransform affineTransform){ + final int numGlyps = paths.length; + for (int index=0;index> initializeTriangles(float sharpness){ + ArrayList> triangles = new ArrayList>(); + for(GlyphShape glyph:glyphs){ + ArrayList> tris = glyph.triangulate(sharpness); + triangles.addAll(tris); + } + return triangles; + } + + /** Generate a OGL Region to represent this Object. + * @param context the GLContext which the region is defined by. + * @param shaprness the curvature sharpness of the object. + * @param st shader state + */ + public void generateRegion(GLContext context, float shaprness, ShaderState st, int type){ + region = RegionFactory.create(context, st, type); + region.setFlipped(true); + + ArrayList> tris = initializeTriangles(shaprness); + region.addTriangles(tris); + + int numVertices = region.getNumVertices(); + for(GlyphShape glyph:glyphs){ + ArrayList gVertices = glyph.getVertices(); + for(PointTex vert:gVertices){ + vert.setId(numVertices++); + } + region.addVertices(gVertices); + } + + /** initialize the region */ + region.update(); + } + + /** Generate a Hashcode for this object + * @return a string defining the hashcode + */ + public String getTextHashCode(){ + return "" + fontname.hashCode() + str.hashCode(); + } + + /** Render the Object based using the associated Region + * previously generated. + */ + public void renderString3D() { + region.render(null, 0, 0, 0); + } + /** Render the Object based using the associated Region + * previously generated. + */ + public void renderString3D(PMVMatrix matrix, int vp_width, int vp_height, int size) { + region.render(matrix, vp_width, vp_height, size); + } + + /** Get the Origion of this GlyphString + * @return + */ + public PointTex getOrigin() { + return origin; + } + + /** Destroy the associated OGL objects + */ + public void destroy(){ + region.destroy(); + } +} diff --git a/src/jogamp/graph/math/VectorFloatUtil.java b/src/jogamp/graph/math/VectorFloatUtil.java new file mode 100755 index 000000000..172cacd09 --- /dev/null +++ b/src/jogamp/graph/math/VectorFloatUtil.java @@ -0,0 +1,293 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.math; + +import java.util.ArrayList; + +import com.jogamp.graph.geom.Point; + +public class VectorFloatUtil { + + public static final int CW = -1; + public static final int CCW = 1; + public static final int COLLINEAR = 0; + + /** compute the dot product of two points + * @param vec1 vector 1 + * @param vec2 vector 2 + * @return the dot product as float + */ + public static float dot(float[] vec1, float[] vec2) + { + return (vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2]); + } + /** Normalize a vector + * @param vector input vector + * @return normalized vector + */ + public static float[] normalize(float[] vector) + { + float[] newVector = new float[3]; + + float d = MathFloat.sqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]); + if(d> 0.0f) + { + newVector[0] = vector[0]/d; + newVector[1] = vector[1]/d; + newVector[2] = vector[2]/d; + } + return newVector; + } + + /** Scales a vector by param + * @param vector input vector + * @param scale constant to scale by + * @return scaled vector + */ + public static float[] scale(float[] vector, float scale) + { + float[] newVector = new float[3]; + + newVector[0] = vector[0]*scale; + newVector[1] = vector[1]*scale; + newVector[2] = vector[2]*scale; + return newVector; + } + + /** Adds to vectors + * @param v1 vector 1 + * @param v2 vector 2 + * @return v1 + v2 + */ + public static float[] vectorAdd(float[] v1, float[] v2) + { + float[] newVector = new float[3]; + + newVector[0] = v1[0] + v2[0]; + newVector[1] = v1[1] + v2[1]; + newVector[2] = v1[2] + v2[2]; + return newVector; + } + + /** cross product vec1 x vec2 + * @param vec1 vector 1 + * @param vec2 vecttor 2 + * @return the resulting vector + */ + public static float[] cross(float[] vec1, float[] vec2) + { + float[] out = new float[3]; + + out[0] = vec2[2]*vec1[1] - vec2[1]*vec1[2]; + out[1] = vec2[0]*vec1[2] - vec2[2]*vec1[0]; + out[2] = vec2[1]*vec1[0] - vec2[0]*vec1[1]; + + return out; + } + + /** Column Matrix Vector multiplication + * @param colMatrix column matrix (4x4) + * @param vec vector(x,y,z) + * @return result new float[3] + */ + public static float[] colMatrixVectorMult(float[] colMatrix, float[] vec) + { + float[] out = new float[3]; + + out[0] = vec[0]*colMatrix[0] + vec[1]*colMatrix[4] + vec[2]*colMatrix[8] + colMatrix[12]; + out[1] = vec[0]*colMatrix[1] + vec[1]*colMatrix[5] + vec[2]*colMatrix[9] + colMatrix[13]; + out[2] = vec[0]*colMatrix[2] + vec[1]*colMatrix[6] + vec[2]*colMatrix[10] + colMatrix[14]; + + return out; + } + + /** Matrix Vector multiplication + * @param rawMatrix column matrix (4x4) + * @param vec vector(x,y,z) + * @return result new float[3] + */ + public static float[] rowMatrixVectorMult(float[] rawMatrix, float[] vec) + { + float[] out = new float[3]; + + out[0] = vec[0]*rawMatrix[0] + vec[1]*rawMatrix[1] + vec[2]*rawMatrix[2] + rawMatrix[3]; + out[1] = vec[0]*rawMatrix[4] + vec[1]*rawMatrix[5] + vec[2]*rawMatrix[6] + rawMatrix[7]; + out[2] = vec[0]*rawMatrix[8] + vec[1]*rawMatrix[9] + vec[2]*rawMatrix[10] + rawMatrix[11]; + + return out; + } + + /** Calculate the midpoint of two values + * @param p1 first value + * @param p2 second vale + * @return midpoint + */ + public static float mid(float p1, float p2) + { + return (p1+p2)/2.0f; + } + /** Calculate the midpoint of two points + * @param p1 first point + * @param p2 second point + * @return midpoint + */ + public static float[] mid(float[] p1, float[] p2) + { + float[] midPoint = new float[3]; + midPoint[0] = (p1[0] + p2[0])/2.0f; + midPoint[1] = (p1[1] + p2[1])/2.0f; + midPoint[2] = (p1[2] + p2[2])/2.0f; + + return midPoint; + } + /** Compute the norm of a vector + * @param vec vector + * @return vorm + */ + public static float norm(float[] vec) + { + return MathFloat.sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); + } + /** Compute distance between 2 points + * @param p0 a ref point on the line + * @param vec vector representing the direction of the line + * @param point the point to compute the relative distance of + * @return distance float + */ + public static float computeLength(float[] p0, float[] point) + { + float[] w = new float[]{point[0]-p0[0],point[1]-p0[1],point[2]-p0[2]}; + + float distance = MathFloat.sqrt(w[0]*w[0] + w[1]*w[1] + w[2]*w[2]); + + return distance; + } + + /**Check equality of 2 vec3 vectors + * @param v1 vertex 1 + * @param v2 vertex 2 + * @return + */ + public static boolean checkEquality(float[] v1, float[] v2) + { + if(Float.compare(v1[0], v2[0]) == 0 + && Float.compare(v1[1] , v2[1]) == 0 + && Float.compare(v1[2], v2[2]) == 0 ) + return true; + return false; + } + + /** Compute the determinant of 3 vectors + * @param a vector 1 + * @param b vector 2 + * @param c vector 3 + * @return the determinant value + */ + public static float computeDeterminant(float[] a, float[] b, float[] c) + { + float area = a[0]*b[1]*c[2] + a[1]*b[2]*c[0] + a[2]*b[0]*c[1] - a[0]*b[2]*c[1] - a[1]*b[0]*c[2] - a[2]*b[1]*c[0]; + return area; + } + + /** Check if three vertices are colliniear + * @param v1 vertex 1 + * @param v2 vertex 2 + * @param v3 vertex 3 + * @return true if collinear, false otherwise + */ + public static boolean checkCollinear(float[] v1, float[] v2, float[] v3) + { + return (computeDeterminant(v1, v2, v3) == VectorFloatUtil.COLLINEAR); + } + + /** Compute Vector + * @param v1 vertex 1 + * @param v2 vertex2 2 + * @return Vector V1V2 + */ + public static float[] computeVector(float[] v1, float[] v2) + { + float[] vector = new float[3]; + vector[0] = v2[0] - v1[0]; + vector[1] = v2[1] - v1[1]; + vector[2] = v2[2] - v1[2]; + return vector; + } + + /** Check if vertices in triangle circumcircle + * @param a triangle vertex 1 + * @param b triangle vertex 2 + * @param c triangle vertex 3 + * @param d vertex in question + * @return true if the vertex d is inside the circle defined by the + * vertices a, b, c. from paper by Guibas and Stolfi (1985). + */ + public static boolean inCircle(Point a, Point b, Point c, Point d){ + return (a.getX() * a.getX() + a.getY() * a.getY()) * triArea(b, c, d) - + (b.getX() * b.getX() + b.getY() * b.getY()) * triArea(a, c, d) + + (c.getX() * c.getX() + c.getY() * c.getY()) * triArea(a, b, d) - + (d.getX() * d.getX() + d.getY() * d.getY()) * triArea(a, b, c) > 0; + } + + /** Computes oriented area of a triangle + * @param a first vertex + * @param b second vertex + * @param c third vertex + * @return compute twice the area of the oriented triangle (a,b,c), the area + * is positive if the triangle is oriented counterclockwise. + */ + public static float triArea(Point a, Point b, Point c){ + return (b.getX() - a.getX()) * (c.getY() - a.getY()) - (b.getY() - a.getY())*(c.getX() - a.getX()); + } + + /** Check if points are in ccw order + * @param a first vertex + * @param b second vertex + * @param c third vertex + * @return true if the points a,b,c are in a ccw order + */ + public static boolean ccw(Point a, Point b, Point c){ + return triArea(a,b,c) > 0; + } + + /** Computes the area of a list of vertices to check if ccw + * @param vertices + * @return positve area if ccw else negative area value + */ + public static float area(ArrayList vertices) { + int n = vertices.size(); + float area = 0.0f; + for (int p = n - 1, q = 0; q < n; p = q++) + { + float[] pCoord = vertices.get(p).getCoord(); + float[] qCoord = vertices.get(q).getCoord(); + area += pCoord[0] * qCoord[1] - qCoord[0] * pCoord[1]; + } + return area; + } +} -- cgit v1.2.3