Commits

Michael Ludwig  committed 96dac6b

Update Renderable component to new system, and simplify interface for Camera to be a perspective only frustum.

  • Participants
  • Parent commits e0e7570

Comments (0)

Files changed (2)

File ferox-scene/src/main/java/com/ferox/scene/Camera.java

 package com.ferox.scene;
 
-import com.ferox.math.bounds.Frustum;
 import com.ferox.renderer.Surface;
-import com.ferox.scene.controller.CameraController;
-import com.googlecode.entreri.Component;
-import com.googlecode.entreri.EntitySystem;
-import com.googlecode.entreri.InitParams;
-import com.googlecode.entreri.TypedId;
-import com.googlecode.entreri.property.IntProperty;
-import com.googlecode.entreri.property.ObjectProperty;
-import com.googlecode.entreri.property.Parameter;
+import com.lhkbob.entreri.ComponentData;
+import com.lhkbob.entreri.TypeId;
+import com.lhkbob.entreri.annot.DefaultValue;
+import com.lhkbob.entreri.property.DoubleProperty;
+import com.lhkbob.entreri.property.ObjectProperty;
 
 /**
  * <p>
- * Camera is a Component that specifies the viewing point and projection
- * information necessary for rendering a scene onto a {@link Surface}. Cameras
- * are linked with a Surface that acts the target for any rendering performed
- * with the Camera. Each Camera contains a single {@link Frustum} that stores
- * the location, orientation and projection information to use when rendering.
- * </p>
- * <p>
- * Initially each Frustum (and thus Camera) uses a location of (0, 0, 0) with a
- * right-handed coordinate system pointing down the negative z-axis. A Camera
- * can be positioned manually by updating the orientation of its associated
- * Frustum. Alternatively, a {@link CameraController} can be used to have a
- * Camera match the position and direction provided by a {@link Transform}.
- * </p>
- * <p>
- * Camera defines a single initialization parameter representing the Surface it
- * is attached to. By default the frustum is configured to be a perspective
- * frustum with an aspect ratio matching the ratio of the surface.
+ * Camera is a Component that specifies the viewing settings for a "camera" into
+ * the scene of the EntitySystem. It represents a perspective projection and
+ * stores the field of view, near and far z planes. It is also attached to a
+ * {@link Surface} to actually render into. This surface determines the aspect
+ * ratio that must be used when rendering with this camera. Additionally, the
+ * camera takes its position and orientation from any transform-providing
+ * component attached to the same entity.
  * </p>
  * 
  * @author Michael Ludwig
  */
