;;-*-Lisp-*-
(in-package goal)
(bundles "ENGINE.CGO" "GAME.CGO")
(require "engine/math/quaternion-h.gc")
(require "pc/pckernel-impl.gc")
(require "engine/ps2/vu1-macros.gc")
(require "engine/math/trigonometry.gc")
(require "engine/math/transform-h.gc")

;;;;;;;;;;;;;;;;;;;;;;
;; math camera basics
;;;;;;;;;;;;;;;;;;;;;;

;; the math camera computes the perspective matrix, hvdf offset, and hmge scale.

;; multiplying a point by the perspective matrix, doing the persepctive divide, then adding hvdf offset gives you:
;; H : horizontal position (in GS coordinates)
;; V : vertical position (in GS coordinates)
;; D : depth (as a 24-bit integer for the z buffer)
;; F : fog

;; Multiplying by hmge then checking the clipping flags can be used to see if a point is outside the view frustum.

;; The "camera-temp" matrix is the perspective matrix multplied by the camera tranformation and is used
;; renderers that want a single matrix.

;;;;;;;;;;;;;;;;;;;
;; fog correction
;;;;;;;;;;;;;;;;;;;

;; The math-camera matrices are used to compute fogging values, which are a per-vertex uint8 that
;; tells the GS how "foggy" the color should be. This should be proportional to how far away the vertex
;; is. There is a scaling factor applied so the fog intensity isn't affected by the field of view angle.

;; DECOMP BEGINS

;; The fog-corrector stores a fog-end fog-start value that is corrected for the field of view.
;; the actual correction factor is computed in cam-update.gc
(deftype fog-corrector (structure)
  ((fog-end   float)
   (fog-start float)))

(defun fog-corrector-setup ((corrector fog-corrector) (math-cam math-camera))
  "Set the fog corrector based on the supplied math-camera"
  (set! (-> corrector fog-end) (* (-> math-cam fog-end) (-> math-cam fov-correction-factor)))
  (set! (-> corrector fog-start) (* (-> math-cam fog-start) (-> math-cam fov-correction-factor)))
  (none))

(define *math-camera-fog-correction* (new 'global 'fog-corrector))

(define-extern sprite-distorter-generate-tables (function none))

