anim3D/src/PerspCameraGO.m3


Copyright (C) 1994, Digital Equipment Corp.
Digital Internal Use Only
                                                                           
       Created on Mon Feb 14 16:03:12 PST 1994 by najork                   

MODULE PerspCameraGO EXPORTS PerspCameraGO, PerspCameraGOProxy;

IMPORT CameraGO, CameraGOPrivate, GO, GOPrivate, GraphicsBase,
       GraphicsBasePrivate, Matrix4, Mth, Point3, PointProp, PointPropPrivate,
       Prop, RealProp, RealPropPrivate, TransformPropPrivate;

PROCEDURE New (from, to, up : Point3.T; fovy : REAL) : T =
  VAR
    cam := NEW (T).init ();
  BEGIN
    cam.setProp (CameraGO.From.bind (PointProp.NewConst (from)));
    cam.setProp (CameraGO.To.bind (PointProp.NewConst (to)));
    cam.setProp (CameraGO.Up.bind (PointProp.NewConst (up)));
    cam.setProp (Fovy.bind (RealProp.NewConst (fovy)));
    RETURN cam;
  END New;

REVEAL
  T = Public BRANDED OBJECT
    lookat   : Matrix4.T;
    distance : REAL;
    aspect   : REAL;
    fovy     : REAL;
  OVERRIDES
    init := Init;
    draw := Draw;
    view := View;
    damageIfDependent := DamageIfDependent;
  END;

PROCEDURE Init (self : T) : T =
  BEGIN
    EVAL CameraGO.T.init (self);

    IF MkProxyT # NIL AND self.proxy = NIL THEN
      MkProxyT (self);
    END;

    RETURN self;
  END Init;

PROCEDURE DamageIfDependent (self : T; pn : Prop.Name) =
  BEGIN
    IF pn = CameraGO.From OR pn = CameraGO.To OR pn = CameraGO.Up OR
       pn = CameraGO.Aspect OR pn = Fovy OR pn = GO.Transform THEN
      self.damaged := TRUE;
    END;
  END DamageIfDependent;

PROCEDURE Draw (self : T; state : GraphicsBase.T) =
  BEGIN
    IF self.damaged THEN
      state.push (self);

      WITH tm   = GO.Transform.getState (state),
           from = Matrix4.TransformPoint3 (tm, CameraGO.From.getState (state)),
           to   = Matrix4.TransformPoint3 (tm, CameraGO.To.getState (state)),
           up   = Matrix4.TransformPoint3 (tm, CameraGO.Up.getState (state)) DO
        self.lookat   := Matrix4.LookatViewMatrix (from, to, up);
        self.distance := Point3.Distance (from, to);
        self.flag     := TRUE;
        self.aspect   := CameraGO.Aspect.getState (state);
        self.fovy     := Fovy.getState (state);
        self.damaged  := FALSE;
      END;
      (* If the transformation state contains a non-uniform matrix,
         it is not possible to determine a correct value for fovy ... *)

      state.pop (self);
    END;
  END Draw;

PROCEDURE View (self : T; state : GraphicsBase.T) =
  VAR
    near : REAL;
    far  : REAL;
  BEGIN
    self.flag := FALSE;

    (*** map the bounding sphere into viewing coordinates ***)
    WITH bs = state.getBoundingVolume(),
         tm = self.lookat,
         center = Point3.T {
                      tm[0][0] * bs.center.x + tm[0][1] * bs.center.y +
                      tm[0][2] * bs.center.z + tm[0][3],
                      tm[1][0] * bs.center.x + tm[1][1] * bs.center.y +
                      tm[1][2] * bs.center.z + tm[1][3],
                      tm[2][0] * bs.center.x + tm[2][1] * bs.center.y +
                      tm[2][2] * bs.center.z + tm[2][3]},
         radius = bs.radius * Mth.sqrt (tm[0][0] * tm[0][0] +
                                        tm[1][0] * tm[1][0] +
                                        tm[2][0] * tm[2][0]) DO

      (* Needed: distance > near > far *)
      near := center.z + radius;
      far  := center.z - radius;

      (* Handling bad cases (cases where the bounding sphere intersects
         the viewing plane) *)
      IF self.distance <= near THEN
        near := self.distance - 0.1;
      END;
      IF near <= far THEN
        far := near - 0.1;
      END;
    END;

    (*** Set the viewing and projection transformations. ***)
    state.setViewProjTransform (self.lookat,
                                Matrix4.PerspProjMatrix (self.fovy,
                                                         self.distance,
                                                         self.aspect,
                                                         near, far));
  END View;
*************************************************************************** Module body ***************************************************************************

BEGIN
  Fovy := NEW (RealProp.Name).init (0.1);
END PerspCameraGO.