1
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
4
Part of the Processing project - http://processing.org
6
Copyright (c) 2004-08 Ben Fry and Casey Reas
7
Copyright (c) 2001-04 Massachusetts Institute of Technology
9
This library is free software; you can redistribute it and/or
10
modify it under the terms of the GNU Lesser General Public
11
License as published by the Free Software Foundation; either
12
version 2.1 of the License, or (at your option) any later version.
14
This library is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
Lesser General Public License for more details.
19
You should have received a copy of the GNU Lesser General
20
Public License along with this library; if not, write to the
21
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
22
Boston, MA 02111-1307 USA
25
package processing.core;
27
import java.awt.Toolkit;
28
import java.awt.image.*;
33
* Subclass of PGraphics that handles 3D rendering.
34
* It can render 3D inside a browser window and requires no plug-ins.
36
* The renderer is mostly set up based on the structure of the OpenGL API,
37
* if you have questions about specifics that aren't covered here,
38
* look for reference on the OpenGL implementation of a similar feature.
40
* Lighting and camera implementation by Simon Greenwold.
42
public class PGraphics3D extends PGraphics {
44
/** The depth buffer. */
45
public float[] zbuffer;
47
// ........................................................
49
/** The modelview matrix. */
50
public PMatrix3D modelview;
52
/** Inverse modelview matrix, used for lighting. */
53
public PMatrix3D modelviewInv;
56
* The camera matrix, the modelview will be set to this on beginDraw.
58
public PMatrix3D camera;
60
/** Inverse camera matrix */
61
protected PMatrix3D cameraInv;
63
/** Camera field of view. */
64
public float cameraFOV;
66
/** Position of the camera. */
67
public float cameraX, cameraY, cameraZ;
68
public float cameraNear, cameraFar;
69
/** Aspect ratio of camera's view. */
70
public float cameraAspect;
72
/** Current projection matrix. */
73
public PMatrix3D projection;
76
//////////////////////////////////////////////////////////////
80
* Maximum lights by default is 8, which is arbitrary for this renderer,
81
* but is the minimum defined by OpenGL
83
public static final int MAX_LIGHTS = 8;
85
public int lightCount = 0;
88
public int[] lightType;
90
/** Light positions */
91
//public float[][] lightPosition;
92
public PVector[] lightPosition;
94
/** Light direction (normalized vector) */
95
//public float[][] lightNormal;
96
public PVector[] lightNormal;
99
public float[] lightFalloffConstant;
100
public float[] lightFalloffLinear;
101
public float[] lightFalloffQuadratic;
103
/** Light spot angle */
104
public float[] lightSpotAngle;
106
/** Cosine of light spot angle */
107
public float[] lightSpotAngleCos;
109
/** Light spot concentration */
110
public float[] lightSpotConcentration;
112
/** Diffuse colors for lights.
113
* For an ambient light, this will hold the ambient color.
114
* Internally these are stored as numbers between 0 and 1. */
115
public float[][] lightDiffuse;
117
/** Specular colors for lights.
118
Internally these are stored as numbers between 0 and 1. */
119
public float[][] lightSpecular;
121
/** Current specular color for lighting */
122
public float[] currentLightSpecular;
124
/** Current light falloff */
125
public float currentLightFalloffConstant;
126
public float currentLightFalloffLinear;
127
public float currentLightFalloffQuadratic;
130
//////////////////////////////////////////////////////////////
133
static public final int TRI_DIFFUSE_R = 0;
134
static public final int TRI_DIFFUSE_G = 1;
135
static public final int TRI_DIFFUSE_B = 2;
136
static public final int TRI_DIFFUSE_A = 3;
137
static public final int TRI_SPECULAR_R = 4;
138
static public final int TRI_SPECULAR_G = 5;
139
static public final int TRI_SPECULAR_B = 6;
140
static public final int TRI_COLOR_COUNT = 7;
142
// ........................................................
144
// Whether or not we have to worry about vertex position for lighting calcs
145
private boolean lightingDependsOnVertexPosition;
147
static final int LIGHT_AMBIENT_R = 0;
148
static final int LIGHT_AMBIENT_G = 1;
149
static final int LIGHT_AMBIENT_B = 2;
150
static final int LIGHT_DIFFUSE_R = 3;
151
static final int LIGHT_DIFFUSE_G = 4;
152
static final int LIGHT_DIFFUSE_B = 5;
153
static final int LIGHT_SPECULAR_R = 6;
154
static final int LIGHT_SPECULAR_G = 7;
155
static final int LIGHT_SPECULAR_B = 8;
156
static final int LIGHT_COLOR_COUNT = 9;
158
// Used to shuttle lighting calcs around
159
// (no need to re-allocate all the time)
160
protected float[] tempLightingContribution = new float[LIGHT_COLOR_COUNT];
161
// protected float[] worldNormal = new float[4];
163
/// Used in lightTriangle(). Allocated here once to avoid re-allocating
164
protected PVector lightTriangleNorm = new PVector();
166
// ........................................................
169
* This is turned on at beginCamera, and off at endCamera
170
* Currently we don't support nested begin/end cameras.
171
* If we wanted to, this variable would have to become a stack.
173
protected boolean manipulatingCamera;
175
float[][] matrixStack = new float[MATRIX_STACK_DEPTH][16];
176
float[][] matrixInvStack = new float[MATRIX_STACK_DEPTH][16];
177
int matrixStackDepth;
179
// These two matrices always point to either the modelview
180
// or the modelviewInv, but they are swapped during
181
// when in camera manipulation mode. That way camera transforms
182
// are automatically accumulated in inverse on the modelview matrix.
183
protected PMatrix3D forwardTransform;
184
protected PMatrix3D reverseTransform;
186
// Added by ewjordan for accurate texturing purposes. Screen plane is
187
// not scaled to pixel-size, so these manually keep track of its size
188
// from frustum() calls. Sorry to add public vars, is there a way
189
// to compute these from something publicly available without matrix ops?
190
// (used once per triangle in PTriangle with ENABLE_ACCURATE_TEXTURES)
191
protected float leftScreen;
192
protected float rightScreen;
193
protected float topScreen;
194
protected float bottomScreen;
195
protected float nearPlane; //depth of near clipping plane
197
/** true if frustum has been called to set perspective, false if ortho */
198
private boolean frustumMode = false;
201
* Use PSmoothTriangle for rendering instead of PTriangle?
202
* Usually set by calling smooth() or noSmooth()
204
static protected boolean s_enableAccurateTextures = false; //maybe just use smooth instead?
206
/** Used for anti-aliased and perspective corrected rendering. */
207
public PSmoothTriangle smoothTriangle;
210
// ........................................................
212
// pos of first vertex of current shape in vertices array
213
protected int shapeFirst;
215
// i think vertex_end is actually the last vertex in the current shape
216
// and is separate from vertexCount for occasions where drawing happens
217
// on endDraw() with all the triangles being depth sorted
218
protected int shapeLast;
220
// vertices may be added during clipping against the near plane.
221
protected int shapeLastPlusClipped;
223
// used for sorting points when triangulating a polygon
224
// warning - maximum number of vertices for a polygon is DEFAULT_VERTICES
225
protected int vertexOrder[] = new int[DEFAULT_VERTICES];
227
// ........................................................
229
// This is done to keep track of start/stop information for lines in the
230
// line array, so that lines can be shown as a single path, rather than just
231
// individual segments. Currently only in use inside PGraphicsOpenGL.
232
protected int pathCount;
233
protected int[] pathOffset = new int[64];
234
protected int[] pathLength = new int[64];
236
// ........................................................
238
// line & triangle fields (note that these overlap)
239
// static protected final int INDEX = 0; // shape index
240
static protected final int VERTEX1 = 0;
241
static protected final int VERTEX2 = 1;
242
static protected final int VERTEX3 = 2; // (triangles only)
243
/** used to store the strokeColor int for efficient drawing. */
244
static protected final int STROKE_COLOR = 1; // (points only)
245
static protected final int TEXTURE_INDEX = 3; // (triangles only)
246
//static protected final int STROKE_MODE = 2; // (lines only)
247
//static protected final int STROKE_WEIGHT = 3; // (lines only)
249
static protected final int POINT_FIELD_COUNT = 2; //4
250
static protected final int LINE_FIELD_COUNT = 2; //4
251
static protected final int TRIANGLE_FIELD_COUNT = 4;
254
static final int DEFAULT_POINTS = 512;
255
protected int[][] points = new int[DEFAULT_POINTS][POINT_FIELD_COUNT];
256
protected int pointCount;
259
static final int DEFAULT_LINES = 512;
260
public PLine line; // used for drawing
261
protected int[][] lines = new int[DEFAULT_LINES][LINE_FIELD_COUNT];
262
protected int lineCount;
265
static final int DEFAULT_TRIANGLES = 256;
266
public PTriangle triangle;
267
protected int[][] triangles =
268
new int[DEFAULT_TRIANGLES][TRIANGLE_FIELD_COUNT];
269
protected float triangleColors[][][] =
270
new float[DEFAULT_TRIANGLES][3][TRI_COLOR_COUNT];
271
protected int triangleCount; // total number of triangles
273
// cheap picking someday
274
//public int shape_index;
276
// ........................................................
278
static final int DEFAULT_TEXTURES = 3;
279
protected PImage[] textures = new PImage[DEFAULT_TEXTURES];
282
// ........................................................
285
MemoryImageSource mis;
288
//////////////////////////////////////////////////////////////
291
public PGraphics3D() { }
294
//public void setParent(PApplet parent)
297
//public void setPrimary(boolean primary)
300
//public void setPath(String path)
304
* Called in response to a resize event, handles setting the
305
* new width and height internally, as well as re-allocating
306
* the pixel buffer for the new size.
308
* Note that this will nuke any cameraMode() settings.
310
public void setSize(int iwidth, int iheight) { // ignore
314
height1 = height - 1;
319
// init lights (in resize() instead of allocate() b/c needed by opengl)
320
lightType = new int[MAX_LIGHTS];
321
lightPosition = new PVector[MAX_LIGHTS];
322
lightNormal = new PVector[MAX_LIGHTS];
323
for (int i = 0; i < MAX_LIGHTS; i++) {
324
lightPosition[i] = new PVector();
325
lightNormal[i] = new PVector();
327
lightDiffuse = new float[MAX_LIGHTS][3];
328
lightSpecular = new float[MAX_LIGHTS][3];
329
lightFalloffConstant = new float[MAX_LIGHTS];
330
lightFalloffLinear = new float[MAX_LIGHTS];
331
lightFalloffQuadratic = new float[MAX_LIGHTS];
332
lightSpotAngle = new float[MAX_LIGHTS];
333
lightSpotAngleCos = new float[MAX_LIGHTS];
334
lightSpotConcentration = new float[MAX_LIGHTS];
335
currentLightSpecular = new float[3];
337
projection = new PMatrix3D();
338
modelview = new PMatrix3D();
339
modelviewInv = new PMatrix3D();
341
// modelviewStack = new float[MATRIX_STACK_DEPTH][16];
342
// modelviewInvStack = new float[MATRIX_STACK_DEPTH][16];
343
// modelviewStackPointer = 0;
345
forwardTransform = modelview;
346
reverseTransform = modelviewInv;
348
// init perspective projection based on new dimensions
349
cameraFOV = 60 * DEG_TO_RAD; // at least for now
350
cameraX = width / 2.0f;
351
cameraY = height / 2.0f;
352
cameraZ = cameraY / ((float) Math.tan(cameraFOV / 2.0f));
353
cameraNear = cameraZ / 10.0f;
354
cameraFar = cameraZ * 10.0f;
355
cameraAspect = (float)width / (float)height;
357
camera = new PMatrix3D();
358
cameraInv = new PMatrix3D();
360
// set up the default camera
363
// defaults to perspective, if the user has setup up their
364
// own projection, they'll need to fix it after resize anyway.
365
// this helps the people who haven't set up their own projection.
370
protected void allocate() {
371
//System.out.println(this + " allocating for " + width + " " + height);
372
//new Exception().printStackTrace();
374
pixelCount = width * height;
375
pixels = new int[pixelCount];
376
zbuffer = new float[pixelCount];
378
if (primarySurface) {
379
cm = new DirectColorModel(32, 0x00ff0000, 0x0000ff00, 0x000000ff);;
380
mis = new MemoryImageSource(width, height, pixels, 0, width);
381
mis.setFullBufferUpdates(true);
382
mis.setAnimated(true);
383
image = Toolkit.getDefaultToolkit().createImage(mis);
386
// when not the main drawing surface, need to set the zbuffer,
387
// because there's a possibility that background() will not be called
388
Arrays.fill(zbuffer, Float.MAX_VALUE);
391
line = new PLine(this);
392
triangle = new PTriangle(this);
393
smoothTriangle = new PSmoothTriangle(this);
397
//public void dispose()
400
////////////////////////////////////////////////////////////
403
//public boolean canDraw()
406
public void beginDraw() {
407
// need to call defaults(), but can only be done when it's ok
408
// to draw (i.e. for opengl, no drawing can be done outside
409
// beginDraw/endDraw).
410
if (!settingsInited) defaultSettings();
412
resetMatrix(); // reset model matrix
417
modelview.set(camera);
418
modelviewInv.set(cameraInv);
420
// clear out the lights, they'll have to be turned on again
422
lightingDependsOnVertexPosition = false;
423
lightFalloff(1, 0, 0);
424
lightSpecular(0, 0, 0);
429
if (line != null) line.reset(); // is this necessary?
434
if (triangle != null) triangle.reset(); // necessary?
447
* See notes in PGraphics.
448
* If z-sorting has been turned on, then the triangles will
449
* all be quicksorted here (to make alpha work more properly)
450
* and then blit to the screen.
452
public void endDraw() {
453
// no need to z order and render
454
// shapes were already rendered in endShape();
455
// (but can't return, since needs to update memimgsrc)
456
if (hints[ENABLE_DEPTH_SORT]) {
460
mis.newPixels(pixels, cm, 0, width);
462
// mark pixels as having been updated, so that they'll work properly
463
// when this PGraphics is drawn using image().
468
////////////////////////////////////////////////////////////
471
//protected void checkSettings()
474
protected void defaultSettings() {
475
super.defaultSettings();
477
manipulatingCamera = false;
478
forwardTransform = modelview;
479
reverseTransform = modelviewInv;
481
// set up the default camera
484
// defaults to perspective, if the user has setup up their
485
// own projection, they'll need to fix it after resize anyway.
486
// this helps the people who haven't set up their own projection.
489
// easiest for beginners
498
//protected void reapplySettings()
501
////////////////////////////////////////////////////////////
504
public void hint(int which) {
505
if (which == DISABLE_DEPTH_SORT) {
507
} else if (which == DISABLE_DEPTH_TEST) {
508
if (zbuffer != null) { // will be null in OpenGL and others
509
Arrays.fill(zbuffer, Float.MAX_VALUE);
516
//////////////////////////////////////////////////////////////
519
//public void beginShape()
522
public void beginShape(int kind) {
525
// shape_index = shape_index + 1;
526
// if (shape_index == -1) {
530
if (hints[ENABLE_DEPTH_SORT]) {
531
// continue with previous vertex, line and triangle count
532
// all shapes are rendered at endDraw();
533
shapeFirst = vertexCount;
537
// reset vertex, line and triangle information
538
// every shape is rendered at endShape();
540
if (line != null) line.reset(); // necessary?
543
if (triangle != null) triangle.reset(); // necessary?
548
curveVertexCount = 0;
549
normalMode = NORMAL_MODE_AUTO;
554
//public void normal(float nx, float ny, float nz)
557
//public void textureMode(int mode)
560
public void texture(PImage image) {
561
textureImage = image;
563
if (textureIndex == textures.length - 1) {
564
textures = (PImage[]) PApplet.expand(textures);
566
if (textures[textureIndex] != null) { // ???
569
textures[textureIndex] = image;
573
public void vertex(float x, float y) {
574
// override so that the default 3D implementation will be used,
575
// which will pick up all 3D settings (e.g. emissive, ambient)
580
//public void vertex(float x, float y, float z)
583
public void vertex(float x, float y, float u, float v) {
584
// see vertex(x, y) for note
585
vertex(x, y, 0, u, v);
589
//public void vertex(float x, float y, float z, float u, float v)
592
//public void breakShape()
595
//public void endShape()
598
public void endShape(int mode) {
599
shapeLast = vertexCount;
600
shapeLastPlusClipped = shapeLast;
602
// don't try to draw if there are no vertices
603
// (fixes a bug in LINE_LOOP that re-adds a nonexistent vertex)
604
if (vertexCount == 0) {
609
// convert points from model (X/Y/Z) to camera space (VX/VY/VZ).
610
// Do this now because we will be clipping them on add_triangle.
611
endShapeModelToCamera(shapeFirst, shapeLast);
614
endShapeStroke(mode);
617
if (fill || textureImage != null) {
621
// transform, light, and clip
622
endShapeLighting(lightCount > 0 && fill);
624
// convert points from camera space (VX, VY, VZ) to screen space (X, Y, Z)
625
// (this appears to be wasted time with the OpenGL renderer)
626
endShapeCameraToScreen(shapeFirst, shapeLastPlusClipped);
628
// render shape and fill here if not saving the shapes for later
629
// if true, the shapes will be rendered on endDraw
630
if (!hints[ENABLE_DEPTH_SORT]) {
631
if (fill || textureImage != null) {
632
if (triangleCount > 0) {
633
renderTriangles(0, triangleCount);
635
rawTriangles(0, triangleCount);
641
if (pointCount > 0) {
642
renderPoints(0, pointCount);
644
rawPoints(0, pointCount);
650
renderLines(0, lineCount);
652
rawLines(0, lineCount);
664
protected void endShapeModelToCamera(int start, int stop) {
665
for (int i = start; i < stop; i++) {
666
float vertex[] = vertices[i];
669
modelview.m00*vertex[X] + modelview.m01*vertex[Y] +
670
modelview.m02*vertex[Z] + modelview.m03;
672
modelview.m10*vertex[X] + modelview.m11*vertex[Y] +
673
modelview.m12*vertex[Z] + modelview.m13;
675
modelview.m20*vertex[X] + modelview.m21*vertex[Y] +
676
modelview.m22*vertex[Z] + modelview.m23;
678
modelview.m30*vertex[X] + modelview.m31*vertex[Y] +
679
modelview.m32*vertex[Z] + modelview.m33;
682
if (vertex[VW] != 0 && vertex[VW] != 1) {
683
vertex[VX] /= vertex[VW];
684
vertex[VY] /= vertex[VW];
685
vertex[VZ] /= vertex[VW];
692
protected void endShapeStroke(int mode) {
696
int stop = shapeLast;
697
for (int i = shapeFirst; i < stop; i++) {
698
// if (strokeWeight == 1) {
701
// addLineBreak(); // total overkill for points
710
// store index of first vertex
711
int first = lineCount;
712
int stop = shapeLast - 1;
713
//increment = (shape == LINES) ? 2 : 1;
715
// for LINE_STRIP and LINE_LOOP, make this all one path
716
if (shape != LINES) addLineBreak();
718
for (int i = shapeFirst; i < stop; i += 2) {
719
// for LINES, make a new path for each segment
720
if (shape == LINES) addLineBreak();
724
// for LINE_LOOP, close the loop with a final segment
725
//if (shape == LINE_LOOP) {
727
addLine(stop, lines[first][VERTEX1]);
734
for (int i = shapeFirst; i < shapeLast-2; i += 3) {
736
//counter = i - vertex_start;
746
// first draw all vertices as a line strip
747
int stop = shapeLast-1;
750
for (int i = shapeFirst; i < stop; i++) {
751
//counter = i - vertex_start;
755
// then draw from vertex (n) to (n+2)
757
for (int i = shapeFirst; i < stop; i++) {
766
// this just draws a series of line segments
767
// from the center to each exterior point
768
for (int i = shapeFirst + 1; i < shapeLast; i++) {
770
addLine(shapeFirst, i);
773
// then a single line loop around the outside.
775
for (int i = shapeFirst + 1; i < shapeLast-1; i++) {
779
addLine(shapeLast-1, shapeFirst + 1);
785
for (int i = shapeFirst; i < shapeLast; i += 4) {
787
//counter = i - vertex_start;
798
for (int i = shapeFirst; i < shapeLast - 3; i += 2) {
810
// store index of first vertex
811
int stop = shapeLast - 1;
814
for (int i = shapeFirst; i < stop; i++) {
818
// draw the last line connecting back to the first point in poly
819
addLine(stop, shapeFirst); //lines[first][VERTEX1]);
827
protected void endShapeFill() {
831
int stop = shapeLast - 1;
832
for (int i = shapeFirst + 1; i < stop; i++) {
833
addTriangle(shapeFirst, i, i+1);
840
int stop = shapeLast - 2;
841
for (int i = shapeFirst; i < stop; i += 3) {
842
// have to switch between clockwise/counter-clockwise
843
// otherwise the feller is backwards and renderer won't draw
845
addTriangle(i, i+2, i+1);
847
addTriangle(i, i+1, i+2);
855
int stop = shapeLast - 2;
856
for (int i = shapeFirst; i < stop; i++) {
857
// have to switch between clockwise/counter-clockwise
858
// otherwise the feller is backwards and renderer won't draw
860
addTriangle(i, i+2, i+1);
862
addTriangle(i, i+1, i+2);
870
int stop = vertexCount-3;
871
for (int i = shapeFirst; i < stop; i += 4) {
873
addTriangle(i, i+1, i+2);
875
addTriangle(i, i+2, i+3);
882
int stop = vertexCount-3;
883
for (int i = shapeFirst; i < stop; i += 2) {
885
addTriangle(i+0, i+2, i+1);
887
addTriangle(i+2, i+3, i+1);
894
addPolygonTriangles();
901
protected void endShapeLighting(boolean lights) {
903
// If the lighting does not depend on vertex position and there is a single
904
// normal specified for this shape, go ahead and apply the same lighting
905
// contribution to every vertex in this shape (one lighting calc!)
906
if (!lightingDependsOnVertexPosition && normalMode == NORMAL_MODE_SHAPE) {
907
calcLightingContribution(shapeFirst, tempLightingContribution);
908
for (int tri = 0; tri < triangleCount; tri++) {
909
lightTriangle(tri, tempLightingContribution);
911
} else { // Otherwise light each triangle individually...
912
for (int tri = 0; tri < triangleCount; tri++) {
917
for (int tri = 0; tri < triangleCount; tri++) {
918
int index = triangles[tri][VERTEX1];
919
copyPrelitVertexColor(tri, index, 0);
920
index = triangles[tri][VERTEX2];
921
copyPrelitVertexColor(tri, index, 1);
922
index = triangles[tri][VERTEX3];
923
copyPrelitVertexColor(tri, index, 2);
929
protected void endShapeCameraToScreen(int start, int stop) {
930
for (int i = start; i < stop; i++) {
931
float vx[] = vertices[i];
934
projection.m00*vx[VX] + projection.m01*vx[VY] +
935
projection.m02*vx[VZ] + projection.m03*vx[VW];
937
projection.m10*vx[VX] + projection.m11*vx[VY] +
938
projection.m12*vx[VZ] + projection.m13*vx[VW];
940
projection.m20*vx[VX] + projection.m21*vx[VY] +
941
projection.m22*vx[VZ] + projection.m23*vx[VW];
943
projection.m30*vx[VX] + projection.m31*vx[VY] +
944
projection.m32*vx[VZ] + projection.m33*vx[VW];
946
if (ow != 0 && ow != 1) {
947
ox /= ow; oy /= ow; oz /= ow;
950
vx[TX] = width * (1 + ox) / 2.0f;
951
vx[TY] = height * (1 + oy) / 2.0f;
952
vx[TZ] = (oz + 1) / 2.0f;
958
/////////////////////////////////////////////////////////////////////////////
963
protected void addPoint(int a) {
964
if (pointCount == points.length) {
965
int[][] temp = new int[pointCount << 1][LINE_FIELD_COUNT];
966
System.arraycopy(points, 0, temp, 0, lineCount);
969
points[pointCount][VERTEX1] = a;
970
//points[pointCount][STROKE_MODE] = strokeCap | strokeJoin;
971
points[pointCount][STROKE_COLOR] = strokeColor;
972
//points[pointCount][STROKE_WEIGHT] = (int) (strokeWeight + 0.5f); // hmm
977
protected void renderPoints(int start, int stop) {
978
if (strokeWeight != 1) {
979
for (int i = start; i < stop; i++) {
980
float[] a = vertices[points[i][VERTEX1]];
981
renderLineVertices(a, a);
984
for (int i = start; i < stop; i++) {
985
float[] a = vertices[points[i][VERTEX1]];
986
int sx = (int) (a[TX] + 0.4999f);
987
int sy = (int) (a[TY] + 0.4999f);
988
if (sx >= 0 && sx < width && sy >= 0 && sy < height) {
989
int index = sy*width + sx;
990
pixels[index] = points[i][STROKE_COLOR];
991
zbuffer[index] = a[TZ];
998
// alternative implementations of point rendering code...
1001
int sx = (int) (screenX(x, y, z) + 0.5f);
1002
int sy = (int) (screenY(x, y, z) + 0.5f);
1004
int index = sy*width + sx;
1005
pixels[index] = strokeColor;
1006
zbuffer[index] = screenZ(x, y, z);
1011
protected void renderPoints(int start, int stop) {
1012
for (int i = start; i < stop; i++) {
1013
float a[] = vertices[points[i][VERTEX1]];
1017
line.setIntensities(a[SR], a[SG], a[SB], a[SA],
1018
a[SR], a[SG], a[SB], a[SA]);
1020
line.setVertices(a[TX], a[TY], a[TZ],
1021
a[TX] + 0.5f, a[TY] + 0.5f, a[TZ] + 0.5f);
1029
// handle points with an actual stroke weight (or scaled by renderer)
1030
private void point3(float x, float y, float z, int color) {
1031
// need to get scaled version of the stroke
1032
float x1 = screenX(x - 0.5f, y - 0.5f, z);
1033
float y1 = screenY(x - 0.5f, y - 0.5f, z);
1034
float x2 = screenX(x + 0.5f, y + 0.5f, z);
1035
float y2 = screenY(x + 0.5f, y + 0.5f, z);
1037
float weight = (abs(x2 - x1) + abs(y2 - y1)) / 2f;
1038
if (weight < 1.5f) {
1039
int xx = (int) ((x1 + x2) / 2f);
1040
int yy = (int) ((y1 + y2) / 2f);
1041
//point0(xx, yy, z, color);
1042
zbuffer[yy*width + xx] = screenZ(x, y, z);
1046
// actually has some weight, need to draw shapes instead
1053
protected void rawPoints(int start, int stop) {
1054
raw.colorMode(RGB, 1);
1056
raw.strokeWeight(vertices[lines[start][VERTEX1]][SW]);
1057
raw.beginShape(POINTS);
1059
for (int i = start; i < stop; i++) {
1060
float a[] = vertices[lines[i][VERTEX1]];
1064
raw.stroke(a[SR], a[SG], a[SB], a[SA]);
1065
raw.vertex(a[VX] / a[VW], a[VY] / a[VW], a[VZ] / a[VW]);
1067
} else { // if is2D()
1068
raw.stroke(a[SR], a[SG], a[SB], a[SA]);
1069
raw.vertex(a[TX], a[TY]);
1077
/////////////////////////////////////////////////////////////////////////////
1083
* Begin a new section of stroked geometry.
1085
protected final void addLineBreak() {
1086
if (pathCount == pathOffset.length) {
1087
pathOffset = PApplet.expand(pathOffset);
1088
pathLength = PApplet.expand(pathLength);
1090
pathOffset[pathCount] = lineCount;
1091
pathLength[pathCount] = 0;
1096
protected void addLine(int a, int b) {
1097
addLineWithClip(a, b);
1101
protected final void addLineWithClip(int a, int b) {
1102
float az = vertices[a][VZ];
1103
float bz = vertices[b][VZ];
1104
if (az > cameraNear) {
1105
if (bz > cameraNear) {
1108
int cb = interpolateClipVertex(a, b);
1109
addLineWithoutClip(cb, b);
1113
if (bz <= cameraNear) {
1114
addLineWithoutClip(a, b);
1117
int cb = interpolateClipVertex(a, b);
1118
addLineWithoutClip(a, cb);
1124
protected final void addLineWithoutClip(int a, int b) {
1125
if (lineCount == lines.length) {
1126
int temp[][] = new int[lineCount<<1][LINE_FIELD_COUNT];
1127
System.arraycopy(lines, 0, temp, 0, lineCount);
1130
lines[lineCount][VERTEX1] = a;
1131
lines[lineCount][VERTEX2] = b;
1133
//lines[lineCount][STROKE_MODE] = strokeCap | strokeJoin;
1134
//lines[lineCount][STROKE_WEIGHT] = (int) (strokeWeight + 0.5f); // hmm
1137
// mark this piece as being part of the current path
1138
pathLength[pathCount-1]++;
1142
protected void renderLines(int start, int stop) {
1143
for (int i = start; i < stop; i++) {
1144
renderLineVertices(vertices[lines[i][VERTEX1]],
1145
vertices[lines[i][VERTEX2]]);
1150
protected void renderLineVertices(float[] a, float[] b) {
1151
// 2D hack added by ewjordan 6/13/07
1152
// Offset coordinates by a little bit if drawing 2D graphics.
1153
// http://dev.processing.org/bugs/show_bug.cgi?id=95
1155
// This hack fixes a bug caused by numerical precision issues when
1156
// applying the 3D transformations to coordinates in the screen plane
1157
// that should actually not be altered under said transformations.
1158
// It will not be applied if any transformations other than translations
1159
// are active, nor should it apply in OpenGL mode (PGraphicsOpenGL
1160
// overrides render_lines(), so this should be fine).
1161
// This fix exposes a last-pixel bug in the lineClipCode() function
1162
// of PLine.java, so that fix must remain in place if this one is used.
1164
// Note: the "true" fix for this bug is to change the pixel coverage
1165
// model so that the threshold for display does not lie on an integer
1166
// boundary. Search "diamond exit rule" for info the OpenGL approach.
1169
// removing for 0149 with the return of P2D
1170
if (drawing2D() && a[Z] == 0) {
1173
a[VX] += 0.01*a[VW];
1174
a[VY] += 0.01*a[VW];
1177
b[VX] += 0.01*b[VW];
1178
b[VY] += 0.01*b[VW];
1183
if (a[SW] > 1.25f || a[SW] < 0.75f) {
1189
// TODO strokeWeight should be transformed!
1190
float weight = a[SW] / 2;
1192
// when drawing points with stroke weight, need to extend a bit
1193
if (ox1 == ox2 && oy1 == oy2) {
1198
float dX = ox2 - ox1 + EPSILON;
1199
float dY = oy2 - oy1 + EPSILON;
1200
float len = (float) Math.sqrt(dX*dX + dY*dY);
1202
float rh = weight / len;
1204
float dx0 = rh * dY;
1205
float dy0 = rh * dX;
1206
float dx1 = rh * dY;
1207
float dy1 = rh * dX;
1209
float ax1 = ox1+dx0;
1210
float ay1 = oy1-dy0;
1212
float ax2 = ox1-dx0;
1213
float ay2 = oy1+dy0;
1215
float bx1 = ox2+dx1;
1216
float by1 = oy2-dy1;
1218
float bx2 = ox2-dx1;
1219
float by2 = oy2+dy1;
1222
smoothTriangle.reset(3);
1223
smoothTriangle.smooth = true;
1224
smoothTriangle.interpARGB = true; // ?
1226
// render first triangle for thick line
1227
smoothTriangle.setVertices(ax1, ay1, a[TZ],
1230
smoothTriangle.setIntensities(a[SR], a[SG], a[SB], a[SA],
1231
b[SR], b[SG], b[SB], b[SA],
1232
a[SR], a[SG], a[SB], a[SA]);
1233
smoothTriangle.render();
1235
// render second triangle for thick line
1236
smoothTriangle.setVertices(ax1, ay1, a[TZ],
1239
smoothTriangle.setIntensities(a[SR], a[SG], a[SB], a[SA],
1240
b[SR], b[SG], b[SB], b[SA],
1241
b[SR], b[SG], b[SB], b[SA]);
1242
smoothTriangle.render();
1247
// render first triangle for thick line
1248
triangle.setVertices(ax1, ay1, a[TZ],
1251
triangle.setIntensities(a[SR], a[SG], a[SB], a[SA],
1252
b[SR], b[SG], b[SB], b[SA],
1253
a[SR], a[SG], a[SB], a[SA]);
1256
// render second triangle for thick line
1257
triangle.setVertices(ax1, ay1, a[TZ],
1260
triangle.setIntensities(a[SR], a[SG], a[SB], a[SA],
1261
b[SR], b[SG], b[SB], b[SA],
1262
b[SR], b[SG], b[SB], b[SA]);
1269
line.setIntensities(a[SR], a[SG], a[SB], a[SA],
1270
b[SR], b[SG], b[SB], b[SA]);
1272
line.setVertices(a[TX], a[TY], a[TZ],
1273
b[TX], b[TY], b[TZ]);
1276
// Seems okay to remove this because these vertices are not used again,
1277
// but if problems arise, this needs to be uncommented because the above
1278
// change is destructive and may need to be undone before proceeding.
1279
if (drawing2D() && a[MZ] == 0) {
1282
a[VX] -= 0.01*a[VW];
1283
a[VY] -= 0.01*a[VW];
1286
b[VX] -= 0.01*b[VW];
1287
b[VY] -= 0.01*b[VW];
1297
* Handle echoing line data to a raw shape recording renderer. This has been
1298
* broken out of the renderLines() procedure so that renderLines() can be
1299
* optimized per-renderer without having to deal with this code. This code,
1300
* for instance, will stay the same when OpenGL is in use, but renderLines()
1301
* can be optimized significantly.
1303
* Values for start and stop are specified, so that in the future, sorted
1304
* rendering can be implemented, which will require sequences of lines,
1305
* triangles, or points to be rendered in the neighborhood of one another.
1306
* That is, if we're gonna depth sort, we can't just draw all the triangles
1307
* and then draw all the lines, cuz that defeats the purpose.
1309
protected void rawLines(int start, int stop) {
1310
raw.colorMode(RGB, 1);
1312
raw.beginShape(LINES);
1314
for (int i = start; i < stop; i++) {
1315
float a[] = vertices[lines[i][VERTEX1]];
1316
float b[] = vertices[lines[i][VERTEX2]];
1317
raw.strokeWeight(vertices[lines[i][VERTEX2]][SW]);
1320
if ((a[VW] != 0) && (b[VW] != 0)) {
1321
raw.stroke(a[SR], a[SG], a[SB], a[SA]);
1322
raw.vertex(a[VX] / a[VW], a[VY] / a[VW], a[VZ] / a[VW]);
1323
raw.stroke(b[SR], b[SG], b[SB], b[SA]);
1324
raw.vertex(b[VX] / b[VW], b[VY] / b[VW], b[VZ] / b[VW]);
1326
} else if (raw.is2D()) {
1327
raw.stroke(a[SR], a[SG], a[SB], a[SA]);
1328
raw.vertex(a[TX], a[TY]);
1329
raw.stroke(b[SR], b[SG], b[SB], b[SA]);
1330
raw.vertex(b[TX], b[TY]);
1338
/////////////////////////////////////////////////////////////////////////////
1343
protected void addTriangle(int a, int b, int c) {
1344
addTriangleWithClip(a, b, c);
1348
protected final void addTriangleWithClip(int a, int b, int c) {
1349
boolean aClipped = false;
1350
boolean bClipped = false;
1351
int clippedCount = 0;
1354
if (vertices[a][VZ] > cameraNear) {
1358
if (vertices[b][VZ] > cameraNear) {
1362
if (vertices[c][VZ] > cameraNear) {
1366
if (clippedCount == 0) {
1367
// if (vertices[a][VZ] < cameraFar &&
1368
// vertices[b][VZ] < cameraFar &&
1369
// vertices[c][VZ] < cameraFar) {
1370
addTriangleWithoutClip(a, b, c);
1373
// } else if (true) {
1376
} else if (clippedCount == 3) {
1377
// In this case there is only one visible point. |/|
1378
// So we'll have to make two new points on the clip line <| |
1379
// and add that triangle instead. |\|
1381
} else if (clippedCount == 2) {
1382
//System.out.println("Clipped two");
1384
int ca, cb, cc, cd, ce;
1390
else if (!bClipped) {
1395
else { //if (!cClipped) {
1401
cd = interpolateClipVertex(ca, cb);
1402
ce = interpolateClipVertex(ca, cc);
1403
addTriangleWithoutClip(ca, cd, ce);
1405
} else { // (clippedCount == 1) {
1407
// In this case there are two visible points. |\|
1408
// So we'll have to make two new points on the clip line | |>
1409
// and then add two new triangles. |/|
1411
//System.out.println("Clipped one");
1412
int ca, cb, cc, cd, ce;
1414
//System.out.println("aClipped");
1419
else if (bClipped) {
1420
//System.out.println("bClipped");
1425
else { //if (cClipped) {
1426
//System.out.println("cClipped");
1432
cd = interpolateClipVertex(ca, cc);
1433
ce = interpolateClipVertex(cb, cc);
1434
addTriangleWithoutClip(ca, cd, cb);
1435
//System.out.println("ca: " + ca + ", " + vertices[ca][VX] + ", " + vertices[ca][VY] + ", " + vertices[ca][VZ]);
1436
//System.out.println("cd: " + cd + ", " + vertices[cd][VX] + ", " + vertices[cd][VY] + ", " + vertices[cd][VZ]);
1437
//System.out.println("cb: " + cb + ", " + vertices[cb][VX] + ", " + vertices[cb][VY] + ", " + vertices[cb][VZ]);
1438
addTriangleWithoutClip(cb, cd, ce);
1443
protected final int interpolateClipVertex(int a, int b) {
1446
// Set up va, vb such that va[VZ] >= vb[VZ]
1447
if (vertices[a][VZ] < vertices[b][VZ]) {
1459
// If they have the same z, just use pt. a.
1463
//float pa = (az - cameraNear) / dz;
1464
//float pb = (cameraNear - bz) / dz;
1465
float pa = (cameraNear - bz) / dz;
1468
vertex(pa * va[X] + pb * vb[X],
1469
pa * va[Y] + pb * vb[Y],
1470
pa * va[Z] + pb * vb[Z]);
1471
int irv = vertexCount - 1;
1472
shapeLastPlusClipped++;
1474
float[] rv = vertices[irv];
1476
rv[TX] = pa * va[TX] + pb * vb[TX];
1477
rv[TY] = pa * va[TY] + pb * vb[TY];
1478
rv[TZ] = pa * va[TZ] + pb * vb[TZ];
1480
rv[VX] = pa * va[VX] + pb * vb[VX];
1481
rv[VY] = pa * va[VY] + pb * vb[VY];
1482
rv[VZ] = pa * va[VZ] + pb * vb[VZ];
1483
rv[VW] = pa * va[VW] + pb * vb[VW];
1485
rv[R] = pa * va[R] + pb * vb[R];
1486
rv[G] = pa * va[G] + pb * vb[G];
1487
rv[B] = pa * va[B] + pb * vb[B];
1488
rv[A] = pa * va[A] + pb * vb[A];
1490
rv[U] = pa * va[U] + pb * vb[U];
1491
rv[V] = pa * va[V] + pb * vb[V];
1493
rv[SR] = pa * va[SR] + pb * vb[SR];
1494
rv[SG] = pa * va[SG] + pb * vb[SG];
1495
rv[SB] = pa * va[SB] + pb * vb[SB];
1496
rv[SA] = pa * va[SA] + pb * vb[SA];
1498
rv[NX] = pa * va[NX] + pb * vb[NX];
1499
rv[NY] = pa * va[NY] + pb * vb[NY];
1500
rv[NZ] = pa * va[NZ] + pb * vb[NZ];
1502
// rv[SW] = pa * va[SW] + pb * vb[SW];
1504
rv[AR] = pa * va[AR] + pb * vb[AR];
1505
rv[AG] = pa * va[AG] + pb * vb[AG];
1506
rv[AB] = pa * va[AB] + pb * vb[AB];
1508
rv[SPR] = pa * va[SPR] + pb * vb[SPR];
1509
rv[SPG] = pa * va[SPG] + pb * vb[SPG];
1510
rv[SPB] = pa * va[SPB] + pb * vb[SPB];
1511
//rv[SPA] = pa * va[SPA] + pb * vb[SPA];
1513
rv[ER] = pa * va[ER] + pb * vb[ER];
1514
rv[EG] = pa * va[EG] + pb * vb[EG];
1515
rv[EB] = pa * va[EB] + pb * vb[EB];
1517
rv[SHINE] = pa * va[SHINE] + pb * vb[SHINE];
1525
protected final void addTriangleWithoutClip(int a, int b, int c) {
1526
if (triangleCount == triangles.length) {
1527
int temp[][] = new int[triangleCount<<1][TRIANGLE_FIELD_COUNT];
1528
System.arraycopy(triangles, 0, temp, 0, triangleCount);
1530
//message(CHATTER, "allocating more triangles " + triangles.length);
1531
float ftemp[][][] = new float[triangleCount<<1][3][TRI_COLOR_COUNT];
1532
System.arraycopy(triangleColors, 0, ftemp, 0, triangleCount);
1533
triangleColors = ftemp;
1535
triangles[triangleCount][VERTEX1] = a;
1536
triangles[triangleCount][VERTEX2] = b;
1537
triangles[triangleCount][VERTEX3] = c;
1539
if (textureImage == null) {
1540
triangles[triangleCount][TEXTURE_INDEX] = -1;
1542
triangles[triangleCount][TEXTURE_INDEX] = textureIndex;
1545
// triangles[triangleCount][INDEX] = shape_index;
1551
* Triangulate the current polygon.
1553
* Simple ear clipping polygon triangulation adapted from code by
1554
* John W. Ratcliff (jratcliff at verant.com). Presumably
1555
* <A HREF="http://www.flipcode.org/cgi-bin/fcarticles.cgi?show=63943">this</A>
1556
* bit of code from the web.
1558
protected void addPolygonTriangles() {
1559
if (vertexOrder.length != vertices.length) {
1560
int[] temp = new int[vertices.length];
1561
// vertex_start may not be zero, might need to keep old stuff around
1562
// also, copy vertexOrder.length, not vertexCount because vertexCount
1563
// may be larger than vertexOrder.length (since this is a post-processing
1564
// step that happens after the vertex arrays are built).
1565
PApplet.arrayCopy(vertexOrder, temp, vertexOrder.length);
1569
// this clipping algorithm only works in 2D, so in cases where a
1570
// polygon is drawn perpendicular to the z-axis, the area will be zero,
1571
// and triangulation will fail. as such, when the area calculates to
1572
// zero, figure out whether x or y is empty, and calculate based on the
1573
// two dimensions that actually contain information.
1574
// http://dev.processing.org/bugs/show_bug.cgi?id=111
1577
// this brings up the nastier point that there may be cases where
1578
// a polygon is irregular in space and will throw off the
1579
// clockwise/counterclockwise calculation. for instance, if clockwise
1580
// relative to x and z, but counter relative to y and z or something
1581
// like that.. will wait to see if this is in fact a problem before
1582
// hurting my head on the math.
1585
// trying to track down bug #774
1586
for (int i = vertex_start; i < vertex_end; i++) {
1587
if (i > vertex_start) {
1588
if (vertices[i-1][MX] == vertices[i][MX] &&
1589
vertices[i-1][MY] == vertices[i][MY]) {
1590
System.out.print("**** " );
1593
System.out.println(i + " " + vertices[i][MX] + " " + vertices[i][MY]);
1595
System.out.println();
1598
// first we check if the polygon goes clockwise or counterclockwise
1600
for (int p = shapeLast - 1, q = shapeFirst; q < shapeLast; p = q++) {
1601
area += (vertices[q][d1] * vertices[p][d2] -
1602
vertices[p][d1] * vertices[q][d2]);
1604
// rather than checking for the perpendicular case first, only do it
1605
// when the area calculates to zero. checking for perpendicular would be
1606
// a needless waste of time for the 99% case.
1608
// figure out which dimension is the perpendicular axis
1609
boolean foundValidX = false;
1610
boolean foundValidY = false;
1612
for (int i = shapeFirst; i < shapeLast; i++) {
1613
for (int j = i; j < shapeLast; j++){
1614
if ( vertices[i][X] != vertices[j][X] ) foundValidX = true;
1615
if ( vertices[i][Y] != vertices[j][Y] ) foundValidY = true;
1620
//d1 = MX; // already the case
1622
} else if (foundValidY) {
1623
// ermm.. which is the proper order for cw/ccw here?
1627
// screw it, this polygon is just f-ed up
1631
// re-calculate the area, with what should be good values
1632
for (int p = shapeLast - 1, q = shapeFirst; q < shapeLast; p = q++) {
1633
area += (vertices[q][d1] * vertices[p][d2] -
1634
vertices[p][d1] * vertices[q][d2]);
1638
// don't allow polygons to come back and meet themselves,
1639
// otherwise it will anger the triangulator
1640
// http://dev.processing.org/bugs/show_bug.cgi?id=97
1641
float vfirst[] = vertices[shapeFirst];
1642
float vlast[] = vertices[shapeLast-1];
1643
if ((abs(vfirst[X] - vlast[X]) < EPSILON) &&
1644
(abs(vfirst[Y] - vlast[Y]) < EPSILON) &&
1645
(abs(vfirst[Z] - vlast[Z]) < EPSILON)) {
1649
// then sort the vertices so they are always in a counterclockwise order
1652
for (int i = shapeFirst; i < shapeLast; i++) {
1657
for (int i = shapeFirst; i < shapeLast; i++) {
1659
vertexOrder[j] = (shapeLast - 1) - j;
1663
// remove vc-2 Vertices, creating 1 triangle every time
1664
int vc = shapeLast - shapeFirst;
1665
int count = 2*vc; // complex polygon detection
1667
for (int m = 0, v = vc - 1; vc > 2; ) {
1668
boolean snip = true;
1670
// if we start over again, is a complex polygon
1671
if (0 >= (count--)) {
1672
break; // triangulation failed
1675
// get 3 consecutive vertices <u,v,w>
1676
int u = v ; if (vc <= u) u = 0; // previous
1677
v = u + 1; if (vc <= v) v = 0; // current
1678
int w = v + 1; if (vc <= w) w = 0; // next
1680
// Upgrade values to doubles, and multiply by 10 so that we can have
1681
// some better accuracy as we tessellate. This seems to have negligible
1682
// speed differences on Windows and Intel Macs, but causes a 50% speed
1683
// drop for PPC Macs with the bug's example code that draws ~200 points
1684
// in a concave polygon. Apple has abandoned PPC so we may as well too.
1685
// http://dev.processing.org/bugs/show_bug.cgi?id=774
1688
double Ax = -10 * vertices[vertexOrder[u]][d1];
1689
double Ay = 10 * vertices[vertexOrder[u]][d2];
1690
double Bx = -10 * vertices[vertexOrder[v]][d1];
1691
double By = 10 * vertices[vertexOrder[v]][d2];
1692
double Cx = -10 * vertices[vertexOrder[w]][d1];
1693
double Cy = 10 * vertices[vertexOrder[w]][d2];
1695
// first we check if <u,v,w> continues going ccw
1696
if (EPSILON > (((Bx-Ax) * (Cy-Ay)) - ((By-Ay) * (Cx-Ax)))) {
1700
for (int p = 0; p < vc; p++) {
1701
if ((p == u) || (p == v) || (p == w)) {
1705
double Px = -10 * vertices[vertexOrder[p]][d1];
1706
double Py = 10 * vertices[vertexOrder[p]][d2];
1708
double ax = Cx - Bx; double ay = Cy - By;
1709
double bx = Ax - Cx; double by = Ay - Cy;
1710
double cx = Bx - Ax; double cy = By - Ay;
1711
double apx = Px - Ax; double apy = Py - Ay;
1712
double bpx = Px - Bx; double bpy = Py - By;
1713
double cpx = Px - Cx; double cpy = Py - Cy;
1715
double aCROSSbp = ax * bpy - ay * bpx;
1716
double cCROSSap = cx * apy - cy * apx;
1717
double bCROSScp = bx * cpy - by * cpx;
1719
if ((aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0)) {
1725
addTriangle(vertexOrder[u], vertexOrder[v], vertexOrder[w]);
1729
// remove v from remaining polygon
1730
for (int s = v, t = v + 1; t < vc; s++, t++) {
1731
vertexOrder[s] = vertexOrder[t];
1735
// reset error detection counter
1742
private void toWorldNormal(float nx, float ny, float nz, float[] out) {
1744
modelviewInv.m00*nx + modelviewInv.m10*ny +
1745
modelviewInv.m20*nz + modelviewInv.m30;
1747
modelviewInv.m01*nx + modelviewInv.m11*ny +
1748
modelviewInv.m21*nz + modelviewInv.m31;
1750
modelviewInv.m02*nx + modelviewInv.m12*ny +
1751
modelviewInv.m22*nz + modelviewInv.m32;
1753
modelviewInv.m03*nx + modelviewInv.m13*ny +
1754
modelviewInv.m23*nz + modelviewInv.m33;
1756
if (out[3] != 0 && out[3] != 1) {
1757
// divide by perspective coordinate
1758
out[0] /= out[3]; out[1] /= out[3]; out[2] /= out[3];
1762
float nlen = mag(out[0], out[1], out[2]); // normalize
1763
if (nlen != 0 && nlen != 1) {
1764
out[0] /= nlen; out[1] /= nlen; out[2] /= nlen;
1769
//private PVector calcLightingNorm = new PVector();
1770
//private PVector calcLightingWorldNorm = new PVector();
1771
float[] worldNormal = new float[4];
1774
private void calcLightingContribution(int vIndex,
1775
float[] contribution) {
1776
calcLightingContribution(vIndex, contribution, false);
1780
private void calcLightingContribution(int vIndex,
1781
float[] contribution,
1782
boolean normalIsWorld) {
1783
float[] v = vertices[vIndex];
1792
float shine = v[SHINE];
1798
if (!normalIsWorld) {
1799
// System.out.println("um, hello?");
1800
// calcLightingNorm.set(nx, ny, nz);
1801
// //modelviewInv.mult(calcLightingNorm, calcLightingWorldNorm);
1803
//// PMatrix3D mvi = modelViewInv;
1804
//// float ox = mvi.m00*nx + mvi.m10*ny + mvi*m20+nz +
1805
// modelviewInv.cmult(calcLightingNorm, calcLightingWorldNorm);
1807
// calcLightingWorldNorm.normalize();
1808
// nx = calcLightingWorldNorm.x;
1809
// ny = calcLightingWorldNorm.y;
1810
// nz = calcLightingWorldNorm.z;
1812
toWorldNormal(v[NX], v[NY], v[NZ], worldNormal);
1813
nx = worldNormal[X];
1814
ny = worldNormal[Y];
1815
nz = worldNormal[Z];
1817
// float wnx = modelviewInv.multX(nx, ny, nz);
1818
// float wny = modelviewInv.multY(nx, ny, nz);
1819
// float wnz = modelviewInv.multZ(nx, ny, nz);
1820
// float wnw = modelviewInv.multW(nx, ny, nz);
1822
// if (wnw != 0 && wnw != 1) {
1827
// float nlen = mag(wnx, wny, wnw);
1828
// if (nlen != 0 && nlen != 1) {
1844
// Since the camera space == world space,
1845
// we can test for visibility by the dot product of
1846
// the normal with the direction from pt. to eye.
1847
float dir = dot(nx, ny, nz, -wx, -wy, -wz);
1848
// If normal is away from camera, choose its opposite.
1849
// If we add backface culling, this will be backfacing
1850
// (but since this is per vertex, it's more complicated)
1857
// These two terms will sum the contributions from the various lights
1858
contribution[LIGHT_AMBIENT_R] = 0;
1859
contribution[LIGHT_AMBIENT_G] = 0;
1860
contribution[LIGHT_AMBIENT_B] = 0;
1862
contribution[LIGHT_DIFFUSE_R] = 0;
1863
contribution[LIGHT_DIFFUSE_G] = 0;
1864
contribution[LIGHT_DIFFUSE_B] = 0;
1866
contribution[LIGHT_SPECULAR_R] = 0;
1867
contribution[LIGHT_SPECULAR_G] = 0;
1868
contribution[LIGHT_SPECULAR_B] = 0;
1870
// for (int i = 0; i < MAX_LIGHTS; i++) {
1871
// if (!light[i]) continue;
1872
for (int i = 0; i < lightCount; i++) {
1874
float denom = lightFalloffConstant[i];
1877
if (lightType[i] == AMBIENT) {
1878
if (lightFalloffQuadratic[i] != 0 || lightFalloffLinear[i] != 0) {
1879
// Falloff depends on distance
1880
float distSq = mag(lightPosition[i].x - wx,
1881
lightPosition[i].y - wy,
1882
lightPosition[i].z - wz);
1884
lightFalloffQuadratic[i] * distSq +
1885
lightFalloffLinear[i] * sqrt(distSq);
1887
if (denom == 0) denom = 1;
1889
contribution[LIGHT_AMBIENT_R] += lightDiffuse[i][0] / denom;
1890
contribution[LIGHT_AMBIENT_G] += lightDiffuse[i][1] / denom;
1891
contribution[LIGHT_AMBIENT_B] += lightDiffuse[i][2] / denom;
1894
// If not ambient, we must deal with direction
1896
// li is the vector from the vertex to the light
1897
float lix, liy, liz;
1898
float lightDir_dot_li = 0;
1901
if (lightType[i] == DIRECTIONAL) {
1902
lix = -lightNormal[i].x;
1903
liy = -lightNormal[i].y;
1904
liz = -lightNormal[i].z;
1906
n_dot_li = (nx * lix + ny * liy + nz * liz);
1907
// If light is lighting the face away from the camera, ditch
1908
if (n_dot_li <= 0) {
1911
} else { // Point or spot light (must deal also with light location)
1912
lix = lightPosition[i].x - wx;
1913
liy = lightPosition[i].y - wy;
1914
liz = lightPosition[i].z - wz;
1916
float distSq = mag(lix, liy, liz);
1922
n_dot_li = (nx * lix + ny * liy + nz * liz);
1923
// If light is lighting the face away from the camera, ditch
1924
if (n_dot_li <= 0) {
1928
if (lightType[i] == SPOT) { // Must deal with spot cone
1930
-(lightNormal[i].x * lix +
1931
lightNormal[i].y * liy +
1932
lightNormal[i].z * liz);
1933
// Outside of spot cone
1934
if (lightDir_dot_li <= lightSpotAngleCos[i]) {
1937
spotTerm = (float) Math.pow(lightDir_dot_li, lightSpotConcentration[i]);
1940
if (lightFalloffQuadratic[i] != 0 || lightFalloffLinear[i] != 0) {
1941
// Falloff depends on distance
1943
lightFalloffQuadratic[i] * distSq +
1944
lightFalloffLinear[i] * (float) sqrt(distSq);
1947
// Directional, point, or spot light:
1949
// We know n_dot_li > 0 from above "continues"
1953
float mul = n_dot_li * spotTerm / denom;
1954
contribution[LIGHT_DIFFUSE_R] += lightDiffuse[i][0] * mul;
1955
contribution[LIGHT_DIFFUSE_G] += lightDiffuse[i][1] * mul;
1956
contribution[LIGHT_DIFFUSE_B] += lightDiffuse[i][2] * mul;
1960
// If the material and light have a specular component.
1961
if ((sr > 0 || sg > 0 || sb > 0) &&
1962
(lightSpecular[i][0] > 0 ||
1963
lightSpecular[i][1] > 0 ||
1964
lightSpecular[i][2] > 0)) {
1966
float vmag = mag(wx, wy, wz);
1972
float sx = lix - wx;
1973
float sy = liy - wy;
1974
float sz = liz - wz;
1975
vmag = mag(sx, sy, sz);
1981
float s_dot_n = (sx * nx + sy * ny + sz * nz);
1984
s_dot_n = (float) Math.pow(s_dot_n, shine);
1985
mul = s_dot_n * spotTerm / denom;
1986
contribution[LIGHT_SPECULAR_R] += lightSpecular[i][0] * mul;
1987
contribution[LIGHT_SPECULAR_G] += lightSpecular[i][1] * mul;
1988
contribution[LIGHT_SPECULAR_B] += lightSpecular[i][2] * mul;
1998
// Multiply the lighting contribution into the vertex's colors.
1999
// Only do this when there is ONE lighting per vertex
2000
// (MANUAL_VERTEX_NORMAL or SHAPE_NORMAL mode).
2001
private void applyLightingContribution(int vIndex, float[] contribution) {
2002
float[] v = vertices[vIndex];
2004
v[R] = clamp(v[ER] + v[AR] * contribution[LIGHT_AMBIENT_R] + v[DR] * contribution[LIGHT_DIFFUSE_R]);
2005
v[G] = clamp(v[EG] + v[AG] * contribution[LIGHT_AMBIENT_G] + v[DG] * contribution[LIGHT_DIFFUSE_G]);
2006
v[B] = clamp(v[EB] + v[AB] * contribution[LIGHT_AMBIENT_B] + v[DB] * contribution[LIGHT_DIFFUSE_B]);
2007
v[A] = clamp(v[DA]);
2009
v[SPR] = clamp(v[SPR] * contribution[LIGHT_SPECULAR_R]);
2010
v[SPG] = clamp(v[SPG] * contribution[LIGHT_SPECULAR_G]);
2011
v[SPB] = clamp(v[SPB] * contribution[LIGHT_SPECULAR_B]);
2012
//v[SPA] = min(1, v[SPA]);
2018
private void lightVertex(int vIndex, float[] contribution) {
2019
calcLightingContribution(vIndex, contribution);
2020
applyLightingContribution(vIndex, contribution);
2024
private void lightUnlitVertex(int vIndex, float[] contribution) {
2025
if (vertices[vIndex][BEEN_LIT] == 0) {
2026
lightVertex(vIndex, contribution);
2031
private void copyPrelitVertexColor(int triIndex, int index, int colorIndex) {
2032
float[] triColor = triangleColors[triIndex][colorIndex];
2033
float[] v = vertices[index];
2035
triColor[TRI_DIFFUSE_R] = v[R];
2036
triColor[TRI_DIFFUSE_G] = v[G];
2037
triColor[TRI_DIFFUSE_B] = v[B];
2038
triColor[TRI_DIFFUSE_A] = v[A];
2039
triColor[TRI_SPECULAR_R] = v[SPR];
2040
triColor[TRI_SPECULAR_G] = v[SPG];
2041
triColor[TRI_SPECULAR_B] = v[SPB];
2042
//triColor[TRI_SPECULAR_A] = v[SPA];
2046
private void copyVertexColor(int triIndex, int index, int colorIndex,
2048
float[] triColor = triangleColors[triIndex][colorIndex];
2049
float[] v = vertices[index];
2051
triColor[TRI_DIFFUSE_R] =
2052
clamp(v[ER] + v[AR] * contrib[LIGHT_AMBIENT_R] + v[DR] * contrib[LIGHT_DIFFUSE_R]);
2053
triColor[TRI_DIFFUSE_G] =
2054
clamp(v[EG] + v[AG] * contrib[LIGHT_AMBIENT_G] + v[DG] * contrib[LIGHT_DIFFUSE_G]);
2055
triColor[TRI_DIFFUSE_B] =
2056
clamp(v[EB] + v[AB] * contrib[LIGHT_AMBIENT_B] + v[DB] * contrib[LIGHT_DIFFUSE_B]);
2057
triColor[TRI_DIFFUSE_A] = clamp(v[DA]);
2059
triColor[TRI_SPECULAR_R] = clamp(v[SPR] * contrib[LIGHT_SPECULAR_R]);
2060
triColor[TRI_SPECULAR_G] = clamp(v[SPG] * contrib[LIGHT_SPECULAR_G]);
2061
triColor[TRI_SPECULAR_B] = clamp(v[SPB] * contrib[LIGHT_SPECULAR_B]);
2065
private void lightTriangle(int triIndex, float[] lightContribution) {
2066
int vIndex = triangles[triIndex][VERTEX1];
2067
copyVertexColor(triIndex, vIndex, 0, lightContribution);
2068
vIndex = triangles[triIndex][VERTEX2];
2069
copyVertexColor(triIndex, vIndex, 1, lightContribution);
2070
vIndex = triangles[triIndex][VERTEX3];
2071
copyVertexColor(triIndex, vIndex, 2, lightContribution);
2075
private void lightTriangle(int triIndex) {
2078
// Handle lighting on, but no lights (in this case, just use emissive)
2079
// This wont be used currently because lightCount == 0 is don't use
2080
// lighting at all... So. OK. If that ever changes, use the below:
2082
if (lightCount == 0) {
2083
vIndex = triangles[triIndex][VERTEX1];
2084
copy_emissive_vertex_color_to_triangle(triIndex, vIndex, 0);
2085
vIndex = triangles[triIndex][VERTEX2];
2086
copy_emissive_vertex_color_to_triangle(triIndex, vIndex, 1);
2087
vIndex = triangles[triIndex][VERTEX3];
2088
copy_emissive_vertex_color_to_triangle(triIndex, vIndex, 2);
2093
// In MANUAL_VERTEX_NORMAL mode, we have a specific normal
2094
// for each vertex. In that case, we light any verts that
2095
// haven't already been lit and copy their colors straight
2096
// into the triangle.
2097
if (normalMode == NORMAL_MODE_VERTEX) {
2098
vIndex = triangles[triIndex][VERTEX1];
2099
lightUnlitVertex(vIndex, tempLightingContribution);
2100
copyPrelitVertexColor(triIndex, vIndex, 0);
2102
vIndex = triangles[triIndex][VERTEX2];
2103
lightUnlitVertex(vIndex, tempLightingContribution);
2104
copyPrelitVertexColor(triIndex, vIndex, 1);
2106
vIndex = triangles[triIndex][VERTEX3];
2107
lightUnlitVertex(vIndex, tempLightingContribution);
2108
copyPrelitVertexColor(triIndex, vIndex, 2);
2112
// If the lighting doesn't depend on the vertex position, do the
2113
// following: We've already dealt with NORMAL_MODE_SHAPE mode before
2114
// we got into this function, so here we only have to deal with
2115
// NORMAL_MODE_AUTO. So we calculate the normal for this triangle,
2116
// and use that for the lighting.
2117
else if (!lightingDependsOnVertexPosition) {
2118
vIndex = triangles[triIndex][VERTEX1];
2119
int vIndex2 = triangles[triIndex][VERTEX2];
2120
int vIndex3 = triangles[triIndex][VERTEX3];
2123
dv1[0] = vertices[vIndex2][VX] - vertices[vIndex][VX];
2124
dv1[1] = vertices[vIndex2][VY] - vertices[vIndex][VY];
2125
dv1[2] = vertices[vIndex2][VZ] - vertices[vIndex][VZ];
2127
dv2[0] = vertices[vIndex3][VX] - vertices[vIndex][VX];
2128
dv2[1] = vertices[vIndex3][VY] - vertices[vIndex][VY];
2129
dv2[2] = vertices[vIndex3][VZ] - vertices[vIndex][VZ];
2131
cross(dv1, dv2, norm);
2134
cross(vertices[vIndex2][VX] - vertices[vIndex][VX],
2135
vertices[vIndex2][VY] - vertices[vIndex][VY],
2136
vertices[vIndex2][VZ] - vertices[vIndex][VZ],
2137
vertices[vIndex3][VX] - vertices[vIndex][VX],
2138
vertices[vIndex3][VY] - vertices[vIndex][VY],
2139
vertices[vIndex3][VZ] - vertices[vIndex][VZ], lightTriangleNorm);
2141
lightTriangleNorm.normalize();
2142
vertices[vIndex][NX] = lightTriangleNorm.x;
2143
vertices[vIndex][NY] = lightTriangleNorm.y;
2144
vertices[vIndex][NZ] = lightTriangleNorm.z;
2146
// The true at the end says the normal is already in world coordinates
2147
calcLightingContribution(vIndex, tempLightingContribution, true);
2148
copyVertexColor(triIndex, vIndex, 0, tempLightingContribution);
2149
copyVertexColor(triIndex, vIndex2, 1, tempLightingContribution);
2150
copyVertexColor(triIndex, vIndex3, 2, tempLightingContribution);
2153
// If lighting is position-dependent
2155
if (normalMode == NORMAL_MODE_SHAPE) {
2156
vIndex = triangles[triIndex][VERTEX1];
2157
vertices[vIndex][NX] = vertices[shapeFirst][NX];
2158
vertices[vIndex][NY] = vertices[shapeFirst][NY];
2159
vertices[vIndex][NZ] = vertices[shapeFirst][NZ];
2160
calcLightingContribution(vIndex, tempLightingContribution);
2161
copyVertexColor(triIndex, vIndex, 0, tempLightingContribution);
2163
vIndex = triangles[triIndex][VERTEX2];
2164
vertices[vIndex][NX] = vertices[shapeFirst][NX];
2165
vertices[vIndex][NY] = vertices[shapeFirst][NY];
2166
vertices[vIndex][NZ] = vertices[shapeFirst][NZ];
2167
calcLightingContribution(vIndex, tempLightingContribution);
2168
copyVertexColor(triIndex, vIndex, 1, tempLightingContribution);
2170
vIndex = triangles[triIndex][VERTEX3];
2171
vertices[vIndex][NX] = vertices[shapeFirst][NX];
2172
vertices[vIndex][NY] = vertices[shapeFirst][NY];
2173
vertices[vIndex][NZ] = vertices[shapeFirst][NZ];
2174
calcLightingContribution(vIndex, tempLightingContribution);
2175
copyVertexColor(triIndex, vIndex, 2, tempLightingContribution);
2178
// lighting mode is AUTO_NORMAL
2180
vIndex = triangles[triIndex][VERTEX1];
2181
int vIndex2 = triangles[triIndex][VERTEX2];
2182
int vIndex3 = triangles[triIndex][VERTEX3];
2185
dv1[0] = vertices[vIndex2][VX] - vertices[vIndex][VX];
2186
dv1[1] = vertices[vIndex2][VY] - vertices[vIndex][VY];
2187
dv1[2] = vertices[vIndex2][VZ] - vertices[vIndex][VZ];
2189
dv2[0] = vertices[vIndex3][VX] - vertices[vIndex][VX];
2190
dv2[1] = vertices[vIndex3][VY] - vertices[vIndex][VY];
2191
dv2[2] = vertices[vIndex3][VZ] - vertices[vIndex][VZ];
2193
cross(dv1, dv2, norm);
2196
cross(vertices[vIndex2][VX] - vertices[vIndex][VX],
2197
vertices[vIndex2][VY] - vertices[vIndex][VY],
2198
vertices[vIndex2][VZ] - vertices[vIndex][VZ],
2199
vertices[vIndex3][VX] - vertices[vIndex][VX],
2200
vertices[vIndex3][VY] - vertices[vIndex][VY],
2201
vertices[vIndex3][VZ] - vertices[vIndex][VZ], lightTriangleNorm);
2202
// float nmag = mag(norm[X], norm[Y], norm[Z]);
2203
// if (nmag != 0 && nmag != 1) {
2204
// norm[X] /= nmag; norm[Y] /= nmag; norm[Z] /= nmag;
2206
lightTriangleNorm.normalize();
2207
vertices[vIndex][NX] = lightTriangleNorm.x;
2208
vertices[vIndex][NY] = lightTriangleNorm.y;
2209
vertices[vIndex][NZ] = lightTriangleNorm.z;
2210
// The true at the end says the normal is already in world coordinates
2211
calcLightingContribution(vIndex, tempLightingContribution, true);
2212
copyVertexColor(triIndex, vIndex, 0, tempLightingContribution);
2214
vertices[vIndex2][NX] = lightTriangleNorm.x;
2215
vertices[vIndex2][NY] = lightTriangleNorm.y;
2216
vertices[vIndex2][NZ] = lightTriangleNorm.z;
2217
// The true at the end says the normal is already in world coordinates
2218
calcLightingContribution(vIndex2, tempLightingContribution, true);
2219
copyVertexColor(triIndex, vIndex2, 1, tempLightingContribution);
2221
vertices[vIndex3][NX] = lightTriangleNorm.x;
2222
vertices[vIndex3][NY] = lightTriangleNorm.y;
2223
vertices[vIndex3][NZ] = lightTriangleNorm.z;
2224
// The true at the end says the normal is already in world coordinates
2225
calcLightingContribution(vIndex3, tempLightingContribution, true);
2226
copyVertexColor(triIndex, vIndex3, 2, tempLightingContribution);
2232
protected void renderTriangles(int start, int stop) {
2233
for (int i = start; i < stop; i++) {
2234
float a[] = vertices[triangles[i][VERTEX1]];
2235
float b[] = vertices[triangles[i][VERTEX2]];
2236
float c[] = vertices[triangles[i][VERTEX3]];
2237
int tex = triangles[i][TEXTURE_INDEX];
2240
// removing for 0149 with the return of P2D
2241
// ewjordan: hack to 'fix' accuracy issues when drawing in 2d
2242
// see also render_lines() where similar hack is employed
2243
float shift = 0.15f;//was 0.49f
2244
boolean shifted = false;
2245
if (drawing2D() && (a[Z] == 0)) {
2249
a[VX] += shift*a[VW];
2250
a[VY] += shift*a[VW];
2253
b[VX] += shift*b[VW];
2254
b[VY] += shift*b[VW];
2257
c[VX] += shift*c[VW];
2258
c[VY] += shift*c[VW];
2264
// This is only true when not textured.
2265
// We really should pass specular straight through to triangle rendering.
2266
float ar = clamp(triangleColors[i][0][TRI_DIFFUSE_R] + triangleColors[i][0][TRI_SPECULAR_R]);
2267
float ag = clamp(triangleColors[i][0][TRI_DIFFUSE_G] + triangleColors[i][0][TRI_SPECULAR_G]);
2268
float ab = clamp(triangleColors[i][0][TRI_DIFFUSE_B] + triangleColors[i][0][TRI_SPECULAR_B]);
2269
float br = clamp(triangleColors[i][1][TRI_DIFFUSE_R] + triangleColors[i][1][TRI_SPECULAR_R]);
2270
float bg = clamp(triangleColors[i][1][TRI_DIFFUSE_G] + triangleColors[i][1][TRI_SPECULAR_G]);
2271
float bb = clamp(triangleColors[i][1][TRI_DIFFUSE_B] + triangleColors[i][1][TRI_SPECULAR_B]);
2272
float cr = clamp(triangleColors[i][2][TRI_DIFFUSE_R] + triangleColors[i][2][TRI_SPECULAR_R]);
2273
float cg = clamp(triangleColors[i][2][TRI_DIFFUSE_G] + triangleColors[i][2][TRI_SPECULAR_G]);
2274
float cb = clamp(triangleColors[i][2][TRI_DIFFUSE_B] + triangleColors[i][2][TRI_SPECULAR_B]);
2276
// ACCURATE TEXTURE CODE
2277
boolean failedToPrecalc = false;
2278
if (s_enableAccurateTextures && frustumMode){
2279
boolean textured = true;
2280
smoothTriangle.reset(3);
2281
smoothTriangle.smooth = true;
2282
smoothTriangle.interpARGB = true;
2283
smoothTriangle.setIntensities(ar, ag, ab, a[A],
2286
if (tex > -1 && textures[tex] != null) {
2287
smoothTriangle.setCamVertices(a[VX], a[VY], a[VZ],
2288
b[VX], b[VY], b[VZ],
2289
c[VX], c[VY], c[VZ]);
2290
smoothTriangle.interpUV = true;
2291
smoothTriangle.texture(textures[tex]);
2292
float umult = textures[tex].width; // apparently no check for textureMode is needed here
2293
float vmult = textures[tex].height;
2294
smoothTriangle.vertices[0][U] = a[U]*umult;
2295
smoothTriangle.vertices[0][V] = a[V]*vmult;
2296
smoothTriangle.vertices[1][U] = b[U]*umult;
2297
smoothTriangle.vertices[1][V] = b[V]*vmult;
2298
smoothTriangle.vertices[2][U] = c[U]*umult;
2299
smoothTriangle.vertices[2][V] = c[V]*vmult;
2301
smoothTriangle.interpUV = false;
2305
smoothTriangle.setVertices(a[TX], a[TY], a[TZ],
2306
b[TX], b[TY], b[TZ],
2307
c[TX], c[TY], c[TZ]);
2310
if (!textured || smoothTriangle.precomputeAccurateTexturing()){
2311
smoothTriangle.render();
2313
// Something went wrong with the precomputation,
2314
// so we need to fall back on normal PTriangle
2316
failedToPrecalc = true;
2320
// Normal triangle rendering
2321
// Note: this is not an end-if from the smoothed texturing mode
2322
// because it's possible that the precalculation will fail and we
2323
// need to fall back on normal rendering.
2324
if (!s_enableAccurateTextures || failedToPrecalc || (frustumMode == false)){
2325
if (tex > -1 && textures[tex] != null) {
2326
triangle.setTexture(textures[tex]);
2327
triangle.setUV(a[U], a[V], b[U], b[V], c[U], c[V]);
2330
triangle.setIntensities(ar, ag, ab, a[A],
2334
triangle.setVertices(a[TX], a[TY], a[TZ],
2335
b[TX], b[TY], b[TZ],
2336
c[TX], c[TY], c[TZ]);
2342
// removing for 0149 with the return of P2D
2343
if (drawing2D() && shifted){
2346
a[VX] -= shift*a[VW];
2347
a[VY] -= shift*a[VW];
2350
b[VX] -= shift*b[VW];
2351
b[VY] -= shift*b[VW];
2354
c[VX] -= shift*c[VW];
2355
c[VY] -= shift*c[VW];
2362
protected void rawTriangles(int start, int stop) {
2363
raw.colorMode(RGB, 1);
2365
raw.beginShape(TRIANGLES);
2367
for (int i = start; i < stop; i++) {
2368
float a[] = vertices[triangles[i][VERTEX1]];
2369
float b[] = vertices[triangles[i][VERTEX2]];
2370
float c[] = vertices[triangles[i][VERTEX3]];
2372
float ar = clamp(triangleColors[i][0][TRI_DIFFUSE_R] + triangleColors[i][0][TRI_SPECULAR_R]);
2373
float ag = clamp(triangleColors[i][0][TRI_DIFFUSE_G] + triangleColors[i][0][TRI_SPECULAR_G]);
2374
float ab = clamp(triangleColors[i][0][TRI_DIFFUSE_B] + triangleColors[i][0][TRI_SPECULAR_B]);
2375
float br = clamp(triangleColors[i][1][TRI_DIFFUSE_R] + triangleColors[i][1][TRI_SPECULAR_R]);
2376
float bg = clamp(triangleColors[i][1][TRI_DIFFUSE_G] + triangleColors[i][1][TRI_SPECULAR_G]);
2377
float bb = clamp(triangleColors[i][1][TRI_DIFFUSE_B] + triangleColors[i][1][TRI_SPECULAR_B]);
2378
float cr = clamp(triangleColors[i][2][TRI_DIFFUSE_R] + triangleColors[i][2][TRI_SPECULAR_R]);
2379
float cg = clamp(triangleColors[i][2][TRI_DIFFUSE_G] + triangleColors[i][2][TRI_SPECULAR_G]);
2380
float cb = clamp(triangleColors[i][2][TRI_DIFFUSE_B] + triangleColors[i][2][TRI_SPECULAR_B]);
2382
int tex = triangles[i][TEXTURE_INDEX];
2383
PImage texImage = (tex > -1) ? textures[tex] : null;
2384
if (texImage != null) {
2386
if ((a[VW] != 0) && (b[VW] != 0) && (c[VW] != 0)) {
2387
raw.fill(ar, ag, ab, a[A]);
2388
raw.vertex(a[VX] / a[VW], a[VY] / a[VW], a[VZ] / a[VW], a[U], a[V]);
2389
raw.fill(br, bg, bb, b[A]);
2390
raw.vertex(b[VX] / b[VW], b[VY] / b[VW], b[VZ] / b[VW], b[U], b[V]);
2391
raw.fill(cr, cg, cb, c[A]);
2392
raw.vertex(c[VX] / c[VW], c[VY] / c[VW], c[VZ] / c[VW], c[U], c[V]);
2394
} else if (raw.is2D()) {
2395
raw.fill(ar, ag, ab, a[A]);
2396
raw.vertex(a[TX], a[TY], a[U], a[V]);
2397
raw.fill(br, bg, bb, b[A]);
2398
raw.vertex(b[TX], b[TY], b[U], b[V]);
2399
raw.fill(cr, cg, cb, c[A]);
2400
raw.vertex(c[TX], c[TY], c[U], c[V]);
2402
} else { // no texture
2404
if ((a[VW] != 0) && (b[VW] != 0) && (c[VW] != 0)) {
2405
raw.fill(ar, ag, ab, a[A]);
2406
raw.vertex(a[VX] / a[VW], a[VY] / a[VW], a[VZ] / a[VW]);
2407
raw.fill(br, bg, bb, b[A]);
2408
raw.vertex(b[VX] / b[VW], b[VY] / b[VW], b[VZ] / b[VW]);
2409
raw.fill(cr, cg, cb, c[A]);
2410
raw.vertex(c[VX] / c[VW], c[VY] / c[VW], c[VZ] / c[VW]);
2412
} else if (raw.is2D()) {
2413
raw.fill(ar, ag, ab, a[A]);
2414
raw.vertex(a[TX], a[TY]);
2415
raw.fill(br, bg, bb, b[A]);
2416
raw.vertex(b[TX], b[TY]);
2417
raw.fill(cr, cg, cb, c[A]);
2418
raw.vertex(c[TX], c[TY]);
2427
//////////////////////////////////////////////////////////////
2430
//public void bezierVertex(float x2, float y2,
2431
// float x3, float y3,
2432
// float x4, float y4)
2435
//public void bezierVertex(float x2, float y2, float z2,
2436
// float x3, float y3, float z3,
2437
// float x4, float y4, float z4)
2441
//////////////////////////////////////////////////////////////
2444
//public void curveVertex(float x, float y)
2447
//public void curveVertex(float x, float y, float z)
2451
////////////////////////////////////////////////////////////
2455
* Emit any sorted geometry that's been collected on this frame.
2457
public void flush() {
2458
if (hints[ENABLE_DEPTH_SORT]) {
2464
if (triangleCount > 0) {
2465
if (hints[ENABLE_DEPTH_SORT]) {
2470
if (lineCount > 0) {
2471
if (hints[ENABLE_DEPTH_SORT]) {
2476
// Clear this out in case flush() is called again.
2477
// For instance, with hint(ENABLE_DEPTH_SORT), it will be called
2478
// once on endRaw(), and once again at endDraw().
2485
protected void render() {
2486
if (pointCount > 0) {
2487
renderPoints(0, pointCount);
2489
rawPoints(0, pointCount);
2493
if (lineCount > 0) {
2494
renderLines(0, lineCount);
2496
rawLines(0, lineCount);
2501
if (triangleCount > 0) {
2502
renderTriangles(0, triangleCount);
2504
rawTriangles(0, triangleCount);
2512
* Handle depth sorting of geometry. Currently this only handles triangles,
2513
* however in the future it will be expanded for points and lines, which
2514
* will also need to be interspersed with one another while rendering.
2516
protected void sort() {
2517
if (triangleCount > 0) {
2518
sortTrianglesInternal(0, triangleCount-1);
2523
private void sortTrianglesInternal(int i, int j) {
2524
int pivotIndex = (i+j)/2;
2525
sortTrianglesSwap(pivotIndex, j);
2526
int k = sortTrianglesPartition(i-1, j);
2527
sortTrianglesSwap(k, j);
2528
if ((k-i) > 1) sortTrianglesInternal(i, k-1);
2529
if ((j-k) > 1) sortTrianglesInternal(k+1, j);
2533
private int sortTrianglesPartition(int left, int right) {
2536
while (sortTrianglesCompare(++left, pivot) < 0) { }
2537
while ((right != 0) &&
2538
(sortTrianglesCompare(--right, pivot) > 0)) { }
2539
sortTrianglesSwap(left, right);
2540
} while (left < right);
2541
sortTrianglesSwap(left, right);
2546
private void sortTrianglesSwap(int a, int b) {
2547
int tempi[] = triangles[a];
2548
triangles[a] = triangles[b];
2549
triangles[b] = tempi;
2550
float tempf[][] = triangleColors[a];
2551
triangleColors[a] = triangleColors[b];
2552
triangleColors[b] = tempf;
2556
private float sortTrianglesCompare(int a, int b) {
2558
if (Float.isNaN(vertices[triangles[a][VERTEX1]][TZ]) ||
2559
Float.isNaN(vertices[triangles[a][VERTEX2]][TZ]) ||
2560
Float.isNaN(vertices[triangles[a][VERTEX3]][TZ]) ||
2561
Float.isNaN(vertices[triangles[b][VERTEX1]][TZ]) ||
2562
Float.isNaN(vertices[triangles[b][VERTEX2]][TZ]) ||
2563
Float.isNaN(vertices[triangles[b][VERTEX3]][TZ])) {
2564
System.err.println("NaN values in triangle");
2567
return ((vertices[triangles[b][VERTEX1]][TZ] +
2568
vertices[triangles[b][VERTEX2]][TZ] +
2569
vertices[triangles[b][VERTEX3]][TZ]) -
2570
(vertices[triangles[a][VERTEX1]][TZ] +
2571
vertices[triangles[a][VERTEX2]][TZ] +
2572
vertices[triangles[a][VERTEX3]][TZ]));
2577
//////////////////////////////////////////////////////////////
2579
// POINT, LINE, TRIANGLE, QUAD
2581
// Because vertex(x, y) is mapped to vertex(x, y, 0), none of these commands
2582
// need to be overridden from their default implementation in PGraphics.
2585
//public void point(float x, float y)
2588
//public void point(float x, float y, float z)
2591
//public void line(float x1, float y1, float x2, float y2)
2594
//public void line(float x1, float y1, float z1,
2595
// float x2, float y2, float z2)
2598
//public void triangle(float x1, float y1, float x2, float y2,
2599
// float x3, float y3)
2602
//public void quad(float x1, float y1, float x2, float y2,
2603
// float x3, float y3, float x4, float y4)
2607
//////////////////////////////////////////////////////////////
2612
//public void rectMode(int mode)
2615
//public void rect(float a, float b, float c, float d)
2618
//protected void rectImpl(float x1, float y1, float x2, float y2)
2622
//////////////////////////////////////////////////////////////
2627
//public void ellipseMode(int mode)
2630
//public void ellipse(float a, float b, float c, float d)
2633
protected void ellipseImpl(float x, float y, float w, float h) {
2634
float radiusH = w / 2;
2635
float radiusV = h / 2;
2637
float centerX = x + radiusH;
2638
float centerY = y + radiusV;
2640
// float sx1 = screenX(x, y);
2641
// float sy1 = screenY(x, y);
2642
// float sx2 = screenX(x+w, y+h);
2643
// float sy2 = screenY(x+w, y+h);
2645
// returning to pre-1.0 version of algorithm because of problems
2646
int rough = (int)(4+Math.sqrt(w+h)*3);
2647
int accuracy = PApplet.constrain(rough, 6, 100);
2650
// returning to pre-1.0 version of algorithm because of problems
2651
// int rough = (int)(4+Math.sqrt(w+h)*3);
2652
// int rough = (int) (TWO_PI * PApplet.dist(sx1, sy1, sx2, sy2) / 20);
2653
// int accuracy = PApplet.constrain(rough, 6, 100);
2655
float inc = (float)SINCOS_LENGTH / accuracy;
2658
boolean strokeSaved = stroke;
2660
boolean smoothSaved = smooth;
2661
if (smooth && stroke) {
2665
beginShape(TRIANGLE_FAN);
2667
vertex(centerX, centerY);
2668
for (int i = 0; i < accuracy; i++) {
2669
vertex(centerX + cosLUT[(int) val] * radiusH,
2670
centerY + sinLUT[(int) val] * radiusV);
2671
val = (val + inc) % SINCOS_LENGTH;
2673
// back to the beginning
2674
vertex(centerX + cosLUT[0] * radiusH,
2675
centerY + sinLUT[0] * radiusV);
2678
stroke = strokeSaved;
2679
smooth = smoothSaved;
2683
// int rough = (int) (TWO_PI * PApplet.dist(sx1, sy1, sx2, sy2) / 8);
2684
// int accuracy = PApplet.constrain(rough, 6, 100);
2686
float inc = (float)SINCOS_LENGTH / accuracy;
2689
boolean savedFill = fill;
2694
for (int i = 0; i < accuracy; i++) {
2695
vertex(centerX + cosLUT[(int) val] * radiusH,
2696
centerY + sinLUT[(int) val] * radiusV);
2697
val = (val + inc) % SINCOS_LENGTH;
2706
//public void arc(float a, float b, float c, float d,
2707
// float start, float stop)
2710
protected void arcImpl(float x, float y, float w, float h,
2711
float start, float stop) {
2715
float centerX = x + hr;
2716
float centerY = y + vr;
2719
// shut off stroke for a minute
2720
boolean savedStroke = stroke;
2723
int startLUT = (int) (0.5f + (start / TWO_PI) * SINCOS_LENGTH);
2724
int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH);
2726
beginShape(TRIANGLE_FAN);
2727
vertex(centerX, centerY);
2728
int increment = 1; // what's a good algorithm? stopLUT - startLUT;
2729
for (int i = startLUT; i < stopLUT; i += increment) {
2730
int ii = i % SINCOS_LENGTH;
2731
// modulo won't make the value positive
2732
if (ii < 0) ii += SINCOS_LENGTH;
2733
vertex(centerX + cosLUT[ii] * hr,
2734
centerY + sinLUT[ii] * vr);
2736
// draw last point explicitly for accuracy
2737
vertex(centerX + cosLUT[stopLUT % SINCOS_LENGTH] * hr,
2738
centerY + sinLUT[stopLUT % SINCOS_LENGTH] * vr);
2741
stroke = savedStroke;
2745
// Almost identical to above, but this uses a LINE_STRIP
2746
// and doesn't include the first (center) vertex.
2748
boolean savedFill = fill;
2751
int startLUT = (int) (0.5f + (start / TWO_PI) * SINCOS_LENGTH);
2752
int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH);
2754
beginShape(); //LINE_STRIP);
2755
int increment = 1; // what's a good algorithm? stopLUT - startLUT;
2756
for (int i = startLUT; i < stopLUT; i += increment) {
2757
int ii = i % SINCOS_LENGTH;
2758
if (ii < 0) ii += SINCOS_LENGTH;
2759
vertex(centerX + cosLUT[ii] * hr,
2760
centerY + sinLUT[ii] * vr);
2762
// draw last point explicitly for accuracy
2763
vertex(centerX + cosLUT[stopLUT % SINCOS_LENGTH] * hr,
2764
centerY + sinLUT[stopLUT % SINCOS_LENGTH] * vr);
2773
//////////////////////////////////////////////////////////////
2778
//public void box(float size)
2781
public void box(float w, float h, float d) {
2782
if (triangle != null) { // triangle is null in gl
2783
triangle.setCulling(true);
2788
if (triangle != null) { // triangle is null in gl
2789
triangle.setCulling(false);
2795
//////////////////////////////////////////////////////////////
2800
//public void sphereDetail(int res)
2803
//public void sphereDetail(int ures, int vres)
2806
public void sphere(float r) {
2807
if (triangle != null) { // triangle is null in gl
2808
triangle.setCulling(true);
2813
if (triangle != null) { // triangle is null in gl
2814
triangle.setCulling(false);
2820
//////////////////////////////////////////////////////////////
2825
//public float bezierPoint(float a, float b, float c, float d, float t)
2828
//public float bezierTangent(float a, float b, float c, float d, float t)
2831
//public void bezierDetail(int detail)
2834
//public void bezier(float x1, float y1,
2835
// float x2, float y2,
2836
// float x3, float y3,
2837
// float x4, float y4)
2840
//public void bezier(float x1, float y1, float z1,
2841
// float x2, float y2, float z2,
2842
// float x3, float y3, float z3,
2843
// float x4, float y4, float z4)
2847
//////////////////////////////////////////////////////////////
2849
// CATMULL-ROM CURVES
2852
//public float curvePoint(float a, float b, float c, float d, float t)
2855
//public float curveTangent(float a, float b, float c, float d, float t)
2858
//public void curveDetail(int detail)
2861
//public void curveTightness(float tightness)
2864
//public void curve(float x1, float y1,
2865
// float x2, float y2,
2866
// float x3, float y3,
2867
// float x4, float y4)
2870
//public void curve(float x1, float y1, float z1,
2871
// float x2, float y2, float z2,
2872
// float x3, float y3, float z3,
2873
// float x4, float y4, float z4)
2877
//////////////////////////////////////////////////////////////
2882
public void smooth() {
2883
//showMethodWarning("smooth");
2884
s_enableAccurateTextures = true;
2889
public void noSmooth() {
2890
s_enableAccurateTextures = false;
2896
//////////////////////////////////////////////////////////////
2901
//public void imageMode(int mode)
2904
//public void image(PImage image, float x, float y)
2907
//public void image(PImage image, float x, float y, float c, float d)
2910
//public void image(PImage image,
2911
// float a, float b, float c, float d,
2912
// int u1, int v1, int u2, int v2)
2915
//protected void imageImpl(PImage image,
2916
// float x1, float y1, float x2, float y2,
2917
// int u1, int v1, int u2, int v2)
2921
//////////////////////////////////////////////////////////////
2926
//public void shapeMode(int mode)
2929
//public void shape(PShape shape)
2932
//public void shape(PShape shape, float x, float y)
2935
//public void shape(PShape shape, float x, float y, float c, float d)
2939
//////////////////////////////////////////////////////////////
2943
// Only textModeCheck overridden from PGraphics, no textAlign, textAscent,
2944
// textDescent, textFont, textLeading, textMode, textSize, textWidth
2947
protected boolean textModeCheck(int mode) {
2948
return (textMode == MODEL) || (textMode == SCREEN);
2953
//////////////////////////////////////////////////////////////
2957
// None of the variations of text() are overridden from PGraphics.
2961
//////////////////////////////////////////////////////////////
2965
// Not even the text drawing implementation stuff is overridden.
2969
//////////////////////////////////////////////////////////////
2974
public void pushMatrix() {
2975
if (matrixStackDepth == MATRIX_STACK_DEPTH) {
2976
throw new RuntimeException(ERROR_PUSHMATRIX_OVERFLOW);
2978
modelview.get(matrixStack[matrixStackDepth]);
2979
modelviewInv.get(matrixInvStack[matrixStackDepth]);
2984
public void popMatrix() {
2985
if (matrixStackDepth == 0) {
2986
throw new RuntimeException(ERROR_PUSHMATRIX_UNDERFLOW);
2989
modelview.set(matrixStack[matrixStackDepth]);
2990
modelviewInv.set(matrixInvStack[matrixStackDepth]);
2995
//////////////////////////////////////////////////////////////
2997
// MATRIX TRANSFORMATIONS
3000
public void translate(float tx, float ty) {
3001
translate(tx, ty, 0);
3005
public void translate(float tx, float ty, float tz) {
3006
forwardTransform.translate(tx, ty, tz);
3007
reverseTransform.invTranslate(tx, ty, tz);
3012
* Two dimensional rotation. Same as rotateZ (this is identical
3013
* to a 3D rotation along the z-axis) but included for clarity --
3014
* it'd be weird for people drawing 2D graphics to be using rotateZ.
3015
* And they might kick our a-- for the confusion.
3017
public void rotate(float angle) {
3022
public void rotateX(float angle) {
3023
forwardTransform.rotateX(angle);
3024
reverseTransform.invRotateX(angle);
3028
public void rotateY(float angle) {
3029
forwardTransform.rotateY(angle);
3030
reverseTransform.invRotateY(angle);
3034
public void rotateZ(float angle) {
3035
forwardTransform.rotateZ(angle);
3036
reverseTransform.invRotateZ(angle);
3041
* Rotate around an arbitrary vector, similar to glRotate(),
3042
* except that it takes radians (instead of degrees).
3044
public void rotate(float angle, float v0, float v1, float v2) {
3045
forwardTransform.rotate(angle, v0, v1, v2);
3046
reverseTransform.invRotate(angle, v0, v1, v2);
3051
* Same as scale(s, s, s).
3053
public void scale(float s) {
3059
* Same as scale(sx, sy, 1).
3061
public void scale(float sx, float sy) {
3067
* Scale in three dimensions.
3069
public void scale(float x, float y, float z) {
3070
forwardTransform.scale(x, y, z);
3071
reverseTransform.invScale(x, y, z);
3076
//////////////////////////////////////////////////////////////
3081
public void resetMatrix() {
3082
forwardTransform.reset();
3083
reverseTransform.reset();
3087
public void applyMatrix(PMatrix2D source) {
3088
applyMatrix(source.m00, source.m01, source.m02,
3089
source.m10, source.m11, source.m12);
3093
public void applyMatrix(float n00, float n01, float n02,
3094
float n10, float n11, float n12) {
3095
applyMatrix(n00, n01, n02, 0,
3102
public void applyMatrix(PMatrix3D source) {
3103
applyMatrix(source.m00, source.m01, source.m02, source.m03,
3104
source.m10, source.m11, source.m12, source.m13,
3105
source.m20, source.m21, source.m22, source.m23,
3106
source.m30, source.m31, source.m32, source.m33);
3111
* Apply a 4x4 transformation matrix. Same as glMultMatrix().
3112
* This call will be slow because it will try to calculate the
3113
* inverse of the transform. So avoid it whenever possible.
3115
public void applyMatrix(float n00, float n01, float n02, float n03,
3116
float n10, float n11, float n12, float n13,
3117
float n20, float n21, float n22, float n23,
3118
float n30, float n31, float n32, float n33) {
3120
forwardTransform.apply(n00, n01, n02, n03,
3123
n30, n31, n32, n33);
3125
reverseTransform.invApply(n00, n01, n02, n03,
3128
n30, n31, n32, n33);
3133
//////////////////////////////////////////////////////////////
3135
// MATRIX GET/SET/PRINT
3138
public PMatrix getMatrix() {
3139
return modelview.get();
3143
//public PMatrix2D getMatrix(PMatrix2D target)
3146
public PMatrix3D getMatrix(PMatrix3D target) {
3147
if (target == null) {
3148
target = new PMatrix3D();
3150
target.set(modelview);
3155
//public void setMatrix(PMatrix source)
3158
public void setMatrix(PMatrix2D source) {
3159
// not efficient, but at least handles the inverse stuff.
3161
applyMatrix(source);
3166
* Set the current transformation to the contents of the specified source.
3168
public void setMatrix(PMatrix3D source) {
3169
// not efficient, but at least handles the inverse stuff.
3171
applyMatrix(source);
3176
* Print the current model (or "transformation") matrix.
3178
public void printMatrix() {
3184
* This function checks if the modelview matrix is set up to likely be
3185
* drawing in 2D. It merely checks if the non-translational piece of the
3186
* matrix is unity. If this is to be used, it should be coupled with a
3187
* check that the raw vertex coordinates lie in the z=0 plane.
3188
* Mainly useful for applying sub-pixel shifts to avoid 2d artifacts
3189
* in the screen plane.
3190
* Added by ewjordan 6/13/07
3192
* TODO need to invert the logic here so that we can simply return
3193
* the value, rather than calculating true/false and returning it.
3196
private boolean drawing2D() {
3197
if (modelview.m00 != 1.0f ||
3198
modelview.m11 != 1.0f ||
3199
modelview.m22 != 1.0f || // check scale
3200
modelview.m01 != 0.0f ||
3201
modelview.m02 != 0.0f || // check rotational pieces
3202
modelview.m10 != 0.0f ||
3203
modelview.m12 != 0.0f ||
3204
modelview.m20 != 0.0f ||
3205
modelview.m21 != 0.0f ||
3206
!((camera.m23-modelview.m23) <= EPSILON &&
3207
(camera.m23-modelview.m23) >= -EPSILON)) { // check for z-translation
3208
// Something about the modelview matrix indicates 3d drawing
3209
// (or rotated 2d, in which case 2d subpixel fixes probably aren't needed)
3212
//The matrix is mapping z=0 vertices to the screen plane,
3213
// which means it's likely that 2D drawing is happening.
3221
//////////////////////////////////////////////////////////////
3227
* Set matrix mode to the camera matrix (instead of the current
3228
* transformation matrix). This means applyMatrix, resetMatrix, etc.
3229
* will affect the camera.
3231
* Note that the camera matrix is *not* the perspective matrix,
3232
* it is in front of the modelview matrix (hence the name "model"
3233
* and "view" for that matrix).
3235
* beginCamera() specifies that all coordinate transforms until endCamera()
3236
* should be pre-applied in inverse to the camera transform matrix.
3237
* Note that this is only challenging when a user specifies an arbitrary
3238
* matrix with applyMatrix(). Then that matrix will need to be inverted,
3239
* which may not be possible. But take heart, if a user is applying a
3240
* non-invertible matrix to the camera transform, then he is clearly
3241
* up to no good, and we can wash our hands of those bad intentions.
3243
* begin/endCamera clauses do not automatically reset the camera transform
3244
* matrix. That's because we set up a nice default camera transform int
3245
* setup(), and we expect it to hold through draw(). So we don't reset
3246
* the camera transform matrix at the top of draw(). That means that an
3247
* innocuous-looking clause like
3250
* translate(0, 0, 10);
3253
* at the top of draw(), will result in a runaway camera that shoots
3254
* infinitely out of the screen over time. In order to prevent this,
3255
* it is necessary to call some function that does a hard reset of the
3256
* camera transform matrix inside of begin/endCamera. Two options are
3258
* camera(); // sets up the nice default camera transform
3259
* resetMatrix(); // sets up the identity camera transform
3261
* So to rotate a camera a constant amount, you might try
3269
public void beginCamera() {
3270
if (manipulatingCamera) {
3271
throw new RuntimeException("beginCamera() cannot be called again " +
3272
"before endCamera()");
3274
manipulatingCamera = true;
3275
forwardTransform = cameraInv;
3276
reverseTransform = camera;
3282
* Record the current settings into the camera matrix, and set
3283
* the matrix mode back to the current transformation matrix.
3285
* Note that this will destroy any settings to scale(), translate(),
3286
* or whatever, because the final camera matrix will be copied
3287
* (not multiplied) into the modelview.
3289
public void endCamera() {
3290
if (!manipulatingCamera) {
3291
throw new RuntimeException("Cannot call endCamera() " +
3292
"without first calling beginCamera()");
3294
// reset the modelview to use this new camera matrix
3295
modelview.set(camera);
3296
modelviewInv.set(cameraInv);
3298
// set matrix mode back to modelview
3299
forwardTransform = modelview;
3300
reverseTransform = modelviewInv;
3303
manipulatingCamera = false;
3308
* Set camera to the default settings.
3310
* Processing camera behavior:
3312
* Camera behavior can be split into two separate components, camera
3313
* transformation, and projection. The transformation corresponds to the
3314
* physical location, orientation, and scale of the camera. In a physical
3315
* camera metaphor, this is what can manipulated by handling the camera
3316
* body (with the exception of scale, which doesn't really have a physcial
3317
* analog). The projection corresponds to what can be changed by
3318
* manipulating the lens.
3320
* We maintain separate matrices to represent the camera transform and
3321
* projection. An important distinction between the two is that the camera
3322
* transform should be invertible, where the projection matrix should not,
3323
* since it serves to map three dimensions to two. It is possible to bake
3324
* the two matrices into a single one just by multiplying them together,
3325
* but it isn't a good idea, since lighting, z-ordering, and z-buffering
3326
* all demand a true camera z coordinate after modelview and camera
3327
* transforms have been applied but before projection. If the camera
3328
* transform and projection are combined there is no way to recover a
3329
* good camera-space z-coordinate from a model coordinate.
3331
* Fortunately, there are no functions that manipulate both camera
3332
* transformation and projection.
3334
* camera() sets the camera position, orientation, and center of the scene.
3335
* It replaces the camera transform with a new one. This is different from
3336
* gluLookAt(), but I think the only reason that GLU's lookat doesn't fully
3337
* replace the camera matrix with the new one, but instead multiplies it,
3338
* is that GL doesn't enforce the separation of camera transform and
3339
* projection, so it wouldn't be safe (you'd probably stomp your projection).
3341
* The transformation functions are the same ones used to manipulate the
3342
* modelview matrix (scale, translate, rotate, etc.). But they are bracketed
3343
* with beginCamera(), endCamera() to indicate that they should apply
3344
* (in inverse), to the camera transformation matrix.
3346
* This differs considerably from camera transformation in OpenGL.
3347
* OpenGL only lets you say, apply everything from here out to the
3348
* projection or modelview matrix. This makes it very hard to treat camera
3349
* manipulation as if it were a physical camera. Imagine that you want to
3350
* move your camera 100 units forward. In OpenGL, you need to apply the
3351
* inverse of that transformation or else you'll move your scene 100 units
3352
* forward--whether or not you've specified modelview or projection matrix.
3353
* Remember they're just multiplied by model coods one after another.
3354
* So in order to treat a camera like a physical camera, it is necessary
3355
* to pre-apply inverse transforms to a matrix that will be applied to model
3356
* coordinates. OpenGL provides nothing of this sort, but Processing does!
3357
* This is the camera transform matrix.
3359
public void camera() {
3360
camera(cameraX, cameraY, cameraZ,
3361
cameraX, cameraY, 0,
3367
* More flexible method for dealing with camera().
3369
* The actual call is like gluLookat. Here's the real skinny on
3373
* camera(ex, ey, ez, cx, cy, cz, ux, uy, uz);
3375
* do not need to be called from with beginCamera();/endCamera();
3376
* That's because they always apply to the camera transformation,
3377
* and they always totally replace it. That means that any coordinate
3378
* transforms done before camera(); in draw() will be wiped out.
3379
* It also means that camera() always operates in untransformed world
3380
* coordinates. Therefore it is always redundant to call resetMatrix();
3381
* before camera(); This isn't technically true of gluLookat, but it's
3382
* pretty much how it's used.
3384
* Now, beginCamera(); and endCamera(); are useful if you want to move
3385
* the camera around using transforms like translate(), etc. They will
3386
* wipe out any coordinate system transforms that occur before them in
3387
* draw(), but they will not automatically wipe out the camera transform.
3388
* This means that they should be at the top of draw(). It also means
3389
* that the following:
3395
* will result in a camera that spins without stopping. If you want to
3396
* just rotate a small constant amount, try this:
3399
* camera(); // sets up the default view
3403
* That will rotate a little off of the default view. Note that this
3404
* is entirely equivalent to
3406
* camera(); // sets up the default view
3411
* because camera() doesn't care whether or not it's inside a
3412
* begin/end clause. Basically it's safe to use camera() or
3413
* camera(ex, ey, ez, cx, cy, cz, ux, uy, uz) as naked calls because
3414
* they do all the matrix resetting automatically.
3416
public void camera(float eyeX, float eyeY, float eyeZ,
3417
float centerX, float centerY, float centerZ,
3418
float upX, float upY, float upZ) {
3419
float z0 = eyeX - centerX;
3420
float z1 = eyeY - centerY;
3421
float z2 = eyeZ - centerZ;
3422
float mag = sqrt(z0*z0 + z1*z1 + z2*z2);
3434
float x0 = y1*z2 - y2*z1;
3435
float x1 = -y0*z2 + y2*z0;
3436
float x2 = y0*z1 - y1*z0;
3439
y1 = -z0*x2 + z2*x0;
3442
mag = sqrt(x0*x0 + x1*x1 + x2*x2);
3449
mag = sqrt(y0*y0 + y1*y1 + y2*y2);
3456
// just does an apply to the main matrix,
3457
// since that'll be copied out on endCamera
3458
camera.set(x0, x1, x2, 0,
3462
camera.translate(-eyeX, -eyeY, -eyeZ);
3465
cameraInv.invApply(x0, x1, x2, 0,
3469
cameraInv.translate(eyeX, eyeY, eyeZ);
3471
modelview.set(camera);
3472
modelviewInv.set(cameraInv);
3477
* Print the current camera matrix.
3479
public void printCamera() {
3484
//////////////////////////////////////////////////////////////
3490
* Calls ortho() with the proper parameters for Processing's
3491
* standard orthographic projection.
3493
public void ortho() {
3494
ortho(0, width, 0, height, -10, 10);
3499
* Similar to gluOrtho(), but wipes out the current projection matrix.
3501
* Implementation partially based on Mesa's matrix.c.
3503
public void ortho(float left, float right,
3504
float bottom, float top,
3505
float near, float far) {
3506
float x = 2.0f / (right - left);
3507
float y = 2.0f / (top - bottom);
3508
float z = -2.0f / (far - near);
3510
float tx = -(right + left) / (right - left);
3511
float ty = -(top + bottom) / (top - bottom);
3512
float tz = -(far + near) / (far - near);
3514
projection.set(x, 0, 0, tx,
3520
frustumMode = false;
3525
* Calls perspective() with Processing's standard coordinate projection.
3527
* Projection functions:
3533
* Each of these three functions completely replaces the projection
3534
* matrix with a new one. They can be called inside setup(), and their
3535
* effects will be felt inside draw(). At the top of draw(), the projection
3536
* matrix is not reset. Therefore the last projection function to be
3537
* called always dominates. On resize, the default projection is always
3538
* established, which has perspective.
3540
* This behavior is pretty much familiar from OpenGL, except where
3541
* functions replace matrices, rather than multiplying against the
3545
public void perspective() {
3546
perspective(cameraFOV, cameraAspect, cameraNear, cameraFar);
3551
* Similar to gluPerspective(). Implementation based on Mesa's glu.c
3553
public void perspective(float fov, float aspect, float zNear, float zFar) {
3554
//float ymax = zNear * tan(fovy * PI / 360.0f);
3555
float ymax = zNear * (float) Math.tan(fov / 2);
3558
float xmin = ymin * aspect;
3559
float xmax = ymax * aspect;
3561
frustum(xmin, xmax, ymin, ymax, zNear, zFar);
3566
* Same as glFrustum(), except that it wipes out (rather than
3567
* multiplies against) the current perspective matrix.
3569
* Implementation based on the explanation in the OpenGL blue book.
3571
public void frustum(float left, float right, float bottom,
3572
float top, float znear, float zfar) {
3575
rightScreen = right;
3576
bottomScreen = bottom;
3581
//System.out.println(projection);
3582
projection.set((2*znear)/(right-left), 0, (right+left)/(right-left), 0,
3583
0, (2*znear)/(top-bottom), (top+bottom)/(top-bottom), 0,
3584
0, 0, -(zfar+znear)/(zfar-znear),-(2*zfar*znear)/(zfar-znear),
3590
/** Called after the 'projection' PMatrix3D has changed. */
3591
protected void updateProjection() {
3596
* Print the current projection matrix.
3598
public void printProjection() {
3604
//////////////////////////////////////////////////////////////
3606
// SCREEN AND MODEL COORDS
3609
public float screenX(float x, float y) {
3610
return screenX(x, y, 0);
3614
public float screenY(float x, float y) {
3615
return screenY(x, y, 0);
3619
public float screenX(float x, float y, float z) {
3621
modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
3623
modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
3625
modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
3627
modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
3630
projection.m00*ax + projection.m01*ay +
3631
projection.m02*az + projection.m03*aw;
3633
projection.m30*ax + projection.m31*ay +
3634
projection.m32*az + projection.m33*aw;
3636
if (ow != 0) ox /= ow;
3637
return width * (1 + ox) / 2.0f;
3641
public float screenY(float x, float y, float z) {
3643
modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
3645
modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
3647
modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
3649
modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
3652
projection.m10*ax + projection.m11*ay +
3653
projection.m12*az + projection.m13*aw;
3655
projection.m30*ax + projection.m31*ay +
3656
projection.m32*az + projection.m33*aw;
3658
if (ow != 0) oy /= ow;
3659
return height * (1 + oy) / 2.0f;
3663
public float screenZ(float x, float y, float z) {
3665
modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
3667
modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
3669
modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
3671
modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
3674
projection.m20*ax + projection.m21*ay +
3675
projection.m22*az + projection.m23*aw;
3677
projection.m30*ax + projection.m31*ay +
3678
projection.m32*az + projection.m33*aw;
3680
if (ow != 0) oz /= ow;
3681
return (oz + 1) / 2.0f;
3685
public float modelX(float x, float y, float z) {
3687
modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
3689
modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
3691
modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
3693
modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
3696
cameraInv.m00*ax + cameraInv.m01*ay +
3697
cameraInv.m02*az + cameraInv.m03*aw;
3699
cameraInv.m30*ax + cameraInv.m31*ay +
3700
cameraInv.m32*az + cameraInv.m33*aw;
3702
return (ow != 0) ? ox / ow : ox;
3706
public float modelY(float x, float y, float z) {
3708
modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
3710
modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
3712
modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
3714
modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
3717
cameraInv.m10*ax + cameraInv.m11*ay +
3718
cameraInv.m12*az + cameraInv.m13*aw;
3720
cameraInv.m30*ax + cameraInv.m31*ay +
3721
cameraInv.m32*az + cameraInv.m33*aw;
3723
return (ow != 0) ? oy / ow : oy;
3727
public float modelZ(float x, float y, float z) {
3729
modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
3731
modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
3733
modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
3735
modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
3738
cameraInv.m20*ax + cameraInv.m21*ay +
3739
cameraInv.m22*az + cameraInv.m23*aw;
3741
cameraInv.m30*ax + cameraInv.m31*ay +
3742
cameraInv.m32*az + cameraInv.m33*aw;
3744
return (ow != 0) ? oz / ow : oz;
3749
//////////////////////////////////////////////////////////////
3753
// pushStyle(), popStyle(), style() and getStyle() inherited.
3757
//////////////////////////////////////////////////////////////
3759
// STROKE CAP/JOIN/WEIGHT
3762
// public void strokeWeight(float weight) {
3763
// if (weight != DEFAULT_STROKE_WEIGHT) {
3764
// showMethodWarning("strokeWeight");
3769
public void strokeJoin(int join) {
3770
if (join != DEFAULT_STROKE_JOIN) {
3771
showMethodWarning("strokeJoin");
3776
public void strokeCap(int cap) {
3777
if (cap != DEFAULT_STROKE_CAP) {
3778
showMethodWarning("strokeCap");
3784
//////////////////////////////////////////////////////////////
3788
// All methods inherited from PGraphics.
3792
//////////////////////////////////////////////////////////////
3796
// All methods inherited from PGraphics.
3800
//////////////////////////////////////////////////////////////
3805
protected void fillFromCalc() {
3806
super.fillFromCalc();
3812
//////////////////////////////////////////////////////////////
3814
// MATERIAL PROPERTIES
3816
// ambient, specular, shininess, and emissive all inherited.
3820
//////////////////////////////////////////////////////////////
3825
PVector lightPositionVec = new PVector();
3826
PVector lightDirectionVec = new PVector();
3829
* Sets up an ambient and directional light.
3831
* The Lighting Skinny:
3833
* The way lighting works is complicated enough that it's worth
3834
* producing a document to describe it. Lighting calculations proceed
3835
* pretty much exactly as described in the OpenGL red book.
3837
* Light-affecting material properties:
3840
* - multiplies by light's ambient component
3841
* - for believability this should match diffuse color
3844
* - multiplies by light's diffuse component
3847
* - multiplies by light's specular component
3848
* - usually less colored than diffuse/ambient
3851
* - the concentration of specular effect
3852
* - this should be set pretty high (20-50) to see really
3853
* noticeable specularity
3856
* - constant additive color effect
3862
* - no specular color
3864
* - may have falloff (constant, linear, and quadratic)
3865
* - may have position (which matters in non-constant falloff case)
3866
* - multiplies by a material's ambient reflection
3869
* - has diffuse color
3870
* - has specular color
3874
* - multiplies by a material's diffuse and specular reflections
3877
* - has diffuse color
3878
* - has specular color
3881
* - may have falloff (constant, linear, and quadratic)
3882
* - multiplies by a material's diffuse and specular reflections
3885
* - has diffuse color
3886
* - has specular color
3889
* - has cone angle (set to half the total cone angle)
3890
* - has concentration value
3891
* - may have falloff (constant, linear, and quadratic)
3892
* - multiplies by a material's diffuse and specular reflections
3896
* All of the primitives (rect, box, sphere, etc.) have their normals
3897
* set nicely. During beginShape/endShape normals can be set by the user.
3900
* - if no normal is set during the shape, we are in auto-normal mode
3901
* - auto-normal calculates one normal per triangle (face-normal mode)
3904
* - if one normal is set during the shape, it will be used for
3908
* - if multiple normals are set, each normal applies to
3909
* subsequent vertices
3910
* - (except for the first one, which applies to previous
3911
* and subsequent vertices)
3913
* Efficiency consequences:
3915
* There is a major efficiency consequence of position-dependent
3916
* lighting calculations per vertex. (See below for determining
3917
* whether lighting is vertex position-dependent.) If there is no
3918
* position dependency then the only factors that affect the lighting
3919
* contribution per vertex are its colors and its normal.
3920
* There is a major efficiency win if
3922
* 1) lighting is not position dependent
3923
* 2) we are in AUTO-NORMAL or SHAPE-NORMAL mode
3925
* because then we can calculate one lighting contribution per shape
3926
* (SHAPE-NORMAL) or per triangle (AUTO-NORMAL) and simply multiply it
3927
* into the vertex colors. The converse is our worst-case performance when
3929
* 1) lighting is position dependent
3930
* 2) we are in AUTO-NORMAL mode
3932
* because then we must calculate lighting per-face * per-vertex.
3933
* Each vertex has a different lighting contribution per face in
3934
* which it appears. Yuck.
3936
* Determining vertex position dependency:
3938
* If any of the following factors are TRUE then lighting is
3939
* vertex position dependent:
3941
* 1) Any lights uses non-constant falloff
3942
* 2) There are any point or spot lights
3943
* 3) There is a light with specular color AND there is a
3944
* material with specular color
3946
* So worth noting is that default lighting (a no-falloff ambient
3947
* and a directional without specularity) is not position-dependent.
3948
* We should capitalize.
3950
* Simon Greenwold, April 2005
3953
public void lights() {
3954
// need to make sure colorMode is RGB 255 here
3955
int colorModeSaved = colorMode;
3958
lightFalloff(1, 0, 0);
3959
lightSpecular(0, 0, 0);
3961
ambientLight(colorModeX * 0.5f,
3964
directionalLight(colorModeX * 0.5f,
3969
colorMode = colorModeSaved;
3971
lightingDependsOnVertexPosition = false;
3976
* Turn off all lights.
3978
public void noLights() {
3979
// write any queued geometry, because lighting will be goofed after
3981
// set the light count back to zero
3987
* Add an ambient light based on the current color mode.
3989
public void ambientLight(float r, float g, float b) {
3990
ambientLight(r, g, b, 0, 0, 0);
3995
* Add an ambient light based on the current color mode.
3996
* This version includes an (x, y, z) position for situations
3997
* where the falloff distance is used.
3999
public void ambientLight(float r, float g, float b,
4000
float x, float y, float z) {
4001
if (lightCount == MAX_LIGHTS) {
4002
throw new RuntimeException("can only create " + MAX_LIGHTS + " lights");
4005
lightDiffuse[lightCount][0] = calcR;
4006
lightDiffuse[lightCount][1] = calcG;
4007
lightDiffuse[lightCount][2] = calcB;
4009
lightType[lightCount] = AMBIENT;
4010
lightFalloffConstant[lightCount] = currentLightFalloffConstant;
4011
lightFalloffLinear[lightCount] = currentLightFalloffLinear;
4012
lightFalloffQuadratic[lightCount] = currentLightFalloffQuadratic;
4013
lightPosition(lightCount, x, y, z);
4015
//return lightCount-1;
4019
public void directionalLight(float r, float g, float b,
4020
float nx, float ny, float nz) {
4021
if (lightCount == MAX_LIGHTS) {
4022
throw new RuntimeException("can only create " + MAX_LIGHTS + " lights");
4025
lightDiffuse[lightCount][0] = calcR;
4026
lightDiffuse[lightCount][1] = calcG;
4027
lightDiffuse[lightCount][2] = calcB;
4029
lightType[lightCount] = DIRECTIONAL;
4030
lightFalloffConstant[lightCount] = currentLightFalloffConstant;
4031
lightFalloffLinear[lightCount] = currentLightFalloffLinear;
4032
lightFalloffQuadratic[lightCount] = currentLightFalloffQuadratic;
4033
lightSpecular[lightCount][0] = currentLightSpecular[0];
4034
lightSpecular[lightCount][1] = currentLightSpecular[1];
4035
lightSpecular[lightCount][2] = currentLightSpecular[2];
4036
lightDirection(lightCount, nx, ny, nz);
4041
public void pointLight(float r, float g, float b,
4042
float x, float y, float z) {
4043
if (lightCount == MAX_LIGHTS) {
4044
throw new RuntimeException("can only create " + MAX_LIGHTS + " lights");
4047
lightDiffuse[lightCount][0] = calcR;
4048
lightDiffuse[lightCount][1] = calcG;
4049
lightDiffuse[lightCount][2] = calcB;
4051
lightType[lightCount] = POINT;
4052
lightFalloffConstant[lightCount] = currentLightFalloffConstant;
4053
lightFalloffLinear[lightCount] = currentLightFalloffLinear;
4054
lightFalloffQuadratic[lightCount] = currentLightFalloffQuadratic;
4055
lightSpecular[lightCount][0] = currentLightSpecular[0];
4056
lightSpecular[lightCount][1] = currentLightSpecular[1];
4057
lightSpecular[lightCount][2] = currentLightSpecular[2];
4058
lightPosition(lightCount, x, y, z);
4061
lightingDependsOnVertexPosition = true;
4065
public void spotLight(float r, float g, float b,
4066
float x, float y, float z,
4067
float nx, float ny, float nz,
4068
float angle, float concentration) {
4069
if (lightCount == MAX_LIGHTS) {
4070
throw new RuntimeException("can only create " + MAX_LIGHTS + " lights");
4073
lightDiffuse[lightCount][0] = calcR;
4074
lightDiffuse[lightCount][1] = calcG;
4075
lightDiffuse[lightCount][2] = calcB;
4077
lightType[lightCount] = SPOT;
4078
lightFalloffConstant[lightCount] = currentLightFalloffConstant;
4079
lightFalloffLinear[lightCount] = currentLightFalloffLinear;
4080
lightFalloffQuadratic[lightCount] = currentLightFalloffQuadratic;
4081
lightSpecular[lightCount][0] = currentLightSpecular[0];
4082
lightSpecular[lightCount][1] = currentLightSpecular[1];
4083
lightSpecular[lightCount][2] = currentLightSpecular[2];
4084
lightPosition(lightCount, x, y, z);
4085
lightDirection(lightCount, nx, ny, nz);
4086
lightSpotAngle[lightCount] = angle;
4087
lightSpotAngleCos[lightCount] = Math.max(0, (float) Math.cos(angle));
4088
lightSpotConcentration[lightCount] = concentration;
4091
lightingDependsOnVertexPosition = true;
4096
* Set the light falloff rates for the last light that was created.
4097
* Default is lightFalloff(1, 0, 0).
4099
public void lightFalloff(float constant, float linear, float quadratic) {
4100
currentLightFalloffConstant = constant;
4101
currentLightFalloffLinear = linear;
4102
currentLightFalloffQuadratic = quadratic;
4104
lightingDependsOnVertexPosition = true;
4109
* Set the specular color of the last light created.
4111
public void lightSpecular(float x, float y, float z) {
4113
currentLightSpecular[0] = calcR;
4114
currentLightSpecular[1] = calcG;
4115
currentLightSpecular[2] = calcB;
4117
lightingDependsOnVertexPosition = true;
4122
* internal function to set the light position
4123
* based on the current modelview matrix.
4125
protected void lightPosition(int num, float x, float y, float z) {
4126
lightPositionVec.set(x, y, z);
4127
modelview.mult(lightPositionVec, lightPosition[num]);
4129
lightPosition[num][0] =
4130
modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
4131
lightPosition[num][1] =
4132
modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
4133
lightPosition[num][2] =
4134
modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
4140
* internal function to set the light direction
4141
* based on the current modelview matrix.
4143
protected void lightDirection(int num, float x, float y, float z) {
4144
lightNormal[num].set(modelviewInv.m00*x + modelviewInv.m10*y + modelviewInv.m20*z + modelviewInv.m30,
4145
modelviewInv.m01*x + modelviewInv.m11*y + modelviewInv.m21*z + modelviewInv.m31,
4146
modelviewInv.m02*x + modelviewInv.m12*y + modelviewInv.m22*z + modelviewInv.m32);
4147
lightNormal[num].normalize();
4150
lightDirectionVec.set(x, y, z);
4151
System.out.println("dir vec " + lightDirectionVec);
4152
//modelviewInv.mult(lightDirectionVec, lightNormal[num]);
4153
modelviewInv.cmult(lightDirectionVec, lightNormal[num]);
4154
System.out.println("cmult vec " + lightNormal[num]);
4155
lightNormal[num].normalize();
4156
System.out.println("setting light direction " + lightNormal[num]);
4160
// Multiply by inverse transpose.
4161
lightNormal[num][0] =
4162
modelviewInv.m00*x + modelviewInv.m10*y +
4163
modelviewInv.m20*z + modelviewInv.m30;
4164
lightNormal[num][1] =
4165
modelviewInv.m01*x + modelviewInv.m11*y +
4166
modelviewInv.m21*z + modelviewInv.m31;
4167
lightNormal[num][2] =
4168
modelviewInv.m02*x + modelviewInv.m12*y +
4169
modelviewInv.m22*z + modelviewInv.m32;
4171
float n = mag(lightNormal[num][0], lightNormal[num][1], lightNormal[num][2]);
4172
if (n == 0 || n == 1) return;
4174
lightNormal[num][0] /= n;
4175
lightNormal[num][1] /= n;
4176
lightNormal[num][2] /= n;
4182
//////////////////////////////////////////////////////////////
4186
// Base background() variations inherited from PGraphics.
4189
protected void backgroundImpl(PImage image) {
4190
System.arraycopy(image.pixels, 0, pixels, 0, pixels.length);
4191
Arrays.fill(zbuffer, Float.MAX_VALUE);
4196
* Clear pixel buffer. With P3D and OPENGL, this also clears the zbuffer.
4198
protected void backgroundImpl() {
4199
Arrays.fill(pixels, backgroundColor);
4200
Arrays.fill(zbuffer, Float.MAX_VALUE);
4205
//////////////////////////////////////////////////////////////
4209
// all colorMode() variations inherited from PGraphics.
4213
//////////////////////////////////////////////////////////////
4215
// COLOR CALCULATIONS
4217
// protected colorCalc and colorCalcARGB inherited.
4221
//////////////////////////////////////////////////////////////
4223
// COLOR DATATYPE STUFFING
4225
// final color() variations inherited.
4229
//////////////////////////////////////////////////////////////
4231
// COLOR DATATYPE EXTRACTION
4233
// final methods alpha, red, green, blue,
4234
// hue, saturation, and brightness all inherited.
4238
//////////////////////////////////////////////////////////////
4240
// COLOR DATATYPE INTERPOLATION
4242
// both lerpColor variants inherited.
4246
//////////////////////////////////////////////////////////////
4250
// beginRaw, endRaw() both inherited.
4254
//////////////////////////////////////////////////////////////
4256
// WARNINGS and EXCEPTIONS
4258
// showWarning and showException inherited.
4262
//////////////////////////////////////////////////////////////
4264
// RENDERER SUPPORT QUERIES
4267
//public boolean displayable()
4270
public boolean is2D() {
4275
public boolean is3D() {
4281
//////////////////////////////////////////////////////////////
4285
// All these methods are inherited, because this render has a
4286
// pixels[] array that can be accessed directly.
4289
// setCache, getCache, removeCache
4290
// isModified, setModified
4291
// loadPixels, updatePixels
4293
// get, getImpl, set, setImpl
4297
// blendColor, blend
4301
//////////////////////////////////////////////////////////////
4303
// MATH (internal use only)
4306
private final float sqrt(float a) {
4307
return (float) Math.sqrt(a);
4311
private final float mag(float a, float b, float c) {
4312
return (float) Math.sqrt(a*a + b*b + c*c);
4316
private final float clamp(float a) {
4317
return (a < 1) ? a : 1;
4321
private final float abs(float a) {
4322
return (a < 0) ? -a : a;
4326
private float dot(float ax, float ay, float az,
4327
float bx, float by, float bz) {
4328
return ax*bx + ay*by + az*bz;
4333
private final void cross(float a0, float a1, float a2,
4334
float b0, float b1, float b2,
4336
out[0] = a1*b2 - a2*b1;
4337
out[1] = a2*b0 - a0*b2;
4338
out[2] = a0*b1 - a1*b0;
4343
private final void cross(float a0, float a1, float a2,
4344
float b0, float b1, float b2,
4346
out.x = a1*b2 - a2*b1;
4347
out.y = a2*b0 - a0*b2;
4348
out.z = a0*b1 - a1*b0;
4353
private final void cross(float[] a, float[] b, float[] out) {
4354
out[0] = a[1]*b[2] - a[2]*b[1];
4355
out[1] = a[2]*b[0] - a[0]*b[2];
4356
out[2] = a[0]*b[1] - a[1]*b[0];