From ea62e872a1e6c36fa98661cd543d58cff265a4a0 Mon Sep 17 00:00:00 2001 From: codeanticode Date: Mon, 21 May 2012 22:31:27 +0000 Subject: [PATCH] Moved shape creation and IO methods to PGraphicsOpenGL subclasses, check for 3D operations in PGraphics2D and PShape2D --- .../src/processing/opengl/PGraphics2D.java | 179 +++++- .../src/processing/opengl/PGraphics3D.java | 516 ++++++++++++++++++ .../processing/opengl/PGraphicsOpenGL.java | 498 ----------------- .../src/processing/opengl/PShape2D.java | 59 +- .../src/processing/opengl/PShape3D.java | 7 +- .../src/processing/opengl/PShapeOpenGL.java | 48 +- 6 files changed, 768 insertions(+), 539 deletions(-) diff --git a/java/libraries/opengl/src/processing/opengl/PGraphics2D.java b/java/libraries/opengl/src/processing/opengl/PGraphics2D.java index 679c45d8a..a1fd2699e 100644 --- a/java/libraries/opengl/src/processing/opengl/PGraphics2D.java +++ b/java/libraries/opengl/src/processing/opengl/PGraphics2D.java @@ -23,6 +23,7 @@ package processing.opengl; import processing.core.PMatrix3D; +import processing.core.PShape; public class PGraphics2D extends PGraphicsOpenGL { @@ -30,6 +31,151 @@ public class PGraphics2D extends PGraphicsOpenGL { super(); hints[ENABLE_PERSPECTIVE_CORRECTED_LINES] = false; } + + ////////////////////////////////////////////////////////////// + + // RENDERER SUPPORT QUERIES + + public boolean is2D() { + return true; + } + + public boolean is3D() { + return false; + } + + ////////////////////////////////////////////////////////////// + + // SHAPE CREATORS + + + public PShape createShape() { + return createShape(POLYGON); + } + + + public PShape createShape(int type) { + PShape2D shape = null; + if (type == PShape.GROUP) { + shape = new PShape2D(parent, PShape.GROUP); + } else if (type == PShape.PATH) { + shape = new PShape2D(parent, PShape.PATH); + } else if (type == POINTS) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(POINTS); + } else if (type == LINES) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(LINES); + } else if (type == TRIANGLE || type == TRIANGLES) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(TRIANGLES); + } else if (type == TRIANGLE_FAN) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(TRIANGLE_FAN); + } else if (type == TRIANGLE_STRIP) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(TRIANGLE_STRIP); + } else if (type == QUAD || type == QUADS) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(QUADS); + } else if (type == QUAD_STRIP) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(QUAD_STRIP); + } else if (type == POLYGON) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(POLYGON); + } + return shape; + } + + + public PShape createShape(int kind, float... p) { + PShape2D shape = null; + int len = p.length; + + if (kind == POINT) { + if (len != 2) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(POINT); + } else if (kind == LINE) { + if (len != 4) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(LINE); + } else if (kind == TRIANGLE) { + if (len != 6) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(TRIANGLE); + } else if (kind == QUAD) { + if (len != 8) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(QUAD); + } else if (kind == RECT) { + if (len != 4 && len != 5 && len != 8) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(RECT); + } else if (kind == ELLIPSE) { + if (len != 4) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(ELLIPSE); + } else if (kind == ARC) { + if (len != 6) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(ARC); + } else if (kind == BOX) { + showWarning("Primitive not supported in 2D"); + } else if (kind == SPHERE) { + showWarning("Primitive not supported in 2D"); + } else { + showWarning("Unrecognized primitive type"); + } + + if (shape != null) { + shape.setParams(p); + } + + return shape; + } + + ////////////////////////////////////////////////////////////// + + // SHAPE I/O + + protected String[] getSupportedShapeFormats() { + return new String[] { "svg" }; + } + + + public PShape loadShape(String filename) { + // TODO: loadShape in PApplet probably needs to be + // re-implemented. + PShape svg = parent.loadShape(filename); + + // TODO: rework base API in PShape to do this... + PShape2D p2d = (PShape2D) svg.copy(this); + + return p2d; + } ////////////////////////////////////////////////////////////// @@ -98,6 +244,14 @@ public class PGraphics2D extends PGraphicsOpenGL { showDepthWarningXYZ("vertex"); } + ////////////////////////////////////////////////////////////// + + // MATRIX TRANSFORMATIONS + + public void translate(float tx, float ty, float tz) { + showVariationWarning("translate"); + } + public void rotateX(float angle) { showDepthWarning("rotateX"); } @@ -125,6 +279,10 @@ public class PGraphics2D extends PGraphicsOpenGL { showDepthWarningXYZ("scale"); } + ////////////////////////////////////////////////////////////// + + // SCREEN AND MODEL COORDS + public float screenX(float x, float y, float z) { showDepthWarningXYZ("screenX"); return 0; @@ -149,7 +307,6 @@ public class PGraphics2D extends PGraphicsOpenGL { showVariationWarning("setMatrix"); } - ////////////////////////////////////////////////////////////// // LIGHTS @@ -195,24 +352,4 @@ public class PGraphics2D extends PGraphicsOpenGL { public void lightSpecular(float v1, float v2, float v3) { showMethodWarning("lightSpecular"); } - - ////////////////////////////////////////////////////////////// - - // RENDERER SUPPORT QUERIES - - /** - * Return true if this renderer supports 2D drawing. Defaults to true. - */ - public boolean is2D() { - return true; - } - - - /** - * Return true if this renderer supports 2D drawing. Defaults to false. - */ - public boolean is3D() { - return false; - } - } \ No newline at end of file diff --git a/java/libraries/opengl/src/processing/opengl/PGraphics3D.java b/java/libraries/opengl/src/processing/opengl/PGraphics3D.java index ca925692e..a8c6d0a1a 100644 --- a/java/libraries/opengl/src/processing/opengl/PGraphics3D.java +++ b/java/libraries/opengl/src/processing/opengl/PGraphics3D.java @@ -22,6 +22,522 @@ package processing.opengl; +import java.io.BufferedReader; +import java.util.ArrayList; +import java.util.Hashtable; + +import processing.core.PApplet; +import processing.core.PImage; +import processing.core.PShape; +import processing.core.PVector; public class PGraphics3D extends PGraphicsOpenGL { + + ////////////////////////////////////////////////////////////// + + // RENDERER SUPPORT QUERIES + + public boolean is2D() { + return false; + } + + public boolean is3D() { + return true; + } + + ////////////////////////////////////////////////////////////// + + // SHAPE CREATORS + + + public PShape createShape() { + return createShape(POLYGON); + } + + + public PShape createShape(int type) { + PShape3D shape = null; + if (type == PShape.GROUP) { + shape = new PShape3D(parent, PShape.GROUP); + } else if (type == PShape.PATH) { + shape = new PShape3D(parent, PShape.PATH); + } else if (type == POINTS) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(POINTS); + } else if (type == LINES) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(LINES); + } else if (type == TRIANGLE || type == TRIANGLES) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(TRIANGLES); + } else if (type == TRIANGLE_FAN) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(TRIANGLE_FAN); + } else if (type == TRIANGLE_STRIP) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(TRIANGLE_STRIP); + } else if (type == QUAD || type == QUADS) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(QUADS); + } else if (type == QUAD_STRIP) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(QUAD_STRIP); + } else if (type == POLYGON) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(POLYGON); + } + return shape; + } + + + public PShape createShape(int kind, float... p) { + PShape3D shape = null; + int len = p.length; + + if (kind == POINT) { + if (len != 2 && len != 3) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(POINT); + } else if (kind == LINE) { + if (len != 4 && len != 6) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(LINE); + } else if (kind == TRIANGLE) { + if (len != 6) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(TRIANGLE); + } else if (kind == QUAD) { + if (len != 8) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(QUAD); + } else if (kind == RECT) { + if (len != 4 && len != 5 && len != 8) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(RECT); + } else if (kind == ELLIPSE) { + if (len != 4) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(ELLIPSE); + } else if (kind == ARC) { + if (len != 6) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(ARC); + } else if (kind == BOX) { + if (len != 1 && len != 3) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(BOX); + } else if (kind == SPHERE) { + if (len != 1) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(SPHERE); + } else { + showWarning("Unrecognized primitive type"); + } + + if (shape != null) { + shape.setParams(p); + } + + return shape; + } + + + ////////////////////////////////////////////////////////////// + + // SHAPE I/O + + + protected String[] getSupportedShapeFormats() { + return new String[] { "obj" }; + } + + + public PShape loadShape(String filename) { + ArrayList vertices = new ArrayList(); + ArrayList normals = new ArrayList(); + ArrayList textures = new ArrayList(); + ArrayList faces = new ArrayList(); + ArrayList materials = new ArrayList(); + + BufferedReader reader = parent.createReader(filename); + parseOBJ(reader, vertices, normals, textures, faces, materials); + + int prevColorMode = pg.colorMode; + float prevColorModeX = pg.colorModeX; + float prevColorModeY = pg.colorModeY; + float prevColorModeZ = pg.colorModeZ; + float prevColorModeA = pg.colorModeA; + boolean prevStroke = pg.stroke; + int prevTextureMode = pg.textureMode; + pg.colorMode(RGB, 1); + pg.stroke = false; + pg.textureMode = NORMAL; + + + // The OBJ geometry is stored in a group shape, + // with each face in a separate child geometry + // shape. + PShape root = createShape(GROUP); + + int mtlIdxCur = -1; + OBJMaterial mtl = null; + for (int i = 0; i < faces.size(); i++) { + OBJFace face = faces.get(i); + + // Getting current material. + if (mtlIdxCur != face.matIdx) { + mtlIdxCur = PApplet.max(0, face.matIdx); // To make sure that at least we get the default material. + mtl = materials.get(mtlIdxCur); + } + + // Creating child shape for current face. + PShape child; + if (face.vertIdx.size() == 3) { + child = createShape(TRIANGLES); // Face is a triangle, so using appropriate shape kind. + } else if (face.vertIdx.size() == 4) { + child = createShape(QUADS); // Face is a quad, so using appropriate shape kind. + } else { + child = createShape(POLYGON); // Face is a general polygon + } + + // Setting material properties for the new face + child.fill(mtl.kd.x, mtl.kd.y, mtl.kd.z); + child.ambient(mtl.ka.x, mtl.ka.y, mtl.ka.z); + child.specular(mtl.ks.x, mtl.ks.y, mtl.ks.z); + child.shininess(mtl.ns); + if (mtl.kdMap != null) { + // If current material is textured, then tinting the texture using the diffuse color. + child.tint(mtl.kd.x, mtl.kd.y, mtl.kd.z, mtl.d); + } + + for (int j = 0; j < face.vertIdx.size(); j++){ + int vertIdx, normIdx; + PVector vert, norms; + + vert = norms = null; + + vertIdx = face.vertIdx.get(j).intValue() - 1; + vert = vertices.get(vertIdx); + + if (j < face.normIdx.size()) { + normIdx = face.normIdx.get(j).intValue() - 1; + if (-1 < normIdx) { + norms = normals.get(normIdx); + } + } + + if (mtl != null && mtl.kdMap != null) { + // This face is textured. + int texIdx; + PVector tex = null; + + if (j < face.texIdx.size()) { + texIdx = face.texIdx.get(j).intValue() - 1; + if (-1 < texIdx) { + tex = textures.get(texIdx); + } + } + + child.texture(mtl.kdMap); + if (norms != null) { + child.normal(norms.x, norms.y, norms.z); + } + if (tex != null) { + child.vertex(vert.x, vert.y, vert.z, tex.x, tex.y); + } else { + child.vertex(vert.x, vert.y, vert.z); + } + } else { + // This face is not textured. + if (norms != null) { + child.normal(norms.x, norms.y, norms.z); + } + child.vertex(vert.x, vert.y, vert.z); + } + } + + child.end(CLOSE); + root.addChild(child); + } + + pg.colorMode(prevColorMode, prevColorModeX, prevColorModeY, prevColorModeZ, prevColorModeA); + pg.stroke = prevStroke; + pg.textureMode = prevTextureMode; + + return root; + } + + + protected void parseOBJ(BufferedReader reader, ArrayList vertices, + ArrayList normals, + ArrayList textures, + ArrayList faces, + ArrayList materials) { + Hashtable mtlTable = new Hashtable(); + int mtlIdxCur = -1; + boolean readv, readvn, readvt; + try { + + readv = readvn = readvt = false; + String line; + String gname = "object"; + while ((line = reader.readLine()) != null) { + // Parse the line. + + // The below patch/hack comes from Carlos Tomas Marti and is a + // fix for single backslashes in Rhino obj files + + // BEGINNING OF RHINO OBJ FILES HACK + // Statements can be broken in multiple lines using '\' at the + // end of a line. + // In regular expressions, the backslash is also an escape + // character. + // The regular expression \\ matches a single backslash. This + // regular expression as a Java string, becomes "\\\\". + // That's right: 4 backslashes to match a single one. + while (line.contains("\\")) { + line = line.split("\\\\")[0]; + final String s = reader.readLine(); + if (s != null) + line += s; + } + // END OF RHINO OBJ FILES HACK + + String[] elements = line.split("\\s+"); + // if not a blank line, process the line. + if (elements.length > 0) { + if (elements[0].equals("v")) { + // vertex + PVector tempv = new PVector(Float.valueOf(elements[1]).floatValue(), + Float.valueOf(elements[2]).floatValue(), + Float.valueOf(elements[3]).floatValue()); + vertices.add(tempv); + readv = true; + } else if (elements[0].equals("vn")) { + // normal + PVector tempn = new PVector(Float.valueOf(elements[1]).floatValue(), + Float.valueOf(elements[2]).floatValue(), + Float.valueOf(elements[3]).floatValue()); + normals.add(tempn); + readvn = true; + } else if (elements[0].equals("vt")) { + // uv, inverting v to take into account Processing's invertex Y axis with + // respect to OpenGL. + PVector tempv = new PVector(Float.valueOf(elements[1]).floatValue(), + 1 - Float.valueOf(elements[2]).floatValue()); + textures.add(tempv); + readvt = true; + } else if (elements[0].equals("o")) { + // Object name is ignored, for now. + } else if (elements[0].equals("mtllib")) { + if (elements[1] != null) { + BufferedReader mreader = parent.createReader(elements[1]); + if (mreader != null) { + parseMTL(mreader, materials, mtlTable); + } + } + } else if (elements[0].equals("g")) { + gname = 1 < elements.length ? elements[1] : ""; + } else if (elements[0].equals("usemtl")) { + // Getting index of current active material (will be applied on all subsequent faces). + if (elements[1] != null) { + String mtlname = elements[1]; + if (mtlTable.containsKey(mtlname)) { + Integer tempInt = mtlTable.get(mtlname); + mtlIdxCur = tempInt.intValue(); + } else { + mtlIdxCur = -1; + } + } + } else if (elements[0].equals("f")) { + // Face setting + OBJFace face = new OBJFace(); + face.matIdx = mtlIdxCur; + face.name = gname; + + for (int i = 1; i < elements.length; i++) { + String seg = elements[i]; + + if (seg.indexOf("/") > 0) { + String[] forder = seg.split("/"); + + if (forder.length > 2) { + // Getting vertex and texture and normal indexes. + if (forder[0].length() > 0 && readv) { + face.vertIdx.add(Integer.valueOf(forder[0])); + } + + if (forder[1].length() > 0 && readvt) { + face.texIdx.add(Integer.valueOf(forder[1])); + } + + if (forder[2].length() > 0 && readvn) { + face.normIdx.add(Integer.valueOf(forder[2])); + } + } else if (forder.length > 1) { + // Getting vertex and texture/normal indexes. + if (forder[0].length() > 0 && readv) { + face.vertIdx.add(Integer.valueOf(forder[0])); + } + + if (forder[1].length() > 0) { + if (readvt) { + face.texIdx.add(Integer.valueOf(forder[1])); + } else if (readvn) { + face.normIdx.add(Integer.valueOf(forder[1])); + } + + } + + } else if (forder.length > 0) { + // Getting vertex index only. + if (forder[0].length() > 0 && readv) { + face.vertIdx.add(Integer.valueOf(forder[0])); + } + } + } else { + // Getting vertex index only. + if (seg.length() > 0 && readv) { + face.vertIdx.add(Integer.valueOf(seg)); + } + } + } + + faces.add(face); + } + } + } + + if (materials.size() == 0) { + // No materials definition so far. Adding one default material. + OBJMaterial defMtl = new OBJMaterial(); + materials.add(defMtl); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + protected void parseMTL(BufferedReader reader, ArrayList materials, Hashtable materialsHash) { + try { + String line; + OBJMaterial currentMtl = null; + while ((line = reader.readLine()) != null) { + // Parse the line + line = line.trim(); + + String elements[] = line.split("\\s+"); + + if (elements.length > 0) { + // Extract the material data. + + if (elements[0].equals("newmtl")) { + // Starting new material. + String mtlname = elements[1]; + currentMtl = new OBJMaterial(mtlname); + materialsHash.put(mtlname, new Integer(materials.size())); + materials.add(currentMtl); + } else if (elements[0].equals("map_Kd") && elements.length > 1) { + // Loading texture map. + String texname = elements[1]; + currentMtl.kdMap = parent.loadImage(texname); + } else if (elements[0].equals("Ka") && elements.length > 3) { + // The ambient color of the material + currentMtl.ka.x = Float.valueOf(elements[1]).floatValue(); + currentMtl.ka.y = Float.valueOf(elements[2]).floatValue(); + currentMtl.ka.z = Float.valueOf(elements[3]).floatValue(); + } else if (elements[0].equals("Kd") && elements.length > 3) { + // The diffuse color of the material + currentMtl.kd.x = Float.valueOf(elements[1]).floatValue(); + currentMtl.kd.y = Float.valueOf(elements[2]).floatValue(); + currentMtl.kd.z = Float.valueOf(elements[3]).floatValue(); + } else if (elements[0].equals("Ks") && elements.length > 3) { + // The specular color weighted by the specular coefficient + currentMtl.ks.x = Float.valueOf(elements[1]).floatValue(); + currentMtl.ks.y = Float.valueOf(elements[2]).floatValue(); + currentMtl.ks.z = Float.valueOf(elements[3]).floatValue(); + } else if ((elements[0].equals("d") || elements[0].equals("Tr")) && elements.length > 1) { + // Reading the alpha transparency. + currentMtl.d = Float.valueOf(elements[1]).floatValue(); + } else if (elements[0].equals("Ns") && elements.length > 1) { + // The specular component of the Phong shading model + currentMtl.ns = Float.valueOf(elements[1]).floatValue(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // Stores a face from an OBJ file + protected class OBJFace { + ArrayList vertIdx; + ArrayList texIdx; + ArrayList normIdx; + int matIdx; + String name; + + OBJFace() { + vertIdx = new ArrayList(); + texIdx = new ArrayList(); + normIdx = new ArrayList(); + matIdx = -1; + name = ""; + } + } + + + // Stores a material defined in an MTL file. + protected class OBJMaterial { + String name; + PVector ka; + PVector kd; + PVector ks; + float d; + float ns; + PImage kdMap; + + OBJMaterial() { + this("default"); + } + + OBJMaterial(String name) { + this.name = name; + ka = new PVector(0.5f, 0.5f, 0.5f); + kd = new PVector(0.5f, 0.5f, 0.5f); + ks = new PVector(0.5f, 0.5f, 0.5f); + d = 1.0f; + ns = 0.0f; + kdMap = null; + } + } } \ No newline at end of file diff --git a/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java b/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java index 10d92354e..59bf54c96 100644 --- a/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java +++ b/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java @@ -1945,130 +1945,6 @@ public class PGraphicsOpenGL extends PGraphics { } - ////////////////////////////////////////////////////////////// - - // SHAPE CREATORS - - - public PShape createShape() { - return createShape(POLYGON); - } - - - public PShape createShape(int type) { - PShapeOpenGL shape = null; - if (type == PShape.GROUP) { - shape = new PShapeOpenGL(parent, PShape.GROUP); - } else if (type == PShape.PATH) { - shape = new PShapeOpenGL(parent, PShape.PATH); - } else if (type == POINTS) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(POINTS); - } else if (type == LINES) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(LINES); - } else if (type == TRIANGLE || type == TRIANGLES) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(TRIANGLES); - } else if (type == TRIANGLE_FAN) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(TRIANGLE_FAN); - } else if (type == TRIANGLE_STRIP) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(TRIANGLE_STRIP); - } else if (type == QUAD || type == QUADS) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(QUADS); - } else if (type == QUAD_STRIP) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(QUAD_STRIP); - } else if (type == POLYGON) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); - shape.setKind(POLYGON); - } - return shape; - } - - - public PShape createShape(int kind, float... p) { - PShapeOpenGL shape = null; - int len = p.length; - - if (kind == POINT) { - if (len != 2 && len != 3) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); - shape.setKind(POINT); - } else if (kind == LINE) { - if (len != 4 && len != 6) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); - shape.setKind(LINE); - } else if (kind == TRIANGLE) { - if (len != 6) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); - shape.setKind(TRIANGLE); - } else if (kind == QUAD) { - if (len != 8) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); - shape.setKind(QUAD); - } else if (kind == RECT) { - if (len != 4 && len != 5 && len != 8) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); - shape.setKind(RECT); - } else if (kind == ELLIPSE) { - if (len != 4) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); - shape.setKind(ELLIPSE); - } else if (kind == ARC) { - if (len != 6) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); - shape.setKind(ARC); - } else if (kind == BOX) { - if (len != 1 && len != 3) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); - shape.setKind(BOX); - } else if (kind == SPHERE) { - if (len != 1) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); - shape.setKind(SPHERE); - } else { - showWarning("Unrecognized primitive type"); - } - - if (shape != null) { - shape.setParams(p); - } - - return shape; - } - - ////////////////////////////////////////////////////////////// // VERTEX SHAPES @@ -5105,380 +4981,6 @@ public class PGraphicsOpenGL extends PGraphics { // public void save(String filename) // PImage calls loadPixels() - - ////////////////////////////////////////////////////////////// - - // SHAPE I/O - - - // Stores a face from an OBJ file - protected class OBJFace { - ArrayList vertIdx; - ArrayList texIdx; - ArrayList normIdx; - int matIdx; - String name; - - OBJFace() { - vertIdx = new ArrayList(); - texIdx = new ArrayList(); - normIdx = new ArrayList(); - matIdx = -1; - name = ""; - } - } - - - // Stores a material defined in an MTL file. - protected class OBJMaterial { - String name; - PVector ka; - PVector kd; - PVector ks; - float d; - float ns; - PImage kdMap; - - OBJMaterial() { - this("default"); - } - - OBJMaterial(String name) { - this.name = name; - ka = new PVector(0.5f, 0.5f, 0.5f); - kd = new PVector(0.5f, 0.5f, 0.5f); - ks = new PVector(0.5f, 0.5f, 0.5f); - d = 1.0f; - ns = 0.0f; - kdMap = null; - } - } - - - protected String[] getSupportedShapeFormats() { - return new String[] { "obj" }; - } - - - public PShape loadShape(String filename) { - ArrayList vertices = new ArrayList(); - ArrayList normals = new ArrayList(); - ArrayList textures = new ArrayList(); - ArrayList faces = new ArrayList(); - ArrayList materials = new ArrayList(); - - BufferedReader reader = parent.createReader(filename); - parseOBJ(reader, vertices, normals, textures, faces, materials); - - int prevColorMode = pg.colorMode; - float prevColorModeX = pg.colorModeX; - float prevColorModeY = pg.colorModeY; - float prevColorModeZ = pg.colorModeZ; - float prevColorModeA = pg.colorModeA; - boolean prevStroke = pg.stroke; - int prevTextureMode = pg.textureMode; - pg.colorMode(RGB, 1); - pg.stroke = false; - pg.textureMode = NORMAL; - - - // The OBJ geometry is stored in a group shape, - // with each face in a separate child geometry - // shape. - PShape root = createShape(GROUP); - - int mtlIdxCur = -1; - OBJMaterial mtl = null; - for (int i = 0; i < faces.size(); i++) { - OBJFace face = faces.get(i); - - // Getting current material. - if (mtlIdxCur != face.matIdx) { - mtlIdxCur = PApplet.max(0, face.matIdx); // To make sure that at least we get the default material. - mtl = materials.get(mtlIdxCur); - } - - // Creating child shape for current face. - PShape child; - if (face.vertIdx.size() == 3) { - child = createShape(TRIANGLES); // Face is a triangle, so using appropriate shape kind. - } else if (face.vertIdx.size() == 4) { - child = createShape(QUADS); // Face is a quad, so using appropriate shape kind. - } else { - child = createShape(POLYGON); // Face is a general polygon - } - - // Setting material properties for the new face - child.fill(mtl.kd.x, mtl.kd.y, mtl.kd.z); - child.ambient(mtl.ka.x, mtl.ka.y, mtl.ka.z); - child.specular(mtl.ks.x, mtl.ks.y, mtl.ks.z); - child.shininess(mtl.ns); - if (mtl.kdMap != null) { - // If current material is textured, then tinting the texture using the diffuse color. - child.tint(mtl.kd.x, mtl.kd.y, mtl.kd.z, mtl.d); - } - - for (int j = 0; j < face.vertIdx.size(); j++){ - int vertIdx, normIdx; - PVector vert, norms; - - vert = norms = null; - - vertIdx = face.vertIdx.get(j).intValue() - 1; - vert = vertices.get(vertIdx); - - if (j < face.normIdx.size()) { - normIdx = face.normIdx.get(j).intValue() - 1; - if (-1 < normIdx) { - norms = normals.get(normIdx); - } - } - - if (mtl != null && mtl.kdMap != null) { - // This face is textured. - int texIdx; - PVector tex = null; - - if (j < face.texIdx.size()) { - texIdx = face.texIdx.get(j).intValue() - 1; - if (-1 < texIdx) { - tex = textures.get(texIdx); - } - } - - child.texture(mtl.kdMap); - if (norms != null) { - child.normal(norms.x, norms.y, norms.z); - } - if (tex != null) { - child.vertex(vert.x, vert.y, vert.z, tex.x, tex.y); - } else { - child.vertex(vert.x, vert.y, vert.z); - } - } else { - // This face is not textured. - if (norms != null) { - child.normal(norms.x, norms.y, norms.z); - } - child.vertex(vert.x, vert.y, vert.z); - } - } - - child.end(CLOSE); - root.addChild(child); - } - - pg.colorMode(prevColorMode, prevColorModeX, prevColorModeY, prevColorModeZ, prevColorModeA); - pg.stroke = prevStroke; - pg.textureMode = prevTextureMode; - - return root; - } - - - protected void parseOBJ(BufferedReader reader, ArrayList vertices, - ArrayList normals, - ArrayList textures, - ArrayList faces, - ArrayList materials) { - Hashtable mtlTable = new Hashtable(); - int mtlIdxCur = -1; - boolean readv, readvn, readvt; - try { - - readv = readvn = readvt = false; - String line; - String gname = "object"; - while ((line = reader.readLine()) != null) { - // Parse the line. - - // The below patch/hack comes from Carlos Tomas Marti and is a - // fix for single backslashes in Rhino obj files - - // BEGINNING OF RHINO OBJ FILES HACK - // Statements can be broken in multiple lines using '\' at the - // end of a line. - // In regular expressions, the backslash is also an escape - // character. - // The regular expression \\ matches a single backslash. This - // regular expression as a Java string, becomes "\\\\". - // That's right: 4 backslashes to match a single one. - while (line.contains("\\")) { - line = line.split("\\\\")[0]; - final String s = reader.readLine(); - if (s != null) - line += s; - } - // END OF RHINO OBJ FILES HACK - - String[] elements = line.split("\\s+"); - // if not a blank line, process the line. - if (elements.length > 0) { - if (elements[0].equals("v")) { - // vertex - PVector tempv = new PVector(Float.valueOf(elements[1]).floatValue(), - Float.valueOf(elements[2]).floatValue(), - Float.valueOf(elements[3]).floatValue()); - vertices.add(tempv); - readv = true; - } else if (elements[0].equals("vn")) { - // normal - PVector tempn = new PVector(Float.valueOf(elements[1]).floatValue(), - Float.valueOf(elements[2]).floatValue(), - Float.valueOf(elements[3]).floatValue()); - normals.add(tempn); - readvn = true; - } else if (elements[0].equals("vt")) { - // uv, inverting v to take into account Processing's invertex Y axis with - // respect to OpenGL. - PVector tempv = new PVector(Float.valueOf(elements[1]).floatValue(), - 1 - Float.valueOf(elements[2]).floatValue()); - textures.add(tempv); - readvt = true; - } else if (elements[0].equals("o")) { - // Object name is ignored, for now. - } else if (elements[0].equals("mtllib")) { - if (elements[1] != null) { - BufferedReader mreader = parent.createReader(elements[1]); - if (mreader != null) { - parseMTL(mreader, materials, mtlTable); - } - } - } else if (elements[0].equals("g")) { - gname = 1 < elements.length ? elements[1] : ""; - } else if (elements[0].equals("usemtl")) { - // Getting index of current active material (will be applied on all subsequent faces). - if (elements[1] != null) { - String mtlname = elements[1]; - if (mtlTable.containsKey(mtlname)) { - Integer tempInt = mtlTable.get(mtlname); - mtlIdxCur = tempInt.intValue(); - } else { - mtlIdxCur = -1; - } - } - } else if (elements[0].equals("f")) { - // Face setting - OBJFace face = new OBJFace(); - face.matIdx = mtlIdxCur; - face.name = gname; - - for (int i = 1; i < elements.length; i++) { - String seg = elements[i]; - - if (seg.indexOf("/") > 0) { - String[] forder = seg.split("/"); - - if (forder.length > 2) { - // Getting vertex and texture and normal indexes. - if (forder[0].length() > 0 && readv) { - face.vertIdx.add(Integer.valueOf(forder[0])); - } - - if (forder[1].length() > 0 && readvt) { - face.texIdx.add(Integer.valueOf(forder[1])); - } - - if (forder[2].length() > 0 && readvn) { - face.normIdx.add(Integer.valueOf(forder[2])); - } - } else if (forder.length > 1) { - // Getting vertex and texture/normal indexes. - if (forder[0].length() > 0 && readv) { - face.vertIdx.add(Integer.valueOf(forder[0])); - } - - if (forder[1].length() > 0) { - if (readvt) { - face.texIdx.add(Integer.valueOf(forder[1])); - } else if (readvn) { - face.normIdx.add(Integer.valueOf(forder[1])); - } - - } - - } else if (forder.length > 0) { - // Getting vertex index only. - if (forder[0].length() > 0 && readv) { - face.vertIdx.add(Integer.valueOf(forder[0])); - } - } - } else { - // Getting vertex index only. - if (seg.length() > 0 && readv) { - face.vertIdx.add(Integer.valueOf(seg)); - } - } - } - - faces.add(face); - } - } - } - - if (materials.size() == 0) { - // No materials definition so far. Adding one default material. - OBJMaterial defMtl = new OBJMaterial(); - materials.add(defMtl); - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - protected void parseMTL(BufferedReader reader, ArrayList materials, Hashtable materialsHash) { - try { - String line; - OBJMaterial currentMtl = null; - while ((line = reader.readLine()) != null) { - // Parse the line - line = line.trim(); - - String elements[] = line.split("\\s+"); - - if (elements.length > 0) { - // Extract the material data. - - if (elements[0].equals("newmtl")) { - // Starting new material. - String mtlname = elements[1]; - currentMtl = new OBJMaterial(mtlname); - materialsHash.put(mtlname, new Integer(materials.size())); - materials.add(currentMtl); - } else if (elements[0].equals("map_Kd") && elements.length > 1) { - // Loading texture map. - String texname = elements[1]; - currentMtl.kdMap = parent.loadImage(texname); - } else if (elements[0].equals("Ka") && elements.length > 3) { - // The ambient color of the material - currentMtl.ka.x = Float.valueOf(elements[1]).floatValue(); - currentMtl.ka.y = Float.valueOf(elements[2]).floatValue(); - currentMtl.ka.z = Float.valueOf(elements[3]).floatValue(); - } else if (elements[0].equals("Kd") && elements.length > 3) { - // The diffuse color of the material - currentMtl.kd.x = Float.valueOf(elements[1]).floatValue(); - currentMtl.kd.y = Float.valueOf(elements[2]).floatValue(); - currentMtl.kd.z = Float.valueOf(elements[3]).floatValue(); - } else if (elements[0].equals("Ks") && elements.length > 3) { - // The specular color weighted by the specular coefficient - currentMtl.ks.x = Float.valueOf(elements[1]).floatValue(); - currentMtl.ks.y = Float.valueOf(elements[2]).floatValue(); - currentMtl.ks.z = Float.valueOf(elements[3]).floatValue(); - } else if ((elements[0].equals("d") || elements[0].equals("Tr")) && elements.length > 1) { - // Reading the alpha transparency. - currentMtl.d = Float.valueOf(elements[1]).floatValue(); - } else if (elements[0].equals("Ns") && elements.length > 1) { - // The specular component of the Phong shading model - currentMtl.ns = Float.valueOf(elements[1]).floatValue(); - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - ////////////////////////////////////////////////////////////// diff --git a/java/libraries/opengl/src/processing/opengl/PShape2D.java b/java/libraries/opengl/src/processing/opengl/PShape2D.java index 42f0e9235..102c3ce71 100644 --- a/java/libraries/opengl/src/processing/opengl/PShape2D.java +++ b/java/libraries/opengl/src/processing/opengl/PShape2D.java @@ -22,15 +22,72 @@ package processing.opengl; +import processing.core.PApplet; +import processing.core.PGraphics; + public class PShape2D extends PShapeOpenGL { + public PShape2D(PApplet parent, int family) { + super(parent, family); + } + public boolean is2D() { return true; } - public boolean is3D() { return false; } + public void vertex(float x, float y, float z) { + PGraphics.showDepthWarningXYZ("vertex"); + } + + public void vertex(float x, float y, float z, float u, float v) { + PGraphics.showDepthWarningXYZ("vertex"); + } + + public void translate(float tx, float ty, float tz) { + PGraphics.showVariationWarning("translate"); + } + + public void rotateX(float angle) { + PGraphics.showDepthWarning("rotateX"); + } + + public void rotateY(float angle) { + PGraphics.showDepthWarning("rotateY"); + } + + public void rotateZ(float angle) { + PGraphics.showDepthWarning("rotateZ"); + } + + public void rotate(float angle, float vx, float vy, float vz) { + PGraphics.showVariationWarning("rotate"); + } + + public void applyMatrix(float n00, float n01, float n02, float n03, + float n10, float n11, float n12, float n13, + float n20, float n21, float n22, float n23, + float n30, float n31, float n32, float n33) { + PGraphics.showVariationWarning("applyMatrix"); + } + + public void scale(float sx, float sy, float sz) { + PGraphics.showDepthWarningXYZ("scale"); + } + + public float getVertexZ(int index) { + PGraphics.showDepthWarningXYZ("getVertexZ"); + return 0; + } + + public void setVertex(int index, float x, float y) { + super.setVertex(index, x, y, 0); + } + + public void setVertex(int index, float x, float y, float z) { + PGraphics.showDepthWarningXYZ("setVertex"); + } } diff --git a/java/libraries/opengl/src/processing/opengl/PShape3D.java b/java/libraries/opengl/src/processing/opengl/PShape3D.java index dbbc96b73..0b3df34cc 100644 --- a/java/libraries/opengl/src/processing/opengl/PShape3D.java +++ b/java/libraries/opengl/src/processing/opengl/PShape3D.java @@ -22,13 +22,18 @@ package processing.opengl; +import processing.core.PApplet; + public class PShape3D extends PShapeOpenGL { + public PShape3D(PApplet parent, int family) { + super(parent, family); + } + public boolean is2D() { return false; } - public boolean is3D() { return true; } diff --git a/java/libraries/opengl/src/processing/opengl/PShapeOpenGL.java b/java/libraries/opengl/src/processing/opengl/PShapeOpenGL.java index 1a127d4a1..f6f442f98 100644 --- a/java/libraries/opengl/src/processing/opengl/PShapeOpenGL.java +++ b/java/libraries/opengl/src/processing/opengl/PShapeOpenGL.java @@ -3,8 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2004-12 Ben Fry and Casey Reas - Copyright (c) 2001-04 Massachusetts Institute of Technology + Copyright (c) 2011-12 Ben Fry and Casey Reas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -659,6 +658,7 @@ public class PShapeOpenGL extends PShape { } + // TODO: what about updating parent? protected void removeTexture(PImage tex) { if (textures != null) { boolean childHasIt = false; @@ -4128,28 +4128,38 @@ public class PShapeOpenGL extends PShape { } } + boolean renderingFill = false, renderingStroke = false; PolyShader shader = null; IndexCache cache = tessGeo.polyIndexCache; for (int n = firstPolyIndexCache; n <= lastPolyIndexCache; n++) { - // TODO: make sure that either of these two branches is executed only once... if (tex != null && (is3D() || n < firstLineIndexCache && n < firstPointIndexCache)) { - // Rendering fill triangles, which can be lit and textured. - shader = g.getPolyShader(g.lights, tex != null); - shader.start(); + // Rendering fill triangles, which can be lit and textured. + if (!renderingFill) { + shader = g.getPolyShader(g.lights, tex != null); + shader.start(); + renderingFill = true; + } } else { // Rendering line or point triangles, which are never lit nor textured. - if (tex != null) { - pgl.glBindTexture(tex.glTarget, 0); - pgl.disableTexturing(tex.glTarget); - tex = null; + if (!renderingStroke) { + if (tex != null) { + pgl.glBindTexture(tex.glTarget, 0); + pgl.disableTexturing(tex.glTarget); + tex = null; + } + + if (shader != null && shader.active()) { + shader.stop(); + } + + // If the renderer is 2D, then g.lights should always be false, + // so no need to worry about that. + shader = g.getPolyShader(g.lights, false); + shader.start(); + + renderingFill = false; + renderingStroke = true; } - - if (shader != null) { - shader.stop(); - } - - shader = g.getPolyShader(g.lights, false); - shader.start(); } int ioffset = cache.indexOffset[n]; @@ -4177,7 +4187,9 @@ public class PShapeOpenGL extends PShape { pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); } - shader.stop(); + if (shader != null && shader.active()) { + shader.stop(); + } if (tex != null) { pgl.glBindTexture(tex.glTarget, 0);