-@InitParams(Surface.class)
-public final class Camera extends Component {
+public final class Camera extends ComponentData<Camera> {
     /**
      * The shared TypedId instance corresponding to Camera.
      */
-    public static final TypedId<Camera> ID = Component.getTypedId(Camera.class);
-    
-    // Indexes into the viewport bulk property
-    private static final int LEFT = 0;
-    private static final int RIGHT = 1;
-    private static final int BOTTOM = 2;
-    private static final int TOP = 3;
-    private static final int SCALE = 4;
+    public static final TypeId<Camera> ID = TypeId.get(Camera.class);
     
     private ObjectProperty<Surface> surface;
-    private ObjectProperty<Frustum> frustum;
     
-    @Parameter(type=int.class, value="5")
-    private IntProperty viewport;
-
-    private Camera(EntitySystem system, int index) {
-        super(system, index);
+    @DefaultValue(defaultDouble=60.0)
+    private DoubleProperty fov;
+    
+    @DefaultValue(defaultDouble=0.01)
+    private DoubleProperty znear;
+    
+    @DefaultValue(defaultDouble=100.0)
+    private DoubleProperty zfar;
+    
+    private Camera() { }
+    
+    /**
+     * @return The field of view for this Camera, in degrees
+     */
+    public double getFieldOfView() {
+        return fov.get(getIndex(), 0);
     }
     
-    @Override
-    protected void init(Object... initParams) {
-        frustum.set(new Frustum(60f, 1f, .1f, 100f), getIndex(), 0);
-        setSurface((Surface) initParams[0]);
+    /**
+     * Set the field of view for this Camera, in degrees.
+     * 
+     * @param fov The new field of view
+     * @return This Camera for chaining purposes
+     * @throws IllegalArgumentException if fov is less than 0 or greater than
+     *             180
+     */
+    public Camera setFieldOfView(double fov) {
+        if (fov < 0.0 || fov > 180.0)
+            throw new IllegalArgumentException("Field of view must be in [0, 180]: " + fov);
+        this.fov.set(fov, getIndex(), 0);
+        return this;
+    }
+    
+    /**
+     * @return The distance to the near z plane of the camera
+     */
+    public double getNearZDistance() {
+        return znear.get(getIndex(), 0);
+    }
+    
+    /**
+     * Set the distance to the near and far z planes.
+     * 
+     * @param znear The new near distance
+     * @param zfar The new far distance
+     * @return This Camera for chaining purposes
+     * @throws IllegalArgumentException if znear is less than or equal to 0, or
+     *             if zfar is less than znear
+     */
+    public Camera setZDistances(double znear, double zfar) {
+        if (znear <= 0.0)
+            throw new IllegalArgumentException("Near distances must be greater than 0: " + znear);
+        if (znear > zfar)
+            throw new IllegalArgumentException("Near distance must be less than far: " + znear + ", " + zfar);
+        this.znear.set(znear, getIndex(), 0);
+        this.zfar.set(zfar, getIndex(), 0);
+        return this;
+    }
+    
+    /**
+     * @return The distance to the far z plane of the camera
+     */
+    public double getFarZDistance() {
+        return zfar.get(getIndex(), 0);
     }
 
     /**
-     * <p>
-     * Return the Frustum instance that represents how a rendered image should
-     * be projected onto the {@link #getSurface() Surface of this Camera}. The
-     * Frustum may be modified to change how things are rendered.
-     * </p>
-     * <p>
-     * However, keep in mind that the default behavior of the
-     * {@link CameraController} is to preserve aspect ratios of the Frustum when
-     * the Surface changes size.
-     * </p>
-     * 
-     * @return The Frustum representing the projection for this Camera
-     */
-    public Frustum getFrustum() {
-        return frustum.get(getIndex(), 0);
-    }
-
-    /**
-     * Return the Surface that this Camera is linked to. This will be
-     * null if the camera is meant to be "disabled".
+     * Return the Surface that this Camera is linked to.
      * 
      * @return The Surface of this Camera
      */
     }
 
     /**
-     * Set the current Surface of this Camera, and update its Frustum to be a
-     * perspective projection fitting the surface's dimensions.
+     * Set the current Surface of this Camera.
      * 
      * @param surface The new surface
      * @return This camera for chaining purposes
      * @throws NullPointerException if surface is null
      */
     public Camera setSurface(Surface surface) {
-        return setSurface(surface, true);
-    }
-
-    /**
-     * <p>
-     * Set the Surface that this Camera is linked to. If <tt>perspective</tt> is
-     * true, the frustum will be updated to fit the surface's aspect ratio and
-     * be a perspective projection. If it is false, the it will be set to an
-     * orthographic projection fitted to the dimensions of the surface.
-     * </p>
-     * <p>
-     * If the frustum was previously a perspective projection and it's being
-     * updated to a perspective projection, the field-of-view and near and far
-     * planes will be preserved; otherwise a field-of-view of 60 and planes at
-     * .1 and 100 will be used. Similarly, if the frustum was previously an
-     * orthographic projection and it's updated to an orthographic projection,
-     * the near and far planes will be preserved; otherwise they will be updated
-     * to -1 and 1.
-     * </p>
-     * 
-     * @param surface The Surface that this camera will use
-     * @param perspective True if the frustum should be a perspective transform,
-     *            or false for an orthographic one
-     * @return This camera for chaining purposes
-     * @throws NullPointerException if surface is null
-     */
-    public Camera setSurface(Surface surface, boolean perspective) {
-        if (surface == null)
-            throw new NullPointerException("Surface cannot be null");
-        
         this.surface.set(surface, getIndex(), 0);
-        // update the frustum
-        Frustum f = getFrustum();
-        if (perspective) {
-            float znear = f.getFrustumNear();
-            float zfar = f.getFrustumFar();
-            float fov = f.getFieldOfView();
-            if (f.isOrthogonalProjection()) {
-                znear = .1f;
-                zfar = 100f;
-                fov = 60f;
-            }
-
-            f.setPerspective(fov, surface.getWidth() / (float) surface.getHeight(), znear, zfar);
-        } else {
-            float znear = f.getFrustumNear();
-            float zfar = f.getFrustumFar();
-            if (!f.isOrthogonalProjection()) {
-                znear = -1f;
-                zfar = 1f;
-            }
-            f.setFrustum(true, 0, surface.getWidth(), 0, surface.getHeight(), znear, zfar);
-        }
-        
-        viewport.set(0, getIndex(), LEFT);
-        viewport.set(surface.getWidth(), getIndex(), RIGHT);
-        viewport.set(0, getIndex(), BOTTOM);
-        viewport.set(surface.getHeight(), getIndex(), TOP);
-        
-        return this;
-    }
-    
-    public int getViewportLeft() {
-        return viewport.get(getIndex(), LEFT);
-    }
-    
-    public Camera setViewportLeft(int left) {
-        viewport.set(left, getIndex(), LEFT);
-        return this;
-    }
-    
-    public int getViewportRight() {
-        return viewport.get(getIndex(), RIGHT);
-    }
-    
-    public Camera setViewportRight(int right) {
-        viewport.set(right, getIndex(), RIGHT);
-        return this;
-    }
-    
-    public int getViewportBottom() {
-        return viewport.get(getIndex(), BOTTOM);
-    }
-    
-    public Camera setViewportBottom(int bottom) {
-        viewport.set(bottom, getIndex(), BOTTOM);
-        return this;
-    }
-    
-    public int getViewportTop() {
-        return viewport.get(getIndex(), TOP);
-    }
-    
-    public Camera setViewportTop(int top) {
-        viewport.set(top, getIndex(), TOP);
-        return this;
-    }
-    
-    public Camera setViewport(int left, int right, int bottom, int top) {
-        return setViewportLeft(left)
-              .setViewportRight(right)
-              .setViewportBottom(bottom)
-              .setViewportTop(top);
-    }
-    
-    public boolean isViewportScaled() {
-        return viewport.get(getIndex(), SCALE) != 0;
-    }
-    
-    public Camera setViewportScaled(boolean scale) {
-        if (scale)
-            viewport.set(1, getIndex(), SCALE);
-        else
-            viewport.set(0, getIndex(), SCALE);
         return this;
     }
 }

File ferox-scene/src/main/java/com/ferox/scene/Renderable.java

 package com.ferox.scene;
 
+import com.ferox.math.Const;
 import com.ferox.math.bounds.AxisAlignedBox;
 import com.ferox.math.bounds.Frustum;
-import com.ferox.math.bounds.ReadOnlyAxisAlignedBox;
 import com.ferox.math.entreri.AxisAlignedBoxProperty;
 import com.ferox.renderer.Renderer.DrawStyle;
 import com.ferox.renderer.Renderer.PolygonType;
 import com.ferox.resource.VertexAttribute;
 import com.ferox.resource.VertexBufferObject;
 import com.ferox.util.geom.Geometry;
-import com.googlecode.entreri.Component;
-import com.googlecode.entreri.Controller;
-import com.googlecode.entreri.Entity;
-import com.googlecode.entreri.EntitySystem;
-import com.googlecode.entreri.InitParams;
-import com.googlecode.entreri.TypedId;
-import com.googlecode.entreri.property.IntProperty;
-import com.googlecode.entreri.property.ObjectProperty;
-import com.googlecode.entreri.property.Parameter;
+import com.lhkbob.entreri.Controller;
+import com.lhkbob.entreri.Entity;
+import com.lhkbob.entreri.TypeId;
+import com.lhkbob.entreri.annot.ElementSize;
+import com.lhkbob.entreri.annot.Unmanaged;
+import com.lhkbob.entreri.property.IntProperty;
+import com.lhkbob.entreri.property.ObjectProperty;
 
 /**
  * Renderable is a Component that enables an Entity to be rendered. It provides
  * can be used to describe the materials, shaders and textures used to color and
  * light the rendered Entity.
  * </p>
- * <p>
- * Renderable defines one initialization parameter: the VertexAttribute
- * representing its vertices. It defaults to using no indices, a PolygonType of
- * POINT, and an index offset and count of 0. It is highly recommended that
- * adding a Renderable, is immediately followed up with a call to
- * {@link #setIndices(PolygonType, VertexBufferObject, int, int)}.
- * </p>
  * 
  * @author Michael Ludwig
  */
-@InitParams({ VertexAttribute.class })
-public final class Renderable extends EntitySetComponent {
+public final class Renderable extends EntitySetComponent<Renderable> {
     /**
      * The shared TypedId representing Renderable.
      */
-    public static final TypedId<Renderable> ID = Component.getTypedId(Renderable.class);
+    public static final TypeId<Renderable> ID = TypeId.get(Renderable.class);
+    
+    private static final int INDEX_OFFSET = 0;
+    private static final int INDEX_COUNT = 1;
     
     private ObjectProperty<VertexAttribute> vertices;
     private ObjectProperty<VertexBufferObject> indices;
     private ObjectProperty<PolygonType> polyType;
     
-    @Parameter(type=int.class, value="2")
+    @ElementSize(2)
     private IntProperty indexConfig; // 0 = offset, 1 = count
     
     private AxisAlignedBoxProperty localBounds;
     private AxisAlignedBoxProperty worldBounds;
+    
+    @Unmanaged
+    private final AxisAlignedBox localBoundsCache = new AxisAlignedBox();
+    
+    @Unmanaged
+    private final AxisAlignedBox worldBoundsCache = new AxisAlignedBox();
 
-    private Renderable(EntitySystem system, int index) {
-        super(system, index);
-    }
-    
-    @Override
-    protected void init(Object... initParams) {
-        super.init();
-        
-        setVertices((VertexAttribute) initParams[0]);
-        
-        // This is a little lame, but it will result in entirely valid
-        // geometry, so it's a good default
-        setArrayIndices(PolygonType.POINTS, 0, 0);
-    }
+    private Renderable() { }
     
     /**
      * Set the vertex attribute that holds the vertex position information for
         
         this.indices.set(indices, componentIndex, 0);
         polyType.set(type, componentIndex, 0);
-        indexConfig.set(first, componentIndex, 0);
-        indexConfig.set(count, componentIndex, 1);
+        indexConfig.set(first, componentIndex, INDEX_OFFSET);
+        indexConfig.set(count, componentIndex, INDEX_COUNT);
         
         return this;
     }
      *         array indices)
      */
     public int getIndexCount() {
-        return indexConfig.get(getIndex(), 1);
+        return indexConfig.get(getIndex(), INDEX_COUNT);
     }
 
     /**
      *         indices)
      */
     public int getIndexOffset() {
-        return indexConfig.get(getIndex(), 0);
+        return indexConfig.get(getIndex(), INDEX_OFFSET);
     }
     
     /**
     }
 
     /**
-     * Return the local bounds of the Renderable. The local bounds will be
-     * placed in <tt>store</tt>, or a new AxisAlignedBox will be created to hold
-     * the local bounds.
-     * 
-     * @return The local bounds
-     */
-    public AxisAlignedBox getLocalBounds(AxisAlignedBox store) {
-        return localBounds.get(getIndex(), store);
-    }
-
-    /**
-     * Return the local bounds of this Renderable within a read-only cached
-     * instance. This instance is shared across all renderables in the same
-     * system, so calling this method on another renderable will overwrite the
-     * returned bounds state. Use {@link #getLocalBounds(AxisAlignedBox)} or
-     * clone the returned instance manually if correctness is required outside
-     * of iteration.
+     * Return the local bounds of this Renderable. The returned AxisAlignedBox
+     * instance is reused by this Renderable instance so it should be cloned
+     * before changing which Component is referenced.
      * 
      * @return A cached local bounds instance
      */
-    public ReadOnlyAxisAlignedBox getLocalBounds() {
-        return localBounds.get(getIndex());
+    public @Const AxisAlignedBox getLocalBounds() {
+        return localBoundsCache;
     }
 
     /**
      * @return This component, for chaining purposes
      * @throws NullPointerException if bounds is null
      */
-    public Renderable setLocalBounds(ReadOnlyAxisAlignedBox bounds) {
+    public Renderable setLocalBounds(@Const AxisAlignedBox bounds) {
+        localBoundsCache.set(bounds);
         localBounds.set(bounds, getIndex());
         return this;
     }
 
     /**
-     * Return the world bounds of the Renderable. The world bounds will be
-     * placed in <tt>store</tt>, or a new AxisAlignedBox will be created to hold
-     * the local bounds. The returned world bounds may not accurately reflect
-     * the local bounds if the local bounds have been modified without
-     * recomputing the world bounds.
-     * 
-     * @return The world bounds
-     */
-    public AxisAlignedBox getWorldBounds(AxisAlignedBox store) {
-        return worldBounds.get(getIndex(), store);
-    }
-
-    /**
-     * Return the world bounds of this Renderable within a read-only cached
-     * instance. This instance is shared across all renderables in the same
-     * system, so calling this method on another renderable will overwrite the
-     * returned bounds state. Use {@link #getWorldBounds(AxisAlignedBox)} or
-     * clone the returned instance manually if correctness is required outside
-     * of iteration.
+     * Return the world bounds of this Renderable. The returned AxisAlignedBox
+     * instance is reused by this Renderable instance so it should be cloned
+     * before changing which Component is referenced.
      * 
      * @return A cached world bounds instance
      */
-    public ReadOnlyAxisAlignedBox getWorldBounds() {
-        return worldBounds.get(getIndex());
+    public @Const AxisAlignedBox getWorldBounds() {
+        return worldBoundsCache;
     }
 
     /**
      * @return This component, for chaining purposes
      * @throws NullPointerException if bounds is null
      */
-    public Renderable setWorldBounds(ReadOnlyAxisAlignedBox bounds) {
+    public Renderable setWorldBounds(@Const AxisAlignedBox bounds) {
+        worldBoundsCache.set(bounds);
         worldBounds.set(bounds, getIndex());
         return this;
     }
+    
+    @Override
+    protected void onSet(int index) {
+        worldBounds.get(index, worldBoundsCache);
+        localBounds.get(index, localBoundsCache);
+    }
 }