2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
7 package gov
.nasa
.worldwind
.geom
;
9 import gov
.nasa
.worldwind
.*;
11 import java
.util
.logging
.Level
;
13 //todo: check javadoc accuracy,
16 * Instances of <code>Frustum</code> are immutable. </p>
19 * @version $Id: Frustum.java 1994 2007-06-11 16:33:33Z dcollins $
23 private final Plane left
;
24 private final Plane right
;
25 private final Plane bottom
;
26 private final Plane top
;
27 private final Plane near
;
28 private final Plane far
;
31 * Create a default frustum with six <code>Plane</code>s. This defines a box of dimension (2, 2, 2) centered at the
37 new Plane(1, 0, 0, 1), // Left
38 new Plane(-1, 0, 0, 1), // Right
39 new Plane(0, 1, 0, 1), // Bottom
40 new Plane(0, -1, 0, 1), // Top
41 new Plane(0, 0, 1, 1), // Near
42 new Plane(0, 0, -1, 1)); // Far
46 * Create a frustum from six <code>Plane</code>s, which define its boundaries. Does not except null arguments.
48 * @param near the near plane
49 * @param far the far plane
50 * @param left the left side of the view frustum
51 * @param right the right side of the view frustm
52 * @param top the top of the view frustum
53 * @param bottom the bottom of the view frustum
54 * @throws IllegalArgumentException if any argument is null
56 public Frustum(Plane left
, Plane right
, Plane bottom
, Plane top
, Plane near
, Plane far
)
58 if (left
== null || right
== null || bottom
== null || top
== null || near
== null || far
== null)
60 String message
= WorldWind
.retrieveErrMsg("nullValue.PlaneIsNull");
61 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, message
);
62 throw new IllegalArgumentException(message
);
72 public boolean equals(Object obj
)
76 if (obj
== null || getClass() != obj
.getClass())
79 Frustum that
= (Frustum
) obj
;
80 return this.left
.equals(that
.left
)
81 && this.right
.equals(that
.right
)
82 && this.bottom
.equals(that
.bottom
)
83 && this.top
.equals(that
.top
)
84 && this.near
.equals(that
.near
)
85 && this.far
.equals(that
.far
);
88 public final Plane
getLeft()
93 public final Plane
getRight()
98 public final Plane
getBottom()
103 public final Plane
getTop()
108 public final Plane
getNear()
113 public final Plane
getFar()
118 public int hashCode()
121 result
= this.left
.hashCode();
122 result
= 31 * result
+ this.right
.hashCode();
123 result
= 19 * result
+ this.bottom
.hashCode();
124 result
= 23 * result
+ this.top
.hashCode();
125 result
= 17 * result
+ this.near
.hashCode();
126 result
= 19 * result
+ this.far
.hashCode();
131 public String
toString()
133 StringBuilder sb
= new StringBuilder();
135 sb
.append("left=").append(this.near
);
136 sb
.append(", right=").append(this.near
);
137 sb
.append(", bottom=").append(this.near
);
138 sb
.append(", top=").append(this.near
);
139 sb
.append(", near=").append(this.near
);
140 sb
.append(", far=").append(this.near
);
142 return sb
.toString();
145 // ============== Factory Functions ======================= //
146 // ============== Factory Functions ======================= //
147 // ============== Factory Functions ======================= //
149 public static Frustum
fromProjectionMatrix(Matrix projectionMatrix
)
151 Matrix m
= projectionMatrix
;
154 String message
= WorldWind
.retrieveErrMsg("nullValue.MatrixIsNull");
155 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, message
);
156 throw new IllegalArgumentException(message
);
159 // Extract the left clipping plane from the projectionMatrix-matrix.
160 double leftMag
= Math
.sqrt((m
.m41
+ m
.m11
) * (m
.m41
+ m
.m11
) + (m
.m42
+ m
.m12
) * (m
.m42
+ m
.m12
)
161 + (m
.m43
+ m
.m13
) * (m
.m43
+ m
.m13
));
162 Plane leftPlane
= new Plane((m
.m41
+ m
.m11
) / leftMag
, (m
.m42
+ m
.m12
) / leftMag
, (m
.m43
+ m
.m13
) / leftMag
,
164 // Extract the right clipping plane from the projectionMatrix-matrix.
165 double rightMag
= Math
.sqrt((m
.m41
- m
.m11
) * (m
.m41
- m
.m11
) + (m
.m42
- m
.m12
) * (m
.m42
- m
.m12
)
166 + (m
.m43
- m
.m13
) * (m
.m43
- m
.m13
));
167 Plane rightPlane
= new Plane((m
.m41
- m
.m11
) / rightMag
, (m
.m42
- m
.m12
) / rightMag
, (m
.m43
- m
.m13
) / rightMag
,
169 // Extract the bottom clipping plane from the projectionMatrix-matrix.
170 double bottomMag
= Math
.sqrt((m
.m41
+ m
.m21
) * (m
.m41
+ m
.m21
) + (m
.m42
+ m
.m22
) * (m
.m42
+ m
.m22
)
171 + (m
.m43
+ m
.m23
) * (m
.m43
+ m
.m23
));
172 Plane bottomPlane
= new Plane((m
.m41
+ m
.m21
) / bottomMag
, (m
.m42
+ m
.m22
) / bottomMag
,
173 (m
.m43
+ m
.m23
) / bottomMag
,
175 // Extract the top clipping plane from the projectionMatrix-matrix.
176 double topMag
= Math
.sqrt((m
.m41
- m
.m21
) * (m
.m41
- m
.m21
) + (m
.m42
- m
.m22
) * (m
.m42
- m
.m22
)
177 + (m
.m43
- m
.m23
) * (m
.m43
- m
.m23
));
178 Plane topPlane
= new Plane((m
.m41
- m
.m21
) / topMag
, (m
.m42
- m
.m22
) / topMag
, (m
.m43
- m
.m23
) / topMag
,
180 // Extract the near clipping plane from the projectionMatrix-matrix.
181 double nearMag
= Math
.sqrt((m
.m41
+ m
.m31
) * (m
.m41
+ m
.m31
) + (m
.m42
+ m
.m32
) * (m
.m42
+ m
.m32
)
182 + (m
.m43
+ m
.m33
) * (m
.m43
+ m
.m33
));
183 Plane nearPlane
= new Plane((m
.m41
+ m
.m31
) / nearMag
, (m
.m42
+ m
.m32
) / nearMag
, (m
.m43
+ m
.m33
) / nearMag
,
185 // Extract the far clipping plane from the projectionMatrix-matrix.
186 double farMag
= Math
.sqrt((m
.m41
- m
.m31
) * (m
.m41
- m
.m31
) + (m
.m42
- m
.m32
) * (m
.m42
- m
.m32
)
187 + (m
.m43
- m
.m33
) * (m
.m43
- m
.m33
));
188 Plane farPlane
= new Plane((m
.m41
- m
.m31
) / farMag
, (m
.m42
- m
.m32
) / farMag
, (m
.m43
- m
.m33
) / farMag
,
190 return new Frustum(leftPlane
, rightPlane
, bottomPlane
, topPlane
, nearPlane
, farPlane
);
194 * Creates a <code>Frustum</code> from a horizontal field-of-view, viewport aspect ratio and distance to near and
195 * far depth clipping planes. The near plane must be closer than the far plane, and both planes must be a positive
198 * @param horizontalFieldOfView horizontal field-of-view angle in the range (0, 180)
199 * @param viewportWidth the width of the viewport in screen pixels
200 * @param viewportHeight the height of the viewport in screen pixels
201 * @param near distance to the near depth clipping plane
202 * @param far distance to far depth clipping plane
203 * @throws IllegalArgumentException if fov is not in the range (0, 180), if either near or far are negative, or near
204 * is greater than or equal to far
206 public static Frustum
fromPerspective(Angle horizontalFieldOfView
, int viewportWidth
, int viewportHeight
,
207 double near
, double far
)
209 if (horizontalFieldOfView
== null)
211 String message
= WorldWind
.retrieveErrMsg("geom.ViewFrustum.FieldOfViewIsNull");
212 WorldWind
.logger().log(Level
.FINE
, message
);
213 throw new IllegalArgumentException(message
);
215 double fov
= horizontalFieldOfView
.getDegrees();
216 double farMinusNear
= far
- near
;
217 String message
= null;
218 if (fov
<= 0 || fov
> 180)
219 message
= WorldWind
.retrieveErrMsg("geom.ViewFrustum.FieldOfViewOutOfRange");
220 if (near
<= 0 || farMinusNear
<= 0)
221 message
= WorldWind
.retrieveErrMsg("geom.ViewFrusutm.ClippingDistanceOutOfRange");
224 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, message
);
225 throw new IllegalArgumentException(message
);
228 double focalLength
= 1d
/ horizontalFieldOfView
.tanHalfAngle();
229 double aspect
= viewportHeight
/ (double) viewportWidth
;
230 double lrLen
= Math
.sqrt(focalLength
* focalLength
+ 1);
231 double btLen
= Math
.sqrt(focalLength
* focalLength
+ aspect
* aspect
);
232 Plane leftPlane
= new Plane(focalLength
/ lrLen
, 0d
, 0d
- 1d
/ lrLen
, 0);
233 Plane rightPlane
= new Plane(0d
- focalLength
/ lrLen
, 0d
, 0d
- 1d
/ lrLen
, 0d
);
234 Plane bottomPlane
= new Plane(0d
, focalLength
/ btLen
, 0d
- aspect
/ btLen
, 0d
);
235 Plane topPlane
= new Plane(0d
, 0d
- focalLength
/ btLen
, 0d
- aspect
/ btLen
, 0d
);
236 Plane nearPlane
= new Plane(0d
, 0d
, 0d
- 1d
, 0d
- near
);
237 Plane farPlane
= new Plane(0d
, 0d
, 1d
, far
);
238 return new Frustum(leftPlane
, rightPlane
, bottomPlane
, topPlane
, nearPlane
, farPlane
);
242 * Creates a <code>Frustum</code> from three sets of parallel clipping planes (a parallel projectionMatrix). In this
243 * case, the near and far depth clipping planes may be a negative distance away.
245 * @param near distance to the near depth clipping plane
246 * @param far distance to far depth clipping plane
247 * @param width horizontal dimension of the near clipping plane
248 * @param height vertical dimension of the near clipping plane
249 * @throws IllegalArgumentException if the difference of any plane set (lright - left, top - bottom, far - near) is
250 * less than or equal to zero.
252 public static Frustum
fromPerspective(double width
, double height
, double near
, double far
)
254 double farMinusNear
= far
- near
;
255 if (farMinusNear
<= 0.0 || width
<= 0.0 || height
<= 0.0)
257 String message
= WorldWind
.retrieveErrMsg("geom.ViewFrusutm.ClippingDistanceOutOfRange");
258 WorldWind
.logger().log(Level
.FINE
, message
);
259 throw new IllegalArgumentException(message
);
262 double width_over_2
= width
/ 2.0;
263 double height_over_2
= height
/ 2.0;
264 Plane leftPlane
= new Plane(1.0, 0.0, 0.0, width_over_2
);
265 Plane rightPlane
= new Plane(-1.0, 0.0, 0.0, width_over_2
);
266 Plane bottomPlane
= new Plane(0.0, 1.0, 0.0, height_over_2
);
267 Plane topPlane
= new Plane(0.0, -1.0, 0.0, height_over_2
);
268 Plane nearPlane
= new Plane(0.0, 0.0, -1.0, (near
< 0.0) ? near
: -near
);
269 Plane farPlane
= new Plane(0.0, 0.0, 1.0, (far
< 0.0) ?
-far
: far
);
270 return new Frustum(leftPlane
, rightPlane
, bottomPlane
, topPlane
, nearPlane
, farPlane
);
273 // ============== Intersection Functions ======================= //
274 // ============== Intersection Functions ======================= //
275 // ============== Intersection Functions ======================= //
280 * @throws IllegalArgumentException if <code>extent</code> is null
282 public final boolean intersects(Extent extent
)
286 String msg
= WorldWind
.retrieveErrMsg("nullValue.ExtentIsNull");
287 WorldWind
.logger().log(Level
.FINE
, msg
);
288 throw new IllegalArgumentException(msg
);
291 // See if the extent's bounding sphere is within or intersects the frustum.
292 Vec4 c
= extent
.getCenter();
293 double nr
= -extent
.getRadius();
295 if (this.far
.dot(c
) <= nr
)
297 if (this.left
.dot(c
) <= nr
)
299 if (this.right
.dot(c
) <= nr
)
301 if (this.top
.dot(c
) <= nr
)
303 if (this.bottom
.dot(c
) <= nr
)
305 //noinspection RedundantIfStatement
306 if (this.near
.dot(c
) <= nr
)
315 * @throws IllegalArgumentException if <code>point</code> is null
317 public final boolean contains(Vec4 point
)
321 String msg
= WorldWind
.retrieveErrMsg("nullValue.PointIsNull");
322 WorldWind
.logger().log(Level
.FINE
, msg
);
323 throw new IllegalArgumentException(msg
);
326 if (this.far
.dot(point
) < 0)
328 if (this.left
.dot(point
) < 0)
330 if (this.right
.dot(point
) < 0)
332 if (this.top
.dot(point
) < 0)
334 if (this.bottom
.dot(point
) < 0)
336 //noinspection RedundantIfStatement
337 if (this.near
.dot(point
) < 0)
343 // ============== Geometric Functions ======================= //
344 // ============== Geometric Functions ======================= //
345 // ============== Geometric Functions ======================= //
350 * @throws IllegalArgumentException if <code>matrix</code> is null
352 public Frustum
transformBy(Matrix matrix
)
354 ensureNonNull(matrix
);
355 Plane left
= new Plane(this.left
.getVector().transformBy4(matrix
));
356 Plane right
= new Plane(this.right
.getVector().transformBy4(matrix
));
357 Plane bottom
= new Plane(this.bottom
.getVector().transformBy4(matrix
));
358 Plane top
= new Plane(this.top
.getVector().transformBy4(matrix
));
359 Plane near
= new Plane(this.near
.getVector().transformBy4(matrix
));
360 Plane far
= new Plane(this.far
.getVector().transformBy4(matrix
));
361 return new Frustum(left
, right
, bottom
, top
, near
, far
);
364 // ============== Helper Functions ======================= //
365 // ============== Helper Functions ======================= //
366 // ============== Helper Functions ======================= //
368 private static void ensureNonNull(Matrix matrix
)
372 String msg
= WorldWind
.retrieveErrMsg("nullValue.MatrixIsNull");
373 WorldWind
.logger().log(Level
.FINE
, msg
);
374 throw new IllegalArgumentException(msg
);