Moved shape creation and IO methods to PGraphicsOpenGL subclasses, check for 3D operations in PGraphics2D and PShape2D

This commit is contained in:
codeanticode
2012-05-21 22:31:27 +00:00
parent 53d78ec2ae
commit ea62e872a1
6 changed files with 768 additions and 539 deletions
@@ -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;
}
}
@@ -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<PVector> vertices = new ArrayList<PVector>();
ArrayList<PVector> normals = new ArrayList<PVector>();
ArrayList<PVector> textures = new ArrayList<PVector>();
ArrayList<OBJFace> faces = new ArrayList<OBJFace>();
ArrayList<OBJMaterial> materials = new ArrayList<OBJMaterial>();
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<PVector> vertices,
ArrayList<PVector> normals,
ArrayList<PVector> textures,
ArrayList<OBJFace> faces,
ArrayList<OBJMaterial> materials) {
Hashtable<String, Integer> mtlTable = new Hashtable<String, Integer>();
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<OBJMaterial> materials, Hashtable<String, Integer> 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<Integer> vertIdx;
ArrayList<Integer> texIdx;
ArrayList<Integer> normIdx;
int matIdx;
String name;
OBJFace() {
vertIdx = new ArrayList<Integer>();
texIdx = new ArrayList<Integer>();
normIdx = new ArrayList<Integer>();
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;
}
}
}
@@ -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<Integer> vertIdx;
ArrayList<Integer> texIdx;
ArrayList<Integer> normIdx;
int matIdx;
String name;
OBJFace() {
vertIdx = new ArrayList<Integer>();
texIdx = new ArrayList<Integer>();
normIdx = new ArrayList<Integer>();
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<PVector> vertices = new ArrayList<PVector>();
ArrayList<PVector> normals = new ArrayList<PVector>();
ArrayList<PVector> textures = new ArrayList<PVector>();
ArrayList<OBJFace> faces = new ArrayList<OBJFace>();
ArrayList<OBJMaterial> materials = new ArrayList<OBJMaterial>();
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<PVector> vertices,
ArrayList<PVector> normals,
ArrayList<PVector> textures,
ArrayList<OBJFace> faces,
ArrayList<OBJMaterial> materials) {
Hashtable<String, Integer> mtlTable = new Hashtable<String, Integer>();
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<OBJMaterial> materials, Hashtable<String, Integer> 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();
}
}
//////////////////////////////////////////////////////////////
@@ -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");
}
}
@@ -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;
}
@@ -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);