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 3301 2007-10-16 00:31:34Z dcollins $
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
;
27 private final Plane
[] allPlanes
;
30 * Create a default frustum with six <code>Plane</code>s. This defines a box of dimension (2, 2, 2) centered at the
36 new Plane(1, 0, 0, 1), // Left
37 new Plane(-1, 0, 0, 1), // Right
38 new Plane(0, 1, 0, 1), // Bottom
39 new Plane(0, -1, 0, 1), // Top
40 new Plane(0, 0, -1, 1), // Near
41 new Plane(0, 0, 1, 1)); // Far
45 * Create a frustum from six <code>Plane</code>s, which define its boundaries. Does not except null arguments.
47 * @param near the near plane
48 * @param far the far plane
49 * @param left the left side of the view frustum
50 * @param right the right side of the view frustm
51 * @param top the top of the view frustum
52 * @param bottom the bottom of the view frustum
53 * @throws IllegalArgumentException if any argument is null
55 public Frustum(Plane left
, Plane right
, Plane bottom
, Plane top
, Plane near
, Plane far
)
57 if (left
== null || right
== null || bottom
== null || top
== null || near
== null || far
== null)
59 String message
= Logging
.getMessage("nullValue.PlaneIsNull");
60 Logging
.logger().severe(message
);
61 throw new IllegalArgumentException(message
);
70 this.allPlanes
= new Plane
[] {this.left
, this.right
, this.bottom
, this.top
, this.near
, this.far
};
73 public boolean equals(Object obj
)
77 if (obj
== null || getClass() != obj
.getClass())
80 Frustum that
= (Frustum
) obj
;
81 return this.left
.equals(that
.left
)
82 && this.right
.equals(that
.right
)
83 && this.bottom
.equals(that
.bottom
)
84 && this.top
.equals(that
.top
)
85 && this.near
.equals(that
.near
)
86 && this.far
.equals(that
.far
);
89 public final Plane
getLeft()
94 public final Plane
getRight()
99 public final Plane
getBottom()
104 public final Plane
getTop()
109 public final Plane
getNear()
114 public final Plane
getFar()
119 public Plane
[] getAllPlanes()
121 return this.allPlanes
;
124 public int hashCode()
127 result
= this.left
.hashCode();
128 result
= 31 * result
+ this.right
.hashCode();
129 result
= 19 * result
+ this.bottom
.hashCode();
130 result
= 23 * result
+ this.top
.hashCode();
131 result
= 17 * result
+ this.near
.hashCode();
132 result
= 19 * result
+ this.far
.hashCode();
137 public String
toString()
139 StringBuilder sb
= new StringBuilder();
141 sb
.append("left=").append(this.near
);
142 sb
.append(", right=").append(this.near
);
143 sb
.append(", bottom=").append(this.near
);
144 sb
.append(", top=").append(this.near
);
145 sb
.append(", near=").append(this.near
);
146 sb
.append(", far=").append(this.near
);
148 return sb
.toString();
151 // ============== Factory Functions ======================= //
152 // ============== Factory Functions ======================= //
153 // ============== Factory Functions ======================= //
155 public static Frustum
fromProjectionMatrix(Matrix projectionMatrix
)
157 //noinspection UnnecessaryLocalVariable
158 Matrix m
= projectionMatrix
;
161 String message
= Logging
.getMessage("nullValue.MatrixIsNull");
162 Logging
.logger().severe(message
);
163 throw new IllegalArgumentException(message
);
166 // Extract the left clipping plane from the projection-matrix.
167 double leftMag
= Math
.sqrt((m
.m41
+ m
.m11
) * (m
.m41
+ m
.m11
) + (m
.m42
+ m
.m12
) * (m
.m42
+ m
.m12
)
168 + (m
.m43
+ m
.m13
) * (m
.m43
+ m
.m13
));
169 Plane leftPlane
= new Plane((m
.m41
+ m
.m11
) / leftMag
, (m
.m42
+ m
.m12
) / leftMag
, (m
.m43
+ m
.m13
) / leftMag
,
171 // Extract the right clipping plane from the projection-matrix.
172 double rightMag
= Math
.sqrt((m
.m41
- m
.m11
) * (m
.m41
- m
.m11
) + (m
.m42
- m
.m12
) * (m
.m42
- m
.m12
)
173 + (m
.m43
- m
.m13
) * (m
.m43
- m
.m13
));
174 Plane rightPlane
= new Plane((m
.m41
- m
.m11
) / rightMag
, (m
.m42
- m
.m12
) / rightMag
, (m
.m43
- m
.m13
) / rightMag
,
176 // Extract the bottom clipping plane from the projection-matrix.
177 double bottomMag
= Math
.sqrt((m
.m41
+ m
.m21
) * (m
.m41
+ m
.m21
) + (m
.m42
+ m
.m22
) * (m
.m42
+ m
.m22
)
178 + (m
.m43
+ m
.m23
) * (m
.m43
+ m
.m23
));
179 Plane bottomPlane
= new Plane((m
.m41
+ m
.m21
) / bottomMag
, (m
.m42
+ m
.m22
) / bottomMag
,
180 (m
.m43
+ m
.m23
) / bottomMag
,
182 // Extract the top clipping plane from the projection-matrix.
183 double topMag
= Math
.sqrt((m
.m41
- m
.m21
) * (m
.m41
- m
.m21
) + (m
.m42
- m
.m22
) * (m
.m42
- m
.m22
)
184 + (m
.m43
- m
.m23
) * (m
.m43
- m
.m23
));
185 Plane topPlane
= new Plane((m
.m41
- m
.m21
) / topMag
, (m
.m42
- m
.m22
) / topMag
, (m
.m43
- m
.m23
) / topMag
,
187 // Extract the near clipping plane from the projection-matrix.
188 double nearMag
= Math
.sqrt((m
.m41
+ m
.m31
) * (m
.m41
+ m
.m31
) + (m
.m42
+ m
.m32
) * (m
.m42
+ m
.m32
)
189 + (m
.m43
+ m
.m33
) * (m
.m43
+ m
.m33
));
190 Plane nearPlane
= new Plane((m
.m41
+ m
.m31
) / nearMag
, (m
.m42
+ m
.m32
) / nearMag
, (m
.m43
+ m
.m33
) / nearMag
,
192 // Extract the far clipping plane from the projection-matrix.
193 double farMag
= Math
.sqrt((m
.m41
- m
.m31
) * (m
.m41
- m
.m31
) + (m
.m42
- m
.m32
) * (m
.m42
- m
.m32
)
194 + (m
.m43
- m
.m33
) * (m
.m43
- m
.m33
));
195 Plane farPlane
= new Plane((m
.m41
- m
.m31
) / farMag
, (m
.m42
- m
.m32
) / farMag
, (m
.m43
- m
.m33
) / farMag
,
197 return new Frustum(leftPlane
, rightPlane
, bottomPlane
, topPlane
, nearPlane
, farPlane
);
201 * Creates a <code>Frustum</code> from a horizontal field-of-view, viewport aspect ratio and distance to near and
202 * far depth clipping planes. The near plane must be closer than the far plane, and both planes must be a positive
205 * @param horizontalFieldOfView horizontal field-of-view angle in the range (0, 180)
206 * @param viewportWidth the width of the viewport in screen pixels
207 * @param viewportHeight the height of the viewport in screen pixels
208 * @param near distance to the near depth clipping plane
209 * @param far distance to far depth clipping plane
210 * @throws IllegalArgumentException if fov is not in the range (0, 180), if either near or far are negative, or near
211 * is greater than or equal to far
213 public static Frustum
fromPerspective(Angle horizontalFieldOfView
, int viewportWidth
, int viewportHeight
,
214 double near
, double far
)
216 if (horizontalFieldOfView
== null)
218 String message
= Logging
.getMessage("Geom.ViewFrustum.FieldOfViewIsNull");
219 Logging
.logger().fine(message
);
220 throw new IllegalArgumentException(message
);
222 double fov
= horizontalFieldOfView
.getDegrees();
223 double farMinusNear
= far
- near
;
224 String message
= null;
225 if (fov
<= 0 || fov
> 180)
226 message
= Logging
.getMessage("Geom.ViewFrustum.FieldOfViewOutOfRange", fov
);
227 if (near
<= 0 || farMinusNear
<= 0)
228 message
= Logging
.getMessage("Geom.ViewFrusutm.ClippingDistanceOutOfRange");
231 Logging
.logger().severe(message
);
232 throw new IllegalArgumentException(message
);
235 double focalLength
= 1d
/ horizontalFieldOfView
.tanHalfAngle();
236 double aspect
= viewportHeight
/ (double) viewportWidth
;
237 double lrLen
= Math
.sqrt(focalLength
* focalLength
+ 1);
238 double btLen
= Math
.sqrt(focalLength
* focalLength
+ aspect
* aspect
);
239 Plane leftPlane
= new Plane(focalLength
/ lrLen
, 0d
, 0d
- 1d
/ lrLen
, 0);
240 Plane rightPlane
= new Plane(0d
- focalLength
/ lrLen
, 0d
, 0d
- 1d
/ lrLen
, 0d
);
241 Plane bottomPlane
= new Plane(0d
, focalLength
/ btLen
, 0d
- aspect
/ btLen
, 0d
);
242 Plane topPlane
= new Plane(0d
, 0d
- focalLength
/ btLen
, 0d
- aspect
/ btLen
, 0d
);
243 Plane nearPlane
= new Plane(0d
, 0d
, 0d
- 1d
, 0d
- near
);
244 Plane farPlane
= new Plane(0d
, 0d
, 1d
, far
);
245 return new Frustum(leftPlane
, rightPlane
, bottomPlane
, topPlane
, nearPlane
, farPlane
);
249 * Creates a <code>Frustum</code> from three sets of parallel clipping planes (a parallel projectionMatrix). In this
250 * case, the near and far depth clipping planes may be a negative distance away.
252 * @param near distance to the near depth clipping plane
253 * @param far distance to far depth clipping plane
254 * @param width horizontal dimension of the near clipping plane
255 * @param height vertical dimension of the near clipping plane
256 * @throws IllegalArgumentException if the difference of any plane set (lright - left, top - bottom, far - near) is
257 * less than or equal to zero.
259 public static Frustum
fromPerspective(double width
, double height
, double near
, double far
)
261 double farMinusNear
= far
- near
;
262 if (farMinusNear
<= 0.0 || width
<= 0.0 || height
<= 0.0)
264 String message
= Logging
.getMessage("Geom.ViewFrusutm.ClippingDistanceOutOfRange");
265 Logging
.logger().fine(message
);
266 throw new IllegalArgumentException(message
);
269 double width_over_2
= width
/ 2.0;
270 double height_over_2
= height
/ 2.0;
271 Plane leftPlane
= new Plane(1.0, 0.0, 0.0, width_over_2
);
272 Plane rightPlane
= new Plane(-1.0, 0.0, 0.0, width_over_2
);
273 Plane bottomPlane
= new Plane(0.0, 1.0, 0.0, height_over_2
);
274 Plane topPlane
= new Plane(0.0, -1.0, 0.0, height_over_2
);
275 Plane nearPlane
= new Plane(0.0, 0.0, -1.0, (near
< 0.0) ? near
: -near
);
276 Plane farPlane
= new Plane(0.0, 0.0, 1.0, (far
< 0.0) ?
-far
: far
);
277 return new Frustum(leftPlane
, rightPlane
, bottomPlane
, topPlane
, nearPlane
, farPlane
);
280 // ============== Intersection Functions ======================= //
281 // ============== Intersection Functions ======================= //
282 // ============== Intersection Functions ======================= //
287 * @throws IllegalArgumentException if <code>extent</code> is null
289 public final boolean intersects(Extent extent
)
293 String msg
= Logging
.getMessage("nullValue.ExtentIsNull");
294 Logging
.logger().fine(msg
);
295 throw new IllegalArgumentException(msg
);
298 // See if the extent's bounding sphere is within or intersects the frustum.
299 Vec4 c
= extent
.getCenter();
300 double nr
= -extent
.getRadius();
302 if (this.far
.dot(c
) <= nr
)
304 if (this.left
.dot(c
) <= nr
)
306 if (this.right
.dot(c
) <= nr
)
308 if (this.top
.dot(c
) <= nr
)
310 if (this.bottom
.dot(c
) <= nr
)
312 //noinspection RedundantIfStatement
313 if (this.near
.dot(c
) <= nr
)
322 * @throws IllegalArgumentException if <code>point</code> is null
324 public final boolean contains(Vec4 point
)
328 String msg
= Logging
.getMessage("nullValue.PointIsNull");
329 Logging
.logger().fine(msg
);
330 throw new IllegalArgumentException(msg
);
333 if (this.far
.dot(point
) <= 0)
335 if (this.left
.dot(point
) <= 0)
337 if (this.right
.dot(point
) <= 0)
339 if (this.top
.dot(point
) <= 0)
341 if (this.bottom
.dot(point
) <= 0)
343 //noinspection RedundantIfStatement
344 if (this.near
.dot(point
) <= 0)
350 // ============== Geometric Functions ======================= //
351 // ============== Geometric Functions ======================= //
352 // ============== Geometric Functions ======================= //
357 * @throws IllegalArgumentException if <code>matrix</code> is null
359 public Frustum
transformBy(Matrix matrix
)
361 ensureNonNull(matrix
);
362 Plane left
= new Plane(this.left
.getVector().transformBy4(matrix
));
363 Plane right
= new Plane(this.right
.getVector().transformBy4(matrix
));
364 Plane bottom
= new Plane(this.bottom
.getVector().transformBy4(matrix
));
365 Plane top
= new Plane(this.top
.getVector().transformBy4(matrix
));
366 Plane near
= new Plane(this.near
.getVector().transformBy4(matrix
));
367 Plane far
= new Plane(this.far
.getVector().transformBy4(matrix
));
368 return new Frustum(left
, right
, bottom
, top
, near
, far
);
371 // ============== Helper Functions ======================= //
372 // ============== Helper Functions ======================= //
373 // ============== Helper Functions ======================= //
375 private static void ensureNonNull(Matrix matrix
)
379 String msg
= Logging
.getMessage("nullValue.MatrixIsNull");
380 Logging
.logger().fine(msg
);
381 throw new IllegalArgumentException(msg
);