From 41f5c96c37fa3d7c2ed7e6e8052e65394f1d01b0 Mon Sep 17 00:00:00 2001 From: codeanticode Date: Tue, 16 Nov 2010 11:48:31 +0000 Subject: [PATCH] Implemented automatic normal calculation in A3D (this closes issue 345) --- .../core/src/processing/core/PConstants.java | 32 +++++++-- .../core/src/processing/core/PGraphics.java | 71 +++++++++---------- .../processing/core/PGraphicsAndroid3D.java | 53 ++++++++++++-- .../core/src/processing/core/PShape3D.java | 8 ++- 4 files changed, 117 insertions(+), 47 deletions(-) diff --git a/android/core/src/processing/core/PConstants.java b/android/core/src/processing/core/PConstants.java index fdbd9791b..0a7366336 100644 --- a/android/core/src/processing/core/PConstants.java +++ b/android/core/src/processing/core/PConstants.java @@ -81,6 +81,9 @@ public interface PConstants { // material properties + // TODO: check whether we still need them in PGraphicsAndroid3D and if yes + // how the work in combination with the global material properties (ambient, diffuse, + // emissive, specular colors). // Ambient color (usually to be kept the same as diffuse) // fill(_) sets both ambient and diffuse. @@ -105,10 +108,10 @@ public interface PConstants { static public final int ER = 32; static public final int EG = 33; static public final int EB = 34; - - // has this vertex been lit yet - static public final int BEEN_LIT = 35; - + + // has this vertex been assigned a normal yet + static public final int HAS_NORMAL = 35; + static public final int VERTEX_FIELD_COUNT = 36; @@ -371,6 +374,27 @@ public interface PConstants { static final int SHAPE = 5; + // normal modes. + + /** + * In the vertex mode, the user should specify a normal for + * each vertex. + */ + static final int VERTEX = 6; + + /** + * Normal vectors are automatically calculated if not set. + */ + static final int AUTO = 7; + + /** + * ... and, the constant SHAPE from text placement modes + * is used to set the SHAPE normal mode, where the same shape + * is used for all the vertices in a shape. This conflict in the + * name of constants should be solved (TODO). + */ + + // text alignment modes // are inherited from LEFT, CENTER, RIGHT diff --git a/android/core/src/processing/core/PGraphics.java b/android/core/src/processing/core/PGraphics.java index aeab2a7cb..2c2266f42 100644 --- a/android/core/src/processing/core/PGraphics.java +++ b/android/core/src/processing/core/PGraphics.java @@ -403,6 +403,8 @@ public class PGraphics extends PImage implements PConstants { protected float vertices[][] = new float[DEFAULT_VERTICES][VERTEX_FIELD_COUNT]; protected int vertexCount; // total number of vertices + + // This array allows to assign a different texture for each vertex. protected PImage verticesTexture[] = new PImage[DEFAULT_VERTICES]; // ........................................................ @@ -490,19 +492,9 @@ public class PGraphics extends PImage implements PConstants { // ........................................................ - /// normal calculated per triangle - static protected final int NORMAL_MODE_AUTO = 0; - /// one normal manually specified per shape - static protected final int NORMAL_MODE_SHAPE = 1; - /// normals specified for each shape vertex - static protected final int NORMAL_MODE_VERTEX = 2; - /// Current mode for normals, one of AUTO, SHAPE, or VERTEX protected int normalMode; - - /// Keep track of how many calls to normal, to determine the mode. - //protected int normalCount; - + /** Current normal vector. */ public float normalX, normalY, normalZ; @@ -703,6 +695,8 @@ public class PGraphics extends PImage implements PConstants { rectMode(CORNER); ellipseMode(DIAMETER); + + normalMode = AUTO; // no current font textFont = null; @@ -875,6 +869,14 @@ public class PGraphics extends PImage implements PConstants { } + /** + * Sets the normal mode, either AUTO, SHAPE or VERTEX. + */ + public void normalMode(int mode) { + this.normalMode = mode; + } + + /** * Sets the current normal vector. Only applies with 3D rendering * and inside a beginShape/endShape block. @@ -883,10 +885,6 @@ public class PGraphics extends PImage implements PConstants { * allowing you to specify a vector perpendicular to the surface * of the shape, which determines how lighting affects it. *

- * For the most part, PGraphics3D will attempt to automatically - * assign normals to shapes, but since that's imperfect, - * this is a better option when you want more control. - *

* For people familiar with OpenGL, this function is basically * identical to glNormal3f(). */ @@ -894,25 +892,6 @@ public class PGraphics extends PImage implements PConstants { normalX = nx; normalY = ny; normalZ = nz; - - // if drawing a shape and the normal hasn't been set yet, - // then we need to set the normals for each vertex so far - if (shape != 0) { - if (normalMode == NORMAL_MODE_AUTO) { - // either they set the normals, or they don't [0149] -// for (int i = vertex_start; i < vertexCount; i++) { -// vertices[i][NX] = normalX; -// vertices[i][NY] = normalY; -// vertices[i][NZ] = normalZ; -// } - // One normal per begin/end shape - normalMode = NORMAL_MODE_SHAPE; - - } else if (normalMode == NORMAL_MODE_SHAPE) { - // a separate normal for each vertex - normalMode = NORMAL_MODE_VERTEX; - } - } } @@ -1078,11 +1057,31 @@ public class PGraphics extends PImage implements PConstants { vertex[V] = textureV; } + float norm2 = normalX * normalX + normalY * normalY + normalZ * normalZ; + if (norm2 < EPSILON) { + vertex[HAS_NORMAL] = 0; + } else { + if (Math.abs(norm2 - 1) < EPSILON) { + float norm = PApplet.sqrt(norm2); + normalX /= norm; + normalY /= norm; + normalZ /= norm; + } + vertex[HAS_NORMAL] = 1; + } + vertex[NX] = normalX; vertex[NY] = normalY; vertex[NZ] = normalZ; - - vertex[BEEN_LIT] = 0; + + if (normalMode == VERTEX || normalMode == AUTO) { + // The AUTO mode works as the VERTEX mode (it allows the user to specify + // per-vertex normals) but if there are triangles where some of the vertices + // have no normal, then it will calculate the normal for that triangle (in + // the renderTriangles() method of PGraphicsAndroid3D). + normalX = normalY = normalZ = 0; + } + verticesTexture[vertexCount] = textureImage; diff --git a/android/core/src/processing/core/PGraphicsAndroid3D.java b/android/core/src/processing/core/PGraphicsAndroid3D.java index d042fa62d..6458cdf7d 100644 --- a/android/core/src/processing/core/PGraphicsAndroid3D.java +++ b/android/core/src/processing/core/PGraphicsAndroid3D.java @@ -163,7 +163,8 @@ public class PGraphicsAndroid3D extends PGraphics { /** Used to store empty values to be passed when a light has no ambient, diffuse or specular component **/ public float[] zeroLight = { 0.0f, 0.0f, 0.0f, 1.0f }; - public float[] baseLight = { 0.0f, 0.0f, 0.0f, 1.0f }; + /** Default ambient light for the entire scene **/ + public float[] baseLight = { 0.05f, 0.05f, 0.05f, 1.0f }; boolean lightsAllocated = false; @@ -866,6 +867,9 @@ public class PGraphicsAndroid3D extends PGraphics { shapeFirst = 0; + // The current normal vector is set to zero. + normalX = normalY = normalZ = 0; + if (primarySurface) { // This instance of PGraphicsAndroid3D is the primary (onscreen) drawing surface. @@ -1164,7 +1168,7 @@ public class PGraphicsAndroid3D extends PGraphics { textureImage = null; textureImagePrev = null; - normalMode = NORMAL_MODE_AUTO; + normalMode = AUTO; } // public void edge(boolean e) @@ -1232,6 +1236,10 @@ public class PGraphicsAndroid3D extends PGraphics { } shape = 0; + + if (normalMode == SHAPE) { + normalX = normalY = normalZ = 0; + } } protected void endShapeStroke(int mode) { @@ -1892,6 +1900,41 @@ public class PGraphicsAndroid3D extends PGraphics { float b[] = vertices[triangles[i][VERTEX2]]; float c[] = vertices[triangles[i][VERTEX3]]; + if (normalMode == AUTO && (a[HAS_NORMAL] == 0 || + b[HAS_NORMAL] == 0 || + c[HAS_NORMAL] == 0)) { + // Ok, some of the vertices defining the current triangle have not been + // assigned a normal, and the normal mode is AUTO, so we generate the normal + // for all the vertices of this triangle. + + // Assuming CW vertex ordering, so the outside direction for this triangle + // should be given by the cross product (b - a) x (b - c): + float x1 = b[X] - a[X]; + float y1 = b[Y] - a[Y]; + float z1 = b[Z] - a[Z]; + + float x2 = b[X] - c[X]; + float y2 = b[Y] - c[Y]; + float z2 = b[Z] - c[Z]; + + float cx = y1 * z2 - y2 * z1; + float cy = z1 * x2 - z2 * x1; + float cz = x1 * y2 - x2 * y1; + + float norm = PApplet.sqrt(cx * cx + cy * cy + cz * cz); + + cx /= norm; + cy /= norm; + cz /= norm; + + // Same normal vector assigned to the three vertices: + a[NX] = b[NX] = c[NX] = cx; + a[NY] = b[NY] = c[NY] = cy; + a[NZ] = b[NZ] = c[NZ] = cz; + + a[HAS_NORMAL] = b[HAS_NORMAL] = c[HAS_NORMAL] = 1; + } + float uscale = 1.0f; float vscale = 1.0f; float cx = 0.0f; @@ -5574,9 +5617,9 @@ public class PGraphicsAndroid3D extends PGraphics { float t = 1.0f - c; mult((t*rx*rx) + c, (t*rx*ry) - (s*rz), (t*rx*rz) + (s*ry), 0, - (t*rx*ry) + (s*rz), (t*ry*ry) + c, (t*ry*rz) - (s*rx), 0, - (t*rx*rz) - (s*ry), (t*ry*rz) + (s*rx), (t*rz*rz) + c, 0, - 0, 0, 0, 1); + (t*rx*ry) + (s*rz), (t*ry*ry) + c, (t*ry*rz) - (s*rx), 0, + (t*rx*rz) - (s*ry), (t*ry*rz) + (s*rx), (t*rz*rz) + c, 0, + 0, 0, 0, 1); } public void scale(float sx, float sy, float sz) { diff --git a/android/core/src/processing/core/PShape3D.java b/android/core/src/processing/core/PShape3D.java index 2e70c37e1..98bd56f2d 100644 --- a/android/core/src/processing/core/PShape3D.java +++ b/android/core/src/processing/core/PShape3D.java @@ -2317,7 +2317,7 @@ public class PShape3D extends PShape implements PConstants { // Loading texture map. String texname = elements[1]; currentMtl.kdMap = parent.loadImage(texname); - // Texture orientation in Processing is inverted with respecto to OpenGL. + // Texture orientation in Processing is inverted with respect to OpenGL. currentMtl.kdMap.getTexture().setFlippedY(true); } else if (elements[0].equals("Ka") && elements.length > 3) { // The ambient color of the material @@ -2358,6 +2358,9 @@ public class PShape3D extends PShape implements PConstants { int tMode0 = a3d.textureMode; a3d.textureMode = NORMAL; + int nMode0 = a3d.normalMode; + a3d.normalMode = AUTO; + // Using RGB mode for coloring. int cMode0 = a3d.colorMode; a3d.colorMode = RGB; @@ -2502,9 +2505,10 @@ public class PShape3D extends PShape implements PConstants { a3d.endShapeRecorderImpl(this); - // Restore texture and color modes. + // Restore texture, color, and normal modes. a3d.textureMode = tMode0; a3d.colorMode = cMode0; + a3d.normalMode = nMode0; // Restore colors a3d.calcR = specularR0;