diff --git a/.classpath b/.classpath index cba2a9a..717dd99 100644 --- a/.classpath +++ b/.classpath @@ -2,7 +2,6 @@ - diff --git a/examples/CameraStates/CameraStates.pde b/examples/CameraStates/CameraStates.pde new file mode 100644 index 0000000..952a382 --- /dev/null +++ b/examples/CameraStates/CameraStates.pde @@ -0,0 +1,42 @@ + +import peasy.*; + + +// +// '1' save current camera-state +// '2' apply saved camera-state +// + +PeasyCam cam; + +public void settings() { + size(800, 600, P3D); +} + +public void setup() { + cam = new PeasyCam(this, 400); + state = cam.getState(); +} + +public void draw() { + rotateX(-.5f); + rotateY(-.5f); + lights(); + scale(10); + strokeWeight(1 / 10f); + background(0); + fill(220, 255, 0); + box(30); + pushMatrix(); + translate(0, 0, 20); + fill(0, 96, 255); + box(5); + popMatrix(); +} + +CameraState state; + +public void keyReleased() { + if (key == '1') state = cam.getState(); + if (key == '2') cam.setState(state, 1000); +} diff --git a/examples/HeadUpDisplay/HeadUpDisplay.pde b/examples/HeadUpDisplay/HeadUpDisplay.pde new file mode 100644 index 0000000..a142ad0 --- /dev/null +++ b/examples/HeadUpDisplay/HeadUpDisplay.pde @@ -0,0 +1,41 @@ + +import peasy.PeasyCam; + + +// +// screen-aligned, orthographic HUD-scope +// + +PeasyCam cam; + +public void settings() { + size(800, 600, P3D); + smooth(8); +} + +public void setup() { + cam = new PeasyCam(this, 400); +} + +public void draw() { + rotateX(-.5f); + rotateY(-.5f); + lights(); + scale(10); + strokeWeight(1 / 10f); + background(0); + fill(96, 255, 0); + box(30); + pushMatrix(); + translate(0, 0, 20); + fill(0, 96, 255); + box(5); + popMatrix(); + + cam.beginHUD(); + fill(0, 128); + rect(0, 0, 70, 30); + fill(255); + text("" + nfc(frameRate, 2), 10, 18); + cam.endHUD(); +} diff --git a/examples/HelloPeasy/HelloPeasy.pde b/examples/HelloPeasy/HelloPeasy.pde index 28a8339..aaaf98c 100644 --- a/examples/HelloPeasy/HelloPeasy.pde +++ b/examples/HelloPeasy/HelloPeasy.pde @@ -1,24 +1,29 @@ -import peasy.*; - -PeasyCam cam; - -void setup() { - size(200,200,P3D); - cam = new PeasyCam(this, 100); - cam.setMinimumDistance(50); - cam.setMaximumDistance(500); -} -void draw() { - rotateX(-.5); - rotateY(-.5); - background(0); - fill(255,0,0); - box(30); - pushMatrix(); - translate(0,0,20); - fill(0,0,255); - box(5); - popMatrix(); -} - +import peasy.PeasyCam; + + +PeasyCam cam; + +public void settings() { + size(800, 600, P3D); +} + +public void setup() { + cam = new PeasyCam(this, 400); +} + +public void draw() { + rotateX(-.5f); + rotateY(-.5f); + lights(); + scale(10); + strokeWeight(1 / 10f); + background(0); + fill(255, 0, 0); + box(30); + pushMatrix(); + translate(0, 0, 20); + fill(0, 0, 255); + box(5); + popMatrix(); +} diff --git a/examples/MultiView_Offscreen/MultiView_Offscreen.pde b/examples/MultiView_Offscreen/MultiView_Offscreen.pde new file mode 100644 index 0000000..ad3a180 --- /dev/null +++ b/examples/MultiView_Offscreen/MultiView_Offscreen.pde @@ -0,0 +1,115 @@ + + +import peasy.PeasyCam; + + +// +// +// MultiView (advanced version) +// +// N x N Camera Views of the same scene, using N x N separate pgraphics. +// +// + +final int NX = 3; +final int NY = 2; +PeasyCam[] cameras = new PeasyCam[NX * NY]; + +public void settings() { + size(1280, 720, P2D); // 2D + smooth(8); +} + +public void setup() { + + int gap = 5; + + // tiling size + int tilex = floor((width - gap) / NX); + int tiley = floor((height - gap) / NY); + + // viewport offset ... corrected gap due to floor() + int offx = (width - (tilex * NX - gap)) / 2; + int offy = (height - (tiley * NY - gap)) / 2; + + // viewport dimension + int cw = tilex - gap; + int ch = tiley - gap; + + // create new viewport for each camera + for(int y = 0; y < NY; y++){ + for(int x = 0; x < NX; x++){ + int id = y * NX + x; + int cx = offx + x * tilex; + int cy = offy + y * tiley; + PGraphics pg = createGraphics(cw, ch, P3D); + cameras[id] = new PeasyCam(this, pg, 400); + cameras[id].setViewport(cx, cy, cw, ch); // this is the key of this whole demo + } + } + +} + + +public void draw(){ + // render scene once per camera/viewport + for(int i = 0; i < cameras.length; i++){ + displayScene(cameras[i], i); + } + + background(0); + for(int i = 0; i < cameras.length; i++){ + int[] viewport = cameras[i].getViewport(); + image(cameras[i].getCanvas(), viewport[0], viewport[1], viewport[2], viewport[3]); + } +} + + +public void displayScene(PeasyCam cam, int ID){ + + PGraphics pg = cam.getCanvas(); + + int[] viewport = cam.getViewport(); + int w = viewport[2]; + int h = viewport[3]; + + pg.beginDraw(); + pg.resetMatrix(); + + // modelview - using camera state + cam.feed(); + + // projection - using camera viewport + pg.perspective(60 * PI/180, w/(float)h, 1, 5000); + + // clear background (scissors makes sure we only clear the region we own) + pg.background(24); + pg.stroke(0); + pg.strokeWeight(0.3f); + + // scene objects + pg.pushMatrix(); + pg.translate(-100, 0, 0); + pg.fill(0,96,255); + pg.box(100); + pg.popMatrix(); + + pg.pushMatrix(); + pg.translate(100, 0, 0); + pg.rotateX(PI/2); + float c = 255 * ID/(float) (cameras.length-1); + pg.fill(255, 255-c/2, 255-c); + pg.sphere(80); + pg.popMatrix(); + + // screen-aligned 2D HUD + cam.beginHUD(); + pg.rectMode(CORNER); + pg.fill(0); + pg.rect(0, 0, 60, 23); + pg.fill(255,128,0); + pg.text("cam "+ID, 10, 15); + cam.endHUD(); + + pg.endDraw(); +} diff --git a/examples/MultiView_Onscreen/MultiView_Onscreen.pde b/examples/MultiView_Onscreen/MultiView_Onscreen.pde new file mode 100644 index 0000000..6ee305e --- /dev/null +++ b/examples/MultiView_Onscreen/MultiView_Onscreen.pde @@ -0,0 +1,140 @@ + + +import peasy.PeasyCam; + +import processing.opengl.PGL; +import processing.opengl.PGraphics3D; +import processing.opengl.PJOGL; + + +// +// +// MultiView (advanced version) +// +// N x N Camera Views of the same scene, on only one PGraphics. +// +// In this demo only one render-target is used -> the primary PGraphics3D ... this.g +// Each Camera still has its own mouse-handler. +// Only the viewport-position/dimension is used to build the camera state. +// +// For rendering, some OpenGL instructions (viewport, scissors) are used to +// render the scene to its actual camera viewport position/size. +// +// + +final int NX = 3; +final int NY = 2; +PeasyCam[] cameras = new PeasyCam[NX * NY]; + +public void settings() { + size(1280, 720, P3D); // 3D + smooth(8); +} + +public void setup() { + + int gap = 5; + + // tiling size + int tilex = floor((width - gap) / NX); + int tiley = floor((height - gap) / NY); + + // viewport offset ... corrected gap due to floor() + int offx = (width - (tilex * NX - gap)) / 2; + int offy = (height - (tiley * NY - gap)) / 2; + + // viewport dimension + int cw = tilex - gap; + int ch = tiley - gap; + + // create new viewport for each camera + for(int y = 0; y < NY; y++){ + for(int x = 0; x < NX; x++){ + int id = y * NX + x; + int cx = offx + x * tilex; + int cy = offy + y * tiley; + cameras[id] = new PeasyCam(this, 400); + cameras[id].setViewport(cx, cy, cw, ch); // this is the key of this whole demo + } + } + +} + + +public void draw(){ + // clear background once, for the whole window + setGLGraphicsViewport(0, 0, width, height); + background(0); + + // render scene once per camera/viewport + for(int i = 0; i < cameras.length; i++){ + pushStyle(); + pushMatrix(); + displayScene(cameras[i], i); + popMatrix(); + popStyle(); + } + // setGLGraphicsViewport(0, 0, width, height); +} + + +// some OpenGL instructions to set our custom viewport +// https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glViewport.xhtml +// https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glScissor.xhtml +void setGLGraphicsViewport(int x, int y, int w, int h){ + PGraphics3D pg = (PGraphics3D) this.g; + PJOGL pgl = (PJOGL) pg.beginPGL(); pg.endPGL(); + + pgl.enable(PGL.SCISSOR_TEST); + pgl.scissor (x,y,w,h); + pgl.viewport(x,y,w,h); +} + + +public void displayScene(PeasyCam cam, int ID){ + + int[] viewport = cam.getViewport(); + int w = viewport[2]; + int h = viewport[3]; + int x = viewport[0]; + int y = viewport[1]; + int y_inv = height - y - h; // inverted y-axis + + // scissors-test and viewport transformation + setGLGraphicsViewport(x, y_inv, w, h); + + // modelview - using camera state + cam.feed(); + + // projection - using camera viewport + perspective(60 * PI/180, w/(float)h, 1, 5000); + + // clear background (scissors makes sure we only clear the region we own) + background(24); + stroke(0); + strokeWeight(1); + + // scene objects + pushMatrix(); + translate(-100, 0, 0); + fill(0,96,255); + box(100); + popMatrix(); + + pushMatrix(); + translate(100, 0, 0); + rotateX(PI/2); + float c = 255 * ID/(float) (cameras.length-1); + fill(255-c/2, 255, 255-c); + sphere(80); + popMatrix(); + + // screen-aligned 2D HUD + cam.beginHUD(); + rectMode(CORNER); + fill(0); + rect(0, 0, 60, 23); + fill(255,128,0); + text("cam "+ID, 10, 15); + cam.endHUD(); +} diff --git a/examples/cameraHUD.pde b/examples/cameraHUD.pde deleted file mode 100644 index 49efd84..0000000 --- a/examples/cameraHUD.pde +++ /dev/null @@ -1,33 +0,0 @@ -/* PeasyCam provides a dead-simple mouse-driven camera for Processing. - * full documentation at http://mrfeinberg.com/peasycam/ - */ - -import peasy.*; - -PeasyCam cam; - -void setup() { - size(200, 200, P3D); - cam = new PeasyCam(this, 100); - cam.setMinimumDistance(50); - cam.setMaximumDistance(500); -} - -void draw() { - rotateX(-.5); - rotateY(-.5); - background(0); - fill(255, 0, 0); - box(30); - pushMatrix(); - translate(0, 0, 20); - fill(0, 0, 255); - box(5); - popMatrix(); - camera.beginHUD(); // start drawing relative to the camera view - fill(255); - rect(20, 10, 120, 30); - fill(0); - text(str(frameRate), 30, 30); - camera.endHUD(); // and don't forget to stop/close with this! -} diff --git a/src/peasy/PeasyCam.java b/src/peasy/PeasyCam.java index 3430b33..585fcae 100644 --- a/src/peasy/PeasyCam.java +++ b/src/peasy/PeasyCam.java @@ -18,7 +18,6 @@ */ package peasy; - import peasy.org.apache.commons.math.geometry.CardanEulerSingularityException; import peasy.org.apache.commons.math.geometry.Rotation; import peasy.org.apache.commons.math.geometry.RotationOrder; @@ -60,6 +59,9 @@ private static enum Constraint { private Vector3D center; private Rotation rotation; + // viewport for the mouse-pointer [x,y,w,h] + private int[] viewport = new int[4]; + private Constraint dragConstraint = null; private Constraint permaConstraint = null; @@ -67,7 +69,7 @@ private static enum Constraint { private final InterpolationManager centerInterps = new InterpolationManager(); private final InterpolationManager distanceInterps = new InterpolationManager(); - private final PeasyDragHandler panHandler /* ha ha ha */= new PeasyDragHandler() { + private final PeasyDragHandler panHandler /* ha ha ha */ = new PeasyDragHandler() { public void handleDrag(final double dx, final double dy) { dampedPanX.impulse(dx / 8.); dampedPanY.impulse(dy / 8.); @@ -101,7 +103,7 @@ public void handleWheel(final int delta) { private boolean isActive = false; public final String VERSION = "202"; - + public PeasyCam(final PApplet parent, final double distance) { this(parent, parent.g, 0, 0, 0, distance); } @@ -115,14 +117,20 @@ public PeasyCam(final PApplet parent, final PGraphics pg, final double distance) this(parent, pg, 0, 0, 0, distance); } - public PeasyCam(final PApplet parent, PGraphics pg, final double lookAtX, final double lookAtY, - final double lookAtZ, final double distance) { + public PeasyCam(final PApplet parent, PGraphics pg, final double lookAtX, + final double lookAtY, final double lookAtZ, final double distance) { this.p = parent; this.g = pg; this.startCenter = this.center = new Vector3D(lookAtX, lookAtY, lookAtZ); - this.startDistance = this.distance = Math.max(distance, SMALLEST_MINIMUM_DISTANCE); + this.startDistance = this.distance = Math.max(distance, + SMALLEST_MINIMUM_DISTANCE); this.rotation = new Rotation(); + viewport[0] = 0; + viewport[1] = 0; + viewport[2] = pg.width; + viewport[3] = pg.height; + feed(); rotateX = new DampedAction(this) { @@ -268,7 +276,31 @@ public String version() { return VERSION; } + public void setViewport(int x, int y, int w, int h) { + viewport[0] = x; + viewport[1] = y; + viewport[2] = w; + viewport[3] = h; + } + + public int[] getViewport() { + return viewport; + } + + public PGraphics getCanvas() { + return g; + } + + public boolean insideViewport(double x, double y) { + float x0 = viewport[0], x1 = x0 + viewport[2]; + float y0 = viewport[1], y1 = y0 + viewport[3]; + return (x > x0) && (x < x1) && (y > y0) && (y < y1); + } + protected class PeasyEventListener { + + public boolean isActive = false; + public void keyEvent(final KeyEvent e) { if (e.getAction() == KeyEvent.RELEASE && e.isShiftDown()) dragConstraint = null; @@ -276,41 +308,57 @@ public void keyEvent(final KeyEvent e) { public void mouseEvent(final MouseEvent e) { switch (e.getAction()) { - case MouseEvent.WHEEL: - wheelHandler.handleWheel((int)e.getCount()); + + case MouseEvent.PRESS: + if (insideViewport(p.mouseX, p.mouseY)) { + isActive = true; + } break; + case MouseEvent.RELEASE: dragConstraint = null; + isActive = false; break; + case MouseEvent.CLICK: - if (resetOnDoubleClick && 2 == (int)e.getCount()) { - reset(); + if (insideViewport(p.mouseX, p.mouseY)) { + if (resetOnDoubleClick && 2 == (int)e.getCount()) { + reset(); + } } break; - case MouseEvent.DRAG: - final double dx = p.mouseX - p.pmouseX; - final double dy = p.mouseY - p.pmouseY; - if (e.isShiftDown()) { - if (dragConstraint == null && Math.abs(dx - dy) > 1) { - dragConstraint = Math.abs(dx) > Math.abs(dy) ? Constraint.YAW - : Constraint.PITCH; - } - } else if (permaConstraint != null) { - dragConstraint = permaConstraint; - } else { - dragConstraint = null; + case MouseEvent.WHEEL: + if (insideViewport(p.mouseX, p.mouseY)) { + wheelHandler.handleWheel((int)e.getCount()); } + break; + + case MouseEvent.DRAG: + if (isActive) { + final double dx = p.mouseX - p.pmouseX; + final double dy = p.mouseY - p.pmouseY; + + if (e.isShiftDown()) { + if (dragConstraint == null && Math.abs(dx - dy) > 1) { + dragConstraint = Math.abs(dx) > Math.abs(dy) ? Constraint.YAW + : Constraint.PITCH; + } + } else if (permaConstraint != null) { + dragConstraint = permaConstraint; + } else { + dragConstraint = null; + } - final int b = p.mouseButton; - if (centerDragHandler != null - && (b == PConstants.CENTER || (b == PConstants.LEFT && e - .isMetaDown()))) { - centerDragHandler.handleDrag(dx, dy); - } else if (leftDragHandler != null && b == PConstants.LEFT) { - leftDragHandler.handleDrag(dx, dy); - } else if (rightDraghandler != null && b == PConstants.RIGHT) { - rightDraghandler.handleDrag(dx, dy); + final int b = p.mouseButton; + if (centerDragHandler != null && (b == PConstants.CENTER + || (b == PConstants.LEFT && e.isMetaDown()))) { + centerDragHandler.handleDrag(dx, dy); + } else if (leftDragHandler != null && b == PConstants.LEFT) { + leftDragHandler.handleDrag(dx, dy); + } else if (rightDraghandler != null && b == PConstants.RIGHT) { + rightDraghandler.handleDrag(dx, dy); + } } break; } @@ -318,50 +366,55 @@ public void mouseEvent(final MouseEvent e) { } private void mouseZoom(final double delta) { - safeSetDistance(distance + delta * Math.log1p(distance)); + // double new_distance = distance + delta * Math.log1p(distance); + double new_distance = distance + delta * distance * 0.02; + if (new_distance < minimumDistance) { + new_distance = minimumDistance; + dampedZoom.stop(); + } + if (new_distance > maximumDistance) { + new_distance = maximumDistance; + dampedZoom.stop(); + } + safeSetDistance(new_distance); } private void mousePan(final double dxMouse, final double dyMouse) { - final double panScale = Math.sqrt(distance * .005); + // final double panScale = Math.sqrt(distance * .005); + final double panScale = distance * 0.0025; pan(dragConstraint == Constraint.PITCH ? 0 : -dxMouse * panScale, dragConstraint == Constraint.YAW ? 0 : -dyMouse * panScale); } private void mouseRotate(final double dx, final double dy) { - final Vector3D u = LOOK.scalarMultiply(100 + .6 * startDistance).negate(); - final int xSign = dx > 0 ? -1 : 1; - final int ySign = dy < 0 ? -1 : 1; + // double mult = -0.0025; + + double mult = -Math.pow(Math.log10(1 + distance), 0.5) * 0.00125f; + + double dmx = dx * mult; + double dmy = dy * mult; - final double eccentricity = Math.abs((p.height / 2d) - p.mouseY) - / (p.height / 2d); - final double rho = Math.abs((p.width / 2d) - p.mouseX) / (p.width / 2d); + double viewX = viewport[0]; + double viewY = viewport[1]; + double viewW = viewport[2]; + double viewH = viewport[3]; + + // mouse [-1, +1] + double mxNdc = Math.min(Math.max((p.mouseX - viewX) / viewW, 0), 1) * 2 - 1; + double myNdc = Math.min(Math.max((p.mouseY - viewY) / viewH, 0), 1) * 2 - 1; if (dragConstraint == null || dragConstraint == Constraint.YAW || dragConstraint == Constraint.SUPPRESS_ROLL) { - final double adx = Math.abs(dx) * (1 - eccentricity); - final Vector3D vx = u.add(new Vector3D(adx, 0, 0)); - rotateY.impulse(Vector3D.angle(u, vx) * xSign); + rotateY.impulse(+dmx * (1.0 - myNdc * myNdc)); } if (dragConstraint == null || dragConstraint == Constraint.PITCH || dragConstraint == Constraint.SUPPRESS_ROLL) { - final double ady = Math.abs(dy) * (1 - rho); - final Vector3D vy = u.add(new Vector3D(0, ady, 0)); - rotateX.impulse(Vector3D.angle(u, vy) * ySign); + rotateX.impulse(-dmy * (1.0 - mxNdc * mxNdc)); } if (dragConstraint == null || dragConstraint == Constraint.ROLL) { - { - final double adz = Math.abs(dy) * rho; - final Vector3D vz = u.add(new Vector3D(0, adz, 0)); - rotateZ.impulse(Vector3D.angle(u, vz) * -ySign - * (p.mouseX < p.width / 2 ? -1 : 1)); - } - { - final double adz = Math.abs(dx) * eccentricity; - final Vector3D vz = u.add(new Vector3D(0, adz, 0)); - rotateZ.impulse(Vector3D.angle(u, vz) * xSign - * (p.mouseY > p.height / 2 ? -1 : 1)); - } + rotateZ.impulse(-dmx * myNdc); + rotateZ.impulse(+dmy * mxNdc); } } @@ -374,8 +427,8 @@ public void setDistance(final double newDistance) { } public void setDistance(final double newDistance, final long animationTimeMillis) { - distanceInterps.startInterpolation(new DistanceInterp(newDistance, - animationTimeMillis)); + distanceInterps + .startInterpolation(new DistanceInterp(newDistance, animationTimeMillis)); } public float[] getLookAt() { @@ -528,12 +581,12 @@ public void setState(final CameraState state) { public void setState(final CameraState state, final long animationTimeMillis) { if (animationTimeMillis > 0) { - rotationInterps.startInterpolation(new RotationInterp(state.rotation, - animationTimeMillis)); - centerInterps.startInterpolation(new CenterInterp(state.center, - animationTimeMillis)); - distanceInterps.startInterpolation(new DistanceInterp(state.distance, - animationTimeMillis)); + rotationInterps.startInterpolation( + new RotationInterp(state.rotation, animationTimeMillis)); + centerInterps.startInterpolation( + new CenterInterp(state.center, animationTimeMillis)); + distanceInterps.startInterpolation( + new DistanceInterp(state.distance, animationTimeMillis)); } else { this.rotation = state.rotation; this.center = state.center; @@ -579,9 +632,6 @@ public float[] getRotations() { return new float[] { 0, 0, 0 }; } - - - private boolean pushedLights = false; /** @@ -601,23 +651,22 @@ public void beginHUD() { g.pushMatrix(); g.resetMatrix(); // 3D is always GL (in processing 3), so this check is probably redundant. - if(g.isGL() && g.is3D()){ + if (g.isGL() && g.is3D()) { PGraphicsOpenGL pgl = (PGraphicsOpenGL)g; pushedLights = pgl.lights; pgl.lights = false; pgl.pushProjection(); - g.ortho(0, g.width, -g.height, 0, -Float.MAX_VALUE, +Float.MAX_VALUE); + g.ortho(0, viewport[2], -viewport[3], 0, -Float.MAX_VALUE, +Float.MAX_VALUE); } } - /** * * end screen-aligned 2D-drawing. * */ public void endHUD() { - if(g.isGL() && g.is3D()){ + if (g.isGL() && g.is3D()) { PGraphicsOpenGL pgl = (PGraphicsOpenGL)g; pgl.popProjection(); pgl.lights = pushedLights; diff --git a/test/examples/CameraStates/CameraStates.java b/test/examples/CameraStates/CameraStates.java new file mode 100644 index 0000000..ed3e033 --- /dev/null +++ b/test/examples/CameraStates/CameraStates.java @@ -0,0 +1,53 @@ +package examples.CameraStates; + +import peasy.*; +import processing.core.PApplet; + +public class CameraStates extends PApplet { + + // + // '1' save current camera-state + // '2' apply saved camera-state + // + + PeasyCam cam; + + public void settings() { + size(800, 600, P3D); + } + + public void setup() { + cam = new PeasyCam(this, 400); + state = cam.getState(); + } + + public void draw() { + rotateX(-.5f); + rotateY(-.5f); + lights(); + scale(10); + strokeWeight(1 / 10f); + background(0); + fill(220, 255, 0); + box(30); + pushMatrix(); + translate(0, 0, 20); + fill(0, 96, 255); + box(5); + popMatrix(); + } + + CameraState state; + + public void keyReleased() { + if (key == '1') + state = cam.getState(); + if (key == '2') + cam.setState(state, 1000); + } + + public static void main(String args[]) { + PApplet.main(new String[] { CameraStates.class.getName() }); + } + +} \ No newline at end of file diff --git a/test/examples/HeadUpDisplay/HeadUpDisplay.java b/test/examples/HeadUpDisplay/HeadUpDisplay.java new file mode 100644 index 0000000..3e7a981 --- /dev/null +++ b/test/examples/HeadUpDisplay/HeadUpDisplay.java @@ -0,0 +1,49 @@ +package examples.HeadUpDisplay; + +import peasy.PeasyCam; +import processing.core.PApplet; + +public class HeadUpDisplay extends PApplet { + + // + // screen-aligned, orthographic HUD-scope + // + PeasyCam cam; + + public void settings() { + size(800, 600, P3D); + smooth(8); + } + + public void setup() { + cam = new PeasyCam(this, 400); + } + + public void draw() { + rotateX(-.5f); + rotateY(-.5f); + lights(); + scale(10); + strokeWeight(1 / 10f); + background(0); + fill(96, 255, 0); + box(30); + pushMatrix(); + translate(0, 0, 20); + fill(0, 96, 255); + box(5); + popMatrix(); + + cam.beginHUD(); + fill(0, 128); + rect(0, 0, 70, 30); + fill(255); + text("" + nfc(frameRate, 2), 10, 18); + cam.endHUD(); + } + + public static void main(String args[]) { + PApplet.main(new String[] { HeadUpDisplay.class.getName() }); + } + +} \ No newline at end of file diff --git a/test/examples/HelloPeasy/HelloPeasy.java b/test/examples/HelloPeasy/HelloPeasy.java new file mode 100644 index 0000000..1bf88bb --- /dev/null +++ b/test/examples/HelloPeasy/HelloPeasy.java @@ -0,0 +1,38 @@ +package examples.HelloPeasy; + +import peasy.PeasyCam; +import processing.core.PApplet; + +public class HelloPeasy extends PApplet { + + PeasyCam cam; + + public void settings() { + size(800, 600, P3D); + } + + public void setup() { + cam = new PeasyCam(this, 400); + } + + public void draw() { + rotateX(-.5f); + rotateY(-.5f); + lights(); + scale(10); + strokeWeight(1 / 10f); + background(0); + fill(255, 0, 0); + box(30); + pushMatrix(); + translate(0, 0, 20); + fill(0, 0, 255); + box(5); + popMatrix(); + } + + public static void main(String args[]) { + PApplet.main(new String[] { HelloPeasy.class.getName() }); + } + +} \ No newline at end of file diff --git a/test/examples/MultiView_Offscreen/MultiView_Offscreen.java b/test/examples/MultiView_Offscreen/MultiView_Offscreen.java new file mode 100644 index 0000000..c49cc44 --- /dev/null +++ b/test/examples/MultiView_Offscreen/MultiView_Offscreen.java @@ -0,0 +1,123 @@ +package examples.MultiView_Offscreen; + +import peasy.PeasyCam; +import processing.core.PApplet; +import processing.core.PGraphics; + +public class MultiView_Offscreen extends PApplet { + + // + // + // MultiView (advanced version) + // + // N x N Camera Views of the same scene, using N x N separate pgraphics. + // + // + + final int NX = 3; + final int NY = 2; + PeasyCam[] cameras = new PeasyCam[NX * NY]; + + public void settings() { + size(1280, 720, P2D); // 2D + smooth(8); + } + + public void setup() { + + int gap = 5; + + // tiling size + int tilex = floor((width - gap) / NX); + int tiley = floor((height - gap) / NY); + + // viewport offset ... corrected gap due to floor() + int offx = (width - (tilex * NX - gap)) / 2; + int offy = (height - (tiley * NY - gap)) / 2; + + // viewport dimension + int cw = tilex - gap; + int ch = tiley - gap; + + // create new viewport for each camera + for (int y = 0; y < NY; y++) { + for (int x = 0; x < NX; x++) { + int id = y * NX + x; + int cx = offx + x * tilex; + int cy = offy + y * tiley; + PGraphics pg = createGraphics(cw, ch, P3D); + cameras[id] = new PeasyCam(this, pg, 400); + cameras[id].setViewport(cx, cy, cw, ch); // this is the key of this whole demo + } + } + + } + + public void draw() { + // render scene once per camera/viewport + for (int i = 0; i < cameras.length; i++) { + displayScene(cameras[i], i); + } + + background(0); + for (int i = 0; i < cameras.length; i++) { + int[] viewport = cameras[i].getViewport(); + image(cameras[i].getCanvas(), viewport[0], viewport[1], viewport[2], + viewport[3]); + } + } + + public void displayScene(PeasyCam cam, int ID) { + + PGraphics pg = cam.getCanvas(); + + int[] viewport = cam.getViewport(); + int w = viewport[2]; + int h = viewport[3]; + + pg.beginDraw(); + pg.resetMatrix(); + + // modelview - using camera state + cam.feed(); + + // projection - using camera viewport + pg.perspective(60 * PI / 180, w / (float)h, 1, 5000); + + // clear background (scissors makes sure we only clear the region we own) + pg.background(24); + pg.stroke(0); + pg.strokeWeight(0.3f); + + // scene objects + pg.pushMatrix(); + pg.translate(-100, 0, 0); + pg.fill(0, 96, 255); + pg.box(100); + pg.popMatrix(); + + pg.pushMatrix(); + pg.translate(100, 0, 0); + pg.rotateX(PI / 2); + float c = 255 * ID / (float)(cameras.length - 1); + pg.fill(255, 255 - c / 2, 255 - c); + pg.sphere(80); + pg.popMatrix(); + + // screen-aligned 2D HUD + cam.beginHUD(); + pg.rectMode(CORNER); + pg.fill(0); + pg.rect(0, 0, 60, 23); + pg.fill(255, 128, 0); + pg.text("cam " + ID, 10, 15); + cam.endHUD(); + + pg.endDraw(); + } + + public static void main(String args[]) { + PApplet.main(new String[] { MultiView_Offscreen.class.getName() }); + } + +} \ No newline at end of file diff --git a/test/examples/MultiView_Onscreen/MultiView_Onscreen.java b/test/examples/MultiView_Onscreen/MultiView_Onscreen.java new file mode 100644 index 0000000..0390848 --- /dev/null +++ b/test/examples/MultiView_Onscreen/MultiView_Onscreen.java @@ -0,0 +1,145 @@ +package examples.MultiView_Onscreen; + +import peasy.PeasyCam; +import processing.core.PApplet; +import processing.opengl.PGL; +import processing.opengl.PGraphics3D; +import processing.opengl.PJOGL; + +public class MultiView_Onscreen extends PApplet { + + // + // + // MultiView (advanced version) + // + // N x N Camera Views of the same scene, on only one PGraphics. + // + // In this demo only one render-target is used -> the primary PGraphics3D ... this.g + // Each Camera still has its own mouse-handler. + // Only the viewport-position/dimension is used to build the camera state. + // + // For rendering, some OpenGL instructions (viewport, scissors) are used to + // render the scene to its actual camera viewport position/size. + // + // + + final int NX = 3; + final int NY = 2; + PeasyCam[] cameras = new PeasyCam[NX * NY]; + + public void settings() { + size(1280, 720, P3D); // 3D + smooth(8); + } + + public void setup() { + + int gap = 5; + + // tiling size + int tilex = floor((width - gap) / NX); + int tiley = floor((height - gap) / NY); + + // viewport offset ... corrected gap due to floor() + int offx = (width - (tilex * NX - gap)) / 2; + int offy = (height - (tiley * NY - gap)) / 2; + + // viewport dimension + int cw = tilex - gap; + int ch = tiley - gap; + + // create new viewport for each camera + for (int y = 0; y < NY; y++) { + for (int x = 0; x < NX; x++) { + int id = y * NX + x; + int cx = offx + x * tilex; + int cy = offy + y * tiley; + cameras[id] = new PeasyCam(this, 400); + cameras[id].setViewport(cx, cy, cw, ch); // this is the key of this whole demo + } + } + + } + + public void draw() { + // clear background once, for the whole window + setGLGraphicsViewport(0, 0, width, height); + background(0); + + // render scene once per camera/viewport + for (int i = 0; i < cameras.length; i++) { + pushStyle(); + pushMatrix(); + displayScene(cameras[i], i); + popMatrix(); + popStyle(); + } + // setGLGraphicsViewport(0, 0, width, height); + } + + // some OpenGL instructions to set our custom viewport + // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glViewport.xhtml + // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glScissor.xhtml + void setGLGraphicsViewport(int x, int y, int w, int h) { + PGraphics3D pg = (PGraphics3D)this.g; + PJOGL pgl = (PJOGL)pg.beginPGL(); + pg.endPGL(); + + pgl.enable(PGL.SCISSOR_TEST); + pgl.scissor(x, y, w, h); + pgl.viewport(x, y, w, h); + } + + public void displayScene(PeasyCam cam, int ID) { + + int[] viewport = cam.getViewport(); + int w = viewport[2]; + int h = viewport[3]; + int x = viewport[0]; + int y = viewport[1]; + int y_inv = height - y - h; // inverted y-axis + + // scissors-test and viewport transformation + setGLGraphicsViewport(x, y_inv, w, h); + + // modelview - using camera state + cam.feed(); + + // projection - using camera viewport + perspective(60 * PI / 180, w / (float)h, 1, 5000); + + // clear background (scissors makes sure we only clear the region we own) + background(24); + stroke(0); + strokeWeight(1); + + // scene objects + pushMatrix(); + translate(-100, 0, 0); + fill(0, 96, 255); + box(100); + popMatrix(); + + pushMatrix(); + translate(100, 0, 0); + rotateX(PI / 2); + float c = 255 * ID / (float)(cameras.length - 1); + fill(255 - c / 2, 255, 255 - c); + sphere(80); + popMatrix(); + + // screen-aligned 2D HUD + cam.beginHUD(); + rectMode(CORNER); + fill(0); + rect(0, 0, 60, 23); + fill(255, 128, 0); + text("cam " + ID, 10, 15); + cam.endHUD(); + } + + public static void main(String args[]) { + PApplet.main(new String[] { MultiView_Onscreen.class.getName() }); + } + +} \ No newline at end of file diff --git a/test/peasy/test/TestPeasy.java b/test/peasy/test/TestPeasy.java deleted file mode 100755 index 665d031..0000000 --- a/test/peasy/test/TestPeasy.java +++ /dev/null @@ -1,35 +0,0 @@ -package peasy.test; - -import peasy.PeasyCam; -import processing.core.PApplet; - -@SuppressWarnings("serial") -public class TestPeasy extends PApplet { - PeasyCam cam; - - public void setup() { - size(200, 200, P3D); - cam = new PeasyCam(this, 100); - cam.setMinimumDistance(50); - cam.setMaximumDistance(500); - cam.setWheelScale(4.0); - } - - public void draw() { - rotateX(-.5f); - rotateY(-.5f); - background(0); - fill(255, 0, 0); - box(30); - pushMatrix(); - translate(0, 0, 20); - fill(0, 0, 255); - box(5); - popMatrix(); - } - - @Override - public void keyPressed() { - cam.setActive(key == 'a'); - } -} diff --git a/testHUD/test/Peasycam_testHUD.java b/testHUD/test/Peasycam_testHUD.java deleted file mode 100644 index 76d16e6..0000000 --- a/testHUD/test/Peasycam_testHUD.java +++ /dev/null @@ -1,88 +0,0 @@ -package test; - -import peasy.*; -import processing.core.PApplet; - - -public class Peasycam_testHUD extends PApplet { - - - PeasyCam peasycam; - - public void settings() { - size(800, 600, P3D); - smooth(8); - } - - public void setup() { - // surface.setResizable(true); - - // default FoV is 60 - perspective(90 * DEG_TO_RAD, width/(float)height, 1, 5000); - - // camera - peasycam = new PeasyCam(this, 300); - } - - public void draw(){ - - // in case of surface resizing (happens asynchronous) this has no effect in setup - // perspective(90 * DEG_TO_RAD, width/(float)height, 1, 5000); - // peasycam.feed(); - - // 3D scene - ambientLight(128, 128, 128); - pointLight(255, 128, 64, -200, -200, 10); - pointLight(64, 128, 255, +200, +200, 10); - - background(32); - - rectMode(CENTER); - noStroke(); - fill(128); - rect(0, 0, 400, 400); - - strokeWeight(2); - stroke(255, 64, 0); line(0,0,0,100,0,0); - stroke( 32,255, 32); line(0,0,0,0,100,0); - stroke( 0, 64,255); line(0,0,0,0,0,100); - - translate(80,80,80); - noStroke(); - fill(128); - box(50); - - - // screen-aligned 2D HUD - peasycam.beginHUD(); - - int wh = 100; - rectMode(CORNER); - noStroke(); - fill(0xFFFF0000); rect( 0, 0, wh, wh); - fill(0xFF00FF00); rect(width-wh, 0, wh, wh, 30); - fill(0xFF0000FF); rect(width-wh,height-wh, wh, wh); - fill(0xFFFFFFFF); rect( 0,height-wh, wh, wh, 30); - - peasycam.endHUD(); - - - // check if everything got restored - // Note: Depth testing was disabled during the HUD-drawing - translate(0,-80,0); - noStroke(); - fill(128); - box(60); - - - - } - - - - public static void main(String args[]) { - PApplet.main(new String[] { Peasycam_testHUD.class.getName() }); - } - -} -