package org.jzy3d.plot3d.rendering.view;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
// import org.apache.log4j.Logger;
import org.jzy3d.colors.Color;
import org.jzy3d.maths.Array;
import org.jzy3d.maths.Coord3d;
import org.jzy3d.maths.Grid;
import org.jzy3d.maths.PolygonArray;
import org.jzy3d.plot3d.primitives.AbstractDrawable;
import org.jzy3d.plot3d.rendering.compat.GLES2CompatUtils;
import org.jzy3d.plot3d.rendering.view.modes.CameraMode;
import org.jzy3d.plot3d.transform.Transform;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
import com.jogamp.opengl.glu.GLU;
/**
* A {@link Camera} allow to define on the view and target points in a cartesian
* coordinate system.
*
* The {@link Camera} provides the following services:
*
* - allows setting perspective/orthogonal rendering mode through
* {@link CameraMode}.
*
- selects the appropriate clipping planes according to a given target box.
*
- ensure the modelview matrix is always available for GL2 calls
* related to anything else than projection.
*
- methods to convert screen coordinates into 3d coordinates and vice-versa
*
*
* @author Martin Pernollet
*/
public class Camera extends AbstractViewportManager {
// static Logger LOGGER = Logger.getLogger(Camera.class);
/** The polar default view point, i.e. Coord3d(Math.PI/3,Math.PI/5,500). */
public static final Coord3d DEFAULT_VIEW = new Coord3d(Math.PI / 3, Math.PI / 5, 500);
/**
* Defines if camera distance is real, or only squared (squared distance
* avoid computing Math.sqrt() and is thus faster). Second mode requires
* value true
*/
public static final boolean DEFAULT_CAMERA_DISTANCE_MODE = true;
/**
* Set up a Camera looking at target, with a viewpoint standing at
* target+(0,0,100). The top of the camera is set up toward the positive Z
* direction.
*/
public Camera(Coord3d target) {
setTarget(target);
setEye(DEFAULT_VIEW.cartesian().add(target));
setUp(new Coord3d(0, 0, 1));
setViewPort(1, 1, 0, 1);
setRenderingDepth(0.5f, 100000f);
setRenderingSphereRadius(1);
setViewportMode(ViewportMode.RECTANGLE_NO_STRETCH);
}
/** Set the eye's position. */
public void setScale(float scale) {
this.scale = scale;
}
/******************************************************************/
/** Set the eye's position. */
public void setEye(Coord3d eye) {
this.eye = eye;
}
/** Returns the eye's position. */
public Coord3d getEye() {
return eye;
}
/** Set the target point of the camera. */
public void setTarget(Coord3d target) {
this.target = target;
}
/** Returns the target's position that was set at the last call to lookAt(). */
public Coord3d getTarget() {
return target;
}
/** Set the top of the camera. */
public void setUp(Coord3d up) {
this.up = up;
}
/** Returns the top of the camera. */
public Coord3d getUp() {
return this.up;
}
/**
* Returns true if the camera is 'looking up', in other word if the eye's Z
* value is inferior to the target's Z value.
*/
public boolean isTiltUp() {
return eye.z < target.z;
}
/* */
/**
* Set the radius of the sphere that will be contained into the rendered
* view. As a side effect, the "far" clipping plane is modified according to
* the eye-target distance, and the position of the "near" clipping plane.
*/
public void setRenderingSphereRadius(float radius) {
// chekanov
radius= radius/this.scale; // scale; // increase scale
this.radius = radius;
this.near = (float) eye.distance(target) - radius * 2;
this.far = (float) eye.distance(target) + radius * 2;
}
/**
* Return the radius of the sphere that will be contained into the rendered
* view.
*/
public float getRenderingSphereRadius() {
return radius;
}
/**
* Manually set the rendering depth (near and far clipping planes). Note
* that {@link Camera.setRenderingSphereRadius} modified the "far" clipping
* plane.
*/
public void setRenderingDepth(float near, float far) {
this.near = near;
this.far = far;
}
/** Return the position of the "near" clipping plane */
public float getNear() {
return near;
}
/** Return the position of the "far" clipping plane */
public float getFar() {
return far;
}
/** Return true if the given point is on the left of the vector eye->target. */
public boolean side(Coord3d point) {
return 0 < ((point.x - target.x) * (eye.y - target.y) - (point.y - target.y) * (eye.x - target.x));
}
/************************** PROJECT / UNPROJECT ****************************/
/**
* Transform a 2d screen coordinate into a 3d coordinate. The z component of
* the screen coordinate indicates a depth value between the near and far
* clipping plane of the {@link Camera}.
*
* @throws a
* RuntimeException if an error occured while trying to retrieve
* model coordinates
*/
public Coord3d screenToModel(GL gl, GLU glu, Coord3d screen) {
int viewport[] = getViewPortAsInt(gl);
float modelView[] = getModelViewAsFloat(gl);
float projection[] = getProjectionAsFloat(gl);
float worldcoord[] = new float[3];// wx, wy, wz;// returned xyz coords
//float screeny = viewport[3] - (int)screen.y - 1;
//viewport[3] = 600;
//viewport[4] = 600;
//LOGGER.info("viewport : " + Arrays.toString(viewport));
/*LOGGER.info("modelView : " + Arrays.toString(modelView));
LOGGER.info("projection : " + Arrays.toString(projection));
LOGGER.info("worldcoord : " + Arrays.toString(worldcoord));*/
boolean s = glu.gluUnProject(screen.x, screen.y, screen.z, modelView, 0, projection, 0, viewport, 0, worldcoord, 0);
if (!s)
failedProjection("Could not retrieve screen coordinates in model.");
return new Coord3d(worldcoord[0], worldcoord[1], worldcoord[2]);
}
/**
* Transform a 3d point coordinate into its screen position.
*
* @throws a
* RuntimeException if an error occured while trying to retrieve
* model coordinates
*/
public Coord3d modelToScreen(GL gl, GLU glu, Coord3d point) {
int viewport[] = getViewPortAsInt(gl);
float screencoord[] = new float[3];// wx, wy, wz;// returned xyz coords
if (!glu.gluProject(point.x, point.y, point.z, getModelViewAsFloat(gl), 0, getProjectionAsFloat(gl), 0, viewport, 0, screencoord, 0))
failedProjection("Could not retrieve model coordinates in screen for " + point);
return new Coord3d(screencoord[0], screencoord[1], screencoord[2]);
}
public Coord3d[] modelToScreen(GL gl, GLU glu, Coord3d[] points) {
int viewport[] = getViewPortAsInt(gl);
float screencoord[] = new float[3];
Coord3d[] projection = new Coord3d[points.length];
for (int i = 0; i < points.length; i++) {
if (!glu.gluProject(points[i].x, points[i].y, points[i].z, getModelViewAsFloat(gl), 0, getProjectionAsFloat(gl), 0, viewport, 0, screencoord, 0))
failedProjection("Could not retrieve model coordinates in screen for " + points[i]);
projection[i] = new Coord3d(screencoord[0], screencoord[1], screencoord[2]);
}
return projection;
}
public Coord3d[][] modelToScreen(GL gl, GLU glu, Coord3d[][] points) {
int viewport[] = getViewPortAsInt(gl);
float screencoord[] = new float[3];
Coord3d[][] projection = new Coord3d[points.length][points[0].length];
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
if (!glu.gluProject(points[i][j].x, points[i][j].y, points[i][j].z, getModelViewAsFloat(gl), 0, getProjectionAsFloat(gl), 0, viewport, 0, screencoord, 0))
failedProjection("Could not retrieve model coordinates in screen for " + points[i][j]);
projection[i][j] = new Coord3d(screencoord[0], screencoord[1], screencoord[2]);
}
}
return projection;
}
public List modelToScreen(GL gl, GLU glu, List points) {
int viewport[] = getViewPortAsInt(gl);
float screencoord[] = new float[3];
List projection = new Vector();
for (Coord3d point : points) {
if (!glu.gluProject(point.x, point.y, point.z, getModelViewAsFloat(gl), 0, getProjectionAsFloat(gl), 0, viewport, 0, screencoord, 0))
failedProjection("Could not retrieve model coordinates in screen for " + point);
projection.add(new Coord3d(screencoord[0], screencoord[1], screencoord[2]));
}
return projection;
}
public ArrayList> modelToScreen(GL gl, GLU glu, ArrayList> polygons) {
int viewport[] = getViewPortAsInt(gl);
float screencoord[] = new float[3];
ArrayList> projections = new ArrayList>(polygons.size());
for (ArrayList polygon : polygons) {
ArrayList projection = new ArrayList(polygon.size());
for (Coord3d point : polygon) {
if (!glu.gluProject(point.x, point.y, point.z, getModelViewAsFloat(gl), 0, getProjectionAsFloat(gl), 0, viewport, 0, screencoord, 0))
failedProjection("Could not retrieve model coordinates in screen for " + point);
projection.add(new Coord3d(screencoord[0], screencoord[1], screencoord[2]));
}
projections.add(projection);
}
return projections;
}
public PolygonArray modelToScreen(GL gl, GLU glu, PolygonArray polygon) {
int viewport[] = getViewPortAsInt(gl);
float screencoord[] = new float[3];
int len = polygon.length();
float[] x = new float[len];
float[] y = new float[len];
float[] z = new float[len];
for (int i = 0; i < len; i++) {
if (!glu.gluProject(polygon.x[i], polygon.y[i], polygon.z[i], getModelViewAsFloat(gl), 0, getProjectionAsFloat(gl), 0, viewport, 0, screencoord, 0))
failedProjection("Could not retrieve model coordinates in screen for point " + i);
x[i] = screencoord[0];
y[i] = screencoord[1];
z[i] = screencoord[2];
}
return new PolygonArray(x, y, z);
}
public Grid modelToScreen(GL gl, GLU glu, Grid grid) {
int viewport[] = getViewPortAsInt(gl);
double screencoord[] = new double[3];
int xlen = grid.getX().length;
int ylen = grid.getY().length;
float[] x = new float[xlen];
float[] y = new float[ylen];
float[][] z = new float[xlen][ylen];
for (int i = 0; i < xlen; i++) {
for (int j = 0; j < ylen; j++) {
// if( ! glu.gluProject( grid.getX()[i], grid.getY()[i],
// grid.getZ()[i][j], getModelViewAsFloat(gl), 0,
// getProjectionAsFloat(gl), 0, viewport, 0, screencoord, 0 ) )
failedProjection("Could not retrieve model coordinates in screen for point " + i);
x[i] = (float) screencoord[0];
y[j] = (float) screencoord[1]; // STUPID :)
z[i][j] = (float) screencoord[2];
}
}
return new Grid(x, y, z);
}
public PolygonArray[][] modelToScreen(GL gl, GLU glu, PolygonArray[][] polygons) {
int viewport[] = getViewPortAsInt(gl);
float screencoord[] = new float[3];
PolygonArray[][] projections = new PolygonArray[polygons.length][polygons[0].length];
for (int i = 0; i < polygons.length; i++) {
for (int j = 0; j < polygons[i].length; j++) {
PolygonArray polygon = polygons[i][j];
int len = polygon.length();
float[] x = new float[len];
float[] y = new float[len];
float[] z = new float[len];
for (int k = 0; k < len; k++) {
if (!glu.gluProject(polygon.x[k], polygon.y[k], polygon.z[k], getModelViewAsFloat(gl), 0, getProjectionAsFloat(gl), 0, viewport, 0, screencoord, 0))
failedProjection("Could not retrieve model coordinates in screen for point " + k);
x[k] = screencoord[0];
y[k] = screencoord[1];
z[k] = screencoord[2];
}
projections[i][j] = new PolygonArray(x, y, z);
}
}
return projections;
}
protected void failedProjection(String message) {
if (failOnException)
throw new RuntimeException(message);
else
System.err.println(message);
// Logger.getLogger(Camera.class).warn(message);
}
boolean failOnException = false;
/*******************************************************************/
protected int[] getViewPortAsInt(GL gl) {
int viewport[] = new int[4];
gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
return viewport;
}
protected double[] getProjectionAsDouble(GL gl) {
// Consider using single precision (=float ?)
double projection[] = new double[16];
if (gl.isGL2()) {
gl.getGL2().glGetDoublev(GLMatrixFunc.GL_PROJECTION_MATRIX, projection, 0);
} else {
GLES2CompatUtils.glGetDoublev(GLMatrixFunc.GL_PROJECTION_MATRIX, projection, 0);
}
return projection;
}
protected float[] getProjectionAsFloat(GL gl) {
float projection[] = new float[16];
gl.glGetFloatv(GLMatrixFunc.GL_PROJECTION_MATRIX, projection, 0);
return projection;
}
protected double[] getModelViewAsDouble(GL gl) {
// Consider using single precision (=float ?)
double modelview[] = new double[16];
if (gl.isGL2()) {
gl.getGL2().glGetDoublev(GLMatrixFunc.GL_MODELVIEW_MATRIX, modelview, 0);
} else {
GLES2CompatUtils.glGetDoublev(GLMatrixFunc.GL_MODELVIEW_MATRIX, modelview, 0);
}
return modelview;
}
protected float[] getModelViewAsFloat(GL gl) {
float modelview[] = new float[16];
gl.glGetFloatv(GLMatrixFunc.GL_MODELVIEW_MATRIX, modelview, 0);
return modelview;
}
public void show(GL gl, Transform transform, Coord3d scaling) {
if (transform != null)
transform.execute(gl);
Coord3d eye = getEye().mul(scaling);
if (gl.isGL2()) {
gl.getGL2().glBegin(GL.GL_POINTS);
gl.getGL2().glPointSize(camWidth);
gl.getGL2().glColor4f(camColor.r, camColor.g, camColor.b, camColor.a);
gl.getGL2().glVertex3f(eye.x, eye.y, eye.z);
gl.getGL2().glEnd();
} else {
gl.getGL2().glBegin(GL.GL_POINTS);
gl.getGL2().glPointSize(camWidth);
gl.getGL2().glColor4f(camColor.r, camColor.g, camColor.b, camColor.a);
gl.getGL2().glVertex3f(eye.x, eye.y, eye.z);
gl.getGL2().glEnd();
}
}
Color camColor = Color.BLACK;
int camWidth = 3;
/* */
/**
* Sets the projection and the mapping of the 3d model to 2d screen. The
* projection must be either Camera.PERSPECTIVE or Camera.ORTHOGONAL.
* shoot() finally calls the GL function glLookAt, according to the stored
* eye, target, up and scale values.
* Note that the Camera set by itselft the MatrixMode to model view at the
* end of a shoot().
*
* @param gl
* GL2 context.
* @param glu
* GLU context.
* @param projection
* the projection mode.
* @throws a
* Runtime Exception if the projection mode is neither
* Camera.PERSPECTIVE nor Camera.ORTHOGONAL.
*/
public void shoot(GL gl, GLU glu, CameraMode projection) {
shoot(gl, glu, projection, false);
}
public void shoot(GL gl, GLU glu, CameraMode projection, boolean doPushMatrixBeforeShooting) {
if (gl.isGL2()) {
gl.getGL2().glMatrixMode(GLMatrixFunc.GL_PROJECTION);
if (doPushMatrixBeforeShooting)
gl.getGL2().glPushMatrix();
gl.getGL2().glLoadIdentity();
} else {
GLES2CompatUtils.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
if (doPushMatrixBeforeShooting)
GLES2CompatUtils.glPushMatrix();
GLES2CompatUtils.glLoadIdentity();
}
doShoot(gl, glu, projection);
}
public void doShoot(GL gl, GLU glu, CameraMode projection) {
// Set viewport
ViewportConfiguration viewport = applyViewport(gl, glu);
// Set perspective
if (projection == CameraMode.PERSPECTIVE) {
boolean stretchToFill = ViewportMode.STRETCH_TO_FILL.equals(viewport.getMode());
double fov = computeFieldOfView(radius * 2, eye.distance(target));
float aspect = stretchToFill ? ((float) screenWidth) / ((float) screenHeight) : 1;
float nearCorrected = near<=0?0.000000000000000000000000000000000000001f:near;
glu.gluPerspective(fov, aspect, nearCorrected, far);
} else if (projection == CameraMode.ORTHOGONAL) {
if (gl.isGL2()) {
projectionOrthoGL2(gl, viewport);
} else {
projectionOrthoGLES2(viewport);
}
} else
throw new RuntimeException("Camera.shoot(): unknown projection mode '" + projection + "'");
// Set camera position
glu.gluLookAt(eye.x, eye.y, eye.z, target.x, target.y, target.z, up.x, up.y, up.z);
}
private void projectionOrthoGLES2(ViewportConfiguration viewport) {
if (ViewportMode.STRETCH_TO_FILL.equals(viewport.getMode()))
GLES2CompatUtils.glOrtho(-radius, +radius, -radius, +radius, near, far);
else if (ViewportMode.RECTANGLE_NO_STRETCH.equals(viewport.getMode()))
GLES2CompatUtils.glOrtho(-radius * viewport.ratio(), +radius * viewport.ratio(), -radius, +radius, near, far);
else if (ViewportMode.SQUARE.equals(viewport.getMode()))
GLES2CompatUtils.glOrtho(-radius, +radius, -radius, +radius, near, far);
}
private void projectionOrthoGL2(GL gl, ViewportConfiguration viewport) {
if (ViewportMode.STRETCH_TO_FILL.equals(viewport.getMode()))
gl.getGL2().glOrtho(-radius, +radius, -radius, +radius, near, far);
else if (ViewportMode.RECTANGLE_NO_STRETCH.equals(viewport.getMode()))
gl.getGL2().glOrtho(-radius * viewport.ratio(), +radius * viewport.ratio(), -radius, +radius, near, far);
else if (ViewportMode.SQUARE.equals(viewport.getMode()))
gl.getGL2().glOrtho(-radius, +radius, -radius, +radius, near, far);
}
/**
* Compute the field of View, in order to occupy the entire screen in
* PERSPECTIVE mode.
*/
protected double computeFieldOfView(double size, double distance) {
double radtheta;
double degtheta;
radtheta = 2.0 * Math.atan2(size / 2.0, distance);
degtheta = (180.0 * radtheta) / Math.PI;
return degtheta;
}
/**
* Return the distance between the camera eye and the given drawable's
* barycenter.
*/
public double getDistance(AbstractDrawable drawable) {
if (useSquaredDistance)
return drawable.getBarycentre().distanceSq(getEye());
else
return drawable.getBarycentre().distance(getEye());
}
/**
* Apply scaling before computing distance between the camera eye and the
* given drawable's barycenter.
*/
public double getDistance(AbstractDrawable drawable, Coord3d viewScale) {
if (useSquaredDistance)
return drawable.getBarycentre().distanceSq(getEye().div(viewScale));
else
return drawable.getBarycentre().distance(getEye().div(viewScale));
}
/**
* Return the distance between the camera eye and the given coordinate.
*/
public double getDistance(Coord3d coord) {
if (useSquaredDistance)
return coord.distanceSq(getEye());
else
return coord.distance(getEye());
}
/**
* Apply scaling before computing distance between the camera eye and the
* given coordinate.
*/
public double getDistance(Coord3d coord, Coord3d viewScale) {
if (useSquaredDistance)
return coord.distanceSq(getEye().div(viewScale));
else
return coord.distance(getEye().div(viewScale));
}
public boolean isUseSquaredDistance() {
return useSquaredDistance;
}
/**
* Defines what getDistance(...) will return, either:
*
* - Squared distance (faster to compute since no Math.sqrt(...) at the
* end)
*
- Real distance
*
*
* Default value is set to true, meaning it use the faster squared distance.
*/
public void setUseSquaredDistance(boolean useSquaredDistance) {
this.useSquaredDistance = useSquaredDistance;
}
/* */
/**
* Print out in console information concerning the camera.
*/
@Override
public String toString() {
return toString(eye, target, up);
}
protected String toString(Coord3d eye, Coord3d target, Coord3d up) {
String output = "(Camera)";
output += (" lookFrom = {" + eye.x + ", " + eye.y + ", " + eye.z + "}");
output += (" lookTo = {" + target.x + ", " + target.y + ", " + target.z + "}");
output += (" topToward = {" + up.x + ", " + up.y + ", " + up.z + "}");
return output;
}
/* */
protected Coord3d eye;
protected Coord3d target;
protected Coord3d up;
protected float radius;
protected float near;
protected float far;
protected float scale=1.0f;
protected boolean useSquaredDistance = DEFAULT_CAMERA_DISTANCE_MODE;
}
Ads help maintain this website.