From ffbf9af5edf89e60938e85772b9e5acd5e56ea5b Mon Sep 17 00:00:00 2001 From: codeanticode Date: Mon, 12 Dec 2011 04:20:41 +0000 Subject: [PATCH] Added new P3D examples --- .../examples/Animation/Yellowtail/Gesture.pde | 256 ++++++++++++++++++ .../examples/Animation/Yellowtail/Vec3f.pde | 19 ++ .../Animation/Yellowtail/Yellowtail.pde | 189 +++++++++++++ .../examples/Camera/MoveEye/MoveEye.pde | 31 +++ .../OrthoVSPerspective/OrthoVSPerspective.pde | 44 +++ .../Camera/Perspective/Perspective.pde | 41 +++ .../examples/Form/BrickTower/BrickTower.pde | 59 ++++ .../opengl/examples/Form/BrickTower/Cube.pde | 60 ++++ .../examples/Form/CubicGrid/CubicGrid.pde | 47 ++++ .../examples/Form/Geometry/Geometry.pde | 162 +++++++++++ .../examples/Form/Icosahedra/Dimension3D.pde | 10 + .../examples/Form/Icosahedra/Icosahedra.pde | 52 ++++ .../examples/Form/Icosahedra/Icosahedron.pde | 159 +++++++++++ .../examples/Form/Icosahedra/Shape3D.pde | 82 ++++++ .../Form/Primitives3D/Primitives3D.pde | 30 ++ .../opengl/examples/Form/RGBCube/RGBCube.pde | 74 +++++ .../opengl/examples/Form/RunAmuck/Legs.pde | 162 +++++++++++ .../examples/Form/RunAmuck/RunAmuck.pde | 45 +++ .../Form/ShapeTransform/ShapeTransform.pde | 116 ++++++++ .../opengl/examples/Form/SpaceJunk/Cube.pde | 73 +++++ .../examples/Form/SpaceJunk/SpaceJunk.pde | 63 +++++ .../Form/TexturedSphere/TexturedSphere.pde | 172 ++++++++++++ .../opengl/examples/Form/Toroid/Toroid.pde | 182 +++++++++++++ .../examples/Form/Vertices/Vertices.pde | 67 +++++ .../examples/Image/Blending/Blending.pde | 80 ++++++ .../opengl/examples/Image/Explode/Explode.pde | 43 +++ .../examples/Image/Explode/data/eames.jpg.tmp | Bin 0 -> 44496 bytes .../examples/Image/Extrusion/Extrusion.pde | 46 ++++ .../Image/ExtrusionGL/ExtrusionGL.pde | 49 ++++ .../opengl/examples/Image/Zoom/Zoom.pde | 89 ++++++ .../examples/Immediate/Bezier/Bezier.pde | 22 ++ .../Immediate/Robot1_Draw/Robot1_Draw.pde | 41 +++ .../ShapePrimitives/ShapePrimitives.pde | 32 +++ .../Immediate/TriangleStrip/TriangleStrip.pde | 44 +++ .../Lights/CameraLight/CameraLight.pde | 42 +++ .../Lights/Directional/Directional.pde | 28 ++ .../examples/Lights/Lights1/Lights1.pde | 29 ++ .../examples/Lights/Lights2/Lights2.pde | 36 +++ .../examples/Lights/LightsGL/LightsGL.pde | 45 +++ .../examples/Lights/Reflection/Reflection.pde | 25 ++ .../opengl/examples/Lights/Spot/Spot.pde | 35 +++ .../opengl/examples/LowLevel/Nehe/Bitmap.pde | 147 ++++++++++ .../opengl/examples/LowLevel/Nehe/Nehe.pde | 235 ++++++++++++++++ .../opengl/examples/LowLevel/Nehe/Texture.pde | 82 ++++++ .../examples/LowLevel/Nehe/VertData.pde | 147 ++++++++++ .../examples/LowLevel/Nehe/data/glass.bmp | Bin 0 -> 49220 bytes .../Performance/CubicGrid/CubicGrid.pde | 53 ++++ .../examples/Performance/Esfera/Esfera.pde | 89 ++++++ .../LineRendering/LineRendering.pde | 25 ++ .../QuadRendering/QuadRendering.pde | 18 ++ .../TextRendering/TextRendering.pde | 16 ++ .../Retained/PolyImmediate/PolyImmediate.pde | 18 ++ .../Retained/PolyRetain/PolyRetain.pde | 19 ++ .../examples/Retained/Shape2_18/Shape2_18.pde | 15 + .../examples/Textures/Texture1/Texture1.pde | 28 ++ .../examples/Textures/Texture2/Texture2.pde | 25 ++ .../examples/Textures/Texture3/Texture3.pde | 46 ++++ .../Textures/TextureCube/TextureCube.pde | 91 +++++++ .../opengl/examples/Transform/Bird/Bird.pde | 63 +++++ .../opengl/examples/Transform/Birds/Bird.pde | 99 +++++++ .../opengl/examples/Transform/Birds/Birds.pde | 53 ++++ .../Transform/CubesWithinCube/Cube.pde | 72 +++++ .../CubesWithinCube/CubesWithinCube.pde | 117 ++++++++ .../Transform/PushPopCubes/PushPopCubes.pde | 144 ++++++++++ .../examples/Transform/Rotate1/Rotate1.pde | 38 +++ .../examples/Transform/Rotate2/Rotate2.pde | 42 +++ .../Typography/KineticType/KineticType.pde | 64 +++++ .../Typography/KineticType/Letter.pde | 13 + .../examples/Typography/KineticType/Line.pde | 28 ++ .../examples/Typography/KineticType/Word.pde | 10 + .../KineticType/data/Univers-66.vlw | Bin 0 -> 116537 bytes .../examples/Typography/LetterK/LetterK.pde | 125 +++++++++ .../examples/Typography/Typing/Typing.pde | 82 ++++++ .../Typography/Typing/data/Univers45.vlw | Bin 0 -> 95193 bytes 74 files changed, 4885 insertions(+) create mode 100755 java/libraries/opengl/examples/Animation/Yellowtail/Gesture.pde create mode 100755 java/libraries/opengl/examples/Animation/Yellowtail/Vec3f.pde create mode 100755 java/libraries/opengl/examples/Animation/Yellowtail/Yellowtail.pde create mode 100755 java/libraries/opengl/examples/Camera/MoveEye/MoveEye.pde create mode 100755 java/libraries/opengl/examples/Camera/OrthoVSPerspective/OrthoVSPerspective.pde create mode 100755 java/libraries/opengl/examples/Camera/Perspective/Perspective.pde create mode 100755 java/libraries/opengl/examples/Form/BrickTower/BrickTower.pde create mode 100755 java/libraries/opengl/examples/Form/BrickTower/Cube.pde create mode 100755 java/libraries/opengl/examples/Form/CubicGrid/CubicGrid.pde create mode 100755 java/libraries/opengl/examples/Form/Geometry/Geometry.pde create mode 100755 java/libraries/opengl/examples/Form/Icosahedra/Dimension3D.pde create mode 100755 java/libraries/opengl/examples/Form/Icosahedra/Icosahedra.pde create mode 100755 java/libraries/opengl/examples/Form/Icosahedra/Icosahedron.pde create mode 100755 java/libraries/opengl/examples/Form/Icosahedra/Shape3D.pde create mode 100755 java/libraries/opengl/examples/Form/Primitives3D/Primitives3D.pde create mode 100755 java/libraries/opengl/examples/Form/RGBCube/RGBCube.pde create mode 100755 java/libraries/opengl/examples/Form/RunAmuck/Legs.pde create mode 100755 java/libraries/opengl/examples/Form/RunAmuck/RunAmuck.pde create mode 100755 java/libraries/opengl/examples/Form/ShapeTransform/ShapeTransform.pde create mode 100755 java/libraries/opengl/examples/Form/SpaceJunk/Cube.pde create mode 100755 java/libraries/opengl/examples/Form/SpaceJunk/SpaceJunk.pde create mode 100755 java/libraries/opengl/examples/Form/TexturedSphere/TexturedSphere.pde create mode 100755 java/libraries/opengl/examples/Form/Toroid/Toroid.pde create mode 100755 java/libraries/opengl/examples/Form/Vertices/Vertices.pde create mode 100755 java/libraries/opengl/examples/Image/Blending/Blending.pde create mode 100755 java/libraries/opengl/examples/Image/Explode/Explode.pde create mode 100755 java/libraries/opengl/examples/Image/Explode/data/eames.jpg.tmp create mode 100755 java/libraries/opengl/examples/Image/Extrusion/Extrusion.pde create mode 100755 java/libraries/opengl/examples/Image/ExtrusionGL/ExtrusionGL.pde create mode 100755 java/libraries/opengl/examples/Image/Zoom/Zoom.pde create mode 100755 java/libraries/opengl/examples/Immediate/Bezier/Bezier.pde create mode 100755 java/libraries/opengl/examples/Immediate/Robot1_Draw/Robot1_Draw.pde create mode 100755 java/libraries/opengl/examples/Immediate/ShapePrimitives/ShapePrimitives.pde create mode 100755 java/libraries/opengl/examples/Immediate/TriangleStrip/TriangleStrip.pde create mode 100755 java/libraries/opengl/examples/Lights/CameraLight/CameraLight.pde create mode 100755 java/libraries/opengl/examples/Lights/Directional/Directional.pde create mode 100755 java/libraries/opengl/examples/Lights/Lights1/Lights1.pde create mode 100755 java/libraries/opengl/examples/Lights/Lights2/Lights2.pde create mode 100755 java/libraries/opengl/examples/Lights/LightsGL/LightsGL.pde create mode 100755 java/libraries/opengl/examples/Lights/Reflection/Reflection.pde create mode 100755 java/libraries/opengl/examples/Lights/Spot/Spot.pde create mode 100755 java/libraries/opengl/examples/LowLevel/Nehe/Bitmap.pde create mode 100755 java/libraries/opengl/examples/LowLevel/Nehe/Nehe.pde create mode 100755 java/libraries/opengl/examples/LowLevel/Nehe/Texture.pde create mode 100755 java/libraries/opengl/examples/LowLevel/Nehe/VertData.pde create mode 100755 java/libraries/opengl/examples/LowLevel/Nehe/data/glass.bmp create mode 100755 java/libraries/opengl/examples/Performance/CubicGrid/CubicGrid.pde create mode 100755 java/libraries/opengl/examples/Performance/Esfera/Esfera.pde create mode 100755 java/libraries/opengl/examples/Performance/LineRendering/LineRendering.pde create mode 100755 java/libraries/opengl/examples/Performance/QuadRendering/QuadRendering.pde create mode 100755 java/libraries/opengl/examples/Performance/TextRendering/TextRendering.pde create mode 100755 java/libraries/opengl/examples/Retained/PolyImmediate/PolyImmediate.pde create mode 100755 java/libraries/opengl/examples/Retained/PolyRetain/PolyRetain.pde create mode 100755 java/libraries/opengl/examples/Retained/Shape2_18/Shape2_18.pde create mode 100755 java/libraries/opengl/examples/Textures/Texture1/Texture1.pde create mode 100755 java/libraries/opengl/examples/Textures/Texture2/Texture2.pde create mode 100755 java/libraries/opengl/examples/Textures/Texture3/Texture3.pde create mode 100755 java/libraries/opengl/examples/Textures/TextureCube/TextureCube.pde create mode 100755 java/libraries/opengl/examples/Transform/Bird/Bird.pde create mode 100755 java/libraries/opengl/examples/Transform/Birds/Bird.pde create mode 100755 java/libraries/opengl/examples/Transform/Birds/Birds.pde create mode 100755 java/libraries/opengl/examples/Transform/CubesWithinCube/Cube.pde create mode 100755 java/libraries/opengl/examples/Transform/CubesWithinCube/CubesWithinCube.pde create mode 100755 java/libraries/opengl/examples/Transform/PushPopCubes/PushPopCubes.pde create mode 100755 java/libraries/opengl/examples/Transform/Rotate1/Rotate1.pde create mode 100755 java/libraries/opengl/examples/Transform/Rotate2/Rotate2.pde create mode 100755 java/libraries/opengl/examples/Typography/KineticType/KineticType.pde create mode 100755 java/libraries/opengl/examples/Typography/KineticType/Letter.pde create mode 100755 java/libraries/opengl/examples/Typography/KineticType/Line.pde create mode 100755 java/libraries/opengl/examples/Typography/KineticType/Word.pde create mode 100755 java/libraries/opengl/examples/Typography/KineticType/data/Univers-66.vlw create mode 100755 java/libraries/opengl/examples/Typography/LetterK/LetterK.pde create mode 100755 java/libraries/opengl/examples/Typography/Typing/Typing.pde create mode 100755 java/libraries/opengl/examples/Typography/Typing/data/Univers45.vlw diff --git a/java/libraries/opengl/examples/Animation/Yellowtail/Gesture.pde b/java/libraries/opengl/examples/Animation/Yellowtail/Gesture.pde new file mode 100755 index 000000000..64382e9bb --- /dev/null +++ b/java/libraries/opengl/examples/Animation/Yellowtail/Gesture.pde @@ -0,0 +1,256 @@ +class Gesture { + + float damp = 5.0; + float dampInv = 1.0 / damp; + float damp1 = damp - 1; + + int w; + int h; + int capacity; + + Vec3f path[]; + int crosses[]; + Polygon polygons[]; + int nPoints; + int nPolys; + + float jumpDx, jumpDy; + boolean exists; + float INIT_TH = 14; + float thickness = INIT_TH; + + Gesture(int mw, int mh) { + w = mw; + h = mh; + capacity = 600; + path = new Vec3f[capacity]; + polygons = new Polygon[capacity]; + crosses = new int[capacity]; + for (int i=0;i= capacity) { + // there are all sorts of possible solutions here, + // but for abject simplicity, I don't do anything. + } + else { + float v = distToLast(x, y); + float p = getPressureFromVelocity(v); + path[nPoints++].set(x,y,p); + + if (nPoints > 1) { + exists = true; + jumpDx = path[nPoints-1].x - path[0].x; + jumpDy = path[nPoints-1].y - path[0].y; + } + } + + } + + float getPressureFromVelocity(float v) { + final float scale = 18; + final float minP = 0.02; + final float oldP = (nPoints > 0) ? path[nPoints-1].p : 0; + return ((minP + max(0, 1.0 - v/scale)) + (damp1*oldP))*dampInv; + } + + void setPressures() { + // pressures vary from 0...1 + float pressure; + Vec3f tmp; + float t = 0; + float u = 1.0 / (nPoints - 1)*TWO_PI; + for (int i = 0; i < nPoints; i++) { + pressure = sqrt((1.0 - cos(t))*0.5); + path[i].p = pressure; + t += u; + } + } + + float distToLast(float ix, float iy) { + if (nPoints > 0) { + Vec3f v = path[nPoints-1]; + float dx = v.x - ix; + float dy = v.y - iy; + return mag(dx, dy); + } + else { + return 30; + } + } + + void compile() { + // compute the polygons from the path of Vec3f's + if (exists) { + clearPolys(); + + Vec3f p0, p1, p2; + float radius0, radius1; + float ax, bx, cx, dx; + float ay, by, cy, dy; + int axi, bxi, cxi, dxi, axip, axid; + int ayi, byi, cyi, dyi, ayip, ayid; + float p1x, p1y; + float dx01, dy01, hp01, si01, co01; + float dx02, dy02, hp02, si02, co02; + float dx13, dy13, hp13, si13, co13; + float taper = 1.0; + + int nPathPoints = nPoints - 1; + int lastPolyIndex = nPathPoints - 1; + float npm1finv = 1.0 / max(1, nPathPoints - 1); + + // handle the first point + p0 = path[0]; + p1 = path[1]; + radius0 = p0.p * thickness; + dx01 = p1.x - p0.x; + dy01 = p1.y - p0.y; + hp01 = sqrt(dx01*dx01 + dy01*dy01); + if (hp01 == 0) { + hp02 = 0.0001; + } + co01 = radius0 * dx01 / hp01; + si01 = radius0 * dy01 / hp01; + ax = p0.x - si01; + ay = p0.y + co01; + bx = p0.x + si01; + by = p0.y - co01; + + int xpts[]; + int ypts[]; + + int LC = 20; + int RC = w-LC; + int TC = 20; + int BC = h-TC; + float mint = 0.618; + float tapow = 0.4; + + // handle the middle points + int i = 1; + Polygon apoly; + for (i = 1; i < nPathPoints; i++) { + taper = pow((lastPolyIndex-i)*npm1finv,tapow); + + p0 = path[i-1]; + p1 = path[i ]; + p2 = path[i+1]; + p1x = p1.x; + p1y = p1.y; + radius1 = Math.max(mint,taper*p1.p*thickness); + + // assumes all segments are roughly the same length... + dx02 = p2.x - p0.x; + dy02 = p2.y - p0.y; + hp02 = (float) Math.sqrt(dx02*dx02 + dy02*dy02); + if (hp02 != 0) { + hp02 = radius1/hp02; + } + co02 = dx02 * hp02; + si02 = dy02 * hp02; + + // translate the integer coordinates to the viewing rectangle + axi = axip = (int)ax; + ayi = ayip = (int)ay; + axi=(axi<0)?(w-((-axi)%w)):axi%w; + axid = axi-axip; + ayi=(ayi<0)?(h-((-ayi)%h)):ayi%h; + ayid = ayi-ayip; + + // set the vertices of the polygon + apoly = polygons[nPolys++]; + xpts = apoly.xpoints; + ypts = apoly.ypoints; + xpts[0] = axi = axid + axip; + xpts[1] = bxi = axid + (int) bx; + xpts[2] = cxi = axid + (int)(cx = p1x + si02); + xpts[3] = dxi = axid + (int)(dx = p1x - si02); + ypts[0] = ayi = ayid + ayip; + ypts[1] = byi = ayid + (int) by; + ypts[2] = cyi = ayid + (int)(cy = p1y - co02); + ypts[3] = dyi = ayid + (int)(dy = p1y + co02); + + // keep a record of where we cross the edge of the screen + crosses[i] = 0; + if ((axi<=LC)||(bxi<=LC)||(cxi<=LC)||(dxi<=LC)) { + crosses[i]|=1; + } + if ((axi>=RC)||(bxi>=RC)||(cxi>=RC)||(dxi>=RC)) { + crosses[i]|=2; + } + if ((ayi<=TC)||(byi<=TC)||(cyi<=TC)||(dyi<=TC)) { + crosses[i]|=4; + } + if ((ayi>=BC)||(byi>=BC)||(cyi>=BC)||(dyi>=BC)) { + crosses[i]|=8; + } + + //swap data for next time + ax = dx; + ay = dy; + bx = cx; + by = cy; + } + + // handle the last point + p2 = path[nPathPoints]; + apoly = polygons[nPolys++]; + xpts = apoly.xpoints; + ypts = apoly.ypoints; + + xpts[0] = (int)ax; + xpts[1] = (int)bx; + xpts[2] = (int)(p2.x); + xpts[3] = (int)(p2.x); + + ypts[0] = (int)ay; + ypts[1] = (int)by; + ypts[2] = (int)(p2.y); + ypts[3] = (int)(p2.y); + + } + } + + void smooth() { + // average neighboring points + + final float weight = 18; + final float scale = 1.0 / (weight + 2); + int nPointsMinusTwo = nPoints - 2; + Vec3f lower, upper, center; + + for (int i = 1; i < nPointsMinusTwo; i++) { + lower = path[i-1]; + center = path[i]; + upper = path[i+1]; + + center.x = (lower.x + weight*center.x + upper.x)*scale; + center.y = (lower.y + weight*center.y + upper.y)*scale; + } + } +} + diff --git a/java/libraries/opengl/examples/Animation/Yellowtail/Vec3f.pde b/java/libraries/opengl/examples/Animation/Yellowtail/Vec3f.pde new file mode 100755 index 000000000..865d3c32f --- /dev/null +++ b/java/libraries/opengl/examples/Animation/Yellowtail/Vec3f.pde @@ -0,0 +1,19 @@ +class Vec3f { + float x; + float y; + float p; // Pressure + + Vec3f() { + set(0, 0, 0); + } + + Vec3f(float ix, float iy, float ip) { + set(ix, iy, ip); + } + + void set(float ix, float iy, float ip) { + x = ix; + y = iy; + p = ip; + } +} diff --git a/java/libraries/opengl/examples/Animation/Yellowtail/Yellowtail.pde b/java/libraries/opengl/examples/Animation/Yellowtail/Yellowtail.pde new file mode 100755 index 000000000..86ab51271 --- /dev/null +++ b/java/libraries/opengl/examples/Animation/Yellowtail/Yellowtail.pde @@ -0,0 +1,189 @@ +/** + * Yellowtail + * by Golan Levin (www.flong.com). + * + * Click, drag, and release to create a kinetic gesture. + * + * Yellowtail (1998-2000) is an interactive software system for the gestural + * creation and performance of real-time abstract animation. Yellowtail repeats + * a user's strokes end-over-end, enabling simultaneous specification of a + * line's shape and quality of movement. Each line repeats according to its + * own period, producing an ever-changing and responsive display of lively, + * worm-like textures. + */ + + +import java.awt.Polygon; + +Gesture gestureArray[]; +final int nGestures = 36; // Number of gestures +final int minMove = 3; // Minimum travel for a new point +int currentGestureID; + +Polygon tempP; +int tmpXp[]; +int tmpYp[]; + + +void setup() { + size(1024, 768, P3D); + background(0, 0, 0); + noStroke(); + + currentGestureID = -1; + gestureArray = new Gesture[nGestures]; + for (int i = 0; i < nGestures; i++) { + gestureArray[i] = new Gesture(width, height); + } + clearGestures(); +} + + +void draw() { + background(0); + + updateGeometry(); + fill(255, 255, 245); + for (int i = 0; i < nGestures; i++) { + renderGesture(gestureArray[i], width, height); + } +} + +void mousePressed() { + currentGestureID = (currentGestureID+1) % nGestures; + Gesture G = gestureArray[currentGestureID]; + G.clear(); + G.clearPolys(); + G.addPoint(mouseX, mouseY); +} + + +void mouseDragged() { + if (currentGestureID >= 0) { + Gesture G = gestureArray[currentGestureID]; + if (G.distToLast(mouseX, mouseY) > minMove) { + G.addPoint(mouseX, mouseY); + G.smooth(); + G.compile(); + } + } +} + + +void keyPressed() { + if (key == '+' || key == '=') { + if (currentGestureID >= 0) { + float th = gestureArray[currentGestureID].thickness; + gestureArray[currentGestureID].thickness = min(96, th+1); + gestureArray[currentGestureID].compile(); + } + } else if (key == '-') { + if (currentGestureID >= 0) { + float th = gestureArray[currentGestureID].thickness; + gestureArray[currentGestureID].thickness = max(2, th-1); + gestureArray[currentGestureID].compile(); + } + } else if (key == ' ') { + clearGestures(); + } +} + + +void renderGesture(Gesture gesture, int w, int h) { + if (gesture.exists) { + if (gesture.nPolys > 0) { + Polygon polygons[] = gesture.polygons; + int crosses[] = gesture.crosses; + + int xpts[]; + int ypts[]; + Polygon p; + int cr; + + beginShape(QUADS); + int gnp = gesture.nPolys; + for (int i=0; i 0) { + if ((cr & 3)>0) { + vertex(xpts[0]+w, ypts[0]); + vertex(xpts[1]+w, ypts[1]); + vertex(xpts[2]+w, ypts[2]); + vertex(xpts[3]+w, ypts[3]); + + vertex(xpts[0]-w, ypts[0]); + vertex(xpts[1]-w, ypts[1]); + vertex(xpts[2]-w, ypts[2]); + vertex(xpts[3]-w, ypts[3]); + } + if ((cr & 12)>0) { + vertex(xpts[0], ypts[0]+h); + vertex(xpts[1], ypts[1]+h); + vertex(xpts[2], ypts[2]+h); + vertex(xpts[3], ypts[3]+h); + + vertex(xpts[0], ypts[0]-h); + vertex(xpts[1], ypts[1]-h); + vertex(xpts[2], ypts[2]-h); + vertex(xpts[3], ypts[3]-h); + } + + // I have knowingly retained the small flaw of not + // completely dealing with the corner conditions + // (the case in which both of the above are true). + } + } + endShape(); + } + } +} + +void updateGeometry() { + Gesture J; + for (int g=0; g 0) { + path = gesture.path; + for (int i = nPts1; i > 0; i--) { + path[i].x = path[i-1].x; + path[i].y = path[i-1].y; + } + path[0].x = path[nPts1].x - jx; + path[0].y = path[nPts1].y - jy; + gesture.compile(); + } + } +} + +void clearGestures() { + for (int i = 0; i < nGestures; i++) { + gestureArray[i].clear(); + } +} diff --git a/java/libraries/opengl/examples/Camera/MoveEye/MoveEye.pde b/java/libraries/opengl/examples/Camera/MoveEye/MoveEye.pde new file mode 100755 index 000000000..af6bd9b75 --- /dev/null +++ b/java/libraries/opengl/examples/Camera/MoveEye/MoveEye.pde @@ -0,0 +1,31 @@ +// Status: Renders with issues +// Problem: Lights don't work + +/** + * Move Eye. + * by Simon Greenwold. + * + * The camera lifts up (controlled by mouseY) while looking at the same point. + */ + +void setup() { + size(640, 360, P3D); + fill(204); +} + +void draw() { + lights(); + background(0); + + // Change height of the camera with mouseY + camera(30.0, mouseY, 220.0, // eyeX, eyeY, eyeZ + 0.0, 0.0, 0.0, // centerX, centerY, centerZ + 0.0, 1.0, 0.0); // upX, upY, upZ + + noStroke(); + box(90); + stroke(255); + line(-100, 0, 0, 100, 0, 0); + line(0, -100, 0, 0, 100, 0); + line(0, 0, -100, 0, 0, 100); +} diff --git a/java/libraries/opengl/examples/Camera/OrthoVSPerspective/OrthoVSPerspective.pde b/java/libraries/opengl/examples/Camera/OrthoVSPerspective/OrthoVSPerspective.pde new file mode 100755 index 000000000..9589c51c0 --- /dev/null +++ b/java/libraries/opengl/examples/Camera/OrthoVSPerspective/OrthoVSPerspective.pde @@ -0,0 +1,44 @@ +// Status: Wrong rendering + +/** + * Ortho vs Perspective. + * + * Click to see the difference between orthographic projection + * and perspective projection as applied to a simple box. + * The ortho() function sets an orthographic projection and + * defines a parallel clipping volume. All objects with the + * same dimension appear the same size, regardless of whether + * they are near or far from the camera. The parameters to this + * function specify the clipping volume where left and right + * are the minimum and maximum x values, top and bottom are the + * minimum and maximum y values, and near and far are the minimum + * and maximum z values. + */ + +void setup() +{ + size(640, 360, P3D); + noStroke(); + fill(204); +} + +void draw() +{ + background(0); + lights(); + + if(mousePressed) { + float fov = PI/3.0; + float cameraZ = (height/2.0) / tan(PI * fov / 360.0); + perspective(fov, float(width)/float(height), + cameraZ/2.0, cameraZ*2.0); + } else { + ortho(-width/2, width/2, -height/2, height/2, -10, 10); + } + + translate(width/2, height/2, 0); + rotateX(-PI/6); + rotateY(PI/3); + box(160); +} + diff --git a/java/libraries/opengl/examples/Camera/Perspective/Perspective.pde b/java/libraries/opengl/examples/Camera/Perspective/Perspective.pde new file mode 100755 index 000000000..46695397d --- /dev/null +++ b/java/libraries/opengl/examples/Camera/Perspective/Perspective.pde @@ -0,0 +1,41 @@ +/** + * Perspective. + * + * Move the mouse left and right to change the field of view (fov). + * Click to modify the aspect ratio. The perspective() function + * sets a perspective projection applying foreshortening, making + * distant objects appear smaller than closer ones. The parameters + * define a viewing volume with the shape of truncated pyramid. + * Objects near to the front of the volume appear their actual size, + * while farther objects appear smaller. This projection simulates + * the perspective of the world more accurately than orthographic projection. + * The version of perspective without parameters sets the default + * perspective and the version with four parameters allows the programmer + * to set the area precisely. + */ + +void setup() { + size(640, 360, P3D); + noStroke(); +} + +void draw() { + lights(); + background(204); + float cameraY = height/2.0; + float fov = mouseX/float(width) * PI/2; + float cameraZ = cameraY / tan(fov / 2.0); + float aspect = float(width)/float(height); + if (mousePressed) { + aspect = aspect / 2.0; + } + perspective(fov, aspect, cameraZ/10.0, cameraZ*10.0); + + translate(width/2+30, height/2, 0); + rotateX(-PI/6); + rotateY(PI/3 + mouseY/float(height) * PI); + box(45); + translate(0, 0, -50); + box(30); +} + diff --git a/java/libraries/opengl/examples/Form/BrickTower/BrickTower.pde b/java/libraries/opengl/examples/Form/BrickTower/BrickTower.pde new file mode 100755 index 000000000..c40186498 --- /dev/null +++ b/java/libraries/opengl/examples/Form/BrickTower/BrickTower.pde @@ -0,0 +1,59 @@ +/** + * Brick Tower + * by Ira Greenberg. + * + * 3D castle tower constructed out of individual bricks. + * Uses the PVector and Cube classes. + */ + +float bricksPerLayer = 16.0; +float brickLayers = 18.0; +Cube brick; +float brickWidth = 60, brickHeight = 25, brickDepth = 25; +float radius = 175.0; +float angle = 0; + +void setup(){ + size(640, 360, P3D); + brick = new Cube(brickWidth, brickHeight, brickDepth); +} + +void draw(){ + background(0); + float tempX = 0, tempY = 0, tempZ = 0; + fill(182, 62, 29); + noStroke(); + // Add basic light setup + lights(); + translate(width/2, height*1.2, -380); + // Tip tower to see inside + rotateX(radians(-45)); + // Slowly rotate tower + rotateY(frameCount * PI/600); + for (int i = 0; i < brickLayers; i++){ + // Increment rows + tempY-=brickHeight; + // Alternate brick seams + angle = 360.0 / bricksPerLayer * i/2; + for (int j = 0; j < bricksPerLayer; j++){ + tempZ = cos(radians(angle))*radius; + tempX = sin(radians(angle))*radius; + pushMatrix(); + translate(tempX, tempY, tempZ); + rotateY(radians(angle)); + // Add crenelation + if (i==brickLayers-1){ + if (j%2 == 0){ + brick.create(); + } + } + // Create main tower + else { + brick.create(); + } + popMatrix(); + angle += 360.0/bricksPerLayer; + } + } +} + diff --git a/java/libraries/opengl/examples/Form/BrickTower/Cube.pde b/java/libraries/opengl/examples/Form/BrickTower/Cube.pde new file mode 100755 index 000000000..ba4fdb361 --- /dev/null +++ b/java/libraries/opengl/examples/Form/BrickTower/Cube.pde @@ -0,0 +1,60 @@ +class Cube { + + PVector[] vertices = new PVector[24]; + float w, h, d; + + Cube(){ } + + Cube(float w, float h, float d){ + this.w = w; + this.h = h; + this.d = d; + + // Cube composed of 6 quads + // Front + vertices[0] = new PVector(-w/2, -h/2, d/2); + vertices[1] = new PVector(w/2, -h/2, d/2); + vertices[2] = new PVector(w/2, h/2, d/2); + vertices[3] = new PVector(-w/2, h/2, d/2); + + // Left + vertices[4] = new PVector(-w/2, -h/2, d/2); + vertices[5] = new PVector(-w/2, -h/2, -d/2); + vertices[6] = new PVector(-w/2, h/2, -d/2); + vertices[7] = new PVector(-w/2, h/2, d/2); + + // Right + vertices[8] = new PVector(w/2, -h/2, d/2); + vertices[9] = new PVector(w/2, -h/2, -d/2); + vertices[10] = new PVector(w/2, h/2, -d/2); + vertices[11] = new PVector(w/2, h/2, d/2); + + // Back + vertices[12] = new PVector(-w/2, -h/2, -d/2); + vertices[13] = new PVector(w/2, -h/2, -d/2); + vertices[14] = new PVector(w/2, h/2, -d/2); + vertices[15] = new PVector(-w/2, h/2, -d/2); + + // Top + vertices[16] = new PVector(-w/2, -h/2, d/2); + vertices[17] = new PVector(-w/2, -h/2, -d/2); + vertices[18] = new PVector(w/2, -h/2, -d/2); + vertices[19] = new PVector(w/2, -h/2, d/2); + + // Bottom + vertices[20] = new PVector(-w/2, h/2, d/2); + vertices[21] = new PVector(-w/2, h/2, -d/2); + vertices[22] = new PVector(w/2, h/2, -d/2); + vertices[23] = new PVector(w/2, h/2, d/2); + } + + void create(){ + for (int i=0; i<6; i++){ + beginShape(QUADS); + for (int j = 0; j < 4; j++){ + vertex(vertices[j+4*i].x, vertices[j+4*i].y, vertices[j+4*i].z); + } + endShape(); + } + } +} diff --git a/java/libraries/opengl/examples/Form/CubicGrid/CubicGrid.pde b/java/libraries/opengl/examples/Form/CubicGrid/CubicGrid.pde new file mode 100755 index 000000000..111cd3f3f --- /dev/null +++ b/java/libraries/opengl/examples/Form/CubicGrid/CubicGrid.pde @@ -0,0 +1,47 @@ +/** + * Cubic Grid + * by Ira Greenberg. + * + * 3D translucent colored grid uses nested pushMatrix() + * and popMatrix() functions. + */ + +float boxSize = 40; +float margin = boxSize*2; +float depth = 400; +color boxFill; + +void setup() { + size(640, 360, P3D); + noStroke(); +} + +void draw() { + background(255); + + // Center and spin grid + translate(width/2, height/2, -depth); + rotateY(frameCount * 0.01); + rotateX(frameCount * 0.01); + + // Build grid using multiple translations + for (float i =- depth/2+margin; i <= depth/2-margin; i += boxSize){ + pushMatrix(); + for (float j =- height+margin; j <= height-margin; j += boxSize){ + pushMatrix(); + for (float k =- width+margin; k <= width-margin; k += boxSize){ + // Base fill color on counter values, abs function + // ensures values stay within legal range + boxFill = color(abs(i), abs(j), abs(k), 50); + pushMatrix(); + translate(k, j, i); + fill(boxFill); + box(boxSize, boxSize, boxSize); + popMatrix(); + } + popMatrix(); + } + popMatrix(); + } +} + diff --git a/java/libraries/opengl/examples/Form/Geometry/Geometry.pde b/java/libraries/opengl/examples/Form/Geometry/Geometry.pde new file mode 100755 index 000000000..8bbb32bd3 --- /dev/null +++ b/java/libraries/opengl/examples/Form/Geometry/Geometry.pde @@ -0,0 +1,162 @@ +/** + * Geometry + * by Marius Watz. + * + * Using sin/cos lookup tables, blends colors, and draws a series of + * rotating arcs on the screen. +*/ + +// Trig lookup tables borrowed from Toxi; cryptic but effective. +float sinLUT[]; +float cosLUT[]; +float SINCOS_PRECISION=1.0; +int SINCOS_LENGTH= int((360.0/SINCOS_PRECISION)); + +// System data +boolean dosave=false; +int num; +float pt[]; +int style[]; + + +void setup() { + size(1024, 768, P3D); + background(255); + + // Fill the tables + sinLUT=new float[SINCOS_LENGTH]; + cosLUT=new float[SINCOS_LENGTH]; + for (int i = 0; i < SINCOS_LENGTH; i++) { + sinLUT[i]= (float)Math.sin(i*DEG_TO_RAD*SINCOS_PRECISION); + cosLUT[i]= (float)Math.cos(i*DEG_TO_RAD*SINCOS_PRECISION); + } + + num = 150; + pt = new float[6*num]; // rotx, roty, deg, rad, w, speed + style = new int[2*num]; // color, render style + + // Set up arc shapes + int index=0; + float prob; + for (int i=0; i90) pt[index]=(int)random(8,27)*10; + + pt[index++] = int(random(2,50)*5); // Radius. Space them out nicely + + pt[index++] = random(4,32); // Width of band + if(random(100)>90) pt[index]=random(40,60); // Width of band + + pt[index++] = radians(random(5,30))/5; // Speed of rotation + + // get colors + prob = random(100); + if(prob<30) style[i*2]=colorBlended(random(1), 255,0,100, 255,0,0, 210); + else if(prob<70) style[i*2]=colorBlended(random(1), 0,153,255, 170,225,255, 210); + else if(prob<90) style[i*2]=colorBlended(random(1), 200,255,0, 150,255,0, 210); + else style[i*2]=color(255,255,255, 220); + + if(prob<50) style[i*2]=colorBlended(random(1), 200,255,0, 50,120,0, 210); + else if(prob<90) style[i*2]=colorBlended(random(1), 255,100,0, 255,255,0, 210); + else style[i*2]=color(255,255,255, 220); + + style[i*2+1]=(int)(random(100))%3; + } +} + +void draw() { + + background(0); + + int index=0; + translate(width/2, height/2, 0); + rotateX(PI/6); + rotateY(PI/6); + + for (int i = 0; i < num; i++) { + pushMatrix(); + + rotateX(pt[index++]); + rotateY(pt[index++]); + + if(style[i*2+1]==0) { + stroke(style[i*2]); + noFill(); + strokeWeight(1); + arcLine(0,0, pt[index++],pt[index++],pt[index++]); + } + else if(style[i*2+1]==1) { + fill(style[i*2]); + noStroke(); + arcLineBars(0,0, pt[index++],pt[index++],pt[index++]); + } + else { + fill(style[i*2]); + noStroke(); + arc(0,0, pt[index++],pt[index++],pt[index++]); + } + + // increase rotation + pt[index-5]+=pt[index]/10; + pt[index-4]+=pt[index++]/20; + + popMatrix(); + } +} + + +// Get blend of two colors +int colorBlended(float fract, +float r, float g, float b, +float r2, float g2, float b2, float a) { + + r2 = (r2 - r); + g2 = (g2 - g); + b2 = (b2 - b); + return color(r + r2 * fract, g + g2 * fract, b + b2 * fract, a); +} + + +// Draw arc line +void arcLine(float x,float y,float deg,float rad,float w) { + int a=(int)(min (deg/SINCOS_PRECISION,SINCOS_LENGTH-1)); + int numlines=(int)(w/2); + + for (int j=0; j 0.01) { xmag -= diff/4.0; } + + diff = ymag-newYmag; + if (abs(diff) > 0.01) { ymag -= diff/4.0; } + + rotateX(-ymag); + rotateY(-xmag); + + scale(90); + beginShape(QUADS); + + fill(0, 1, 1); vertex(-1, 1, 1); + fill(1, 1, 1); vertex( 1, 1, 1); + fill(1, 0, 1); vertex( 1, -1, 1); + fill(0, 0, 1); vertex(-1, -1, 1); + + fill(1, 1, 1); vertex( 1, 1, 1); + fill(1, 1, 0); vertex( 1, 1, -1); + fill(1, 0, 0); vertex( 1, -1, -1); + fill(1, 0, 1); vertex( 1, -1, 1); + + fill(1, 1, 0); vertex( 1, 1, -1); + fill(0, 1, 0); vertex(-1, 1, -1); + fill(0, 0, 0); vertex(-1, -1, -1); + fill(1, 0, 0); vertex( 1, -1, -1); + + fill(0, 1, 0); vertex(-1, 1, -1); + fill(0, 1, 1); vertex(-1, 1, 1); + fill(0, 0, 1); vertex(-1, -1, 1); + fill(0, 0, 0); vertex(-1, -1, -1); + + fill(0, 1, 0); vertex(-1, 1, -1); + fill(1, 1, 0); vertex( 1, 1, -1); + fill(1, 1, 1); vertex( 1, 1, 1); + fill(0, 1, 1); vertex(-1, 1, 1); + + fill(0, 0, 0); vertex(-1, -1, -1); + fill(1, 0, 0); vertex( 1, -1, -1); + fill(1, 0, 1); vertex( 1, -1, 1); + fill(0, 0, 1); vertex(-1, -1, 1); + + endShape(); + + popMatrix(); +} diff --git a/java/libraries/opengl/examples/Form/RunAmuck/Legs.pde b/java/libraries/opengl/examples/Form/RunAmuck/Legs.pde new file mode 100755 index 000000000..eb74d7859 --- /dev/null +++ b/java/libraries/opengl/examples/Form/RunAmuck/Legs.pde @@ -0,0 +1,162 @@ +/** + * Legs class + * By Ira Greenberg
+ * Processing for Flash Developers, + * Friends of ED, 2009 + */ + +class Legs { + // Instance properties with default values + float x = 0, y = 0, z = 0, w = 150, ht = 125; + color col = #77AA22; + // Advanced properties + float detailW = w/6.0; + float detailHt = ht/8.0; + float shoeBulge = detailHt*2.0; + float legGap = w/7.0; + + // Dynamics properties + float velocity = .02, stepL, stepR, stepRate = random(10, 50); + float speedX = 1.0, speedZ, spring, damping = .5, theta; + + // Default constructor + Legs() { + } + + // Standard constructor + Legs(float x, float z, float w, float ht, color col) { + this.x = x; + this.z = z; + this.w = w; + this.ht = ht; + this.col = col; + fill(col); + detailW = w/6.0; + detailHt = ht/8.0; + shoeBulge = detailHt*2.0; + legGap = w/7.0; + speedX = random(-speedX, speedX); + } + + // Advanced constructor + Legs(float x, float z, float w, float ht, color col, float detailW, + float detailHt, float shoeBulge, float legGap) { + this.x = x; + this.z = z; + this.w = w; + this.ht = ht; + this.col = col; + this.detailW = detailW; + this.detailHt = detailHt; + this.shoeBulge = shoeBulge; + this.legGap = legGap; + speedX = random(-speedX, speedX); + } + + // Draw legs + void create() { + fill(col); + float footWidth = (w - legGap)/2; + beginShape(); + vertex(x - w/2, y - ht, z); + vertex(x - w/2, y - ht + detailHt, z); + vertex(x - w/2 + detailW, y - ht + detailHt, z); + // left foot + vertex(x - w/2 + detailW, y + stepL, z); + curveVertex(x - w/2 + detailW, y + stepL, z); + curveVertex(x - w/2 + detailW, y + stepL, z); + curveVertex(x - w/2 + detailW - shoeBulge, y + detailHt/2 + stepL, z); + curveVertex(x - w/2, y + detailHt + stepL, z); + curveVertex(x - w/2, y + detailHt + stepL, z); + vertex(x - w/2 + footWidth, y + detailHt + stepL*.9, z); + // end left foot + vertex(x - w/2 + footWidth + legGap/2, y - ht + detailHt, z); + vertex(x - w/2 + footWidth + legGap/2, y - ht + detailHt, z); + // right foot + vertex(x - w/2 + footWidth + legGap, y + detailHt + stepR*.9, z); + vertex(x + w/2, y + detailHt + stepR, z); + curveVertex(x + w/2, y + detailHt + stepR, z); + curveVertex(x + w/2, y + detailHt + stepR, z); + curveVertex(x + w/2 - detailW + shoeBulge, y + detailHt/2 + stepR, z); + curveVertex(x + w/2 - detailW, y + stepR, z); + vertex(x + w/2 - detailW, y + stepR, z); + // end right foot + vertex(x + w/2 - detailW, y - ht + detailHt, z); + vertex(x + w/2, y - ht + detailHt, z); + vertex(x + w/2, y - ht, z); + endShape(CLOSE); + } + + // Set advanced property values + void setDetails(float detailW, float detailHt, float shoeBulge, float legGap) { + this.detailW = detailW; + this.detailHt = detailHt; + this.shoeBulge = shoeBulge; + this.legGap = legGap; + } + + // Make the legs step + void step(float stepRate) { + this.stepRate = stepRate; + spring = ht/2.0; + stepL = sin(theta)*spring; + stepR = cos(theta)*spring; + theta += radians(stepRate); + } + + // Alternative overloaded step method + void step() { + spring = ht/2.0; + stepL = sin(theta)*spring; + stepR = cos(theta)*spring; + theta += radians(stepRate); + } + + + // Moves legs along x, y, z axes + void move() { + // Move legs along y-axis + y = stepR*damping; + + // Move legs along x-axis and + // check for collision against frame edge + x += speedX; + if (screenX(x, y, z) > width) { + speedX *= -1; + } + if (screenX(x, y, z) < 0) { + speedX *= -1; + } + + // Move legs along z-axis based on speed of stepping + // and check for collision against extremes + speedZ = (stepRate*velocity); + z += speedZ; + if (z > 400) { + z = 400; + velocity *= -1; + } + if (z < -100) { + z = -100; + velocity *= -1; + } + } + + void setDynamics(float speedX, float spring, float damping) { + this.speedX = speedX; + this.spring = spring; + this.damping = damping; + } +} + + + + + + + + + + + + diff --git a/java/libraries/opengl/examples/Form/RunAmuck/RunAmuck.pde b/java/libraries/opengl/examples/Form/RunAmuck/RunAmuck.pde new file mode 100755 index 000000000..7a5b06fc2 --- /dev/null +++ b/java/libraries/opengl/examples/Form/RunAmuck/RunAmuck.pde @@ -0,0 +1,45 @@ +/** + * Run-Amuck + * By Ira Greenberg
+ * Processing for Flash Developers, + * Friends of ED, 2009 + */ + +int count = 250; +Legs[] legs = new Legs[count]; + +void setup() { + size(640, 360, P3D); + noStroke(); + for (int i = 0; i < legs.length; i++) { + legs[i] = new Legs(random(-10, 10), random(-50, 150), random(.5, 5), + random(.5, 5), color(random(255), random(255), random(255))); + } +} + +void draw() { + background(0); + translate(width/2, height/2); + noStroke(); + fill(35); + + // Draw ground plane + beginShape(); + vertex(-width*2, 0, -1000); + vertex(width*2, 0, -1000); + vertex(width/2, height/2, 400); + vertex(-width/2, height/2, 400); + endShape(CLOSE); + + // Update and draw the legs + for (int i = 0; i < legs.length; i++) { + legs[i].create(); + // Set foot step rate + legs[i].step(random(10, 50)); + // Move legs along x, y, z axes + // z-movement dependent upon step rate + legs[i].move(); + } +} + + diff --git a/java/libraries/opengl/examples/Form/ShapeTransform/ShapeTransform.pde b/java/libraries/opengl/examples/Form/ShapeTransform/ShapeTransform.pde new file mode 100755 index 000000000..c779a3a1a --- /dev/null +++ b/java/libraries/opengl/examples/Form/ShapeTransform/ShapeTransform.pde @@ -0,0 +1,116 @@ +/** + * Shape Transform + * by Ira Greenberg. + * + * Illustrates the geometric relationship + * between Cube, Pyramid, Cone and + * Cylinder 3D primitives. + * + * Instructions:
+ * Up Arrow - increases points
+ * Down Arrow - decreases points
+ * 'p' key toggles between cube/pyramid
+ */ + +int pts = 4; +float angle = 0; +float radius = 99; +float cylinderLength = 95; + +//vertices +PVector vertices[][]; +boolean isPyramid = false; + +float angleInc; + +void setup(){ + size(640, 360, P3D); + noStroke(); + angleInc = PI/300.0; +} + +void draw(){ + background(170, 95, 95); + lights(); + fill(255, 200, 200); + translate(width/2, height/2); + rotateX(frameCount * angleInc); + rotateY(frameCount * angleInc); + rotateZ(frameCount * angleInc); + + // initialize vertex arrays + vertices = new PVector[2][pts+1]; + + // fill arrays + for (int i = 0; i < 2; i++){ + angle = 0; + for(int j = 0; j <= pts; j++){ + vertices[i][j] = new PVector(); + if (isPyramid){ + if (i==1){ + vertices[i][j].x = 0; + vertices[i][j].y = 0; + } + else { + vertices[i][j].x = cos(radians(angle)) * radius; + vertices[i][j].y = sin(radians(angle)) * radius; + } + } + else { + vertices[i][j].x = cos(radians(angle)) * radius; + vertices[i][j].y = sin(radians(angle)) * radius; + } + vertices[i][j].z = cylinderLength; + // the .0 after the 360 is critical + angle += 360.0/pts; + } + cylinderLength *= -1; + } + + // draw cylinder tube + beginShape(QUAD_STRIP); + for(int j = 0; j <= pts; j++){ + vertex(vertices[0][j].x, vertices[0][j].y, vertices[0][j].z); + vertex(vertices[1][j].x, vertices[1][j].y, vertices[1][j].z); + } + endShape(); + + //draw cylinder ends + for (int i = 0; i < 2; i++){ + beginShape(); + for(int j = 0; j < pts; j++){ + vertex(vertices[i][j].x, vertices[i][j].y, vertices[i][j].z); + } + endShape(CLOSE); + } +} + + +/* + up/down arrow keys control + polygon detail. + */ +void keyPressed(){ + if(key == CODED) { + // pts + if (keyCode == UP) { + if (pts < 90){ + pts++; + } + } + else if (keyCode == DOWN) { + if (pts > 4){ + pts--; + } + } + } + if (key =='p'){ + if (isPyramid){ + isPyramid = false; + } + else { + isPyramid = true; + } + } +} + diff --git a/java/libraries/opengl/examples/Form/SpaceJunk/Cube.pde b/java/libraries/opengl/examples/Form/SpaceJunk/Cube.pde new file mode 100755 index 000000000..168ceb5ca --- /dev/null +++ b/java/libraries/opengl/examples/Form/SpaceJunk/Cube.pde @@ -0,0 +1,73 @@ + +class Cube { + + // Properties + int w, h, d; + int shiftX, shiftY, shiftZ; + + // Constructor + Cube(int w, int h, int d, int shiftX, int shiftY, int shiftZ){ + this.w = w; + this.h = h; + this.d = d; + this.shiftX = shiftX; + this.shiftY = shiftY; + this.shiftZ = shiftZ; + } + + // Main cube drawing method, which looks + // more confusing than it really is. It's + // just a bunch of rectangles drawn for + // each cube face + void drawCube(){ + beginShape(QUADS); + // Front face + normal(0, 0, 1); + vertex(-w/2 + shiftX, -h/2 + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, -h/2 + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, h + shiftY, -d/2 + shiftZ); + vertex(-w/2 + shiftX, h + shiftY, -d/2 + shiftZ); + + // Back face + normal(0, 0, -1); + vertex(-w/2 + shiftX, -h/2 + shiftY, d + shiftZ); + vertex(w + shiftX, -h/2 + shiftY, d + shiftZ); + vertex(w + shiftX, h + shiftY, d + shiftZ); + vertex(-w/2 + shiftX, h + shiftY, d + shiftZ); + + // Left face + normal(1, 0, 0); + vertex(-w/2 + shiftX, -h/2 + shiftY, -d/2 + shiftZ); + vertex(-w/2 + shiftX, -h/2 + shiftY, d + shiftZ); + vertex(-w/2 + shiftX, h + shiftY, d + shiftZ); + vertex(-w/2 + shiftX, h + shiftY, -d/2 + shiftZ); + + // Right face + normal(-1, 0, 0); + vertex(w + shiftX, -h/2 + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, -h/2 + shiftY, d + shiftZ); + vertex(w + shiftX, h + shiftY, d + shiftZ); + vertex(w + shiftX, h + shiftY, -d/2 + shiftZ); + + // Top face + normal(0, 1, 0); + vertex(-w/2 + shiftX, -h/2 + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, -h/2 + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, -h/2 + shiftY, d + shiftZ); + vertex(-w/2 + shiftX, -h/2 + shiftY, d + shiftZ); + + // Bottom face + normal(0, -1, 0); + vertex(-w/2 + shiftX, h + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, h + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, h + shiftY, d + shiftZ); + vertex(-w/2 + shiftX, h + shiftY, d + shiftZ); + + endShape(); + + // Add some rotation to each box for pizazz. + rotateY(radians(1)); + rotateX(radians(1)); + rotateZ(radians(1)); + } +} diff --git a/java/libraries/opengl/examples/Form/SpaceJunk/SpaceJunk.pde b/java/libraries/opengl/examples/Form/SpaceJunk/SpaceJunk.pde new file mode 100755 index 000000000..5fbe45f58 --- /dev/null +++ b/java/libraries/opengl/examples/Form/SpaceJunk/SpaceJunk.pde @@ -0,0 +1,63 @@ +/** + * Space Junk + * by Ira Greenberg. + * Zoom suggestion + * by Danny Greenberg. + * + * Rotating cubes in space using a custom Cube class. + * Color controlled by light sources. Move the mouse left + * and right to zoom. + */ + +// Used for oveall rotation +float ang; + +// Cube count-lower/raise to test P3D/OPENGL performance +int limit = 500; + +// Array for all cubes +Cube[]cubes = new Cube[limit]; + +void setup() { + size(1024, 768, P3D); + background(0); + noStroke(); + + // Instantiate cubes, passing in random vals for size and postion + for (int i = 0; i< cubes.length; i++){ + cubes[i] = new Cube(int(random(-10, 10)), int(random(-10, 10)), + int(random(-10, 10)), int(random(-140, 140)), int(random(-140, 140)), + int(random(-140, 140))); + } +} + +void draw(){ + background(0); + fill(200); + + // Set up some different colored lights + pointLight(51, 102, 255, 65, 60, 100); + pointLight(200, 40, 60, -65, -60, -150); + + // Raise overall light in scene + ambientLight(70, 70, 10); + + // Center geometry in display windwow. + // you can change 3rd argument ('0') + // to move block group closer(+)/further(-) + translate(width/2, height/2, -200 + mouseX * 0.65); + + // Rotate around y and x axes + rotateY(radians(ang)); + rotateX(radians(ang)); + + // Draw cubes + for (int i = 0; i < cubes.length; i++){ + cubes[i].drawCube(); + } + + // Used in rotate function calls above + ang++; +} + + diff --git a/java/libraries/opengl/examples/Form/TexturedSphere/TexturedSphere.pde b/java/libraries/opengl/examples/Form/TexturedSphere/TexturedSphere.pde new file mode 100755 index 000000000..734838469 --- /dev/null +++ b/java/libraries/opengl/examples/Form/TexturedSphere/TexturedSphere.pde @@ -0,0 +1,172 @@ +/** + * Textured Sphere + * by Mike 'Flux' Chang (cleaned up by Aaron Koblin). + * Based on code by Toxi. + * + * A 3D textured sphere with simple rotation control. + * Note: Controls will be inverted when sphere is upside down. + * Use an "arc ball" to deal with this appropriately. + */ + +PImage bg; +PImage texmap; + +int sDetail = 35; // Sphere detail setting +float rotationX = 0; +float rotationY = 0; +float velocityX = 0; +float velocityY = 0; +float globeRadius = 450; +float pushBack = 0; + +float[] cx, cz, sphereX, sphereY, sphereZ; +float sinLUT[]; +float cosLUT[]; +float SINCOS_PRECISION = 0.5; +int SINCOS_LENGTH = int(360.0 / SINCOS_PRECISION); + + +void setup() { + size(1024, 768, P3D); + texmap = loadImage("world32k.jpg"); + initializeSphere(sDetail); +} + +void draw() { + background(0); + renderGlobe(); +} + +void renderGlobe() { + pushMatrix(); + translate(width/2.0, height/2.0, pushBack); + pushMatrix(); + noFill(); + stroke(255,200); + strokeWeight(2); + smooth(); + popMatrix(); + lights(); + pushMatrix(); + rotateX( radians(-rotationX) ); + rotateY( radians(270 - rotationY) ); + fill(200); + noStroke(); + textureMode(IMAGE); + texturedSphere(globeRadius, texmap); + popMatrix(); + popMatrix(); + rotationX += velocityX; + rotationY += velocityY; + velocityX *= 0.95; + velocityY *= 0.95; + + // Implements mouse control (interaction will be inverse when sphere is upside down) + if(mousePressed){ + velocityX += (mouseY-pmouseY) * 0.01; + velocityY -= (mouseX-pmouseX) * 0.01; + } +} + +void initializeSphere(int res) +{ + sinLUT = new float[SINCOS_LENGTH]; + cosLUT = new float[SINCOS_LENGTH]; + + for (int i = 0; i < SINCOS_LENGTH; i++) { + sinLUT[i] = (float) Math.sin(i * DEG_TO_RAD * SINCOS_PRECISION); + cosLUT[i] = (float) Math.cos(i * DEG_TO_RAD * SINCOS_PRECISION); + } + + float delta = (float)SINCOS_LENGTH/res; + float[] cx = new float[res]; + float[] cz = new float[res]; + + // Calc unit circle in XZ plane + for (int i = 0; i < res; i++) { + cx[i] = -cosLUT[(int) (i*delta) % SINCOS_LENGTH]; + cz[i] = sinLUT[(int) (i*delta) % SINCOS_LENGTH]; + } + + // Computing vertexlist vertexlist starts at south pole + int vertCount = res * (res-1) + 2; + int currVert = 0; + + // Re-init arrays to store vertices + sphereX = new float[vertCount]; + sphereY = new float[vertCount]; + sphereZ = new float[vertCount]; + float angle_step = (SINCOS_LENGTH*0.5f)/res; + float angle = angle_step; + + // Step along Y axis + for (int i = 1; i < res; i++) { + float curradius = sinLUT[(int) angle % SINCOS_LENGTH]; + float currY = -cosLUT[(int) angle % SINCOS_LENGTH]; + for (int j = 0; j < res; j++) { + sphereX[currVert] = cx[j] * curradius; + sphereY[currVert] = currY; + sphereZ[currVert++] = cz[j] * curradius; + } + angle += angle_step; + } + sDetail = res; +} + +// Generic routine to draw textured sphere +void texturedSphere(float r, PImage t) +{ + int v1,v11,v2; + r = (r + 240 ) * 0.33; + beginShape(TRIANGLE_STRIP); + texture(t); + float iu=(float)(t.width-1)/(sDetail); + float iv=(float)(t.height-1)/(sDetail); + float u=0,v=iv; + for (int i = 0; i < sDetail; i++) { + vertex(0, -r, 0,u,0); + vertex(sphereX[i]*r, sphereY[i]*r, sphereZ[i]*r, u, v); + u+=iu; + } + vertex(0, -r, 0,u,0); + vertex(sphereX[0]*r, sphereY[0]*r, sphereZ[0]*r, u, v); + endShape(); + + // Middle rings + int voff = 0; + for(int i = 2; i < sDetail; i++) { + v1=v11=voff; + voff += sDetail; + v2=voff; + u=0; + beginShape(TRIANGLE_STRIP); + texture(t); + for (int j = 0; j < sDetail; j++) { + vertex(sphereX[v1]*r, sphereY[v1]*r, sphereZ[v1++]*r, u, v); + vertex(sphereX[v2]*r, sphereY[v2]*r, sphereZ[v2++]*r, u, v+iv); + u+=iu; + } + + // Close each ring + v1=v11; + v2=voff; + vertex(sphereX[v1]*r, sphereY[v1]*r, sphereZ[v1]*r, u, v); + vertex(sphereX[v2]*r, sphereY[v2]*r, sphereZ[v2]*r, u, v+iv); + endShape(); + v+=iv; + } + u=0; + + // Add the northern cap + beginShape(TRIANGLE_STRIP); + texture(t); + for (int i = 0; i < sDetail; i++) { + v2 = voff + i; + vertex(sphereX[v2]*r, sphereY[v2]*r, sphereZ[v2]*r, u, v); + vertex(0, r, 0,u,v+iv); + u+=iu; + } + vertex(sphereX[voff]*r, sphereY[voff]*r, sphereZ[voff]*r, u, v); + endShape(); + +} diff --git a/java/libraries/opengl/examples/Form/Toroid/Toroid.pde b/java/libraries/opengl/examples/Form/Toroid/Toroid.pde new file mode 100755 index 000000000..f79a8d174 --- /dev/null +++ b/java/libraries/opengl/examples/Form/Toroid/Toroid.pde @@ -0,0 +1,182 @@ +/** + * Interactive Toroid + * by Ira Greenberg. + * + * Illustrates the geometric relationship between Toroid, Sphere, and Helix + * 3D primitives, as well as lathing principal. + * + * Instructions:
+ * UP arrow key pts++
+ * DOWN arrow key pts--
+ * LEFT arrow key segments--
+ * RIGHT arrow key segments++
+ * 'a' key toroid radius--
+ * 's' key toroid radius++
+ * 'z' key initial polygon radius--
+ * 'x' key initial polygon radius++
+ * 'w' key toggle wireframe/solid shading
+ * 'h' key toggle sphere/helix
+ */ + +int pts = 40; +float angle = 0; +float radius = 60.0; + +// lathe segments +int segments = 60; +float latheAngle = 0; +float latheRadius = 100.0; + +//vertices +PVector vertices[], vertices2[]; + +// for shaded or wireframe rendering +boolean isWireFrame = false; + +// for optional helix +boolean isHelix = false; +float helixOffset = 5.0; + +void setup(){ + size(640, 360, P3D); +} + +void draw(){ + background(50, 64, 42); + // basic lighting setup + lights(); + // 2 rendering styles + // wireframe or solid + if (isWireFrame){ + stroke(255, 255, 150); + noFill(); + } + else { + noStroke(); + fill(150, 195, 125); + } + //center and spin toroid + translate(width/2, height/2, -100); + + rotateX(frameCount*PI/150); + rotateY(frameCount*PI/170); + rotateZ(frameCount*PI/90); + + // initialize point arrays + vertices = new PVector[pts+1]; + vertices2 = new PVector[pts+1]; + + // fill arrays + for(int i=0; i<=pts; i++){ + vertices[i] = new PVector(); + vertices2[i] = new PVector(); + vertices[i].x = latheRadius + sin(radians(angle))*radius; + if (isHelix){ + vertices[i].z = cos(radians(angle))*radius-(helixOffset* + segments)/2; + } + else{ + vertices[i].z = cos(radians(angle))*radius; + } + angle+=360.0/pts; + } + + // draw toroid + latheAngle = 0; + for(int i=0; i<=segments; i++){ + beginShape(QUAD_STRIP); + for(int j=0; j<=pts; j++){ + if (i>0){ + vertex(vertices2[j].x, vertices2[j].y, vertices2[j].z); + } + vertices2[j].x = cos(radians(latheAngle))*vertices[j].x; + vertices2[j].y = sin(radians(latheAngle))*vertices[j].x; + vertices2[j].z = vertices[j].z; + // optional helix offset + if (isHelix){ + vertices[j].z+=helixOffset; + } + vertex(vertices2[j].x, vertices2[j].y, vertices2[j].z); + } + // create extra rotation for helix + if (isHelix){ + latheAngle+=720.0/segments; + } + else { + latheAngle+=360.0/segments; + } + endShape(); + } +} + +/* + left/right arrow keys control ellipse detail + up/down arrow keys control segment detail. + 'a','s' keys control lathe radius + 'z','x' keys control ellipse radius + 'w' key toggles between wireframe and solid + 'h' key toggles between toroid and helix + */ +void keyPressed(){ + if(key == CODED) { + // pts + if (keyCode == UP) { + if (pts<40){ + pts++; + } + } + else if (keyCode == DOWN) { + if (pts>3){ + pts--; + } + } + // extrusion length + if (keyCode == LEFT) { + if (segments>3){ + segments--; + } + } + else if (keyCode == RIGHT) { + if (segments<80){ + segments++; + } + } + } + // lathe radius + if (key =='a'){ + if (latheRadius>0){ + latheRadius--; + } + } + else if (key == 's'){ + latheRadius++; + } + // ellipse radius + if (key =='z'){ + if (radius>10){ + radius--; + } + } + else if (key == 'x'){ + radius++; + } + // wireframe + if (key =='w'){ + if (isWireFrame){ + isWireFrame=false; + } + else { + isWireFrame=true; + } + } + // helix + if (key =='h'){ + if (isHelix){ + isHelix=false; + } + else { + isHelix=true; + } + } +} + diff --git a/java/libraries/opengl/examples/Form/Vertices/Vertices.pde b/java/libraries/opengl/examples/Form/Vertices/Vertices.pde new file mode 100755 index 000000000..5609ff94f --- /dev/null +++ b/java/libraries/opengl/examples/Form/Vertices/Vertices.pde @@ -0,0 +1,67 @@ +/** + * Vertices + * by Simon Greenwold. + * + * Draw a cylinder centered on the y-axis, going down + * from y=0 to y=height. The radius at the top can be + * different from the radius at the bottom, and the + * number of sides drawn is variable. + */ + +void setup() { + size(640, 360, P3D); +} + +void draw() { + background(0); + lights(); + translate(width / 2, height / 2); + rotateY(map(mouseX, 0, width, 0, PI)); + rotateZ(map(mouseY, 0, height, 0, -PI)); + noStroke(); + fill(255, 255, 255); + translate(0, -40, 0); + drawCylinder(10, 180, 200, 16); // Draw a mix between a cylinder and a cone + //drawCylinder(70, 70, 120, 64); // Draw a cylinder + //drawCylinder(0, 180, 200, 4); // Draw a pyramid +} + +void drawCylinder(float topRadius, float bottomRadius, float tall, int sides) { + float angle = 0; + float angleIncrement = TWO_PI / sides; + beginShape(QUAD_STRIP); + for (int i = 0; i < sides + 1; ++i) { + vertex(topRadius*cos(angle), 0, topRadius*sin(angle)); + vertex(bottomRadius*cos(angle), tall, bottomRadius*sin(angle)); + angle += angleIncrement; + } + endShape(); + + // If it is not a cone, draw the circular top cap + if (topRadius != 0) { + angle = 0; + beginShape(TRIANGLE_FAN); + + // Center point + vertex(0, 0, 0); + for (int i = 0; i < sides + 1; i++) { + vertex(topRadius * cos(angle), 0, topRadius * sin(angle)); + angle += angleIncrement; + } + endShape(); + } + + // If it is not a cone, draw the circular bottom cap + if (bottomRadius != 0) { + angle = 0; + beginShape(TRIANGLE_FAN); + + // Center point + vertex(0, tall, 0); + for (int i = 0; i < sides + 1; i++) { + vertex(bottomRadius * cos(angle), tall, bottomRadius * sin(angle)); + angle += angleIncrement; + } + endShape(); + } +} diff --git a/java/libraries/opengl/examples/Image/Blending/Blending.pde b/java/libraries/opengl/examples/Image/Blending/Blending.pde new file mode 100755 index 000000000..166fdc222 --- /dev/null +++ b/java/libraries/opengl/examples/Image/Blending/Blending.pde @@ -0,0 +1,80 @@ +// Blending, by Andres Colubri +// Images can be blended using one of the 10 blending modes +// available in OPENGL2. +// Images by Kevin Bjorke. + +PImage pic1, pic2; +int selMode = REPLACE; +String name = "replace"; +int picAlpha = 255; + +void setup() { + size(800, 480, P3D); + + PFont font = createFont(PFont.list()[0], 20); + textFont(font, 20); + + pic1 = loadImage("bjorke1.jpg"); + pic2 = loadImage("bjorke2.jpg"); +} + +void draw() { + background(0); + + tint(255, 255); + image(pic1, 0, 0, pic1.width, pic1.height); + + blendMode(selMode); + tint(255, picAlpha); + image(pic2, 0, 0, pic2.width, pic2.height); + + blendMode(REPLACE); + fill(200, 50, 50); + rect(0, height - 50, map(picAlpha, 0, 255, 0, width), 50); + fill(255); + + text("Selected blend mode: " + name + ". Click to move to next", 10, pic1.height + 30); + text("Drag this bar to change alpha of image", 10, height - 18); +} + +void mousePressed() { + if (height - 50 < mouseY) return; + + if (selMode == REPLACE) { + selMode = BLEND; + name = "blend"; + } else if (selMode == BLEND) { + selMode = ADD; + name = "add"; + } else if (selMode == ADD) { + selMode = SUBTRACT; + name = "subtract"; + } else if (selMode == SUBTRACT) { + selMode = LIGHTEST; + name = "lightest"; + } else if (selMode == LIGHTEST) { + selMode = DARKEST; + name = "darkest"; + } else if (selMode == DARKEST) { + selMode = DIFFERENCE; + name = "difference"; + } else if (selMode == DIFFERENCE) { + selMode = EXCLUSION; + name = "exclusion"; + } else if (selMode == EXCLUSION) { + selMode = MULTIPLY; + name = "multiply"; + } else if (selMode == MULTIPLY) { + selMode = SCREEN; + name = "screen"; + } else if (selMode == SCREEN) { + selMode = REPLACE; + name = "replace"; + } +} + +void mouseDragged() { + if (height - 50 < mouseY) { + picAlpha = int(map(mouseX, 0, width, 0, 255)); + } +} \ No newline at end of file diff --git a/java/libraries/opengl/examples/Image/Explode/Explode.pde b/java/libraries/opengl/examples/Image/Explode/Explode.pde new file mode 100755 index 000000000..436f9e8fd --- /dev/null +++ b/java/libraries/opengl/examples/Image/Explode/Explode.pde @@ -0,0 +1,43 @@ +/** + * Explode + * by Daniel Shiffman. + * + * Mouse horizontal location controls breaking apart of image and + * Maps pixels from a 2D image into 3D space. Pixel brightness controls + * translation along z axis. + */ + +PImage img; // The source image +int cellsize = 2; // Dimensions of each cell in the grid +int columns, rows; // Number of columns and rows in our system + +void setup() { + size(640, 360, P3D); + img = loadImage("eames.jpg"); // Load the image + columns = img.width / cellsize; // Calculate # of columns + rows = img.height / cellsize; // Calculate # of rows +} + +void draw() { + background(0); + // Begin loop for columns + for ( int i = 0; i < columns; i++) { + // Begin loop for rows + for ( int j = 0; j < rows; j++) { + int x = i*cellsize + cellsize/2; // x position + int y = j*cellsize + cellsize/2; // y position + int loc = x + y*img.width; // Pixel array location + color c = img.pixels[loc]; // Grab the color + // Calculate a z position as a function of mouseX and pixel brightness + float z = (mouseX / float(width)) * brightness(img.pixels[loc]) - 20.0; + // Translate to the location, set fill and stroke, and draw the rect + pushMatrix(); + translate(x + 200, y + 100, z); + fill(c, 204); + noStroke(); + rectMode(CENTER); + rect(0, 0, cellsize, cellsize); + popMatrix(); + } + } +} diff --git a/java/libraries/opengl/examples/Image/Explode/data/eames.jpg.tmp b/java/libraries/opengl/examples/Image/Explode/data/eames.jpg.tmp new file mode 100755 index 0000000000000000000000000000000000000000..c89377e40b1ac8538f10112f7d566b5829c6172f GIT binary patch literal 44496 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�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 E+2};vKL7v# literal 0 HcmV?d00001 diff --git a/java/libraries/opengl/examples/Image/Extrusion/Extrusion.pde b/java/libraries/opengl/examples/Image/Extrusion/Extrusion.pde new file mode 100755 index 000000000..637ac6689 --- /dev/null +++ b/java/libraries/opengl/examples/Image/Extrusion/Extrusion.pde @@ -0,0 +1,46 @@ +/** + * Extrusion. + * + * Converts a flat image into spatial data points and rotates the points + * around the center. + */ + +PImage extrude; +int[][] values; +float angle = 0; + +void setup() { + size(640, 360, P3D); + + // Load the image into a new array + extrude = loadImage("ystone08.jpg"); + extrude.loadPixels(); + values = new int[extrude.width][extrude.height]; + for (int y = 0; y < extrude.height; y++) { + for (int x = 0; x < extrude.width; x++) { + color pixel = extrude.get(x, y); + values[x][y] = int(brightness(pixel)); + } + } +} + +void draw() { + background(0); + + // Update the angle + angle += 0.005; + + // Rotate around the center axis + translate(width/2, 0, -128); + rotateY(angle); + translate(-extrude.width/2, 100, -128); + + // Display the image mass + for (int y = 0; y < extrude.height; y++) { + for (int x = 0; x < extrude.width; x++) { + stroke(values[x][y]); + point(x, y, -values[x][y]); + } + } + +} diff --git a/java/libraries/opengl/examples/Image/ExtrusionGL/ExtrusionGL.pde b/java/libraries/opengl/examples/Image/ExtrusionGL/ExtrusionGL.pde new file mode 100755 index 000000000..b779d590b --- /dev/null +++ b/java/libraries/opengl/examples/Image/ExtrusionGL/ExtrusionGL.pde @@ -0,0 +1,49 @@ +/** + * Extrusion. + * + * Converts a flat image into spatial data points and rotates the points + * around the center. + */ + +PImage a; +boolean onetime = true; +int[][] aPixels; +int[][] values; +float angle; + +void setup() { + size(1024, 768, P3D); + + aPixels = new int[width][height]; + values = new int[width][height]; + noFill(); + + // Load the image into a new array + // Extract the values and store in an array + a = loadImage("ystone08.jpg"); + a.loadPixels(); + for (int i = 0; i < a.height; i++) { + for (int j = 0; j < a.width; j++) { + aPixels[j][i] = a.pixels[i*a.width + j]; + values[j][i] = int(blue(aPixels[j][i])); + } + } +} + +void draw() { + background(255); + translate(width/2, height/2, 0); + scale(2.0); + + // Update and constrain the angle + angle += 0.005; + rotateY(angle); + + // Display the image mass + for (int i = 0; i < a.height; i += 2) { + for (int j = 0; j < a.width; j += 2) { + stroke(values[j][i], 153); + line(j-a.width/2, i-a.height/2, -values[j][i], j-a.width/2, i-a.height/2, -values[j][i]-10); + } + } +} diff --git a/java/libraries/opengl/examples/Image/Zoom/Zoom.pde b/java/libraries/opengl/examples/Image/Zoom/Zoom.pde new file mode 100755 index 000000000..d80e842a3 --- /dev/null +++ b/java/libraries/opengl/examples/Image/Zoom/Zoom.pde @@ -0,0 +1,89 @@ +/** + * Zoom. + * + * Move the cursor over the image to alter its position. Click and press + * the mouse to zoom and set the density of the matrix by typing numbers 1-5. + * This program displays a series of lines with their heights corresponding to + * a color value read from an image. + */ + +PImage img; +//boolean onetime = true; +int[][] imgPixels; +float sval = 1.0; +float nmx, nmy; +int res = 5; + +void setup() +{ + size(640, 360, P3D); + noFill(); + stroke(255); + img = loadImage("ystone08.jpg"); + imgPixels = new int[img.width][img.height]; + for (int i = 0; i < img.height; i++) { + for (int j = 0; j < img.width; j++) { + imgPixels[j][i] = img.get(j, i); + } + } +} + +void draw() +{ + background(0); + + nmx = nmx + (mouseX-nmx)/20; + nmy += (mouseY-nmy)/20; + + if(mousePressed) { + sval += 0.005; + } + else { + sval -= 0.01; + } + + sval = constrain(sval, 1.0, 2.5); + + translate(width/2 + nmx * sval-100, height/2 + nmy*sval - 200, -50); + scale(sval); + rotateZ(PI/9 - sval + 1.0); + rotateX(PI/sval/8 - 0.125); + rotateY(sval/8 - 0.125); + + translate(-width/2, -height/2, 0); + + for (int i = 0; i < img.height; i += res) { + for (int j = 0; j < img.width; j += res) { + float rr = red(imgPixels[j][i]); + float gg = green(imgPixels[j][i]); + float bb = blue(imgPixels[j][i]); + float tt = rr+gg+bb; + stroke(rr, gg, gg); + line(i, j, tt/10-20, i, j, tt/10 ); + } + } +} + +void keyPressed() { + if(key == '1') { + res = 1; + } + else if (key == '2') { + res = 2; + } + else if (key == '3') { + res = 3; + } + else if (key == '4') { + res = 4; + } + else if (key == '5') { + res = 5; + } +} + + + + + + diff --git a/java/libraries/opengl/examples/Immediate/Bezier/Bezier.pde b/java/libraries/opengl/examples/Immediate/Bezier/Bezier.pde new file mode 100755 index 000000000..8e0d082a9 --- /dev/null +++ b/java/libraries/opengl/examples/Immediate/Bezier/Bezier.pde @@ -0,0 +1,22 @@ +/** + * Bezier. + * + * The first two parameters for the bezier() function specify the + * first point in the curve and the last two parameters specify + * the last point. The middle parameters set the control points + * that define the shape of the curve. + */ + +void setup() { + size(640, 360, P3D); + stroke(255); + noFill(); +} + +void draw() { + background(0); + for (int i = 0; i < 200; i += 20) { + bezier(mouseX-(i/2.0), 40+i, 410, 20, 440, 300, 240-(i/16.0), 300+(i/8.0)); + } +} + diff --git a/java/libraries/opengl/examples/Immediate/Robot1_Draw/Robot1_Draw.pde b/java/libraries/opengl/examples/Immediate/Robot1_Draw/Robot1_Draw.pde new file mode 100755 index 000000000..43dc0cc57 --- /dev/null +++ b/java/libraries/opengl/examples/Immediate/Robot1_Draw/Robot1_Draw.pde @@ -0,0 +1,41 @@ +// Robot 1: Draw from "Getting Started with Processing" +// by Reas & Fry. O'Reilly / Make 2010 + +size(720, 480, P3D); +smooth(); +strokeWeight(2); +background(204); +ellipseMode(RADIUS); + +// Neck +stroke(102); // Set stroke to gray +line(266, 257, 266, 162); // Left +line(276, 257, 276, 162); // Middle +line(286, 257, 286, 162); // Right + +// Antennae +line(276, 155, 246, 112); // Small +line(276, 155, 306, 56); // Tall +line(276, 155, 342, 170); // Medium + +// Body +noStroke(); // Disable stroke +fill(102); // Set fill to gray +ellipse(264, 377, 33, 33); // Antigravity orb +fill(0); // Set fill to black +rect(219, 257, 90, 120); // Main body +fill(102); // Set fill to gray +rect(219, 274, 90, 6); // Gray stripe + +// Head +fill(0); // Set fill to black +ellipse(276, 155, 45, 45); // Head +fill(255); // Set fill to white +ellipse(288, 150, 14, 14); // Large eye +fill(0); // Set fill to black +ellipse(288, 150, 3, 3); // Pupil +fill(153); // Set fill to light gray +ellipse(263, 148, 5, 5); // Small eye 1 +ellipse(296, 130, 4, 4); // Small eye 2 +ellipse(305, 162, 3, 3); // Small eye 3 + diff --git a/java/libraries/opengl/examples/Immediate/ShapePrimitives/ShapePrimitives.pde b/java/libraries/opengl/examples/Immediate/ShapePrimitives/ShapePrimitives.pde new file mode 100755 index 000000000..9c39c1f2a --- /dev/null +++ b/java/libraries/opengl/examples/Immediate/ShapePrimitives/ShapePrimitives.pde @@ -0,0 +1,32 @@ +/** + * Shape Primitives. + * + * The basic shape primitive functions are triangle(), + * rect(), quad(), ellipse(), and arc(). Squares are made + * with rect() and circles are made with ellipse(). Each + * of these functions requires a number of parameters to + * determine the shape's position and size. + */ + +size(640, 360, P3D); +background(0); +noStroke(); + +fill(204); +triangle(18, 18, 18, 360, 81, 360); + +fill(102); +rect(81, 81, 63, 63); + +fill(204); +quad(189, 18, 216, 18, 216, 360, 144, 360); + +fill(255); +ellipse(252, 144, 72, 72); + +fill(204); +triangle(288, 18, 351, 360, 288, 360); + +fill(255); +arc(479, 300, 280, 280, PI, TWO_PI); + diff --git a/java/libraries/opengl/examples/Immediate/TriangleStrip/TriangleStrip.pde b/java/libraries/opengl/examples/Immediate/TriangleStrip/TriangleStrip.pde new file mode 100755 index 000000000..307d842a5 --- /dev/null +++ b/java/libraries/opengl/examples/Immediate/TriangleStrip/TriangleStrip.pde @@ -0,0 +1,44 @@ +/** + * TRIANGLE_STRIP Mode + * by Ira Greenberg. + * + * Generate a closed ring using the vertex() function and + * beginShape(TRIANGLE_STRIP) mode. The outerRad and innerRad + * variables control ring's radii respectively. + */ + +int x; +int y; +float outerRad; +float innerRad; + +void setup() { + size(640, 360, P3D); + background(204); + x = width/2; + y = height/2; + outerRad = min(width, height) * 0.4; + innerRad = outerRad * 0.6; +} + +void draw() { + background(204); + + int pts = int(map(mouseX, 0, width, 6, 60)); + float rot = 180.0/pts; + float angle = 0; + + beginShape(TRIANGLE_STRIP); + for (int i = 0; i <= pts; i++) { + float px = x + cos(radians(angle)) * outerRad; + float py = y + sin(radians(angle)) * outerRad; + angle += rot; + vertex(px, py); + px = x + cos(radians(angle)) * innerRad; + py = y + sin(radians(angle)) * innerRad; + vertex(px, py); + angle += rot; + } + endShape(); +} + diff --git a/java/libraries/opengl/examples/Lights/CameraLight/CameraLight.pde b/java/libraries/opengl/examples/Lights/CameraLight/CameraLight.pde new file mode 100755 index 000000000..551eb20b0 --- /dev/null +++ b/java/libraries/opengl/examples/Lights/CameraLight/CameraLight.pde @@ -0,0 +1,42 @@ +// CameraLight, by Andres Colubri +// Simple example showing a lit rotating cube. The projection +// is set to orthographic if the mouse is pressed. + +float spin = 0.0; + +void setup() { + size(400, 400, P3D); + + noStroke(); +} + +void draw() { + background(51); + + lights(); + + if (mousePressed) { + // The arguments of ortho are specified in screen coordinates, where (0,0) + // is the upper left corner of the screen + ortho(0, width, 0, height); + } else { + + float fov = PI/3.0; + float cameraZ = (height/2.0) / tan(fov/2.0); + perspective(fov, float(width)/float(height), + cameraZ/2.0, cameraZ*2.0); + } + + spin += 0.01; + + pushMatrix(); + translate(width/2, height/2, 0); + rotateX(PI/9); + rotateY(PI/5 + spin); + box(100); + popMatrix(); +} + + + + diff --git a/java/libraries/opengl/examples/Lights/Directional/Directional.pde b/java/libraries/opengl/examples/Lights/Directional/Directional.pde new file mode 100755 index 000000000..6a1af3008 --- /dev/null +++ b/java/libraries/opengl/examples/Lights/Directional/Directional.pde @@ -0,0 +1,28 @@ +/** + * Directional. + * + * Move the mouse the change the direction of the light. + * Directional light comes from one direction and is stronger + * when hitting a surface squarely and weaker if it hits at a + * a gentle angle. After hitting a surface, a directional lights + * scatters in all directions. + */ + +void setup() { + size(640, 360, P3D); + noStroke(); + fill(204); +} + +void draw() { + noStroke(); + background(0); + float dirY = (mouseY / float(height) - 0.5) * 2; + float dirX = (mouseX / float(width) - 0.5) * 2; + directionalLight(204, 204, 204, -dirX, -dirY, -1); + translate(width/2 - 100, height/2, 0); + sphere(80); + translate(200, 0, 0); + sphere(80); +} + diff --git a/java/libraries/opengl/examples/Lights/Lights1/Lights1.pde b/java/libraries/opengl/examples/Lights/Lights1/Lights1.pde new file mode 100755 index 000000000..bf7a36edc --- /dev/null +++ b/java/libraries/opengl/examples/Lights/Lights1/Lights1.pde @@ -0,0 +1,29 @@ +/** + * Lights 1. + * + * Uses the default lights to show a simple box. The lights() function + * is used to turn on the default lighting. + */ + +float spin = 0.0; + +void setup() +{ + size(640, 360, P3D); + noStroke(); +} + +void draw() +{ + background(51); + lights(); + + spin += 0.01; + + pushMatrix(); + translate(width/2, height/2, 0); + rotateX(PI/9); + rotateY(PI/5 + spin); + box(150); + popMatrix(); +} diff --git a/java/libraries/opengl/examples/Lights/Lights2/Lights2.pde b/java/libraries/opengl/examples/Lights/Lights2/Lights2.pde new file mode 100755 index 000000000..e1693e590 --- /dev/null +++ b/java/libraries/opengl/examples/Lights/Lights2/Lights2.pde @@ -0,0 +1,36 @@ +/** + * Lights 2 + * by Simon Greenwold. + * + * Display a box with three different kinds of lights. + */ + +void setup() +{ + size(640, 360, P3D); + noStroke(); +} + +void draw() +{ + background(0); + translate(width / 2, height / 2); + + // Orange point light on the right + pointLight(150, 100, 0, // Color + 200, -150, 0); // Position + + // Blue directional light from the left + directionalLight(0, 102, 255, // Color + 1, 0, 0); // The x-, y-, z-axis direction + + // Yellow spotlight from the front + spotLight(255, 255, 109, // Color + 0, 40, 200, // Position + 0, -0.5, -0.5, // Direction + PI / 2, 2); // Angle, concentration + + rotateY(map(mouseX, 0, width, 0, PI)); + rotateX(map(mouseY, 0, height, 0, PI)); + box(150); +} diff --git a/java/libraries/opengl/examples/Lights/LightsGL/LightsGL.pde b/java/libraries/opengl/examples/Lights/LightsGL/LightsGL.pde new file mode 100755 index 000000000..157ce4216 --- /dev/null +++ b/java/libraries/opengl/examples/Lights/LightsGL/LightsGL.pde @@ -0,0 +1,45 @@ +/** + * LightsGL. + * Modified from an example by Simon Greenwold. + * + * Display a box with three different kinds of lights. + */ + +void setup() +{ + size(1024, 768, P3D); + noStroke(); +} + +void draw() +{ + defineLights(); + background(0); + + for (int x = 0; x <= width; x += 100) { + for (int y = 0; y <= height; y += 100) { + pushMatrix(); + translate(x, y); + rotateY(map(mouseX, 0, width, 0, PI)); + rotateX(map(mouseY, 0, height, 0, PI)); + box(90); + popMatrix(); + } + } +} + +void defineLights() { + // Orange point light on the right + pointLight(150, 100, 0, // Color + 200, -150, 0); // Position + + // Blue directional light from the left + directionalLight(0, 102, 255, // Color + 1, 0, 0); // The x-, y-, z-axis direction + + // Yellow spotlight from the front + spotLight(255, 255, 109, // Color + 0, 40, 200, // Position + 0, -0.5, -0.5, // Direction + PI / 2, 2); // Angle, concentration +} diff --git a/java/libraries/opengl/examples/Lights/Reflection/Reflection.pde b/java/libraries/opengl/examples/Lights/Reflection/Reflection.pde new file mode 100755 index 000000000..f6a3d7a94 --- /dev/null +++ b/java/libraries/opengl/examples/Lights/Reflection/Reflection.pde @@ -0,0 +1,25 @@ +/** + * Reflection + * by Simon Greenwold. + * + * Vary the specular reflection component of a material + * with the horizontal position of the mouse. + */ + +void setup() { + size(640, 360, P3D); + noStroke(); + colorMode(RGB, 1); + fill(0.4); +} + +void draw() { + background(0); + translate(width / 2, height / 2); + // Set the specular color of lights that follow + lightSpecular(1, 1, 1); + directionalLight(0.8, 0.8, 0.8, 0, 0, -1); + float s = mouseX / float(width); + specular(s, s, s); + sphere(120); +} diff --git a/java/libraries/opengl/examples/Lights/Spot/Spot.pde b/java/libraries/opengl/examples/Lights/Spot/Spot.pde new file mode 100755 index 000000000..ce25c4bee --- /dev/null +++ b/java/libraries/opengl/examples/Lights/Spot/Spot.pde @@ -0,0 +1,35 @@ +/** + * Spot. + * + * Move the mouse the change the position and concentation + * of a blue spot light. + */ + +int concentration = 600; // Try values 1 -> 10000 + +void setup() +{ + //size(200, 200, P3D); + size(640, 360, P3D); + noStroke(); + fill(204); + sphereDetail(60); +} + +void draw() +{ + background(0); + + // Light the bottom of the sphere + directionalLight(51, 102, 126, 0, -1, 0); + + // Orange light on the upper-right of the sphere + spotLight(204, 153, 0, 360, 160, 600, 0, 0, -1, PI/2, 600); + + // Moving spotlight that follows the mouse + spotLight(102, 153, 204, 360, mouseY, 600, 0, 0, -1, PI/2, 600); + + translate(width/2, height/2, 0); + sphere(120); +} + diff --git a/java/libraries/opengl/examples/LowLevel/Nehe/Bitmap.pde b/java/libraries/opengl/examples/LowLevel/Nehe/Bitmap.pde new file mode 100755 index 000000000..f9179e16a --- /dev/null +++ b/java/libraries/opengl/examples/LowLevel/Nehe/Bitmap.pde @@ -0,0 +1,147 @@ +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferInt; +import java.io.IOException; +import java.io.InputStream; + +BufferedImage loadBitmap(String file) throws IOException { + BufferedImage image; + InputStream input = null; + try { + input = createInput(file); + + int bitmapFileHeaderLength = 14; + int bitmapInfoHeaderLength = 40; + + byte bitmapFileHeader[] = new byte[bitmapFileHeaderLength]; + byte bitmapInfoHeader[] = new byte[bitmapInfoHeaderLength]; + + input.read(bitmapFileHeader, 0, bitmapFileHeaderLength); + input.read(bitmapInfoHeader, 0, bitmapInfoHeaderLength); + + int nSize = bytesToInt(bitmapFileHeader, 2); + int nWidth = bytesToInt(bitmapInfoHeader, 4); + int nHeight = bytesToInt(bitmapInfoHeader, 8); + int nBiSize = bytesToInt(bitmapInfoHeader, 0); + int nPlanes = bytesToShort(bitmapInfoHeader, 12); + int nBitCount = bytesToShort(bitmapInfoHeader, 14); + int nSizeImage = bytesToInt(bitmapInfoHeader, 20); + int nCompression = bytesToInt(bitmapInfoHeader, 16); + int nColoursUsed = bytesToInt(bitmapInfoHeader, 32); + int nXPixelsMeter = bytesToInt(bitmapInfoHeader, 24); + int nYPixelsMeter = bytesToInt(bitmapInfoHeader, 28); + int nImportantColours = bytesToInt(bitmapInfoHeader, 36); + + if (nBitCount == 24) { + image = read24BitBitmap(nSizeImage, nHeight, nWidth, input); + } + else if (nBitCount == 8) { + image = read8BitBitmap(nColoursUsed, nBitCount, nSizeImage, nWidth, nHeight, input); + } + else { + System.out.println("Not a 24-bit or 8-bit Windows Bitmap, aborting..."); + image = null; + } + } + finally { + try { + if (input != null) + input.close(); + } + catch (IOException e) { + } + } + return image; +} + +BufferedImage read8BitBitmap(int nColoursUsed, int nBitCount, int nSizeImage, int nWidth, int nHeight, InputStream input) throws IOException { + int nNumColors = (nColoursUsed > 0) ? nColoursUsed : (1 & 0xff) << nBitCount; + + if (nSizeImage == 0) { + nSizeImage = ((((nWidth * nBitCount) + 31) & ~31) >> 3); + nSizeImage *= nHeight; + } + + int npalette[] = new int[nNumColors]; + byte bpalette[] = new byte[nNumColors * 4]; + readBuffer(input, bpalette); + int nindex8 = 0; + + for (int n = 0; n < nNumColors; n++) { + npalette[n] = (255 & 0xff) << 24 | + (bpalette[nindex8 + 2] & 0xff) << 16 | + (bpalette[nindex8 + 1] & 0xff) << 8 | + (bpalette[nindex8 + 0] & 0xff); + + nindex8 += 4; + } + + int npad8 = (nSizeImage / nHeight) - nWidth; + BufferedImage bufferedImage = new BufferedImage(nWidth, nHeight, BufferedImage.TYPE_INT_ARGB); + DataBufferInt dataBufferByte = ((DataBufferInt) bufferedImage.getRaster().getDataBuffer()); + int[][] bankData = dataBufferByte.getBankData(); + byte bdata[] = new byte[(nWidth + npad8) * nHeight]; + + readBuffer(input, bdata); + nindex8 = 0; + + for (int j8 = nHeight - 1; j8 >= 0; j8--) { + for (int i8 = 0; i8 < nWidth; i8++) { + bankData[0][j8 * nWidth + i8] = npalette[((int) bdata[nindex8] & 0xff)]; + nindex8++; + } + nindex8 += npad8; + } + + return bufferedImage; +} + +BufferedImage read24BitBitmap(int nSizeImage, int nHeight, int nWidth, InputStream input) throws IOException { + int npad = (nSizeImage / nHeight) - nWidth * 3; + if (npad == 4 || npad < 0) + npad = 0; + int nindex = 0; + BufferedImage bufferedImage = new BufferedImage(nWidth, nHeight, BufferedImage.TYPE_4BYTE_ABGR); + DataBufferByte dataBufferByte = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()); + byte[][] bankData = dataBufferByte.getBankData(); + byte brgb[] = new byte[(nWidth + npad) * 3 * nHeight]; + + readBuffer(input, brgb); + + for (int j = nHeight - 1; j >= 0; j--) { + for (int i = 0; i < nWidth; i++) { + int base = (j * nWidth + i) * 4; + bankData[0][base] = (byte) 255; + bankData[0][base + 1] = brgb[nindex]; + bankData[0][base + 2] = brgb[nindex + 1]; + bankData[0][base + 3] = brgb[nindex + 2]; + nindex += 3; + } + nindex += npad; + } + + return bufferedImage; +} + +int bytesToInt(byte[] bytes, int index) { + return (bytes[index + 3] & 0xff) << 24 | + (bytes[index + 2] & 0xff) << 16 | + (bytes[index + 1] & 0xff) << 8 | + bytes[index + 0] & 0xff; +} + +short bytesToShort(byte[] bytes, int index) { + return (short) (((bytes[index + 1] & 0xff) << 8) | + (bytes[index + 0] & 0xff)); +} + +void readBuffer(InputStream in, byte[] buffer) throws IOException { + int bytesRead = 0; + int bytesToRead = buffer.length; + while (bytesToRead > 0) { + int read = in.read(buffer, bytesRead, bytesToRead); + bytesRead += read; + bytesToRead -= read; + } +} + diff --git a/java/libraries/opengl/examples/LowLevel/Nehe/Nehe.pde b/java/libraries/opengl/examples/LowLevel/Nehe/Nehe.pde new file mode 100755 index 000000000..2c7f873f4 --- /dev/null +++ b/java/libraries/opengl/examples/LowLevel/Nehe/Nehe.pde @@ -0,0 +1,235 @@ +// Nehe by Andres Colubri +// Example of direct OpenGL use inside Processing with the +// OPENGL2 renderer. +// Ported from NeHe tutorial 8: +// http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=08 + +import javax.media.opengl.*; +import java.nio.*; +import javax.media.opengl.glu.gl2.GLUgl2; + +boolean lighting = true; +boolean blending = true; +boolean depthTest = true; +boolean depthMask = false; +boolean texturing = true; + +int selBlend = 1; +int selFilter = 0; +float transparency = 0.5f; + +// The color depth of the sketch can be set with this +// method. The 6 numbers separated by colons correspond +// to the red, green, blue, alpha, depth and stencil bits. +// If this method is not defined, then Processing will let +// OpenGL to automatically choose the color depth. +String sketchColordepth() { + return "8:8:8:8:16:0"; +} + +// Whether the sketch surface supports translucenty or not. +boolean sketchTranslucency() { + return true; +} + +FloatBuffer[] cubeVertexBfr; +FloatBuffer[] cubeNormalBfr; +FloatBuffer[] cubeTextureBfr; + +FloatBuffer lightAmbBfr; +FloatBuffer lightDifBfr; +FloatBuffer lightPosBfr; + +private IntBuffer texturesBuffer; + +private float xRot; +private float yRot; +float xSpeed = 0.2f; +float ySpeed = 0.2f; + +GLUgl2 glu; + +void setup() { + size(400, 400, P3D); + + glu = new GLUgl2(); + + int SIZEOF_FLOAT = Float.SIZE / 8; + + cubeVertexBfr = new FloatBuffer[6]; + cubeNormalBfr = new FloatBuffer[6]; + cubeTextureBfr = new FloatBuffer[6]; + for (int i = 0; i < 6; i++) { + ByteBuffer vbb = ByteBuffer.allocateDirect(4 * 3 * SIZEOF_FLOAT); + vbb.order(ByteOrder.nativeOrder()); + cubeVertexBfr[i] = vbb.asFloatBuffer(); + cubeVertexBfr[i].put(cubeVertexCoords[i]); + cubeVertexBfr[i].flip(); + + ByteBuffer nbb = ByteBuffer.allocateDirect(4 * 3 * SIZEOF_FLOAT); + nbb.order(ByteOrder.nativeOrder()); + cubeNormalBfr[i] = nbb.asFloatBuffer(); + cubeNormalBfr[i].put(cubeNormalCoords[i]); + cubeNormalBfr[i].flip(); + + ByteBuffer tbb = ByteBuffer.allocateDirect(4 * 2 * SIZEOF_FLOAT); + tbb.order(ByteOrder.nativeOrder()); + cubeTextureBfr[i] = tbb.asFloatBuffer(); + cubeTextureBfr[i].put(cubeTextureCoords[i]); + cubeTextureBfr[i].flip(); + } + + lightAmbBfr = FloatBuffer.wrap(lightAmb); + lightDifBfr = FloatBuffer.wrap(lightDif); + lightPosBfr = FloatBuffer.wrap(lightPos); + + PGraphicsOpenGL pgl = (PGraphicsOpenGL)g; + GL gl = pgl.beginGL(); + + Texture teximage = null; + try { + teximage = readTexture("glass.bmp"); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + + texturesBuffer = IntBuffer.allocate(3); + gl.glGenTextures(3, texturesBuffer); + + gl.glEnable(GL.GL_TEXTURE_2D); + // setup texture 0 with nearest filtering + gl.glBindTexture(GL.GL_TEXTURE_2D, texturesBuffer.get(0)); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE); + makeRGBTexture(gl, glu, teximage, GL.GL_TEXTURE_2D, false); + + // setup texture 1 with linear filtering for both minification and magnification, + // this is usually called bilinear sampling + gl.glBindTexture(GL.GL_TEXTURE_2D, texturesBuffer.get(1)); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE); + makeRGBTexture(gl, glu, teximage, GL.GL_TEXTURE_2D, false); + + // setup texture 2 with linear filtering for magnification and linear-linear mipmapping + // (trilinear sampling) + gl.glBindTexture(GL.GL_TEXTURE_2D, texturesBuffer.get(2)); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_NEAREST); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE); + makeRGBTexture(gl, glu, teximage, GL.GL_TEXTURE_2D, true); + gl.glDisable(GL.GL_TEXTURE_2D); + + pgl.endGL(); +} + +public void draw() { + background(0); + + PGraphicsOpenGL pgl = (PGraphicsOpenGL)g; + GL gl = pgl.beginGL(); + + pgl.gl2f.glShadeModel(GL2.GL_SMOOTH); + gl.glClearColor(0, 0, 0, 0); + + if (depthTest) { + gl.glClearDepthf(1.0f); + gl.glEnable(GL.GL_DEPTH_TEST); + gl.glDepthFunc(GL.GL_LEQUAL); + } + else { + gl.glDisable(GL.GL_DEPTH_TEST); + } + gl.glDepthMask(depthMask); + + gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST); + + // lighting + gl.glEnable(GL2.GL_LIGHT0); + pgl.gl2f.glLightfv(GL2.GL_LIGHT0, GL2.GL_AMBIENT, lightAmbBfr); + pgl.gl2f.glLightfv(GL2.GL_LIGHT0, GL2.GL_DIFFUSE, lightDifBfr); + pgl.gl2f.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, lightPosBfr); + + // blending + gl.glEnable(GL2.GL_COLOR_MATERIAL); + pgl.gl2f.glColor4f(1.0f, 1.0f, 1.0f, transparency); + if (selBlend == 0) { + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE); + } + else if (selBlend == 1) { + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); + } + + gl.glViewport(0, 0, width, height); + // setup projection matrix + pgl.gl2f.glMatrixMode(GL2.GL_PROJECTION); + pgl.gl2f.glLoadIdentity(); + glu.gluPerspective(45.0f, (float)width / (float)height, 1.0f, 100.0f); + + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + pgl.gl2f.glMatrixMode(GL2.GL_MODELVIEW); + pgl.gl2f.glLoadIdentity(); + + // update lighting + if (lighting) { + gl.glEnable(GL2.GL_LIGHTING); + } + else { + gl.glDisable(GL2.GL_LIGHTING); + } + + // update blending + if (blending) { + gl.glEnable(GL.GL_BLEND); + gl.glDisable(GL.GL_CULL_FACE); + } + else { + gl.glDisable(GL.GL_BLEND); + gl.glEnable(GL.GL_CULL_FACE); + } + + pgl.gl2f.glTranslatef(0, 0, -6); + pgl.gl2f.glRotatef(xRot, 1, 0, 0); + pgl.gl2f.glRotatef(yRot, 0, 1, 0); + + if (texturing) { + gl.glEnable(GL.GL_TEXTURE_2D); + gl.glBindTexture(GL.GL_TEXTURE_2D, texturesBuffer.get(selFilter)); + } + pgl.gl2f.glEnableClientState(GL2.GL_VERTEX_ARRAY); + pgl.gl2f.glEnableClientState(GL2.GL_NORMAL_ARRAY); + if (texturing) pgl.gl2f.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY); + for (int i = 0; i < 6; i++) // draw each face + { + pgl.gl2f.glVertexPointer(3, GL.GL_FLOAT, 0, cubeVertexBfr[i]); + if (texturing) pgl.gl2f.glTexCoordPointer(2, GL.GL_FLOAT, 0, cubeTextureBfr[i]); + pgl.gl2f.glNormalPointer(GL.GL_FLOAT, 0, cubeNormalBfr[i]); + pgl.gl2f.glDrawArrays(GL.GL_TRIANGLE_FAN, 0, 4); + } + pgl.gl2f.glDisableClientState(GL2.GL_VERTEX_ARRAY); + pgl.gl2f.glDisableClientState(GL2.GL_NORMAL_ARRAY); + if (texturing) { + pgl.gl2f.glDisableClientState(GL2.GL_TEXTURE_COORD_ARRAY); + gl.glDisable(GL.GL_TEXTURE_2D); + } + + // update rotations + xRot += xSpeed; + yRot += ySpeed; + + pgl.endGL(); +} + +void makeRGBTexture(GL gl, GLUgl2 glu, Texture img, int target, boolean mipmapped) { + if (mipmapped) { + glu.gluBuild2DMipmaps(target, GL.GL_RGB8, img.getWidth(), img.getHeight(), GL.GL_RGB, GL.GL_UNSIGNED_BYTE, img.getPixels()); + } else { + gl.glTexImage2D(target, 0, GL.GL_RGB, img.getWidth(), img.getHeight(), 0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, img.getPixels()); + } +} + diff --git a/java/libraries/opengl/examples/LowLevel/Nehe/Texture.pde b/java/libraries/opengl/examples/LowLevel/Nehe/Texture.pde new file mode 100755 index 000000000..67b09712c --- /dev/null +++ b/java/libraries/opengl/examples/LowLevel/Nehe/Texture.pde @@ -0,0 +1,82 @@ +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.awt.image.PixelGrabber; +import java.io.IOException; +import java.nio.ByteBuffer; + +Texture readTexture(String filename) throws IOException { + return readTexture(filename, false); +} + +Texture readTexture(String filename, boolean storeAlphaChannel) throws IOException { + BufferedImage bufferedImage; + if (filename.endsWith(".bmp")) { + bufferedImage = loadBitmap(filename); + } + else { + bufferedImage = readImage(filename); + } + return readPixels(bufferedImage, storeAlphaChannel); +} + +BufferedImage readImage(String resourceName) throws IOException { + return ImageIO.read(createInput(resourceName)); +} + +Texture readPixels(BufferedImage img, boolean storeAlphaChannel) { + int[] packedPixels = new int[img.getWidth() * img.getHeight()]; + + PixelGrabber pixelgrabber = new PixelGrabber(img, 0, 0, img.getWidth(), img.getHeight(), packedPixels, 0, img.getWidth()); + try { + pixelgrabber.grabPixels(); + } + catch (InterruptedException e) { + throw new RuntimeException(); + } + + int bytesPerPixel = storeAlphaChannel ? 4 : 3; + //ByteBuffer unpackedPixels = BufferUtil.newByteBuffer(packedPixels.length * bytesPerPixel); + ByteBuffer unpackedPixels = ByteBuffer.allocate(packedPixels.length * bytesPerPixel); + + for (int row = img.getHeight() - 1; row >= 0; row--) { + for (int col = 0; col < img.getWidth(); col++) { + int packedPixel = packedPixels[row * img.getWidth() + col]; + unpackedPixels.put((byte) ((packedPixel >> 16) & 0xFF)); + unpackedPixels.put((byte) ((packedPixel >> 8) & 0xFF)); + unpackedPixels.put((byte) ((packedPixel >> 0) & 0xFF)); + if (storeAlphaChannel) { + unpackedPixels.put((byte) ((packedPixel >> 24) & 0xFF)); + } + } + } + + unpackedPixels.flip(); + + + return new Texture(unpackedPixels, img.getWidth(), img.getHeight()); +} + +class Texture { + ByteBuffer pixels; + int width; + int height; + + public Texture(ByteBuffer pixels, int width, int height) { + this.height = height; + this.pixels = pixels; + this.width = width; + } + + public int getHeight() { + return height; + } + + public ByteBuffer getPixels() { + return pixels; + } + + public int getWidth() { + return width; + } +} + diff --git a/java/libraries/opengl/examples/LowLevel/Nehe/VertData.pde b/java/libraries/opengl/examples/LowLevel/Nehe/VertData.pde new file mode 100755 index 000000000..f6f813b4d --- /dev/null +++ b/java/libraries/opengl/examples/LowLevel/Nehe/VertData.pde @@ -0,0 +1,147 @@ +float[][] cubeVertexCoords = new float[][] { + new float[] { // top + 1, 1,-1, + -1, 1,-1, + -1, 1, 1, + 1, 1, 1 + } + , + new float[] { // bottom + 1,-1, 1, + -1,-1, 1, + -1,-1,-1, + 1,-1,-1 + } + , + new float[] { // front + 1, 1, 1, + -1, 1, 1, + -1,-1, 1, + 1,-1, 1 + } + , + new float[] { // back + 1,-1,-1, + -1,-1,-1, + -1, 1,-1, + 1, 1,-1 + } + , + new float[] { // left + -1, 1, 1, + -1, 1,-1, + -1,-1,-1, + -1,-1, 1 + } + , + new float[] { // right + 1, 1,-1, + 1, 1, 1, + 1,-1, 1, + 1,-1,-1 + } + , +}; + +float[][] cubeNormalCoords = new float[][] { + new float[] { // top + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0 + } + , + new float[] { // bottom + 0,-1, 0, + 0,-1, 0, + 0,-1, 0, + 0,-1, 0 + } + , + new float[] { // front + 0, 0, 1, + 0, 0, 1, + 0, 0, 1, + 0, 0, 1 + } + , + new float[] { // back + 0, 0,-1, + 0, 0,-1, + 0, 0,-1, + 0, 0,-1 + } + , + new float[] { // left + -1, 0, 0, + -1, 0, 0, + -1, 0, 0, + -1, 0, 0 + } + , + new float[] { // right + 1, 0, 0, + 1, 0, 0, + 1, 0, 0, + 1, 0, 0 + } + , +}; + +float[][] cubeTextureCoords = new float[][] { + new float[] { // top + 1, 0, + 1, 1, + 0, 1, + 0, 0 + } + , + new float[] { // bottom + 0, 0, + 1, 0, + 1, 1, + 0, 1 + } + , + new float[] { // front + 1, 1, + 0, 1, + 0, 0, + 1, 0 + } + , + new float[] { // back + 0, 1, + 0, 0, + 1, 0, + 1, 1 + } + , + new float[] { // left + 1, 1, + 0, 1, + 0, 0, + 1, 0 + } + , + new float[] { // right + 0, 1, + 0, 0, + 1, 0, + 1, 1 + } + , +}; + +float lightAmb[]= { + 0.5f, 0.5f, 0.5f, 1.0f +}; + +float lightDif[]= { + 1.0f, 1.0f, 1.0f, 1.0f +}; + +float lightPos[]= { + 0.0f, 0.0f, 2.0f, 1.0f +}; + diff --git a/java/libraries/opengl/examples/LowLevel/Nehe/data/glass.bmp b/java/libraries/opengl/examples/LowLevel/Nehe/data/glass.bmp new file mode 100755 index 0000000000000000000000000000000000000000..1acf064255835c7f81bb3d15a4271612ce3b6df3 GIT binary patch literal 49220 zcmZs?XOJA%b|$EekRA;T2@u}4t?KgLdtcpM-h1!8_qMyb+Be#Jv~Qprh9ChFBuIh; zpP{#UUQcgUBaJkpSZOELjAA!7ByGOg{W%r4GBUF=t17;G&pF@u&PlG6_~Ot0`-=!{ z`+#@j|9}5ae|8Dj`yan9T?Jq1Q+B~MS4oDe2>sP-I0%NoP-{7A6_Qqy&6G?R9- zr<Lq4R>xOscv=llr{(K)Vv|v5FaUQ6^g6CqBL==otK}(`{d%29r{in2T(yd? zRf~-Jev?6H&9o=u7KY2g@wx{?0ZG883D*3*)vT~u>a}8(d{Cp3=rw~{wbZC(^7)r9bzDcvE>m<@uIjGC4P7^BXF8adRd-SDk06B? zJ^HsVLk(iHu6XaL^x$s#-f`yMgY@xH?A{&UgJavlu61wQd1v3fx9vOJ_uoAX9N%%@ zI}AU57~TYdh+h{ zlXu$>-fZ4EuI?RX_ivYv_iFd=)_`BUcPF*Co!+^f-MJm#SPiW#2A1c&^E1))<lf_RyNH$tRr+eZ1&#S-uGXJZuGr#)U`PoO7UwmNt z^r`F9_hMgt>Uj2+<@poai>J_dfm@ z82g`meDLANd(S?)`|&6HAAYd+;YXX#Ki+!p*~UBXu0MTx`|&#)kKb8;`^namcbD!x zSb6Z~=HqvY6XRz(I!+G^pYp!RN8|mGs5F*PL}QwGT%Sqll1W`EDNV#AiI_MN;`zN) zr@c?Bf|&FuhlLvo%2O#goxvU)G{zzdms{ob0$a7)qjh!MelMB4$7`yL7h>pzZ(1U6zI#>^j}?p z_|9CQUKMZR+>arJCotv~wbRnN96=R81HeU+IQZ84}=<=!DZn+WoU9ZU(G6$ljV9XGTY5fs} zHzu<9gch&d9koRZjzqzk$%X6Hc%_)GluFII#-#6p^h&f!qr<7tS&R;!D;D*oQ=UZJ zlT4T*Av1u9$0c_=auzK4tGEx9PEZbNeniILx2%D46az9RB@ydzEUnw$|NeaKrZLVqynWvVlf)sW}U;R zaoMykmnrBmhuqe1&=~Z1!$GGzpmR9ndexvp#h0nrg90KFjwfQ!yncRvKSQCQN<@5t zRIE_REqbHZZIAjb0jI$YEaQ;DV^=tx0)sIZa2nZ*vAQP?bM zhh3~z(*;5Z0!`s?B?^^Dt>GJhYji4`O=-1CEf#@R$CfK7VlfPfzjhOR7Am}kkY2u` zxPp}R_M$qF;8=T1>2|?TaF5s5)!9e8fz$L+aVNf%k($29awO;ClVM`!>`2Ec6O^*;0!s2(Eo zVN9wnfx27ZN7{l2r>1w1(22oZL>Lj85ZdQQdu)AX8^{>Oczhs}6K275A~;tC{yw=2 zEVFitE!{#>7taA5F!u# zMCl}Wu=a6kx{SB9D2@ihRimay(Rw{jO6kG-(RwFzzyxAD`}h_R-Ox#L_K2MrEvXxW zy}%y0;TK-j@~$a)SC#BbQvNkH|EAu6jmITp{ZKpax>DL>lJ{6-oo4Y3qv*OmP0VC5 z2`9p*?Xwx+Zq5aX=#orxO*3#+X#qQIeOe@_y9bMd^!0M3gOR0asa6T0kcW5gd&>

A& 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 N&!LW3o_pcB{|_*>WrF|! literal 0 HcmV?d00001 diff --git a/java/libraries/opengl/examples/Performance/CubicGrid/CubicGrid.pde b/java/libraries/opengl/examples/Performance/CubicGrid/CubicGrid.pde new file mode 100755 index 000000000..df8c0ea0a --- /dev/null +++ b/java/libraries/opengl/examples/Performance/CubicGrid/CubicGrid.pde @@ -0,0 +1,53 @@ +/** + * Cubic Grid + * by Ira Greenberg. + * + * 3D translucent colored grid uses nested pushMatrix() + * and popMatrix() functions. + */ + +float boxSize = 40; +float margin = boxSize*2; +float depth = 400; +color boxFill; + +void setup() { + size(640, 360, P3D); + frameRate(120); + noStroke(); +} + +void draw() { + background(255); + + hint(DISABLE_DEPTH_TEST); + + // Center and spin grid + translate(width/2, height/2, -depth); + rotateY(frameCount * 0.01); + rotateX(frameCount * 0.01); + + // Build grid using multiple translations + for (float i =- depth/2+margin; i <= depth/2-margin; i += boxSize){ + pushMatrix(); + for (float j =- height+margin; j <= height-margin; j += boxSize){ + pushMatrix(); + for (float k =- width+margin; k <= width-margin; k += boxSize){ + // Base fill color on counter values, abs function + // ensures values stay within legal range + boxFill = color(abs(i), abs(j), abs(k), 50); + pushMatrix(); + translate(k, j, i); + fill(boxFill); + box(boxSize, boxSize, boxSize); + popMatrix(); + } + popMatrix(); + } + popMatrix(); + } + if (frameCount % 10 == 0) { + println(frameRate); + } +} + diff --git a/java/libraries/opengl/examples/Performance/Esfera/Esfera.pde b/java/libraries/opengl/examples/Performance/Esfera/Esfera.pde new file mode 100755 index 000000000..f418792c2 --- /dev/null +++ b/java/libraries/opengl/examples/Performance/Esfera/Esfera.pde @@ -0,0 +1,89 @@ +/** + * Esfera + * by David Pena. + * + * Distribucion aleatoria uniforme sobre la superficie de una esfera. + */ + +int cuantos = 16000; +pelo[] lista ; +float[] z = new float[cuantos]; +float[] phi = new float[cuantos]; +float[] largos = new float[cuantos]; +float radio = 200; +float rx = 0; +float ry =0; + +void setup() { + size(1024, 768, P3D); + radio = height/3.5; + + lista = new pelo[cuantos]; + for (int i=0; i 3) { + flapSpeed *= -1; + } + if (ang < -3) { + flapSpeed *= -1; + } + + // Increment angles + ang2 += 0.01; + ang3 += 2.0; + ang4 += 0.75; +} + diff --git a/java/libraries/opengl/examples/Transform/Birds/Bird.pde b/java/libraries/opengl/examples/Transform/Birds/Bird.pde new file mode 100755 index 000000000..84ada2603 --- /dev/null +++ b/java/libraries/opengl/examples/Transform/Birds/Bird.pde @@ -0,0 +1,99 @@ +class Bird { + + // Properties + float offsetX, offsetY, offsetZ; + float w, h; + int bodyFill; + int wingFill; + float ang = 0, ang2 = 0, ang3 = 0, ang4 = 0; + float radiusX = 120, radiusY = 200, radiusZ = 700; + float rotX = 15, rotY = 10, rotZ = 5; + float flapSpeed = 0.4; + float rotSpeed = 0.1; + + // Constructors + Bird(){ + this(0, 0, 0, 60, 80); + } + + Bird(float offsetX, float offsetY, float offsetZ, + float w, float h){ + this.offsetX = offsetX; + this.offsetY = offsetY; + this.offsetZ = offsetZ; + this.h = h; + this.w = w; + bodyFill = color(153); + wingFill = color(204); + } + + void setFlight(float radiusX, float radiusY, float radiusZ, + float rotX, float rotY, float rotZ){ + this.radiusX = radiusX; + this.radiusY = radiusY; + this.radiusZ = radiusZ; + + this.rotX = rotX; + this.rotY = rotY; + this.rotZ = rotZ; + } + + void setWingSpeed(float flapSpeed){ + this.flapSpeed = flapSpeed; + } + + void setRotSpeed(float rotSpeed){ + this.rotSpeed = rotSpeed; + } + + void fly() { + pushMatrix(); + float px, py, pz; + + // Flight + px = sin(radians(ang3)) * radiusX; + py = cos(radians(ang3)) * radiusY; + pz = sin(radians(ang4)) * radiusZ; + + translate(width/2 + offsetX + px, height/2 + offsetY+py, -700 + offsetZ+pz); + + rotateX(sin(radians(ang2)) * rotX); + rotateY(sin(radians(ang2)) * rotY); + rotateZ(sin(radians(ang2)) * rotZ); + + // Body + fill(bodyFill); + box(w/5, h, w/5); + + // Left wing + fill(wingFill); + pushMatrix(); + rotateY(sin(radians(ang)) * 20); + rect(0, -h/2, w, h); + popMatrix(); + + // Right wing + pushMatrix(); + rotateY(sin(radians(ang)) * -20); + rect(-w, -h/2, w, h); + popMatrix(); + + // Wing flap + ang += flapSpeed; + if (ang > 3) { + flapSpeed*=-1; + } + if (ang < -3) { + flapSpeed*=-1; + } + + // Ang's run trig functions + ang2 += rotSpeed; + ang3 += 1.25; + ang4 += 0.55; + popMatrix(); + } +} + + + diff --git a/java/libraries/opengl/examples/Transform/Birds/Birds.pde b/java/libraries/opengl/examples/Transform/Birds/Birds.pde new file mode 100755 index 000000000..5026bd2ae --- /dev/null +++ b/java/libraries/opengl/examples/Transform/Birds/Birds.pde @@ -0,0 +1,53 @@ +/** + * Crazy Flocking 3D Birds + * by Ira Greenberg. + * + * Simulates a flock of birds using a Bird class and nested + * pushMatrix() / popMatrix() functions. + * Trigonometry functions handle the flapping and sinuous movement. + */ + +// Flock array +int birdCount = 200; +Bird[]birds = new Bird[birdCount]; +float[]x = new float[birdCount]; +float[]y = new float[birdCount]; +float[]z = new float[birdCount]; +float[]rx = new float[birdCount]; +float[]ry = new float[birdCount]; +float[]rz = new float[birdCount]; +float[]spd = new float[birdCount]; +float[]rot = new float[birdCount]; + +void setup() { + size(640, 360, P3D); + noStroke(); + + // Initialize arrays with random values + for (int i = 0; i < birdCount; i++){ + birds[i] = new Bird(random(-300, 300), random(-300, 300), + random(-500, -2500), random(5, 30), random(5, 30)); + + x[i] = random(20, 340); + y[i] = random(30, 350); + z[i] = random(1000, 4800); + rx[i] = random(-160, 160); + ry[i] = random(-55, 55); + rz[i] = random(-20, 20); + spd[i] = random(.1, 3.75); + rot[i] = random(.025, .15); + } +} + +void draw() { + background(0); + lights(); + for (int i = 0; i < birdCount; i++){ + birds[i].setFlight(x[i], y[i], z[i], rx[i], ry[i], rz[i]); + birds[i].setWingSpeed(spd[i]); + birds[i].setRotSpeed(rot[i]); + birds[i].fly(); + } +} + + diff --git a/java/libraries/opengl/examples/Transform/CubesWithinCube/Cube.pde b/java/libraries/opengl/examples/Transform/CubesWithinCube/Cube.pde new file mode 100755 index 000000000..271792623 --- /dev/null +++ b/java/libraries/opengl/examples/Transform/CubesWithinCube/Cube.pde @@ -0,0 +1,72 @@ + +// Custom Cube Class + +class Cube{ + PVector[] vertices = new PVector[24]; + float w, h, d; + + // Default constructor + Cube(){ } + + // Constructor 2 + Cube(float w, float h, float d) { + this.w = w; + this.h = h; + this.d = d; + + // cube composed of 6 quads + //front + vertices[0] = new PVector(-w/2,-h/2,d/2); + vertices[1] = new PVector(w/2,-h/2,d/2); + vertices[2] = new PVector(w/2,h/2,d/2); + vertices[3] = new PVector(-w/2,h/2,d/2); + //left + vertices[4] = new PVector(-w/2,-h/2,d/2); + vertices[5] = new PVector(-w/2,-h/2,-d/2); + vertices[6] = new PVector(-w/2,h/2,-d/2); + vertices[7] = new PVector(-w/2,h/2,d/2); + //right + vertices[8] = new PVector(w/2,-h/2,d/2); + vertices[9] = new PVector(w/2,-h/2,-d/2); + vertices[10] = new PVector(w/2,h/2,-d/2); + vertices[11] = new PVector(w/2,h/2,d/2); + //back + vertices[12] = new PVector(-w/2,-h/2,-d/2); + vertices[13] = new PVector(w/2,-h/2,-d/2); + vertices[14] = new PVector(w/2,h/2,-d/2); + vertices[15] = new PVector(-w/2,h/2,-d/2); + //top + vertices[16] = new PVector(-w/2,-h/2,d/2); + vertices[17] = new PVector(-w/2,-h/2,-d/2); + vertices[18] = new PVector(w/2,-h/2,-d/2); + vertices[19] = new PVector(w/2,-h/2,d/2); + //bottom + vertices[20] = new PVector(-w/2,h/2,d/2); + vertices[21] = new PVector(-w/2,h/2,-d/2); + vertices[22] = new PVector(w/2,h/2,-d/2); + vertices[23] = new PVector(w/2,h/2,d/2); + } + void create(){ + // Draw cube + for (int i=0; i<6; i++){ + beginShape(QUADS); + for (int j=0; j<4; j++){ + vertex(vertices[j+4*i].x, vertices[j+4*i].y, vertices[j+4*i].z); + } + endShape(); + } + } + void create(color[]quadBG){ + // Draw cube + for (int i=0; i<6; i++){ + fill(quadBG[i]); + beginShape(QUADS); + for (int j=0; j<4; j++){ + vertex(vertices[j+4*i].x, vertices[j+4*i].y, vertices[j+4*i].z); + } + endShape(); + } + } +} + + diff --git a/java/libraries/opengl/examples/Transform/CubesWithinCube/CubesWithinCube.pde b/java/libraries/opengl/examples/Transform/CubesWithinCube/CubesWithinCube.pde new file mode 100755 index 000000000..b20420fcc --- /dev/null +++ b/java/libraries/opengl/examples/Transform/CubesWithinCube/CubesWithinCube.pde @@ -0,0 +1,117 @@ +/** + * Cubes Contained Within a Cube + * by Ira Greenberg. + * + * Collision detection against all + * outer cube's surfaces. + * Uses the Point3D and Cube classes. + */ + +Cube stage; // external large cube +int cubies = 20; +Cube[]c = new Cube[cubies]; // internal little cubes +color[][]quadBG = new color[cubies][6]; + +// Controls cubie's movement +float[]x = new float[cubies]; +float[]y = new float[cubies]; +float[]z = new float[cubies]; +float[]xSpeed = new float[cubies]; +float[]ySpeed = new float[cubies]; +float[]zSpeed = new float[cubies]; + +// Controls cubie's rotation +float[]xRot = new float[cubies]; +float[]yRot = new float[cubies]; +float[]zRot = new float[cubies]; + +// Size of external cube +float bounds = 300; + +void setup() { + size(640, 360, P3D); + + for (int i = 0; i < cubies; i++){ + // Each cube face has a random color component + float colorShift = random(-75, 75); + quadBG[i][0] = color(0); + quadBG[i][1] = color(51); + quadBG[i][2] = color(102); + quadBG[i][3] = color(153); + quadBG[i][4] = color(204); + quadBG[i][5] = color(255); + + // Cubies are randomly sized + float cubieSize = random(5, 15); + c[i] = new Cube(cubieSize, cubieSize, cubieSize); + + // Initialize cubie's position, speed and rotation + x[i] = 0; + y[i] = 0; + z[i] = 0; + + xSpeed[i] = random(-1, 1); + ySpeed[i] = random(-1, 1); + zSpeed[i] = random(-1, 1); + + xRot[i] = random(40, 100); + yRot[i] = random(40, 100); + zRot[i] = random(40, 100); + } + + // Instantiate external large cube + stage = new Cube(bounds, bounds, bounds); +} + +void draw(){ + background(50); + lights(); + + // Center in display window + translate(width/2, height/2, -130); + + // Outer transparent cube + noFill(); + + // Rotate everything, including external large cube + rotateX(frameCount * 0.001); + rotateY(frameCount * 0.002); + rotateZ(frameCount * 0.001); + stroke(255); + + // Draw external large cube + stage.create(); + + // Move and rotate cubies + for (int i = 0; i < cubies; i++){ + pushMatrix(); + translate(x[i], y[i], z[i]); + rotateX(frameCount*PI/xRot[i]); + rotateY(frameCount*PI/yRot[i]); + rotateX(frameCount*PI/zRot[i]); + noStroke(); + c[i].create(quadBG[i]); + x[i] += xSpeed[i]; + y[i] += ySpeed[i]; + z[i] += zSpeed[i]; + popMatrix(); + + // Draw lines connecting cubbies + stroke(0); + if (i < cubies-1){ + line(x[i], y[i], z[i], x[i+1], y[i+1], z[i+1]); + } + + // Check wall collisions + if (x[i] > bounds/2 || x[i] < -bounds/2){ + xSpeed[i]*=-1; + } + if (y[i] > bounds/2 || y[i] < -bounds/2){ + ySpeed[i]*=-1; + } + if (z[i] > bounds/2 || z[i] < -bounds/2){ + zSpeed[i]*=-1; + } + } +} + diff --git a/java/libraries/opengl/examples/Transform/PushPopCubes/PushPopCubes.pde b/java/libraries/opengl/examples/Transform/PushPopCubes/PushPopCubes.pde new file mode 100755 index 000000000..52ae70fdc --- /dev/null +++ b/java/libraries/opengl/examples/Transform/PushPopCubes/PushPopCubes.pde @@ -0,0 +1,144 @@ +/** + * PushPop Cubes + * by Ira Greenberg. + * + * Array of rotating cubes creates + * dynamic field patterns. Color + * controlled by light sources. Example + * of pushMatrix() and popMatrix(). + */ + +// Cube class required +float ang; +int rows = 21; +int cols = 21; +int cubeCount = rows*cols; +int colSpan, rowSpan; +float rotspd = 2.0; +Cube[] cubes = new Cube[cubeCount]; +float[] angs = new float[cubeCount]; +float[] rotvals = new float[cubeCount]; + +void setup(){ + size(640, 360, P3D); + + colSpan = width/(cols-1); + rowSpan = height/(rows-1); + noStroke(); + + // instantiate cubes + for (int i = 0; i < cubeCount; i++){ + cubes[i] = new Cube(12, 12, 6, 0, 0, 0); + /* 3 different rotation options + - 1st option: cubes each rotate uniformly + - 2nd option: cubes each rotate randomly + - 3rd option: cube columns rotate as waves + To try the different rotations, leave one + of the rotVals[i] lines uncommented below + and the other 2 commented out. */ + + //rotvals[i] = rotspd; + //rotvals[i] = random(-rotspd * 2, rotspd * 2); + rotvals[i] = rotspd += .01; + } +} + +void draw(){ + int cubeCounter = 0; + background(0); + fill(200); + + // Set up some different colored lights + pointLight(51, 102, 255, width/3, height/2, 100); + pointLight(200, 40, 60, width/1.5, height/2, -150); + + // Raise overall light in scene + ambientLight(170, 170, 100); + + // Translate, rotate and draw cubes + for (int i = 0; i < cols; i++){ + for (int j = 0; j < rows; j++){ + pushMatrix(); + /* Translate each block. + pushmatix and popmatrix add each cube + translation to matrix, but restore + original, so each cube rotates around its + owns center */ + translate(i * colSpan, j * rowSpan, -20); + //rotate each cube around y and x axes + rotateY(radians(angs[cubeCounter])); + rotateX(radians(angs[cubeCounter])); + cubes[cubeCounter].drawCube(); + popMatrix(); + cubeCounter++; + } + } + // Angs used in rotate function calls above + for (int i = 0; i < cubeCount; i++){ + angs[i] += rotvals[i]; + } +} + +// Simple Cube class, based on Quads +class Cube { + + // Properties + int w, h, d; + int shiftX, shiftY, shiftZ; + + // Constructor + Cube(int w, int h, int d, int shiftX, int shiftY, int shiftZ){ + this.w = w; + this.h = h; + this.d = d; + this.shiftX = shiftX; + this.shiftY = shiftY; + this.shiftZ = shiftZ; + } + + /* Main cube drawing method, which looks + more confusing than it really is. It's + just a bunch of rectangles drawn for + each cube face */ + void drawCube(){ + + // Front face + beginShape(QUADS); + vertex(-w/2 + shiftX, -h/2 + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, -h/2 + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, h + shiftY, -d/2 + shiftZ); + vertex(-w/2 + shiftX, h + shiftY, -d/2 + shiftZ); + + // Back face + vertex(-w/2 + shiftX, -h/2 + shiftY, d + shiftZ); + vertex(w + shiftX, -h/2 + shiftY, d + shiftZ); + vertex(w + shiftX, h + shiftY, d + shiftZ); + vertex(-w/2 + shiftX, h + shiftY, d + shiftZ); + + // Left face + vertex(-w/2 + shiftX, -h/2 + shiftY, -d/2 + shiftZ); + vertex(-w/2 + shiftX, -h/2 + shiftY, d + shiftZ); + vertex(-w/2 + shiftX, h + shiftY, d + shiftZ); + vertex(-w/2 + shiftX, h + shiftY, -d/2 + shiftZ); + + // Right face + vertex(w + shiftX, -h/2 + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, -h/2 + shiftY, d + shiftZ); + vertex(w + shiftX, h + shiftY, d + shiftZ); + vertex(w + shiftX, h + shiftY, -d/2 + shiftZ); + + // Top face + vertex(-w/2 + shiftX, -h/2 + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, -h/2 + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, -h/2 + shiftY, d + shiftZ); + vertex(-w/2 + shiftX, -h/2 + shiftY, d + shiftZ); + + // Bottom face + vertex(-w/2 + shiftX, h + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, h + shiftY, -d/2 + shiftZ); + vertex(w + shiftX, h + shiftY, d + shiftZ); + vertex(-w/2 + shiftX, h + shiftY, d + shiftZ); + endShape(); + } +} + diff --git a/java/libraries/opengl/examples/Transform/Rotate1/Rotate1.pde b/java/libraries/opengl/examples/Transform/Rotate1/Rotate1.pde new file mode 100755 index 000000000..7aafc250a --- /dev/null +++ b/java/libraries/opengl/examples/Transform/Rotate1/Rotate1.pde @@ -0,0 +1,38 @@ +/** + * Rotate 1. + * + * Rotating simultaneously in the X and Y axis. + * Transformation functions such as rotate() are additive. + * Successively calling rotate(1.0) and rotate(2.0) + * is equivalent to calling rotate(3.0). + */ + +float a = 0.0; +float rSize; // rectangle size + +void setup() { + size(640, 360, P3D); + rSize = width / 6; + noStroke(); + fill(204, 204); +} + +void draw() { + background(0); + + a += 0.005; + if(a > TWO_PI) { + a = 0.0; + } + + translate(width/2, height/2); + + rotateX(a); + rotateY(a * 2.0); + rect(-rSize, -rSize, rSize*2, rSize*2); + + rotateX(a * 1.001); + rotateY(a * 2.002); + rect(-rSize, -rSize, rSize*2, rSize*2); + +} diff --git a/java/libraries/opengl/examples/Transform/Rotate2/Rotate2.pde b/java/libraries/opengl/examples/Transform/Rotate2/Rotate2.pde new file mode 100755 index 000000000..1d5af0d49 --- /dev/null +++ b/java/libraries/opengl/examples/Transform/Rotate2/Rotate2.pde @@ -0,0 +1,42 @@ +/** + * Rotate 2. + * + * The push() and pop() functions allow for more control over transformations. + * The push function saves the current coordinate system to the stack + * and pop() restores the prior coordinate system. + */ + +float a; // Angle of rotation +float offset = PI/24.0; // Angle offset between boxes +int num = 12; // Number of boxes +color[] colors = new color[num]; // Colors of each box +color safecolor; + +boolean pink = true; + +void setup() +{ + size(640, 360, P3D); + noStroke(); + for(int i=0; izW+16M~;8BY`<2vUnbkf%Jylp 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 + buff = buff.substring(1); + } + break; + case 13: // Avoid special keys + case 10: + case 65535: + case 127: + case 27: + break; + default: + if(textWidth(buff+k)+leftmargin < width-rightmargin){ + didntTypeYet = false; + buff=k+buff; + } + break; + } +} + diff --git a/java/libraries/opengl/examples/Typography/Typing/data/Univers45.vlw b/java/libraries/opengl/examples/Typography/Typing/data/Univers45.vlw new file mode 100755 index 0000000000000000000000000000000000000000..040e02170efb50dbce4576b305e0b6b4f53de200 GIT binary patch literal 95193 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