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
.util
.Logging
;
11 //todo: check javadoc accuracy,
14 * Instances of <code>Frustum</code> are immutable. </p>
17 * @version $Id: Frustum.java 2471 2007-07-31 21:50:57Z tgaskins $
21 private final Plane left
;
22 private final Plane right
;
23 private final Plane bottom
;
24 private final Plane top
;
25 private final Plane near
;
26 private final Plane far
;
29 * Create a default frustum with six <code>Plane</code>s. This defines a box of dimension (2, 2, 2) centered at the
35 new Plane(1, 0, 0, 1), // Left
36 new Plane(-1, 0, 0, 1), // Right
37 new Plane(0, 1, 0, 1), // Bottom
38 new Plane(0, -1, 0, 1), // Top
39 new Plane(0, 0, 1, 1), // Near
40 new Plane(0, 0, -1, 1)); // Far
44 * Create a frustum from six <code>Plane</code>s, which define its boundaries. Does not except null arguments.
46 * @param near the near plane
47 * @param far the far plane
48 * @param left the left side of the view frustum
49 * @param right the right side of the view frustm
50 * @param top the top of the view frustum
51 * @param bottom the bottom of the view frustum
52 * @throws IllegalArgumentException if any argument is null
54 public Frustum(Plane left
, Plane right
, Plane bottom
, Plane top
, Plane near
, Plane far
)
56 if (left
== null || right
== null || bottom
== null || top
== null || near
== null || far
== null)
58 String message
= Logging
.getMessage("nullValue.PlaneIsNull");
59 Logging
.logger().severe(message
);
60 throw new IllegalArgumentException(message
);
70 public boolean equals(Object obj
)
74 if (obj
== null || getClass() != obj
.getClass())
77 Frustum that
= (Frustum
) obj
;
78 return this.left
.equals(that
.left
)
79 && this.right
.equals(that
.right
)
80 && this.bottom
.equals(that
.bottom
)
81 && this.top
.equals(that
.top
)
82 && this.near
.equals(that
.near
)
83 && this.far
.equals(that
.far
);
86 public final Plane
getLeft()
91 public final Plane
getRight()
96 public final Plane
getBottom()
101 public final Plane
getTop()
106 public final Plane
getNear()
111 public final Plane
getFar()
116 public int hashCode()
119 result
= this.left
.hashCode();
120 result
= 31 * result
+ this.right
.hashCode();
121 result
= 19 * result
+ this.bottom
.hashCode();
122 result
= 23 * result
+ this.top
.hashCode();
123 result
= 17 * result
+ this.near
.hashCode();
124 result
= 19 * result
+ this.far
.hashCode();
129 public String
toString()
131 StringBuilder sb
= new StringBuilder();
133 sb
.append("left=").append(this.near
);
134 sb
.append(", right=").append(this.near
);
135 sb
.append(", bottom=").append(this.near
);
136 sb
.append(", top=").append(this.near
);
137 sb
.append(", near=").append(this.near
);
138 sb
.append(", far=").append(this.near
);
140 return sb
.toString();
143 // ============== Factory Functions ======================= //
144 // ============== Factory Functions ======================= //
145 // ============== Factory Functions ======================= //
147 public static Frustum
fromProjectionMatrix(Matrix projectionMatrix
)
149 //noinspection UnnecessaryLocalVariable
150 Matrix m
= projectionMatrix
;
153 String message
= Logging
.getMessage("nullValue.MatrixIsNull");
154 Logging
.logger().severe(message
);
155 throw new IllegalArgumentException(message
);
158 // Extract the left clipping plane from the projectionMatrix-matrix.
159 double leftMag
= Math
.sqrt((m
.m41
+ m
.m11
) * (m
.m41
+ m
.m11
) + (m
.m42
+ m
.m12
) * (m
.m42
+ m
.m12
)
160 + (m
.m43
+ m
.m13
) * (m
.m43
+ m
.m13
));
161 Plane leftPlane
= new Plane((m
.m41
+ m
.m11
) / leftMag
, (m
.m42
+ m
.m12
) / leftMag
, (m
.m43
+ m
.m13
) / leftMag
,
163 // Extract the right clipping plane from the projectionMatrix-matrix.
164 double rightMag
= Math
.sqrt((m
.m41
- m
.m11
) * (m
.m41
- m
.m11
) + (m
.m42
- m
.m12
) * (m
.m42
- m
.m12
)
165 + (m
.m43
- m
.m13
) * (m
.m43
- m
.m13
));
166 Plane rightPlane
= new Plane((m
.m41
- m
.m11
) / rightMag
, (m
.m42
- m
.m12
) / rightMag
, (m
.m43
- m
.m13
) / rightMag
,
168 // Extract the bottom clipping plane from the projectionMatrix-matrix.
169 double bottomMag
= Math
.sqrt((m
.m41
+ m
.m21
) * (m
.m41
+ m
.m21
) + (m
.m42
+ m
.m22
) * (m
.m42
+ m
.m22
)
170 + (m
.m43
+ m
.m23
) * (m
.m43
+ m
.m23
));
171 Plane bottomPlane
= new Plane((m
.m41
+ m
.m21
) / bottomMag
, (m
.m42
+ m
.m22
) / bottomMag
,
172 (m
.m43
+ m
.m23
) / bottomMag
,
174 // Extract the top clipping plane from the projectionMatrix-matrix.
175 double topMag
= Math
.sqrt((m
.m41
- m
.m21
) * (m
.m41
- m
.m21
) + (m
.m42
- m
.m22
) * (m
.m42
- m
.m22
)
176 + (m
.m43
- m
.m23
) * (m
.m43
- m
.m23
));
177 Plane topPlane
= new Plane((m
.m41
- m
.m21
) / topMag
, (m
.m42
- m
.m22
) / topMag
, (m
.m43
- m
.m23
) / topMag
,
179 // Extract the near clipping plane from the projectionMatrix-matrix.
180 double nearMag
= Math
.sqrt((m
.m41
+ m
.m31
) * (m
.m41
+ m
.m31
) + (m
.m42
+ m
.m32
) * (m
.m42
+ m
.m32
)
181 + (m
.m43
+ m
.m33
) * (m
.m43
+ m
.m33
));
182 Plane nearPlane
= new Plane((m
.m41
+ m
.m31
) / nearMag
, (m
.m42
+ m
.m32
) / nearMag
, (m
.m43
+ m
.m33
) / nearMag
,
184 // Extract the far clipping plane from the projectionMatrix-matrix.
185 double farMag
= Math
.sqrt((m
.m41
- m
.m31
) * (m
.m41
- m
.m31
) + (m
.m42
- m
.m32
) * (m
.m42
- m
.m32
)
186 + (m
.m43
- m
.m33
) * (m
.m43
- m
.m33
));
187 Plane farPlane
= new Plane((m
.m41
- m
.m31
) / farMag
, (m
.m42
- m
.m32
) / farMag
, (m
.m43
- m
.m33
) / farMag
,
189 return new Frustum(leftPlane
, rightPlane
, bottomPlane
, topPlane
, nearPlane
, farPlane
);
193 * Creates a <code>Frustum</code> from a horizontal field-of-view, viewport aspect ratio and distance to near and
194 * far depth clipping planes. The near plane must be closer than the far plane, and both planes must be a positive
197 * @param horizontalFieldOfView horizontal field-of-view angle in the range (0, 180)
198 * @param viewportWidth the width of the viewport in screen pixels
199 * @param viewportHeight the height of the viewport in screen pixels
200 * @param near distance to the near depth clipping plane
201 * @param far distance to far depth clipping plane
202 * @throws IllegalArgumentException if fov is not in the range (0, 180), if either near or far are negative, or near
203 * is greater than or equal to far
205 public static Frustum
fromPerspective(Angle horizontalFieldOfView
, int viewportWidth
, int viewportHeight
,
206 double near
, double far
)
208 if (horizontalFieldOfView
== null)
210 String message
= Logging
.getMessage("Geom.ViewFrustum.FieldOfViewIsNull");
211 Logging
.logger().fine(message
);
212 throw new IllegalArgumentException(message
);
214 double fov
= horizontalFieldOfView
.getDegrees();
215 double farMinusNear
= far
- near
;
216 String message
= null;
217 if (fov
<= 0 || fov
> 180)
218 message
= Logging
.getMessage("Geom.ViewFrustum.FieldOfViewOutOfRange", fov
);
219 if (near
<= 0 || farMinusNear
<= 0)
220 message
= Logging
.getMessage("Geom.ViewFrusutm.ClippingDistanceOutOfRange");
223 Logging
.logger().severe(message
);
224 throw new IllegalArgumentException(message
);
227 double focalLength
= 1d
/ horizontalFieldOfView
.tanHalfAngle();
228 double aspect
= viewportHeight
/ (double) viewportWidth
;
229 double lrLen
= Math
.sqrt(focalLength
* focalLength
+ 1);
230 double btLen
= Math
.sqrt(focalLength
* focalLength
+ aspect
* aspect
);
231 Plane leftPlane
= new Plane(focalLength
/ lrLen
, 0d
, 0d
- 1d
/ lrLen
, 0);
232 Plane rightPlane
= new Plane(0d
- focalLength
/ lrLen
, 0d
, 0d
- 1d
/ lrLen
, 0d
);
233 Plane bottomPlane
= new Plane(0d
, focalLength
/ btLen
, 0d
- aspect
/ btLen
, 0d
);
234 Plane topPlane
= new Plane(0d
, 0d
- focalLength
/ btLen
, 0d
- aspect
/ btLen
, 0d
);
235 Plane nearPlane
= new Plane(0d
, 0d
, 0d
- 1d
, 0d
- near
);
236 Plane farPlane
= new Plane(0d
, 0d
, 1d
, far
);
237 return new Frustum(leftPlane
, rightPlane
, bottomPlane
, topPlane
, nearPlane
, farPlane
);
241 * Creates a <code>Frustum</code> from three sets of parallel clipping planes (a parallel projectionMatrix). In this
242 * case, the near and far depth clipping planes may be a negative distance away.
244 * @param near distance to the near depth clipping plane
245 * @param far distance to far depth clipping plane
246 * @param width horizontal dimension of the near clipping plane
247 * @param height vertical dimension of the near clipping plane
248 * @throws IllegalArgumentException if the difference of any plane set (lright - left, top - bottom, far - near) is
249 * less than or equal to zero.
251 public static Frustum
fromPerspective(double width
, double height
, double near
, double far
)
253 double farMinusNear
= far
- near
;
254 if (farMinusNear
<= 0.0 || width
<= 0.0 || height
<= 0.0)
256 String message
= Logging
.getMessage("Geom.ViewFrusutm.ClippingDistanceOutOfRange");
257 Logging
.logger().fine(message
);
258 throw new IllegalArgumentException(message
);
261 double width_over_2
= width
/ 2.0;
262 double height_over_2
= height
/ 2.0;
263 Plane leftPlane
= new Plane(1.0, 0.0, 0.0, width_over_2
);
264 Plane rightPlane
= new Plane(-1.0, 0.0, 0.0, width_over_2
);
265 Plane bottomPlane
= new Plane(0.0, 1.0, 0.0, height_over_2
);
266 Plane topPlane
= new Plane(0.0, -1.0, 0.0, height_over_2
);
267 Plane nearPlane
= new Plane(0.0, 0.0, -1.0, (near
< 0.0) ? near
: -near
);
268 Plane farPlane
= new Plane(0.0, 0.0, 1.0, (far
< 0.0) ?
-far
: far
);
269 return new Frustum(leftPlane
, rightPlane
, bottomPlane
, topPlane
, nearPlane
, farPlane
);
272 // ============== Intersection Functions ======================= //
273 // ============== Intersection Functions ======================= //
274 // ============== Intersection Functions ======================= //
279 * @throws IllegalArgumentException if <code>extent</code> is null
281 public final boolean intersects(Extent extent
)
285 String msg
= Logging
.getMessage("nullValue.ExtentIsNull");
286 Logging
.logger().fine(msg
);
287 throw new IllegalArgumentException(msg
);
290 // See if the extent's bounding sphere is within or intersects the frustum.
291 Vec4 c
= extent
.getCenter();
292 double nr
= -extent
.getRadius();
294 if (this.far
.dot(c
) <= nr
)
296 if (this.left
.dot(c
) <= nr
)
298 if (this.right
.dot(c
) <= nr
)
300 if (this.top
.dot(c
) <= nr
)
302 if (this.bottom
.dot(c
) <= nr
)
304 //noinspection RedundantIfStatement
305 if (this.near
.dot(c
) <= nr
)
314 * @throws IllegalArgumentException if <code>point</code> is null
316 public final boolean contains(Vec4 point
)
320 String msg
= Logging
.getMessage("nullValue.PointIsNull");
321 Logging
.logger().fine(msg
);
322 throw new IllegalArgumentException(msg
);
325 if (this.far
.dot(point
) < 0)
327 if (this.left
.dot(point
) < 0)
329 if (this.right
.dot(point
) < 0)
331 if (this.top
.dot(point
) < 0)
333 if (this.bottom
.dot(point
) < 0)
335 //noinspection RedundantIfStatement
336 if (this.near
.dot(point
) < 0)
342 // ============== Geometric Functions ======================= //
343 // ============== Geometric Functions ======================= //
344 // ============== Geometric Functions ======================= //
349 * @throws IllegalArgumentException if <code>matrix</code> is null
351 public Frustum
transformBy(Matrix matrix
)
353 ensureNonNull(matrix
);
354 Plane left
= new Plane(this.left
.getVector().transformBy4(matrix
));
355 Plane right
= new Plane(this.right
.getVector().transformBy4(matrix
));
356 Plane bottom
= new Plane(this.bottom
.getVector().transformBy4(matrix
));
357 Plane top
= new Plane(this.top
.getVector().transformBy4(matrix
));
358 Plane near
= new Plane(this.near
.getVector().transformBy4(matrix
));
359 Plane far
= new Plane(this.far
.getVector().transformBy4(matrix
));
360 return new Frustum(left
, right
, bottom
, top
, near
, far
);
363 // ============== Helper Functions ======================= //
364 // ============== Helper Functions ======================= //
365 // ============== Helper Functions ======================= //
367 private static void ensureNonNull(Matrix matrix
)
371 String msg
= Logging
.getMessage("nullValue.MatrixIsNull");
372 Logging
.logger().fine(msg
);
373 throw new IllegalArgumentException(msg
);