1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.jme3.renderer; 33 34 import com.jme3.bounding.BoundingBox; 35 import com.jme3.bounding.BoundingVolume; 36 import com.jme3.export.*; 37 import com.jme3.math.*; 38 import com.jme3.util.TempVars; 39 import java.io.IOException; 40 import java.util.logging.Level; 41 import java.util.logging.Logger; 42 43 /** 44 * <code>Camera</code> is a standalone, purely mathematical class for doing 45 * camera-related computations. 46 * 47 * <p> 48 * Given input data such as location, orientation (direction, left, up), 49 * and viewport settings, it can compute data necessary to render objects 50 * with the graphics library. Two matrices are generated, the view matrix 51 * transforms objects from world space into eye space, while the projection 52 * matrix transforms objects from eye space into clip space. 53 * </p> 54 * <p>Another purpose of the camera class is to do frustum culling operations, 55 * defined by six planes which define a 3D frustum shape, it is possible to 56 * test if an object bounded by a mathematically defined volume is inside 57 * the camera frustum, and thus to avoid rendering objects that are outside 58 * the frustum 59 * </p> 60 * 61 * @author Mark Powell 62 * @author Joshua Slack 63 */ 64 public class Camera implements Savable, Cloneable { 65 66 private static final Logger logger = Logger.getLogger(Camera.class.getName()); 67 68 /** 69 * The <code>FrustumIntersect</code> enum is returned as a result 70 * of a culling check operation, 71 * see {@link #contains(com.jme3.bounding.BoundingVolume) } 72 */ 73 public enum FrustumIntersect { 74 75 /** 76 * defines a constant assigned to spatials that are completely outside 77 * of this camera's view frustum. 78 */ 79 Outside, 80 /** 81 * defines a constant assigned to spatials that are completely inside 82 * the camera's view frustum. 83 */ 84 Inside, 85 /** 86 * defines a constant assigned to spatials that are intersecting one of 87 * the six planes that define the view frustum. 88 */ 89 Intersects; 90 } 91 /** 92 * LEFT_PLANE represents the left plane of the camera frustum. 93 */ 94 private static final int LEFT_PLANE = 0; 95 /** 96 * RIGHT_PLANE represents the right plane of the camera frustum. 97 */ 98 private static final int RIGHT_PLANE = 1; 99 /** 100 * BOTTOM_PLANE represents the bottom plane of the camera frustum. 101 */ 102 private static final int BOTTOM_PLANE = 2; 103 /** 104 * TOP_PLANE represents the top plane of the camera frustum. 105 */ 106 private static final int TOP_PLANE = 3; 107 /** 108 * FAR_PLANE represents the far plane of the camera frustum. 109 */ 110 private static final int FAR_PLANE = 4; 111 /** 112 * NEAR_PLANE represents the near plane of the camera frustum. 113 */ 114 private static final int NEAR_PLANE = 5; 115 /** 116 * FRUSTUM_PLANES represents the number of planes of the camera frustum. 117 */ 118 private static final int FRUSTUM_PLANES = 6; 119 /** 120 * MAX_WORLD_PLANES holds the maximum planes allowed by the system. 121 */ 122 private static final int MAX_WORLD_PLANES = 6; 123 /** 124 * Camera's location 125 */ 126 protected Vector3f location; 127 /** 128 * The orientation of the camera. 129 */ 130 protected Quaternion rotation; 131 /** 132 * Distance from camera to near frustum plane. 133 */ 134 protected float frustumNear; 135 /** 136 * Distance from camera to far frustum plane. 137 */ 138 protected float frustumFar; 139 /** 140 * Distance from camera to left frustum plane. 141 */ 142 protected float frustumLeft; 143 /** 144 * Distance from camera to right frustum plane. 145 */ 146 protected float frustumRight; 147 /** 148 * Distance from camera to top frustum plane. 149 */ 150 protected float frustumTop; 151 /** 152 * Distance from camera to bottom frustum plane. 153 */ 154 protected float frustumBottom; 155 //Temporary values computed in onFrustumChange that are needed if a 156 //call is made to onFrameChange. 157 protected float[] coeffLeft; 158 protected float[] coeffRight; 159 protected float[] coeffBottom; 160 protected float[] coeffTop; 161 //view port coordinates 162 /** 163 * Percent value on display where horizontal viewing starts for this camera. 164 * Default is 0. 165 */ 166 protected float viewPortLeft; 167 /** 168 * Percent value on display where horizontal viewing ends for this camera. 169 * Default is 1. 170 */ 171 protected float viewPortRight; 172 /** 173 * Percent value on display where vertical viewing ends for this camera. 174 * Default is 1. 175 */ 176 protected float viewPortTop; 177 /** 178 * Percent value on display where vertical viewing begins for this camera. 179 * Default is 0. 180 */ 181 protected float viewPortBottom; 182 /** 183 * Array holding the planes that this camera will check for culling. 184 */ 185 protected Plane[] worldPlane; 186 /** 187 * A mask value set during contains() that allows fast culling of a Node's 188 * children. 189 */ 190 private int planeState; 191 protected int width; 192 protected int height; 193 protected boolean viewportChanged = true; 194 /** 195 * store the value for field parallelProjection 196 */ 197 private boolean parallelProjection; 198 protected Matrix4f projectionMatrixOverride; 199 protected Matrix4f viewMatrix = new Matrix4f(); 200 protected Matrix4f projectionMatrix = new Matrix4f(); 201 protected Matrix4f viewProjectionMatrix = new Matrix4f(); 202 private BoundingBox guiBounding = new BoundingBox(); 203 /** The camera's name. */ 204 protected String name; 205 206 /** 207 * Serialization only. Do not use. 208 */ 209 public Camera() { 210 worldPlane = new Plane[MAX_WORLD_PLANES]; 211 for (int i = 0; i < MAX_WORLD_PLANES; i++) { 212 worldPlane[i] = new Plane(); 213 } 214 } 215 216 /** 217 * Constructor instantiates a new <code>Camera</code> object. All 218 * values of the camera are set to default. 219 */ 220 public Camera(int width, int height) { 221 this(); 222 location = new Vector3f(); 223 rotation = new Quaternion(); 224 225 frustumNear = 1.0f; 226 frustumFar = 2.0f; 227 frustumLeft = -0.5f; 228 frustumRight = 0.5f; 229 frustumTop = 0.5f; 230 frustumBottom = -0.5f; 231 232 coeffLeft = new float[2]; 233 coeffRight = new float[2]; 234 coeffBottom = new float[2]; 235 coeffTop = new float[2]; 236 237 viewPortLeft = 0.0f; 238 viewPortRight = 1.0f; 239 viewPortTop = 1.0f; 240 viewPortBottom = 0.0f; 241 242 this.width = width; 243 this.height = height; 244 245 onFrustumChange(); 246 onViewPortChange(); 247 onFrameChange(); 248 249 logger.log(Level.INFO, "Camera created (W: {0}, H: {1})", new Object[]{width, height}); 250 } 251 252 @Override 253 public Camera clone() { 254 try { 255 Camera cam = (Camera) super.clone(); 256 cam.viewportChanged = true; 257 cam.planeState = 0; 258 259 cam.worldPlane = new Plane[MAX_WORLD_PLANES]; 260 for (int i = 0; i < worldPlane.length; i++) { 261 cam.worldPlane[i] = worldPlane[i].clone(); 262 } 263 264 cam.coeffLeft = new float[2]; 265 cam.coeffRight = new float[2]; 266 cam.coeffBottom = new float[2]; 267 cam.coeffTop = new float[2]; 268 269 cam.location = location.clone(); 270 cam.rotation = rotation.clone(); 271 272 if (projectionMatrixOverride != null) { 273 cam.projectionMatrixOverride = projectionMatrixOverride.clone(); 274 } 275 276 cam.viewMatrix = viewMatrix.clone(); 277 cam.projectionMatrix = projectionMatrix.clone(); 278 cam.viewProjectionMatrix = viewProjectionMatrix.clone(); 279 cam.guiBounding = (BoundingBox) guiBounding.clone(); 280 281 cam.update(); 282 283 return cam; 284 } catch (CloneNotSupportedException ex) { 285 throw new AssertionError(); 286 } 287 } 288 289 /** 290 * This method copise the settings of the given camera. 291 * 292 * @param cam 293 * the camera we copy the settings from 294 */ 295 public void copyFrom(Camera cam) { 296 location.set(cam.location); 297 rotation.set(cam.rotation); 298 299 frustumNear = cam.frustumNear; 300 frustumFar = cam.frustumFar; 301 frustumLeft = cam.frustumLeft; 302 frustumRight = cam.frustumRight; 303 frustumTop = cam.frustumTop; 304 frustumBottom = cam.frustumBottom; 305 306 coeffLeft[0] = cam.coeffLeft[0]; 307 coeffLeft[1] = cam.coeffLeft[1]; 308 coeffRight[0] = cam.coeffRight[0]; 309 coeffRight[1] = cam.coeffRight[1]; 310 coeffBottom[0] = cam.coeffBottom[0]; 311 coeffBottom[1] = cam.coeffBottom[1]; 312 coeffTop[0] = cam.coeffTop[0]; 313 coeffTop[1] = cam.coeffTop[1]; 314 315 viewPortLeft = cam.viewPortLeft; 316 viewPortRight = cam.viewPortRight; 317 viewPortTop = cam.viewPortTop; 318 viewPortBottom = cam.viewPortBottom; 319 320 this.width = cam.width; 321 this.height = cam.height; 322 323 this.planeState = cam.planeState; 324 this.viewportChanged = cam.viewportChanged; 325 for (int i = 0; i < MAX_WORLD_PLANES; ++i) { 326 worldPlane[i].setNormal(cam.worldPlane[i].getNormal()); 327 worldPlane[i].setConstant(cam.worldPlane[i].getConstant()); 328 } 329 330 this.parallelProjection = cam.parallelProjection; 331 if(cam.projectionMatrixOverride != null) { 332 if(this.projectionMatrixOverride == null) { 333 this.projectionMatrixOverride = cam.projectionMatrixOverride.clone(); 334 } else { 335 this.projectionMatrixOverride.set(cam.projectionMatrixOverride); 336 } 337 } else { 338 this.projectionMatrixOverride = null; 339 } 340 this.viewMatrix.set(cam.viewMatrix); 341 this.projectionMatrix.set(cam.projectionMatrix); 342 this.viewProjectionMatrix.set(cam.viewProjectionMatrix); 343 344 this.guiBounding.setXExtent(cam.guiBounding.getXExtent()); 345 this.guiBounding.setYExtent(cam.guiBounding.getYExtent()); 346 this.guiBounding.setZExtent(cam.guiBounding.getZExtent()); 347 this.guiBounding.setCenter(cam.guiBounding.getCenter()); 348 this.guiBounding.setCheckPlane(cam.guiBounding.getCheckPlane()); 349 350 this.name = cam.name; 351 } 352 353 /** 354 * This method sets the cameras name. 355 * @param name the cameras name 356 */ 357 public void setName(String name) { 358 this.name = name; 359 } 360 361 /** 362 * This method returns the cameras name. 363 * @return the cameras name 364 */ 365 public String getName() { 366 return name; 367 } 368 369 /** 370 * Sets a clipPlane for this camera. 371 * The cliPlane is used to recompute the projectionMatrix using the plane as the near plane 372 * This technique is known as the oblique near-plane clipping method introduced by Eric Lengyel 373 * more info here 374 * <ul> 375 * <li><a href="http://www.terathon.com/code/oblique.html">http://www.terathon.com/code/oblique.html</a> 376 * <li><a href="http://aras-p.info/texts/obliqueortho.html">http://aras-p.info/texts/obliqueortho.html</a> 377 * <li><a href="http://hacksoflife.blogspot.com/2008/12/every-now-and-then-i-come-across.html">http://hacksoflife.blogspot.com/2008/12/every-now-and-then-i-come-across.html</a> 378 * </ul> 379 * 380 * Note that this will work properly only if it's called on each update, and be aware that it won't work properly with the sky bucket. 381 * if you want to handle the sky bucket, look at how it's done in SimpleWaterProcessor.java 382 * @param clipPlane the plane 383 * @param side the side the camera stands from the plane 384 */ 385 public void setClipPlane(Plane clipPlane, Plane.Side side) { 386 float sideFactor = 1; 387 if (side == Plane.Side.Negative) { 388 sideFactor = -1; 389 } 390 //we are on the other side of the plane no need to clip anymore. 391 if (clipPlane.whichSide(location) == side) { 392 return; 393 } 394 Matrix4f p = projectionMatrix.clone(); 395 396 Matrix4f ivm = viewMatrix.clone(); 397 398 Vector3f point = clipPlane.getNormal().mult(clipPlane.getConstant()); 399 Vector3f pp = ivm.mult(point); 400 Vector3f pn = ivm.multNormal(clipPlane.getNormal(), null); 401 Vector4f clipPlaneV = new Vector4f(pn.x * sideFactor, pn.y * sideFactor, pn.z * sideFactor, -(pp.dot(pn)) * sideFactor); 402 403 Vector4f v = new Vector4f(0, 0, 0, 0); 404 405 v.x = (Math.signum(clipPlaneV.x) + p.m02) / p.m00; 406 v.y = (Math.signum(clipPlaneV.y) + p.m12) / p.m11; 407 v.z = -1.0f; 408 v.w = (1.0f + p.m22) / p.m23; 409 410 float dot = clipPlaneV.dot(v);//clipPlaneV.x * v.x + clipPlaneV.y * v.y + clipPlaneV.z * v.z + clipPlaneV.w * v.w; 411 Vector4f c = clipPlaneV.mult(2.0f / dot); 412 413 p.m20 = c.x - p.m30; 414 p.m21 = c.y - p.m31; 415 p.m22 = c.z - p.m32; 416 p.m23 = c.w - p.m33; 417 setProjectionMatrix(p); 418 } 419 420 /** 421 * Sets a clipPlane for this camera. 422 * The cliPlane is used to recompute the projectionMatrix using the plane as the near plane 423 * This technique is known as the oblique near-plane clipping method introduced by Eric Lengyel 424 * more info here 425 * <ul> 426 * <li><a href="http://www.terathon.com/code/oblique.html">http://www.terathon.com/code/oblique.html</a></li> 427 * <li><a href="http://aras-p.info/texts/obliqueortho.html">http://aras-p.info/texts/obliqueortho.html</a></li> 428 * <li><a href="http://hacksoflife.blogspot.com/2008/12/every-now-and-then-i-come-across.html"> 429 * http://hacksoflife.blogspot.com/2008/12/every-now-and-then-i-come-across.html</a></li> 430 * </ul> 431 * 432 * Note that this will work properly only if it's called on each update, and be aware that it won't work properly with the sky bucket. 433 * if you want to handle the sky bucket, look at how it's done in SimpleWaterProcessor.java 434 * @param clipPlane the plane 435 */ 436 public void setClipPlane(Plane clipPlane) { 437 setClipPlane(clipPlane, clipPlane.whichSide(location)); 438 } 439 440 /** 441 * Resizes this camera's view with the given width and height. This is 442 * similar to constructing a new camera, but reusing the same Object. This 443 * method is called by an associated {@link RenderManager} to notify the camera of 444 * changes in the display dimensions. 445 * 446 * @param width the view width 447 * @param height the view height 448 * @param fixAspect If true, the camera's aspect ratio will be recomputed. 449 * Recomputing the aspect ratio requires changing the frustum values. 450 */ 451 public void resize(int width, int height, boolean fixAspect) { 452 this.width = width; 453 this.height = height; 454 onViewPortChange(); 455 456 if (fixAspect /*&& !parallelProjection*/) { 457 frustumRight = frustumTop * ((float) width / height); 458 frustumLeft = -frustumRight; 459 onFrustumChange(); 460 } 461 } 462 463 /** 464 * <code>getFrustumBottom</code> returns the value of the bottom frustum 465 * plane. 466 * 467 * @return the value of the bottom frustum plane. 468 */ 469 public float getFrustumBottom() { 470 return frustumBottom; 471 } 472 473 /** 474 * <code>setFrustumBottom</code> sets the value of the bottom frustum 475 * plane. 476 * 477 * @param frustumBottom the value of the bottom frustum plane. 478 */ 479 public void setFrustumBottom(float frustumBottom) { 480 this.frustumBottom = frustumBottom; 481 onFrustumChange(); 482 } 483 484 /** 485 * <code>getFrustumFar</code> gets the value of the far frustum plane. 486 * 487 * @return the value of the far frustum plane. 488 */ 489 public float getFrustumFar() { 490 return frustumFar; 491 } 492 493 /** 494 * <code>setFrustumFar</code> sets the value of the far frustum plane. 495 * 496 * @param frustumFar the value of the far frustum plane. 497 */ 498 public void setFrustumFar(float frustumFar) { 499 this.frustumFar = frustumFar; 500 onFrustumChange(); 501 } 502 503 /** 504 * <code>getFrustumLeft</code> gets the value of the left frustum plane. 505 * 506 * @return the value of the left frustum plane. 507 */ 508 public float getFrustumLeft() { 509 return frustumLeft; 510 } 511 512 /** 513 * <code>setFrustumLeft</code> sets the value of the left frustum plane. 514 * 515 * @param frustumLeft the value of the left frustum plane. 516 */ 517 public void setFrustumLeft(float frustumLeft) { 518 this.frustumLeft = frustumLeft; 519 onFrustumChange(); 520 } 521 522 /** 523 * <code>getFrustumNear</code> gets the value of the near frustum plane. 524 * 525 * @return the value of the near frustum plane. 526 */ 527 public float getFrustumNear() { 528 return frustumNear; 529 } 530 531 /** 532 * <code>setFrustumNear</code> sets the value of the near frustum plane. 533 * 534 * @param frustumNear the value of the near frustum plane. 535 */ 536 public void setFrustumNear(float frustumNear) { 537 this.frustumNear = frustumNear; 538 onFrustumChange(); 539 } 540 541 /** 542 * <code>getFrustumRight</code> gets the value of the right frustum plane. 543 * 544 * @return frustumRight the value of the right frustum plane. 545 */ 546 public float getFrustumRight() { 547 return frustumRight; 548 } 549 550 /** 551 * <code>setFrustumRight</code> sets the value of the right frustum plane. 552 * 553 * @param frustumRight the value of the right frustum plane. 554 */ 555 public void setFrustumRight(float frustumRight) { 556 this.frustumRight = frustumRight; 557 onFrustumChange(); 558 } 559 560 /** 561 * <code>getFrustumTop</code> gets the value of the top frustum plane. 562 * 563 * @return the value of the top frustum plane. 564 */ 565 public float getFrustumTop() { 566 return frustumTop; 567 } 568 569 /** 570 * <code>setFrustumTop</code> sets the value of the top frustum plane. 571 * 572 * @param frustumTop the value of the top frustum plane. 573 */ 574 public void setFrustumTop(float frustumTop) { 575 this.frustumTop = frustumTop; 576 onFrustumChange(); 577 } 578 579 /** 580 * <code>getLocation</code> retrieves the location vector of the camera. 581 * 582 * @return the position of the camera. 583 * @see Camera#getLocation() 584 */ 585 public Vector3f getLocation() { 586 return location; 587 } 588 589 /** 590 * <code>getRotation</code> retrieves the rotation quaternion of the camera. 591 * 592 * @return the rotation of the camera. 593 */ 594 public Quaternion getRotation() { 595 return rotation; 596 } 597 598 /** 599 * <code>getDirection</code> retrieves the direction vector the camera is 600 * facing. 601 * 602 * @return the direction the camera is facing. 603 * @see Camera#getDirection() 604 */ 605 public Vector3f getDirection() { 606 return rotation.getRotationColumn(2); 607 } 608 609 /** 610 * <code>getLeft</code> retrieves the left axis of the camera. 611 * 612 * @return the left axis of the camera. 613 * @see Camera#getLeft() 614 */ 615 public Vector3f getLeft() { 616 return rotation.getRotationColumn(0); 617 } 618 619 /** 620 * <code>getUp</code> retrieves the up axis of the camera. 621 * 622 * @return the up axis of the camera. 623 * @see Camera#getUp() 624 */ 625 public Vector3f getUp() { 626 return rotation.getRotationColumn(1); 627 } 628 629 /** 630 * <code>getDirection</code> retrieves the direction vector the camera is 631 * facing. 632 * 633 * @return the direction the camera is facing. 634 * @see Camera#getDirection() 635 */ 636 public Vector3f getDirection(Vector3f store) { 637 return rotation.getRotationColumn(2, store); 638 } 639 640 /** 641 * <code>getLeft</code> retrieves the left axis of the camera. 642 * 643 * @return the left axis of the camera. 644 * @see Camera#getLeft() 645 */ 646 public Vector3f getLeft(Vector3f store) { 647 return rotation.getRotationColumn(0, store); 648 } 649 650 /** 651 * <code>getUp</code> retrieves the up axis of the camera. 652 * 653 * @return the up axis of the camera. 654 * @see Camera#getUp() 655 */ 656 public Vector3f getUp(Vector3f store) { 657 return rotation.getRotationColumn(1, store); 658 } 659 660 /** 661 * <code>setLocation</code> sets the position of the camera. 662 * 663 * @param location the position of the camera. 664 */ 665 public void setLocation(Vector3f location) { 666 this.location.set(location); 667 onFrameChange(); 668 } 669 670 /** 671 * <code>setRotation</code> sets the orientation of this camera. 672 * This will be equivelant to setting each of the axes: 673 * <code><br> 674 * cam.setLeft(rotation.getRotationColumn(0));<br> 675 * cam.setUp(rotation.getRotationColumn(1));<br> 676 * cam.setDirection(rotation.getRotationColumn(2));<br> 677 * </code> 678 * 679 * @param rotation the rotation of this camera 680 */ 681 public void setRotation(Quaternion rotation) { 682 this.rotation.set(rotation); 683 onFrameChange(); 684 } 685 686 /** 687 * <code>lookAtDirection</code> sets the direction the camera is facing 688 * given a direction and an up vector. 689 * 690 * @param direction the direction this camera is facing. 691 */ 692 public void lookAtDirection(Vector3f direction, Vector3f up) { 693 this.rotation.lookAt(direction, up); 694 onFrameChange(); 695 } 696 697 /** 698 * <code>setAxes</code> sets the axes (left, up and direction) for this 699 * camera. 700 * 701 * @param left the left axis of the camera. 702 * @param up the up axis of the camera. 703 * @param direction the direction the camera is facing. 704 * 705 * @see Camera#setAxes(com.jme3.math.Quaternion) 706 */ 707 public void setAxes(Vector3f left, Vector3f up, Vector3f direction) { 708 this.rotation.fromAxes(left, up, direction); 709 onFrameChange(); 710 } 711 712 /** 713 * <code>setAxes</code> uses a rotational matrix to set the axes of the 714 * camera. 715 * 716 * @param axes the matrix that defines the orientation of the camera. 717 */ 718 public void setAxes(Quaternion axes) { 719 this.rotation.set(axes); 720 onFrameChange(); 721 } 722 723 /** 724 * normalize normalizes the camera vectors. 725 */ 726 public void normalize() { 727 this.rotation.normalizeLocal(); 728 onFrameChange(); 729 } 730 731 /** 732 * <code>setFrustum</code> sets the frustum of this camera object. 733 * 734 * @param near the near plane. 735 * @param far the far plane. 736 * @param left the left plane. 737 * @param right the right plane. 738 * @param top the top plane. 739 * @param bottom the bottom plane. 740 * @see Camera#setFrustum(float, float, float, float, 741 * float, float) 742 */ 743 public void setFrustum(float near, float far, float left, float right, 744 float top, float bottom) { 745 746 frustumNear = near; 747 frustumFar = far; 748 frustumLeft = left; 749 frustumRight = right; 750 frustumTop = top; 751 frustumBottom = bottom; 752 onFrustumChange(); 753 } 754 755 /** 756 * <code>setFrustumPerspective</code> defines the frustum for the camera. This 757 * frustum is defined by a viewing angle, aspect ratio, and near/far planes 758 * 759 * @param fovY Frame of view angle along the Y in degrees. 760 * @param aspect Width:Height ratio 761 * @param near Near view plane distance 762 * @param far Far view plane distance 763 */ 764 public void setFrustumPerspective(float fovY, float aspect, float near, 765 float far) { 766 if (Float.isNaN(aspect) || Float.isInfinite(aspect)) { 767 // ignore. 768 logger.log(Level.WARNING, "Invalid aspect given to setFrustumPerspective: {0}", aspect); 769 return; 770 } 771 772 float h = FastMath.tan(fovY * FastMath.DEG_TO_RAD * .5f) * near; 773 float w = h * aspect; 774 frustumLeft = -w; 775 frustumRight = w; 776 frustumBottom = -h; 777 frustumTop = h; 778 frustumNear = near; 779 frustumFar = far; 780 781 onFrustumChange(); 782 } 783 784 /** 785 * <code>setFrame</code> sets the orientation and location of the camera. 786 * 787 * @param location the point position of the camera. 788 * @param left the left axis of the camera. 789 * @param up the up axis of the camera. 790 * @param direction the facing of the camera. 791 * @see Camera#setFrame(com.jme3.math.Vector3f, 792 * com.jme3.math.Vector3f, com.jme3.math.Vector3f, com.jme3.math.Vector3f) 793 */ 794 public void setFrame(Vector3f location, Vector3f left, Vector3f up, 795 Vector3f direction) { 796 797 this.location = location; 798 this.rotation.fromAxes(left, up, direction); 799 onFrameChange(); 800 } 801 802 /** 803 * <code>lookAt</code> is a convienence method for auto-setting the frame 804 * based on a world position the user desires the camera to look at. It 805 * repoints the camera towards the given position using the difference 806 * between the position and the current camera location as a direction 807 * vector and the worldUpVector to compute up and left camera vectors. 808 * 809 * @param pos where to look at in terms of world coordinates 810 * @param worldUpVector a normalized vector indicating the up direction of the world. 811 * (typically {0, 1, 0} in jME.) 812 */ 813 public void lookAt(Vector3f pos, Vector3f worldUpVector) { 814 TempVars vars = TempVars.get(); 815 Vector3f newDirection = vars.vect1; 816 Vector3f newUp = vars.vect2; 817 Vector3f newLeft = vars.vect3; 818 819 newDirection.set(pos).subtractLocal(location).normalizeLocal(); 820 821 newUp.set(worldUpVector).normalizeLocal(); 822 if (newUp.equals(Vector3f.ZERO)) { 823 newUp.set(Vector3f.UNIT_Y); 824 } 825 826 newLeft.set(newUp).crossLocal(newDirection).normalizeLocal(); 827 if (newLeft.equals(Vector3f.ZERO)) { 828 if (newDirection.x != 0) { 829 newLeft.set(newDirection.y, -newDirection.x, 0f); 830 } else { 831 newLeft.set(0f, newDirection.z, -newDirection.y); 832 } 833 } 834 835 newUp.set(newDirection).crossLocal(newLeft).normalizeLocal(); 836 837 this.rotation.fromAxes(newLeft, newUp, newDirection); 838 this.rotation.normalizeLocal(); 839 vars.release(); 840 841 onFrameChange(); 842 } 843 844 /** 845 * <code>setFrame</code> sets the orientation and location of the camera. 846 * 847 * @param location 848 * the point position of the camera. 849 * @param axes 850 * the orientation of the camera. 851 */ 852 public void setFrame(Vector3f location, Quaternion axes) { 853 this.location = location; 854 this.rotation.set(axes); 855 onFrameChange(); 856 } 857 858 /** 859 * <code>update</code> updates the camera parameters by calling 860 * <code>onFrustumChange</code>,<code>onViewPortChange</code> and 861 * <code>onFrameChange</code>. 862 * 863 * @see Camera#update() 864 */ 865 public void update() { 866 onFrustumChange(); 867 onViewPortChange(); 868 onFrameChange(); 869 } 870 871 /** 872 * <code>getPlaneState</code> returns the state of the frustum planes. So 873 * checks can be made as to which frustum plane has been examined for 874 * culling thus far. 875 * 876 * @return the current plane state int. 877 */ 878 public int getPlaneState() { 879 return planeState; 880 } 881 882 /** 883 * <code>setPlaneState</code> sets the state to keep track of tested 884 * planes for culling. 885 * 886 * @param planeState the updated state. 887 */ 888 public void setPlaneState(int planeState) { 889 this.planeState = planeState; 890 } 891 892 /** 893 * <code>getViewPortLeft</code> gets the left boundary of the viewport 894 * 895 * @return the left boundary of the viewport 896 */ 897 public float getViewPortLeft() { 898 return viewPortLeft; 899 } 900 901 /** 902 * <code>setViewPortLeft</code> sets the left boundary of the viewport 903 * 904 * @param left the left boundary of the viewport 905 */ 906 public void setViewPortLeft(float left) { 907 viewPortLeft = left; 908 onViewPortChange(); 909 } 910 911 /** 912 * <code>getViewPortRight</code> gets the right boundary of the viewport 913 * 914 * @return the right boundary of the viewport 915 */ 916 public float getViewPortRight() { 917 return viewPortRight; 918 } 919 920 /** 921 * <code>setViewPortRight</code> sets the right boundary of the viewport 922 * 923 * @param right the right boundary of the viewport 924 */ 925 public void setViewPortRight(float right) { 926 viewPortRight = right; 927 onViewPortChange(); 928 } 929 930 /** 931 * <code>getViewPortTop</code> gets the top boundary of the viewport 932 * 933 * @return the top boundary of the viewport 934 */ 935 public float getViewPortTop() { 936 return viewPortTop; 937 } 938 939 /** 940 * <code>setViewPortTop</code> sets the top boundary of the viewport 941 * 942 * @param top the top boundary of the viewport 943 */ 944 public void setViewPortTop(float top) { 945 viewPortTop = top; 946 onViewPortChange(); 947 } 948 949 /** 950 * <code>getViewPortBottom</code> gets the bottom boundary of the viewport 951 * 952 * @return the bottom boundary of the viewport 953 */ 954 public float getViewPortBottom() { 955 return viewPortBottom; 956 } 957 958 /** 959 * <code>setViewPortBottom</code> sets the bottom boundary of the viewport 960 * 961 * @param bottom the bottom boundary of the viewport 962 */ 963 public void setViewPortBottom(float bottom) { 964 viewPortBottom = bottom; 965 onViewPortChange(); 966 } 967 968 /** 969 * <code>setViewPort</code> sets the boundaries of the viewport 970 * 971 * @param left the left boundary of the viewport (default: 0) 972 * @param right the right boundary of the viewport (default: 1) 973 * @param bottom the bottom boundary of the viewport (default: 0) 974 * @param top the top boundary of the viewport (default: 1) 975 */ 976 public void setViewPort(float left, float right, float bottom, float top) { 977 this.viewPortLeft = left; 978 this.viewPortRight = right; 979 this.viewPortBottom = bottom; 980 this.viewPortTop = top; 981 onViewPortChange(); 982 } 983 984 /** 985 * Returns the pseudo distance from the given position to the near 986 * plane of the camera. This is used for render queue sorting. 987 * @param pos The position to compute a distance to. 988 * @return Distance from the far plane to the point. 989 */ 990 public float distanceToNearPlane(Vector3f pos) { 991 return worldPlane[NEAR_PLANE].pseudoDistance(pos); 992 } 993 994 /** 995 * <code>contains</code> tests a bounding volume against the planes of the 996 * camera's frustum. The frustums planes are set such that the normals all 997 * face in towards the viewable scene. Therefore, if the bounding volume is 998 * on the negative side of the plane is can be culled out. 999 * 1000 * NOTE: This method is used internally for culling, for public usage, 1001 * the plane state of the bounding volume must be saved and restored, e.g: 1002 * <code>BoundingVolume bv;<br/> 1003 * Camera c;<br/> 1004 * int planeState = bv.getPlaneState();<br/> 1005 * bv.setPlaneState(0);<br/> 1006 * c.contains(bv);<br/> 1007 * bv.setPlaneState(plateState);<br/> 1008 * </code> 1009 * 1010 * @param bound the bound to check for culling 1011 * @return See enums in <code>FrustumIntersect</code> 1012 */ 1013 public FrustumIntersect contains(BoundingVolume bound) { 1014 if (bound == null) { 1015 return FrustumIntersect.Inside; 1016 } 1017 1018 int mask; 1019 FrustumIntersect rVal = FrustumIntersect.Inside; 1020 1021 for (int planeCounter = FRUSTUM_PLANES; planeCounter >= 0; planeCounter--) { 1022 if (planeCounter == bound.getCheckPlane()) { 1023 continue; // we have already checked this plane at first iteration 1024 } 1025 int planeId = (planeCounter == FRUSTUM_PLANES) ? bound.getCheckPlane() : planeCounter; 1026 // int planeId = planeCounter; 1027 1028 mask = 1 << (planeId); 1029 if ((planeState & mask) == 0) { 1030 Plane.Side side = bound.whichSide(worldPlane[planeId]); 1031 1032 if (side == Plane.Side.Negative) { 1033 //object is outside of frustum 1034 bound.setCheckPlane(planeId); 1035 return FrustumIntersect.Outside; 1036 } else if (side == Plane.Side.Positive) { 1037 //object is visible on *this* plane, so mark this plane 1038 //so that we don't check it for sub nodes. 1039 planeState |= mask; 1040 } else { 1041 rVal = FrustumIntersect.Intersects; 1042 } 1043 } 1044 } 1045 1046 return rVal; 1047 } 1048 1049 /** 1050 * <code>containsGui</code> tests a bounding volume against the ortho 1051 * bounding box of the camera. A bounding box spanning from 1052 * 0, 0 to Width, Height. Constrained by the viewport settings on the 1053 * camera. 1054 * 1055 * @param bound the bound to check for culling 1056 * @return True if the camera contains the gui element bounding volume. 1057 */ 1058 public boolean containsGui(BoundingVolume bound) { 1059 return guiBounding.intersects(bound); 1060 } 1061 1062 /** 1063 * @return the view matrix of the camera. 1064 * The view matrix transforms world space into eye space. 1065 * This matrix is usually defined by the position and 1066 * orientation of the camera. 1067 */ 1068 public Matrix4f getViewMatrix() { 1069 return viewMatrix; 1070 } 1071 1072 /** 1073 * Overrides the projection matrix used by the camera. Will 1074 * use the matrix for computing the view projection matrix as well. 1075 * Use null argument to return to normal functionality. 1076 * 1077 * @param projMatrix 1078 */ 1079 public void setProjectionMatrix(Matrix4f projMatrix) { 1080 projectionMatrixOverride = projMatrix; 1081 updateViewProjection(); 1082 } 1083 1084 /** 1085 * @return the projection matrix of the camera. 1086 * The view projection matrix transforms eye space into clip space. 1087 * This matrix is usually defined by the viewport and perspective settings 1088 * of the camera. 1089 */ 1090 public Matrix4f getProjectionMatrix() { 1091 if (projectionMatrixOverride != null) { 1092 return projectionMatrixOverride; 1093 } 1094 1095 return projectionMatrix; 1096 } 1097 1098 /** 1099 * Updates the view projection matrix. 1100 */ 1101 public void updateViewProjection() { 1102 if (projectionMatrixOverride != null) { 1103 viewProjectionMatrix.set(projectionMatrixOverride).multLocal(viewMatrix); 1104 } else { 1105 //viewProjectionMatrix.set(viewMatrix).multLocal(projectionMatrix); 1106 viewProjectionMatrix.set(projectionMatrix).multLocal(viewMatrix); 1107 } 1108 } 1109 1110 /** 1111 * @return The result of multiplying the projection matrix by the view 1112 * matrix. This matrix is required for rendering an object. It is 1113 * precomputed so as to not compute it every time an object is rendered. 1114 */ 1115 public Matrix4f getViewProjectionMatrix() { 1116 return viewProjectionMatrix; 1117 } 1118 1119 /** 1120 * @return True if the viewport (width, height, left, right, bottom, up) 1121 * has been changed. This is needed in the renderer so that the proper 1122 * viewport can be set-up. 1123 */ 1124 public boolean isViewportChanged() { 1125 return viewportChanged; 1126 } 1127 1128 /** 1129 * Clears the viewport changed flag once it has been updated inside 1130 * the renderer. 1131 */ 1132 public void clearViewportChanged() { 1133 viewportChanged = false; 1134 } 1135 1136 /** 1137 * Called when the viewport has been changed. 1138 */ 1139 public void onViewPortChange() { 1140 viewportChanged = true; 1141 setGuiBounding(); 1142 } 1143 1144 private void setGuiBounding() { 1145 float sx = width * viewPortLeft; 1146 float ex = width * viewPortRight; 1147 float sy = height * viewPortBottom; 1148 float ey = height * viewPortTop; 1149 float xExtent = Math.max(0f, (ex - sx) / 2f); 1150 float yExtent = Math.max(0f, (ey - sy) / 2f); 1151 guiBounding.setCenter(new Vector3f(sx + xExtent, sy + yExtent, 0)); 1152 guiBounding.setXExtent(xExtent); 1153 guiBounding.setYExtent(yExtent); 1154 guiBounding.setZExtent(Float.MAX_VALUE); 1155 } 1156 1157 /** 1158 * <code>onFrustumChange</code> updates the frustum to reflect any changes 1159 * made to the planes. The new frustum values are kept in a temporary 1160 * location for use when calculating the new frame. The projection 1161 * matrix is updated to reflect the current values of the frustum. 1162 */ 1163 public void onFrustumChange() { 1164 if (!isParallelProjection()) { 1165 float nearSquared = frustumNear * frustumNear; 1166 float leftSquared = frustumLeft * frustumLeft; 1167 float rightSquared = frustumRight * frustumRight; 1168 float bottomSquared = frustumBottom * frustumBottom; 1169 float topSquared = frustumTop * frustumTop; 1170 1171 float inverseLength = FastMath.invSqrt(nearSquared + leftSquared); 1172 coeffLeft[0] = frustumNear * inverseLength; 1173 coeffLeft[1] = -frustumLeft * inverseLength; 1174 1175 inverseLength = FastMath.invSqrt(nearSquared + rightSquared); 1176 coeffRight[0] = -frustumNear * inverseLength; 1177 coeffRight[1] = frustumRight * inverseLength; 1178 1179 inverseLength = FastMath.invSqrt(nearSquared + bottomSquared); 1180 coeffBottom[0] = frustumNear * inverseLength; 1181 coeffBottom[1] = -frustumBottom * inverseLength; 1182 1183 inverseLength = FastMath.invSqrt(nearSquared + topSquared); 1184 coeffTop[0] = -frustumNear * inverseLength; 1185 coeffTop[1] = frustumTop * inverseLength; 1186 } else { 1187 coeffLeft[0] = 1; 1188 coeffLeft[1] = 0; 1189 1190 coeffRight[0] = -1; 1191 coeffRight[1] = 0; 1192 1193 coeffBottom[0] = 1; 1194 coeffBottom[1] = 0; 1195 1196 coeffTop[0] = -1; 1197 coeffTop[1] = 0; 1198 } 1199 1200 projectionMatrix.fromFrustum(frustumNear, frustumFar, frustumLeft, frustumRight, frustumTop, frustumBottom, parallelProjection); 1201 // projectionMatrix.transposeLocal(); 1202 1203 // The frame is effected by the frustum values 1204 // update it as well 1205 onFrameChange(); 1206 } 1207 1208 /** 1209 * <code>onFrameChange</code> updates the view frame of the camera. 1210 */ 1211 public void onFrameChange() { 1212 TempVars vars = TempVars.get(); 1213 1214 Vector3f left = getLeft(vars.vect1); 1215 Vector3f direction = getDirection(vars.vect2); 1216 Vector3f up = getUp(vars.vect3); 1217 1218 float dirDotLocation = direction.dot(location); 1219 1220 // left plane 1221 Vector3f leftPlaneNormal = worldPlane[LEFT_PLANE].getNormal(); 1222 leftPlaneNormal.x = left.x * coeffLeft[0]; 1223 leftPlaneNormal.y = left.y * coeffLeft[0]; 1224 leftPlaneNormal.z = left.z * coeffLeft[0]; 1225 leftPlaneNormal.addLocal(direction.x * coeffLeft[1], direction.y 1226 * coeffLeft[1], direction.z * coeffLeft[1]); 1227 worldPlane[LEFT_PLANE].setConstant(location.dot(leftPlaneNormal)); 1228 1229 // right plane 1230 Vector3f rightPlaneNormal = worldPlane[RIGHT_PLANE].getNormal(); 1231 rightPlaneNormal.x = left.x * coeffRight[0]; 1232 rightPlaneNormal.y = left.y * coeffRight[0]; 1233 rightPlaneNormal.z = left.z * coeffRight[0]; 1234 rightPlaneNormal.addLocal(direction.x * coeffRight[1], direction.y 1235 * coeffRight[1], direction.z * coeffRight[1]); 1236 worldPlane[RIGHT_PLANE].setConstant(location.dot(rightPlaneNormal)); 1237 1238 // bottom plane 1239 Vector3f bottomPlaneNormal = worldPlane[BOTTOM_PLANE].getNormal(); 1240 bottomPlaneNormal.x = up.x * coeffBottom[0]; 1241 bottomPlaneNormal.y = up.y * coeffBottom[0]; 1242 bottomPlaneNormal.z = up.z * coeffBottom[0]; 1243 bottomPlaneNormal.addLocal(direction.x * coeffBottom[1], direction.y 1244 * coeffBottom[1], direction.z * coeffBottom[1]); 1245 worldPlane[BOTTOM_PLANE].setConstant(location.dot(bottomPlaneNormal)); 1246 1247 // top plane 1248 Vector3f topPlaneNormal = worldPlane[TOP_PLANE].getNormal(); 1249 topPlaneNormal.x = up.x * coeffTop[0]; 1250 topPlaneNormal.y = up.y * coeffTop[0]; 1251 topPlaneNormal.z = up.z * coeffTop[0]; 1252 topPlaneNormal.addLocal(direction.x * coeffTop[1], direction.y 1253 * coeffTop[1], direction.z * coeffTop[1]); 1254 worldPlane[TOP_PLANE].setConstant(location.dot(topPlaneNormal)); 1255 1256 if (isParallelProjection()) { 1257 worldPlane[LEFT_PLANE].setConstant(worldPlane[LEFT_PLANE].getConstant() + frustumLeft); 1258 worldPlane[RIGHT_PLANE].setConstant(worldPlane[RIGHT_PLANE].getConstant() - frustumRight); 1259 worldPlane[TOP_PLANE].setConstant(worldPlane[TOP_PLANE].getConstant() - frustumTop); 1260 worldPlane[BOTTOM_PLANE].setConstant(worldPlane[BOTTOM_PLANE].getConstant() + frustumBottom); 1261 } 1262 1263 // far plane 1264 worldPlane[FAR_PLANE].setNormal(left); 1265 worldPlane[FAR_PLANE].setNormal(-direction.x, -direction.y, -direction.z); 1266 worldPlane[FAR_PLANE].setConstant(-(dirDotLocation + frustumFar)); 1267 1268 // near plane 1269 worldPlane[NEAR_PLANE].setNormal(direction.x, direction.y, direction.z); 1270 worldPlane[NEAR_PLANE].setConstant(dirDotLocation + frustumNear); 1271 1272 viewMatrix.fromFrame(location, direction, up, left); 1273 1274 vars.release(); 1275 1276 // viewMatrix.transposeLocal(); 1277 updateViewProjection(); 1278 } 1279 1280 /** 1281 * @return true if parallel projection is enable, false if in normal perspective mode 1282 * @see #setParallelProjection(boolean) 1283 */ 1284 public boolean isParallelProjection() { 1285 return this.parallelProjection; 1286 } 1287 1288 /** 1289 * Enable/disable parallel projection. 1290 * 1291 * @param value true to set up this camera for parallel projection is enable, false to enter normal perspective mode 1292 */ 1293 public void setParallelProjection(final boolean value) { 1294 this.parallelProjection = value; 1295 onFrustumChange(); 1296 } 1297 1298 /** 1299 * @see Camera#getWorldCoordinates 1300 */ 1301 public Vector3f getWorldCoordinates(Vector2f screenPos, float zPos) { 1302 return getWorldCoordinates(screenPos, zPos, null); 1303 } 1304 1305 /** 1306 * @see Camera#getWorldCoordinates 1307 */ 1308 public Vector3f getWorldCoordinates(Vector2f screenPosition, 1309 float zPos, Vector3f store) { 1310 if (store == null) { 1311 store = new Vector3f(); 1312 } 1313 1314 Matrix4f inverseMat = new Matrix4f(viewProjectionMatrix); 1315 inverseMat.invertLocal(); 1316 1317 store.set( 1318 (screenPosition.x / getWidth() - viewPortLeft) / (viewPortRight - viewPortLeft) * 2 - 1, 1319 (screenPosition.y / getHeight() - viewPortBottom) / (viewPortTop - viewPortBottom) * 2 - 1, 1320 zPos * 2 - 1); 1321 1322 float w = inverseMat.multProj(store, store); 1323 store.multLocal(1f / w); 1324 1325 return store; 1326 } 1327 1328 /** 1329 * Converts the given position from world space to screen space. 1330 * 1331 * @see Camera#getScreenCoordinates 1332 */ 1333 public Vector3f getScreenCoordinates(Vector3f worldPos) { 1334 return getScreenCoordinates(worldPos, null); 1335 } 1336 1337 /** 1338 * Converts the given position from world space to screen space. 1339 * 1340 * @see Camera#getScreenCoordinates(Vector3f, Vector3f) 1341 */ 1342 public Vector3f getScreenCoordinates(Vector3f worldPosition, Vector3f store) { 1343 if (store == null) { 1344 store = new Vector3f(); 1345 } 1346 1347 // TempVars vars = vars.lock(); 1348 // Quaternion tmp_quat = vars.quat1; 1349 // tmp_quat.set( worldPosition.x, worldPosition.y, worldPosition.z, 1 ); 1350 // viewProjectionMatrix.mult(tmp_quat, tmp_quat); 1351 // tmp_quat.multLocal( 1.0f / tmp_quat.getW() ); 1352 // store.x = ( ( tmp_quat.getX() + 1 ) * ( viewPortRight - viewPortLeft ) / 2 + viewPortLeft ) * getWidth(); 1353 // store.y = ( ( tmp_quat.getY() + 1 ) * ( viewPortTop - viewPortBottom ) / 2 + viewPortBottom ) * getHeight(); 1354 // store.z = ( tmp_quat.getZ() + 1 ) / 2; 1355 // vars.release(); 1356 1357 float w = viewProjectionMatrix.multProj(worldPosition, store); 1358 store.divideLocal(w); 1359 1360 store.x = ((store.x + 1f) * (viewPortRight - viewPortLeft) / 2f + viewPortLeft) * getWidth(); 1361 store.y = ((store.y + 1f) * (viewPortTop - viewPortBottom) / 2f + viewPortBottom) * getHeight(); 1362 store.z = (store.z + 1f) / 2f; 1363 1364 return store; 1365 } 1366 1367 /** 1368 * @return the width/resolution of the display. 1369 */ 1370 public int getWidth() { 1371 return width; 1372 } 1373 1374 /** 1375 * @return the height/resolution of the display. 1376 */ 1377 public int getHeight() { 1378 return height; 1379 } 1380 1381 @Override 1382 public String toString() { 1383 return "Camera[location=" + location + "\n, direction=" + getDirection() + "\n" 1384 + "res=" + width + "x" + height + ", parallel=" + parallelProjection + "\n" 1385 + "near=" + frustumNear + ", far=" + frustumFar + "]"; 1386 } 1387 1388 public void write(JmeExporter e) throws IOException { 1389 OutputCapsule capsule = e.getCapsule(this); 1390 capsule.write(location, "location", Vector3f.ZERO); 1391 capsule.write(rotation, "rotation", Quaternion.DIRECTION_Z); 1392 capsule.write(frustumNear, "frustumNear", 1); 1393 capsule.write(frustumFar, "frustumFar", 2); 1394 capsule.write(frustumLeft, "frustumLeft", -0.5f); 1395 capsule.write(frustumRight, "frustumRight", 0.5f); 1396 capsule.write(frustumTop, "frustumTop", 0.5f); 1397 capsule.write(frustumBottom, "frustumBottom", -0.5f); 1398 capsule.write(coeffLeft, "coeffLeft", new float[2]); 1399 capsule.write(coeffRight, "coeffRight", new float[2]); 1400 capsule.write(coeffBottom, "coeffBottom", new float[2]); 1401 capsule.write(coeffTop, "coeffTop", new float[2]); 1402 capsule.write(viewPortLeft, "viewPortLeft", 0); 1403 capsule.write(viewPortRight, "viewPortRight", 1); 1404 capsule.write(viewPortTop, "viewPortTop", 1); 1405 capsule.write(viewPortBottom, "viewPortBottom", 0); 1406 capsule.write(width, "width", 0); 1407 capsule.write(height, "height", 0); 1408 capsule.write(name, "name", null); 1409 } 1410 1411 public void read(JmeImporter e) throws IOException { 1412 InputCapsule capsule = e.getCapsule(this); 1413 location = (Vector3f) capsule.readSavable("location", Vector3f.ZERO.clone()); 1414 rotation = (Quaternion) capsule.readSavable("rotation", Quaternion.DIRECTION_Z.clone()); 1415 frustumNear = capsule.readFloat("frustumNear", 1); 1416 frustumFar = capsule.readFloat("frustumFar", 2); 1417 frustumLeft = capsule.readFloat("frustumLeft", -0.5f); 1418 frustumRight = capsule.readFloat("frustumRight", 0.5f); 1419 frustumTop = capsule.readFloat("frustumTop", 0.5f); 1420 frustumBottom = capsule.readFloat("frustumBottom", -0.5f); 1421 coeffLeft = capsule.readFloatArray("coeffLeft", new float[2]); 1422 coeffRight = capsule.readFloatArray("coeffRight", new float[2]); 1423 coeffBottom = capsule.readFloatArray("coeffBottom", new float[2]); 1424 coeffTop = capsule.readFloatArray("coeffTop", new float[2]); 1425 viewPortLeft = capsule.readFloat("viewPortLeft", 0); 1426 viewPortRight = capsule.readFloat("viewPortRight", 1); 1427 viewPortTop = capsule.readFloat("viewPortTop", 1); 1428 viewPortBottom = capsule.readFloat("viewPortBottom", 0); 1429 width = capsule.readInt("width", 1); 1430 height = capsule.readInt("height", 1); 1431 name = capsule.readString("name", null); 1432 onFrustumChange(); 1433 onViewPortChange(); 1434 onFrameChange(); 1435 } 1436 } 1437