(defun update-math-camera ((math-cam math-camera) (video-mode symbol) (aspect symbol))
  "Compute some one-time camera constants.  These should only change when changing aspect ratio."
  ;; the x/y ratio are frustum slopes
  (set! (-> math-cam x-ratio) (tan (* 0.5 (-> math-cam fov))))
  (if (= aspect 'aspect4x3)
    (set! (-> math-cam y-ratio) (* (1/ ASPECT_4X3) (-> math-cam x-ratio)))
    (set! (-> math-cam y-ratio) (* (1/ ASPECT_16X9) (-> math-cam x-ratio))))
  (with-pc
    (cond
      ((-> *pc-settings* use-vis?)
       ;; using game vis, cannot allow seeing more of the view
       ;; crops excess aspect ratio at the top and bottom
       ;(set! (-> math-cam y-ratio) (* (1/ (-> *pc-settings* aspect-ratio)) (-> math-cam x-ratio)))
       )
      ((real-movie?)
       ;; force the original 16x9 cropping during cutscenes.
       (if (<= (-> *pc-settings* aspect-ratio) ASPECT_16X9)
         (set! (-> math-cam y-ratio) (* (1/ (-> *pc-settings* aspect-ratio)) (-> math-cam x-ratio)))
         (begin
           (set! (-> math-cam y-ratio) (* (1/ ASPECT_16X9) (-> math-cam x-ratio)))
           (*! (-> math-cam x-ratio) (/ (-> *pc-settings* aspect-ratio) ASPECT_16X9)))))
      (else
       ;; not using game vis, allow *extended* aspect ratios
       ;; there is no vertical cropping, and you can see more of the sides
       (set! (-> math-cam y-ratio) (* (1/ ASPECT_4X3) (-> math-cam x-ratio))) ;; same cropping as 4x3
       (*! (-> math-cam x-ratio) (/ (-> *pc-settings* aspect-ratio) ASPECT_4X3)) ;; extend fov! shows more on the sides.
       )))
  ;; compute some culling constants. Not sure what these mean yet
  (let ((x-rat (-> math-cam x-ratio))
        (y-rat (-> math-cam y-ratio))
        (cull-info (-> math-cam cull-info)))
    (let ((x-thing (/ (+ 1.0 (* 4.0 x-rat x-rat)) (+ 1.0 (* x-rat x-rat))))
          (y-thing (/ (+ 1.0 (* 4.0 y-rat y-rat)) (+ 1.0 (* y-rat y-rat)))))
      (set! (-> cull-info x-fact) (/ (+ 1.0 (* 4.0 x-rat x-rat)) (* x-rat (sqrtf (+ 1.0 (* 16.0 x-rat x-rat))))))
      (set! (-> cull-info y-fact) (/ (+ 1.0 (* 4.0 y-rat y-rat)) (* y-rat (sqrtf (+ 1.0 (* 16.0 y-rat y-rat))))))
      (set! (-> cull-info z-fact)
            (sqrtf (+ (* (+ -4.0 y-thing) (+ -4.0 y-thing) y-rat y-rat) (* (+ -1.0 y-thing) (+ -1.0 y-thing))))))
    ;; radius of sphere containing camera origin and intersection of near plane and frustum
    (let* ((near-x (* x-rat (-> math-cam d)))
           (near-y (* y-rat (-> math-cam d)))
           (near-corner-dist-sqr (+ (* near-x near-x) (* near-y near-y)))
           (near-z (-> math-cam d)))
      (set! (-> cull-info cam-radius) (sqrtf (+ near-corner-dist-sqr (* near-z near-z)))))
    ;; frustum plane equations coefficients?
    (let* ((dx-rat-2 (* (-> math-cam d) (-> math-cam x-ratio)))
           (d-temp-2 (-> math-cam d))
           (dx-rat-times-4 (* 4.0 dx-rat-2))
           (d-temp-3 (-> math-cam d)))
      (let ((inverse-x-len (/ 1.0 (sqrtf (+ (* dx-rat-2 dx-rat-2) (* d-temp-2 d-temp-2)))))
            (inverse-x-len-2 (/ 1.0 (sqrtf (+ (* dx-rat-times-4 dx-rat-times-4) (* d-temp-3 d-temp-3))))))
        (set! (-> cull-info xz-dir-ax) (* dx-rat-2 inverse-x-len))
        (set! (-> cull-info xz-dir-az) (* d-temp-2 inverse-x-len))
        (set! (-> cull-info xz-dir-bx) (* dx-rat-times-4 inverse-x-len-2))
        (set! (-> cull-info xz-dir-bz) (* d-temp-3 inverse-x-len-2)))
      (set! (-> cull-info xz-cross-ab) (- (* dx-rat-2 d-temp-3) (* d-temp-2 dx-rat-times-4))))
    (let* ((dy-rat (* (-> math-cam d) (-> math-cam y-ratio)))
           (d-temp-4 (-> math-cam d))
           (dy-rat-times-4 (* 4.0 dy-rat))
           (d-temp-5 (-> math-cam d)))
      (let ((inverse-y-len (/ 1.0 (sqrtf (+ (* dy-rat dy-rat) (* d-temp-4 d-temp-4)))))
            (inverse-y-len-2 (/ 1.0 (sqrtf (+ (* dy-rat-times-4 dy-rat-times-4) (* d-temp-5 d-temp-5))))))
        (set! (-> cull-info yz-dir-ay) (* dy-rat inverse-y-len))
        (set! (-> cull-info yz-dir-az) (* d-temp-4 inverse-y-len))
        (set! (-> cull-info yz-dir-by) (* dy-rat-times-4 inverse-y-len-2))
        (set! (-> cull-info yz-dir-bz) (* d-temp-5 inverse-y-len-2)))
      (set! (-> cull-info yz-cross-ab) (- (* dy-rat d-temp-5) (* d-temp-4 dy-rat-times-4)))))
  ;; fog setup
  (fog-corrector-setup *math-camera-fog-correction* math-cam)
  ;; reset camera rotation
  (matrix-identity! (-> math-cam camera-rot))
  ;;;;;;;;;;;;;; Perspective matrix setup
  ;; these min/max depths are the values we'd want to write to the 24-bit integer depth buffer
  (let ((min-depth 100.0)
        (max-depth 16760631.0) ;; almost 2^24.
        )
    (let ((f0-21 16777115.0))) ;; unused. this is actually float closest to 2^24
    ;; next, compute the fog slope d(8bit_integer_fog_value)/d(game_world_distance).
    ;; the final fog values we want are an 8-bit integer.
    ;; note that this is the fog at the near plane too.
    (let ((fog-slope (/ (* (-> math-cam d) (- (-> math-cam fog-min) (-> math-cam fog-max)))
                        (- (-> *math-camera-fog-correction* fog-end) (-> *math-camera-fog-correction* fog-start))))
          ;; this is half the range of the depth buffer.
          (depth-buffer-half-range (* -0.5 (- max-depth min-depth))))
      ;; this is the slope to convert game world depths to depth buffer depths
      (let ((half-depth-buffer-slope (/ depth-buffer-half-range (* (-> math-cam d) (- (-> math-cam f) (-> math-cam d)))))
            (cam-fov-mult (-> math-cam fov-correction-factor)))
        ;; finally, build the actual matrix.
        ;; x/y are just the usual scaling
        (set! (-> math-cam perspective vector 0 x)
              (* cam-fov-mult (- (/ (-> math-cam x-pix) (* (-> math-cam x-ratio) (-> math-cam d))))))
        (set! (-> math-cam perspective vector 1 y)
              (* cam-fov-mult (- (/ (-> math-cam y-pix) (* (-> math-cam y-ratio) (-> math-cam d))))))
        ;; depth scaling
        (set! (-> math-cam perspective vector 2 z) (* cam-fov-mult (+ (-> math-cam f) (-> math-cam d)) half-depth-buffer-slope))
        ;; depth to fog
        (set! (-> math-cam perspective vector 2 w) (* (/ cam-fov-mult (-> math-cam d)) fog-slope))
        (set! (-> math-cam perspective vector 3 z)
              (* -2.0 half-depth-buffer-slope (-> math-cam f) (-> math-cam d) cam-fov-mult)))
      ;; hvdf = horizontal, vertical, depth, fog offsets to be applied after transform.
      (let ((hvdf-x 2048.0)
            (hvdf-y 2048.0)
            (hvdf-w (/ (- (* (-> *math-camera-fog-correction* fog-end) (-> math-cam fog-max))
                          (* (-> *math-camera-fog-correction* fog-start) (-> math-cam fog-min)))
                       (- (-> *math-camera-fog-correction* fog-end) (-> *math-camera-fog-correction* fog-start)))))
        (let ((hvdf-z (* 0.5 (+ max-depth min-depth))))
          (set! (-> math-cam hmge-scale x) (/ 1.0 (-> math-cam x-clip)))
          (set! (-> math-cam hmge-scale y) (/ 1.0 (-> math-cam y-clip)))
          (set! (-> math-cam hmge-scale z) (/ 1.0 depth-buffer-half-range))
          (set! (-> math-cam hmge-scale w) (/ 1.0 fog-slope))
          (set! (-> math-cam inv-hmge-scale x) (-> math-cam x-clip))
          (set! (-> math-cam inv-hmge-scale y) (-> math-cam y-clip))
          (set! (-> math-cam inv-hmge-scale z) depth-buffer-half-range)
          (set! (-> math-cam inv-hmge-scale w) fog-slope)
          (set! (-> math-cam hvdf-off x) hvdf-x)
          (set! (-> math-cam hvdf-off y) hvdf-y)
          (set! (-> math-cam hvdf-off z) hvdf-z)
          (set! (-> math-cam hvdf-off w) hvdf-w)
          (set! (-> math-cam guard x) (/ (-> math-cam x-clip) (-> math-cam x-pix)))
          (set! (-> math-cam guard y) (/ (-> math-cam y-clip) (-> math-cam y-pix)))
          (set! (-> math-cam guard z) 1.0)
          (set! (-> math-cam guard w) 1.0)
          (set! (-> math-cam isometric vector 3 z) (- 16777215.0 hvdf-z))
          ;; og:preserve-this PC HACK!
          ;; for whatever reason, the font renderer ends up computing a depth #x1000000 instead of
          ;; #xffffffff, which overflows the 24-bit z buffer.
          ;; cheating this by 1 bit seems to fix it.
          (#when PC_PORT
            ;; #x4b002032 -> #x4b002031
            (set! (-> math-cam isometric vector 3 z) (the-as float (1- (the-as int (-> math-cam isometric vector 3 z)))))
            ;; also do mirror game check here.
            (when (pc-cheats? (-> *pc-settings* cheats) mirror)
              (*! (-> math-cam perspective vector 0 x) -1.))))
        (set! (-> math-cam isometric vector 3 w) fog-slope)
        ;; perspective matrix
        (let ((persp-xx (-> math-cam perspective vector 0 x))
              (persp-yy (-> math-cam perspective vector 1 y))
              (persp-x (* -1.9996 (-> math-cam perspective vector 0 x))))
          (let ((sprite-row-0 (-> math-cam sprite-2d)))
            (set! (-> sprite-row-0 vector 0 x) persp-x)
            (set! (-> sprite-row-0 vector 0 y) 0.0)
            (set! (-> sprite-row-0 vector 0 z) 0.0)
            (set! (-> sprite-row-0 vector 0 w) 0.0))
          (set-vector! (-> math-cam sprite-2d vector 1) 0.0 (- (* (/ persp-yy persp-xx) persp-x)) 0.0 0.0)
          (set-vector! (-> math-cam sprite-2d vector 2) 0.0 0.0 (- persp-x) 0.0)
          (set-vector! (-> math-cam sprite-2d vector 3) 0.0 0.0 (* 500000000.0 persp-x) (* 60.0 persp-x (-> math-cam pfog0))))
        (set! (-> math-cam sprite-2d-hvdf quad) (-> math-cam hvdf-off quad))
        (set! (-> math-cam sprite-2d-hvdf x) 2048.0)
        (set! (-> math-cam sprite-2d-hvdf y) 2048.0)
        (set! (-> math-cam sprite-2d-hvdf z) (-> math-cam hvdf-off z))
        (set! (-> math-cam pfog0) fog-slope)
        (set! (-> math-cam pfog1) hvdf-w))))
  ;; sets up some giftags, but they are totally wrong.
  ;; they use 32-bit variables to store 64-bit parts of the tag.
  0
  (make-u128 0 (shl #x301ec000 32))
  (make-u128 0 (shl #x303ec000 32))
  (let ((pfog (-> math-cam pfog0)))
    (let ((vis-gif-0 (-> math-cam vis-gifs)))
      (set! (-> vis-gif-0 0 fog0) (the-as uint pfog))
      (set! (-> vis-gif-0 0 strip) (the-as uint #x301e4000))
      (set! (-> vis-gif-0 0 regs) (the-as uint 1042))
      (set! (-> vis-gif-0 0 fan) (the-as uint #x301ec000)))
    (let ((vis-gif-1 (&-> math-cam gifgr)))
      (set! (-> vis-gif-1 0) (the-as vis-gif-tag pfog))
      (set! (-> vis-gif-1 1) (the-as vis-gif-tag (make-u128 0 (shl #x20164000 32))))
      (set! (-> vis-gif-1 2) (the-as vis-gif-tag 65))
      (set! (-> vis-gif-1 3) (the-as vis-gif-tag #x301ec000)))
    (let ((vis-gif-1-again (-> math-cam vis-gifs)))
      (set! (-> vis-gif-1-again 0 fog0) (the-as uint pfog))
      (set! (-> vis-gif-1-again 0 strip) (the-as uint #x303e4000))
      (set! (-> vis-gif-1-again 0 regs) (the-as uint 1042))
      (set! (-> vis-gif-1-again 0 fan) (the-as uint #x303ec000)))
    (let ((vis-gif-1-again-again (-> math-cam vis-gifs)))
      (set! (-> vis-gif-1-again-again 0 fog0) (the-as uint pfog))
      (set! (-> vis-gif-1-again-again 0 strip) (the-as uint #x303e4000))
      (set! (-> vis-gif-1-again-again 0 regs) (the-as uint 1042))
      (set! (-> vis-gif-1-again-again 0 fan) (the-as uint #x303ec000))))
  ;; update sprite distort vectors
  (if (nonzero? sprite-distorter-generate-tables) (sprite-distorter-generate-tables))
  math-cam)

(defmethod new math-camera ((allocation symbol) (type-to-make type))
  "Set up a new math-camera in NTSC mode."
  (let ((gp-0 (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
    (set! (-> gp-0 d) 1024.0)
    (set! (-> gp-0 f) 40960000.0)
    (set! (-> gp-0 fov) 11650.845)
    (set! (-> gp-0 x-pix) 256.0)
    (set! (-> gp-0 x-clip) 1024.0)
    (set! (-> gp-0 y-pix) 112.0)
    (set! (-> gp-0 y-clip) 448.0)
    (set! (-> gp-0 fog-start) 40960.0)
    (set! (-> gp-0 fog-end) 819200.0)
    (set! (-> gp-0 fog-max) 255.0)
    (set! (-> gp-0 fog-min) 150.0)
    (matrix-identity! (-> gp-0 inv-camera-rot))
    (matrix-identity! (-> gp-0 camera-rot))
    (vector-reset! (-> gp-0 trans))
    (set! (-> gp-0 isometric vector 0 x) 1.0)
    (set! (-> gp-0 isometric vector 1 y) 0.5)
    (set! (-> gp-0 isometric vector 2 z) -1.0)
    (set! (-> gp-0 reset) 1)
    (set! (-> gp-0 smooth-step) 0.0)
    (set! (-> gp-0 smooth-t) 0.0)
    (update-math-camera gp-0 'ntsc 'aspect4x3)))

(define *math-camera* (new 'global 'math-camera))

(defun math-cam-start-smoothing ((arg0 float) (arg1 float))
  "Unused camera smoothing"
  (set! (-> *math-camera* smooth-step) (/ 1.0 arg0))
  (set! (-> *math-camera* smooth-t) arg1)
  (matrix->quaternion (-> *math-camera* inv-camera-rot-smooth-from) (-> *math-camera* inv-camera-rot-smooth)))

(defun move-target-from-pad ((trans transform) (pad-idx int))
  "Unused function to adjust trans based on inputs from the pad.
   This function must be extremely old because it takes a non-quaternion transform,
   and all target stuff uses quaternions."
  ;; local-trans is the translation in the camera frame.
  (let ((local-trans (new-stack-vector0)))
    ;; circle/square move camera relative x (left and right)
    (set! (-> local-trans x)
          (cond
            ((cpad-hold? pad-idx circle) -80.0)
            ((cpad-hold? pad-idx square) 80.0)
            (else 0.0)))
    ;; no way to move camera relative y (up/down)
    (set! (-> local-trans y) 0.0)
    ;; in and out movement
    (set! (-> local-trans z)
          (cond
            ((cpad-hold? pad-idx down) -80.0)
            ((cpad-hold? pad-idx up) 80.0)
            (else 0.0)))
    (set! (-> local-trans w) 1.0)
    ;; rotate this into world frame
    (let ((inv-cam-rot (new-stack-vector0))
          (cam-rot-mat (new-stack-matrix0)))
      ;; unused.
      (vector-negate! inv-cam-rot (-> trans rot))
      ;; convert rotation to rotation matrix.
      (matrix-rotate-zyx! cam-rot-mat (-> trans rot))
      ;; and rotate the translation.
      (vector-matrix*! local-trans local-trans cam-rot-mat))
    ;; and update the transform
    (vector+! (-> trans trans) (-> trans trans) local-trans))
  ;; don't forget to fix w.
  (set! (-> trans trans w) 1.0)
  ;; global translation
  (if (cpad-hold? pad-idx r1) (+! (-> trans trans y) 80.0))
  (if (cpad-hold? pad-idx r2) (+! (-> trans trans y) -80.0))
  ;; rotation (don't allow camera roll)
  (if (cpad-hold? pad-idx x) (+! (-> trans rot x) 546.13336))
  (if (cpad-hold? pad-idx triangle) (+! (-> trans rot x) -546.13336))
  (if (cpad-hold? pad-idx left) (+! (-> trans rot y) 546.13336))
  (if (cpad-hold? pad-idx right) (+! (-> trans rot y) -546.13336))
  trans)

(defun transform-point-vector! ((arg0 vector) (arg1 vector))
  "Apply camera transformation to a point. Return true if it is visible or not.
   This returns the point in GS coords, but as float instead of int, so it's
   not really useful.  See transform-point-qword! for more details"
  (rlet ((acc :class vf)
         (Q :class vf)
         (vf0 :class vf)
         (vf23 :class vf)
         (vf24 :class vf)
         (vf25 :class vf)
         (vf26 :class vf)
         (vf27 :class vf)
         (vf28 :class vf)
         (vf29 :class vf)
         (vf30 :class vf)
         (vf31 :class vf))
    (init-vf0-vector)
    0
    (.lvf vf24 (&-> *math-camera* camera-temp vector 0 quad))
    (.lvf vf25 (&-> *math-camera* camera-temp vector 1 quad))
    (.lvf vf26 (&-> *math-camera* camera-temp vector 2 quad))
    (.lvf vf27 (&-> *math-camera* camera-temp vector 3 quad))
    (.lvf vf29 (&-> *math-camera* hmge-scale quad))
    (.lvf vf30 (&-> *math-camera* hvdf-off quad))
    (.lvf vf28 (&-> arg1 quad))
    (.mul.x.vf acc vf24 vf28)
    (.add.mul.y.vf acc vf25 vf28 acc)
    (.add.mul.z.vf acc vf26 vf28 acc)
    (.add.mul.w.vf vf28 vf27 vf0 acc)
    (.add.w.vf vf23 vf0 vf0)
    (.mul.vf vf31 vf28 vf29)
    ;;(TODO.VCLIP vf31 vf31)
    (let ((clip (vu-clip vf31 0)))
      (.div.vf Q vf0 vf31 :fsf #b11 :ftf #b11)
      (.wait.vf)
      ;;(.cfc2.i v1-7 Clipping)
      (.mul.vf.xyz vf28 vf28 Q)
      (.mul.vf vf23 vf23 Q)
      (.add.vf vf28 vf28 vf30)
      (.max.x.vf.w vf28 vf28 vf0)
      (.svf (&-> arg0 quad) vf28)
      (zero? (logand clip 63)))))

(defun transform-point-qword! ((arg0 vector4w) (arg1 vector))
  "Apply camera transformation to point, returning fixed point 28.4 position
   that can be given to the GS directly."
  (rlet ((acc :class vf)
         (Q :class vf)
         (vf0 :class vf)
         (vf23 :class vf)
         (vf24 :class vf)
         (vf25 :class vf)
         (vf26 :class vf)
         (vf27 :class vf)
         (vf28 :class vf)
         (vf29 :class vf)
         (vf30 :class vf)
         (vf31 :class vf))
    (init-vf0-vector)
    (let ((v1-0 0)))
    ;; this camera matrix has both the projection and camera translation/rotation
    (.lvf vf24 (&-> *math-camera* camera-temp vector 0 quad))
    (.lvf vf25 (&-> *math-camera* camera-temp vector 1 quad))
    (.lvf vf26 (&-> *math-camera* camera-temp vector 2 quad))
    (.lvf vf27 (&-> *math-camera* camera-temp vector 3 quad))
    ;; scaling
    (.lvf vf29 (&-> *math-camera* hmge-scale quad))
    ;; offset
    (.lvf vf30 (&-> *math-camera* hvdf-off quad))
    ;; input point
    (.lvf vf28 (&-> arg1 quad))
    ;; matrix multiply, result in vf28
    (.mul.x.vf acc vf24 vf28)
    (.add.mul.y.vf acc vf25 vf28 acc)
    (.add.mul.z.vf acc vf26 vf28 acc)
    (.add.mul.w.vf vf28 vf27 vf0 acc)
    (.add.w.vf vf23 vf0 vf0) ;; set w = 1.0
    ;; apply hmge scaling. the result of this multiply sets clipping flags appropriately
    (.mul.vf vf31 vf28 vf29) ;; scale.
    ;;(TODO.VCLIP vf31 vf31)
    (let ((clip (vu-clip vf31 0))) ;; clip!
      ;; perspective divide
      (.div.vf Q vf0 vf31 :fsf #b11 :ftf #b11)
      (.wait.vf)
      ;;(.cfc2.i v1-7 Clipping)
      ;; perspective
      (.mul.vf.xyz vf28 vf28 Q)
      ;; compute scale factor (w was 1.0)
      (.mul.vf vf23 vf23 Q)
      ;; apply hvdf offsets
      (.add.vf vf28 vf28 vf30)
      ;; saturate fog
      (.max.x.vf.w vf28 vf28 vf0)
      ;; convert to GS fixed point
      (vftoi4.xyzw vf28 vf28)
      ;; store result!
      (.svf (&-> arg0 quad) vf28)
      ;; return result of clipping.
      (zero? (logand clip 63)))))

(defun transform-point-vector-scale! ((arg0 vector) (arg1 vector))
  "Similar to transform-point-qword! but returns the scale factor instead."
  (local-vars (v0-0 float))
  (rlet ((acc :class vf)
         (Q :class vf)
         (vf0 :class vf)
         (vf23 :class vf)
         (vf24 :class vf)
         (vf25 :class vf)
         (vf26 :class vf)
         (vf27 :class vf)
         (vf28 :class vf)
         (vf29 :class vf)
         (vf30 :class vf)
         (vf31 :class vf))
    (init-vf0-vector)
    (let ((v1-0 0)))
    (.lvf vf24 (&-> *math-camera* camera-temp vector 0 quad))
    (.lvf vf25 (&-> *math-camera* camera-temp vector 1 quad))
    (.lvf vf26 (&-> *math-camera* camera-temp vector 2 quad))
    (.lvf vf27 (&-> *math-camera* camera-temp vector 3 quad))
    (.lvf vf29 (&-> *math-camera* hmge-scale quad))
    (.lvf vf30 (&-> *math-camera* hvdf-off quad))
    (.lvf vf28 (&-> arg1 quad))
    (.mul.x.vf acc vf24 vf28)
    (.add.mul.y.vf acc vf25 vf28 acc)
    (.add.mul.z.vf acc vf26 vf28 acc)
    (.add.mul.w.vf vf28 vf27 vf0 acc)
    (.add.w.vf vf23 vf0 vf0)
    (.mul.vf vf31 vf28 vf29)
    ;;(TODO.VCLIP vf31 vf31) clip result was unused
    (.div.vf Q vf0 vf31 :fsf #b11 :ftf #b11)
    (.wait.vf)
    ;;(.cfc2.i v1-7 Clipping)
    (.mul.vf.xyz vf28 vf28 Q)
    (.mul.vf vf23 vf23 Q)
    (.add.vf vf28 vf28 vf30)
    (.max.x.vf.w vf28 vf28 vf0)
    (.svf (&-> arg0 quad) vf28)
    ;;(let ((a0-2 (zero? (logand v1-7 63))))
    ;; )
    (.mov v0-0 vf23)
    v0-0))

(defun init-for-transform ((arg0 matrix))
  "Sets up VU0 registers with camera info.
   This is probably a very old function and it's only used by jungle mirrors.
   It stashes some data in vector float registers that must be there before calling transform-float-point."
  (rlet ((vf1 :class vf)
         (vf17 :class vf)
         (vf18 :class vf)
         (vf19 :class vf)
         (vf2 :class vf)
         (vf23 :class vf)
         (vf24 :class vf)
         (vf25 :class vf)
         (vf26 :class vf)
         (vf27 :class vf)
         (vf28 :class vf)
         (vf29 :class vf)
         (vf3 :class vf)
         (vf4 :class vf)
         (vf6 :class vf)
         (vf7 :class vf)
         (vf8 :class vf)
         (vf9 :class vf))
    (let ((gp-0 (new-stack-matrix0))
          (s5-0 (new-stack-matrix0))
          (s4-0 (new 'stack 'vector4s-3))
          (s3-0 (new-stack-vector0))
          (s2-0 (new 'stack 'vector4s-3)))
      (matrix*! s5-0 arg0 (-> *math-camera* camera-temp))
      (matrix-3x3-inverse-transpose! gp-0 arg0)
      (let ((v1-3 s3-0)) (set! (-> v1-3 x) 0.4) (set! (-> v1-3 y) 0.4) (set! (-> v1-3 z) 0.4) (set! (-> v1-3 w) 1.0))
      (let ((v1-4 (-> s4-0 data)))
        (set! (-> v1-4 0) 1.0)
        (set! (-> v1-4 1) 1.0)
        (set! (-> v1-4 2) 1.0)
        (set! (-> v1-4 3) 1.0))
      (let ((v1-5 (&-> s4-0 data 4)))
        (set! (-> v1-5 0) 0.0)
        (set! (-> v1-5 1) 0.0)
        (set! (-> v1-5 2) 0.0)
        (set! (-> v1-5 3) 1.0))
      (let ((v1-6 (&-> s4-0 data 8)))
        (set! (-> v1-6 0) 0.0)
        (set! (-> v1-6 1) 0.0)
        (set! (-> v1-6 2) 0.0)
        (set! (-> v1-6 3) 1.0))
      (let ((v1-7 (-> s2-0 data)))
        (set! (-> v1-7 0) 1.0)
        (set! (-> v1-7 1) 0.0)
        (set! (-> v1-7 2) 0.0)
        (set! (-> v1-7 3) 1.0))
      (let ((v1-8 (&-> s2-0 data 4)))
        (set! (-> v1-8 0) 0.0)
        (set! (-> v1-8 1) 1.0)
        (set! (-> v1-8 2) 0.0)
        (set! (-> v1-8 3) 1.0))
      (let ((v1-9 (&-> s2-0 data 8)))
        (set! (-> v1-9 0) 0.0)
        (set! (-> v1-9 1) 0.0)
        (set! (-> v1-9 2) 1.0)
        (set! (-> v1-9 3) 1.0))
      (.lvf vf7 (&-> *math-camera* hmge-scale quad))
      (.lvf vf8 (&-> *math-camera* hvdf-off quad))
      (.lvf vf9 (&-> *math-camera* giftex))
      (let ((v1-13 255)) (.mov vf6 v1-13))
      ;;(.mov v1-14 vf6)
      (.itof.vf vf6 vf6)
      (.svf (&-> *transform-regs* vf7) vf7)
      (.svf (&-> *transform-regs* vf8) vf8)
      (.svf (&-> *transform-regs* vf9) vf9)
      (.svf (&-> *transform-regs* vf6) vf6)
      (set! (-> *transform-regs* vf1) (-> s5-0 vector 0 quad))
      (set! (-> *transform-regs* vf2) (-> s5-0 vector 1 quad))
      (set! (-> *transform-regs* vf3) (-> s5-0 vector 2 quad))
      (set! (-> *transform-regs* vf4) (-> s5-0 vector 3 quad))
      (set! (-> *transform-regs* vf17) (-> gp-0 vector 0 quad))
      (set! (-> *transform-regs* vf18) (-> gp-0 vector 1 quad))
      (set! (-> *transform-regs* vf19) (-> gp-0 vector 2 quad))
      (set! (-> *transform-regs* vf23) (-> s2-0 quad 0))
      (set! (-> *transform-regs* vf24) (-> s2-0 quad 1))
      (set! (-> *transform-regs* vf25) (-> s2-0 quad 2))
      (set! (-> *transform-regs* vf27) (-> s4-0 quad 0))
      (set! (-> *transform-regs* vf28) (-> s4-0 quad 1))
      (set! (-> *transform-regs* vf29) (-> s4-0 quad 2))
      (set! (-> *transform-regs* vf26) (-> s3-0 quad)))
    (none)))
