;;-*-Lisp-*-
(in-package goal)

;; name: joint-mod-h.gc
;; name in dgo: joint-mod-h
;; dgos: ENGINE, GAME

(defenum joint-mod-blend-flags
  :bitfield #t
  :type uint32
  (trans)
  (scale)
  (rotation)
  )

(defenum joint-mod-mode
  :bitfield #t
  :type uint32
  (flex-blend)
  (look-at)
  (world-look-at)
  (rotate)
  (joint-set)
  (joint-set*)
  (rotate2) ;; ??
  (reset)
  (polar-look-at)
  (joint-set*-world)
  (gun-look-at)
  (foot-rot)
  (joint-set-world)
  )

(defenum joint-mod-ik-flags
  :bitfield #t
  :type uint32
  (enable)
  (elbow-trans-neg)
  (elbow-rot-neg)
  )

;; +++track-mode
(defenum track-mode
  :bitfield #t
  :type uint16
  (track-on       0) ;; 1
  (track-x        1) ;; 2
  (track-y        2) ;; 4
  (lock-on        3) ;; 8
  (no-trans       4) ;; 16
  (no-rotate      5) ;; 32
  (no-scale       6) ;; 64
  )
;; ---track-mode

;; NOTE - for cam-debug
(declare-type try-to-look-at-info basic)
(define-extern last-try-to-look-at-data try-to-look-at-info)

;; DECOMP BEGINS

(deftype joint-mod (basic)
  "Utility to modify a joint transform from code, rather than just getting it from animation.
This is used to make jak look toward an enemy, for example."
  ((mode                     joint-mod-mode)
   (process                  process-drawable)
   (joint                    cspace)
   (target                   vector      :inline)
   (twist                    vector      :inline)
   (twist-max                vector      :inline)
   (extra-twist              degrees     :overlay-at (-> twist data 2))
   (track-mode               track-mode  :overlay-at (-> twist data 3))
   (loock-at-count           uint16      :offset  46)
   (twist-range-x            meters      :overlay-at (-> twist-max data 2))
   (twist-range-y            meters      :overlay-at (-> twist-max data 3))
   (twist-speed-x            float       :offset  64)
   (twist-speed-y            float       :offset  68)
   (twist-min-x              float       :offset  72)
   (twist-min-y              float       :offset  76)
   (trans                    vector      :inline)
   (shmushy-old              float       :overlay-at (-> trans data 0))
   (smushy-off               float       :overlay-at (-> trans data 1))
   (smushyv                  float       :overlay-at (-> trans data 2))
   (quat                     quaternion  :inline)
   (scale                    vector      :inline)
   (notice-time              time-frame)
   (flex-blend               float)
   (blend                    float)
   (max-dist                 meters)
   (ignore-angle             degrees)
   (up                       uint8)
   (nose                     uint8)
   (ear                      uint8)
   (base-joint               uint8)
   (base-nose                uint8)
   (shutting-down?           symbol)
   (parented-scale?          symbol)
   (polar-internal-tilt-max  float)
   (polar-internal-radius    float)
   (polar-external-tilt-max  float)
   (polar-external-radius    float)
   )
  (:methods
    (new (symbol type joint-mod-mode process-drawable int) _type_)
    (mode-set! (_type_ joint-mod-mode) none)
    (target-set! (_type_ vector) none)
    (look-at! (_type_ vector symbol process) none :behavior process)
    (reset-blend! (_type_) _type_)
    (twist-set! (_type_ float float float) vector)
    (trs-set! (_type_ vector quaternion vector) none)
    (shut-down (_type_) none)
    )
  )


(deftype try-to-look-at-info (basic)
  ((who   handle)
   (horz  float)
   (vert  float)
   )
  )


(defun-debug joint-mod-debug-draw ((jmod joint-mod))
  "Debug draw the bone transform for the associated bone of a joint-mod"
  (add-debug-matrix #t (bucket-id debug-no-zbuf1) (-> jmod joint bone transform) (meters 2))
  0
  (none)
  )

(defmethod reset-blend! ((this joint-mod))
  "Set the blend to 0."
  (set! (-> this blend) 0.0)
  this
  )

(define *joint-axis-vectors* (new 'static 'inline-array vector 6
                               (new 'static 'vector :x 1.0 :w 1.0)
                               (new 'static 'vector :y 1.0 :w 1.0)
                               (new 'static 'vector :z 1.0 :w 1.0)
                               (new 'static 'vector :x -1.0 :w 1.0)
                               (new 'static 'vector :y -1.0 :w 1.0)
                               (new 'static 'vector :z -1.0 :w 1.0)
                               )
        )

(deftype joint-mod-wheel (basic)
  ((last-position  vector  :inline)
   (angle          float)
   (process        process-drawable)
   (wheel-radius   float)
   (wheel-axis     int8)
   )
  (:methods
    (new (symbol type process-drawable int float int) _type_)
    )
  )


(defun joint-mod-wheel-callback ((arg0 cspace) (arg1 transformq))
  (let ((s4-0 (the-as joint-mod-wheel (-> arg0 param1))))
    (let ((v1-1 (-> s4-0 process root))
          (s1-0 (new-stack-vector0))
          (s3-0 (new-stack-vector0))
          (s2-0 (new-stack-vector0))
          )
      0.0
      0.0
      (vector-z-quaternion! s2-0 (-> v1-1 quat))
      (vector<-cspace! s1-0 arg0)
      (vector-! s3-0 s1-0 (-> s4-0 last-position))
      (set! (-> s4-0 last-position quad) (-> s1-0 quad))
      (let* ((f0-3 (vector-dot s2-0 s3-0))
             (f1-0 65536.0)
             (f2-1 (* 6.28318 (-> s4-0 wheel-radius)))
             (f0-4 (* f1-0 (/ 1.0 f2-1) f0-3))
             )
        (+! (-> s4-0 angle) f0-4)
        )
      )
    (quaternion-vector-angle! (-> arg1 quat) (-> *joint-axis-vectors* (-> s4-0 wheel-axis)) (-> s4-0 angle))
    )
  (cspace<-parented-transformq-joint! arg0 arg1)
  (none)
  )

(defmethod new joint-mod-wheel ((allocation symbol) (type-to-make type) (arg0 process-drawable) (arg1 int) (arg2 float) (arg3 int))
  (let ((v0-0 (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
    (set! (-> v0-0 process) arg0)
    (set! (-> v0-0 wheel-radius) arg2)
    (set! (-> v0-0 wheel-axis) arg3)
    (set! (-> v0-0 angle) 0.0)
    (set-vector! (-> v0-0 last-position) 0.0 0.0 0.0 1.0)
    (let ((a0-3 (-> arg0 node-list data arg1)))
      (set! (-> a0-3 param0) joint-mod-wheel-callback)
      (set! (-> a0-3 param1) v0-0)
      )
    v0-0
    )
  )

(deftype joint-mod-set-local (basic)
  "Override the trans, quat, and scale of the joint transform. The component to override is selected by the flag."
  ((transform        transformq  :inline)
   (set-rotation     symbol)
   (set-scale        symbol)
   (set-translation  symbol)
   (enable           symbol)
   )
  (:methods
    (new (symbol type process-drawable int symbol symbol symbol) _type_)
    )
  )


(defun joint-mod-set-local-callback ((arg0 cspace) (arg1 transformq))
  "Callback for joint-mod-rotate-local. See comment on that type."
  (let ((v1-0 (the-as joint-mod-set-local (-> arg0 param1))))
    (cond
      ((-> v1-0 enable)
       (if (not (-> v1-0 set-translation))
           (set! (-> v1-0 transform trans quad) (-> arg1 trans quad))
           )
       (if (not (-> v1-0 set-rotation))
           (set! (-> v1-0 transform quat quad) (-> arg1 quat quad))
           )
       (if (not (-> v1-0 set-scale))
           (set! (-> v1-0 transform scale quad) (-> arg1 scale quad))
           )
       (cspace<-parented-transformq-joint! arg0 (-> v1-0 transform))
       )
      (else
        (cspace<-parented-transformq-joint! arg0 arg1)
        )
      )
    )
  (none)
  )

(defmethod new joint-mod-set-local ((allocation symbol)
                                  (type-to-make type)
                                  (arg0 process-drawable)
                                  (arg1 int)
                                  (arg2 symbol)
                                  (arg3 symbol)
                                  (arg4 symbol)
                                  )
  (let ((v0-0 (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
    (set! (-> v0-0 set-translation) arg2)
    (set! (-> v0-0 set-rotation) arg3)
    (set! (-> v0-0 set-scale) arg4)
    (set! (-> v0-0 enable) #t)
    (set! (-> v0-0 transform trans quad) (-> *null-vector* quad))
    (set! (-> v0-0 transform quat quad) (-> *null-vector* quad))
    (set! (-> v0-0 transform scale quad) (-> *identity-vector* quad))
    (let ((a0-8 (-> arg0 node-list data arg1)))
      (set! (-> a0-8 param0) joint-mod-set-local-callback)
      (set! (-> a0-8 param1) v0-0)
      )
    v0-0
    )
  )

(deftype joint-mod-add-local (basic)
  "Add to the trans, rotate the quat, and multiply the scale of the joint transform. The components can be selected by the flag.
Unlike jak 2, this actually multiplies the scale, instead of adding."
  ((transform        transformq  :inline)
   (add-rotation     symbol)
   (add-scale        symbol)
   (add-translation  symbol)
   (enable           symbol)
   )
  (:methods
    (new (symbol type process-drawable int symbol symbol symbol) _type_)
    )
  )


(defun joint-mod-add-local-callback ((arg0 cspace) (arg1 transformq))
  "Callback for joint-mod-add-local. See comment on that type."
  (let ((s4-0 (the-as joint-mod-add-local (-> arg0 param1))))
    (when (-> s4-0 enable)
      (if (-> s4-0 add-translation)
          (vector+! (-> arg1 trans) (-> arg1 trans) (the-as vector (-> s4-0 transform)))
          )
      (if (-> s4-0 add-rotation)
          (quaternion-normalize! (quaternion*! (-> arg1 quat) (-> arg1 quat) (-> s4-0 transform quat)))
          )
      (if (-> s4-0 add-scale)
          (vector+! (-> arg1 scale) (-> arg1 scale) (-> s4-0 transform scale))
          )
      )
    )
  (cspace<-parented-transformq-joint! arg0 arg1)
  (none)
  )

(defmethod new joint-mod-add-local ((allocation symbol)
                                  (type-to-make type)
                                  (arg0 process-drawable)
                                  (arg1 int)
                                  (arg2 symbol)
                                  (arg3 symbol)
                                  (arg4 symbol)
                                  )
  (let ((gp-0 (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
    (set! (-> gp-0 add-translation) arg2)
    (set! (-> gp-0 add-rotation) arg3)
    (set! (-> gp-0 add-scale) arg4)
    (set! (-> gp-0 enable) #t)
    (set! (-> gp-0 transform trans quad) (-> *null-vector* quad))
    (quaternion-copy! (-> gp-0 transform quat) *unity-quaternion*)
    (set! (-> gp-0 transform scale quad) (-> *identity-vector* quad))
    (let ((a0-7 (-> arg0 node-list data arg1)))
      (set! (-> a0-7 param0) joint-mod-add-local-callback)
      (set! (-> a0-7 param1) gp-0)
      )
    gp-0
    )
  )

(deftype joint-mod-set-world (basic)
  "Directly overwrite the _bone_ transform (ignoring the parent entirely).
This does not pay attention to the flags."
  ((transform   transformq  :inline)
   (node-index  int32)
   (enable      symbol)
   )
  (:methods
    (new (symbol type process-drawable int symbol) _type_)
    )
  )


;; WARN: Return type mismatch object vs none.
(defun joint-mod-set-world-callback ((arg0 cspace) (arg1 transformq))
  "Callback for joint-mod-set-world. See comment on that type."
  (let ((v1-0 (the-as joint-mod-set-world (-> arg0 param1))))
    (if (-> v1-0 enable)
        (cspace<-transformq! arg0 (-> v1-0 transform))
        (cspace<-parented-transformq-joint! arg0 arg1)
        )
    )
  (none)
  )

(defmethod new joint-mod-set-world ((allocation symbol) (type-to-make type) (arg0 process-drawable) (arg1 int) (arg2 symbol))
  (let ((v0-0 (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
    (set! (-> v0-0 node-index) arg1)
    (set! (-> v0-0 enable) arg2)
    (set! (-> v0-0 transform trans quad) (-> *null-vector* quad))
    (set! (-> v0-0 transform quat quad) (-> *null-vector* quad))
    (set! (-> v0-0 transform scale quad) (-> *identity-vector* quad))
    (let ((a0-8 (-> arg0 node-list data arg1)))
      (set! (-> a0-8 param0) joint-mod-set-world-callback)
      (set! (-> a0-8 param1) v0-0)
      )
    v0-0
    )
  )

(deftype joint-mod-blend-local (basic)
  "Blend the _joint_ transform between this transform and the animated one.
Then, apply the normal parented transform."
  ((transform        transformq  :inline)
   (blend-transform  transformq  :inline)
   (node-index       int32)
   (blend            float)
   (enable           symbol)
   )
  (:methods
    (new (symbol type process-drawable int symbol) _type_)
    )
  )


(defun joint-mod-blend-local-callback ((arg0 cspace) (arg1 transformq))
  "Callback for joint-mod-blend-local. See comment on that type."
  (let ((gp-0 (the-as joint-mod-blend-local (-> arg0 param1))))
    (cond
      ((-> gp-0 enable)
       (vector-lerp!
         (the-as vector (-> gp-0 blend-transform))
         (-> arg1 trans)
         (the-as vector (-> gp-0 transform))
         (-> gp-0 blend)
         )
       (vector-lerp! (-> gp-0 blend-transform scale) (-> arg1 scale) (-> gp-0 transform scale) (-> gp-0 blend))
       (quaternion-slerp! (-> gp-0 blend-transform quat) (-> arg1 quat) (-> gp-0 transform quat) (-> gp-0 blend))
       (cspace<-parented-transformq-joint! arg0 (-> gp-0 blend-transform))
       )
      (else
        (cspace<-parented-transformq-joint! arg0 arg1)
        )
      )
    )
  (none)
  )

(defmethod new joint-mod-blend-local ((allocation symbol) (type-to-make type) (arg0 process-drawable) (arg1 int) (arg2 symbol))
  (let ((v0-0 (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
    (set! (-> v0-0 node-index) arg1)
    (set! (-> v0-0 enable) arg2)
    (set! (-> v0-0 blend) 0.0)
    (set! (-> v0-0 transform trans quad) (-> *null-vector* quad))
    (set! (-> v0-0 transform quat quad) (-> *null-vector* quad))
    (set! (-> v0-0 transform scale quad) (-> *identity-vector* quad))
    (let ((a0-8 (-> arg0 node-list data arg1)))
      (set! (-> a0-8 param0) joint-mod-blend-local-callback)
      (set! (-> a0-8 param1) v0-0)
      )
    v0-0
    )
  )

(deftype joint-mod-spinner (basic)
  "Control a joint by just spinning it around an axis."
  ((spin-axis  vector  :inline)
   (angle      float)
   (spin-rate  float)
   (enable     symbol)
   )
  (:methods
    (new (symbol type process-drawable int vector float) _type_)
    )
  )


(defun joint-mod-spinner-callback ((bone-cspace cspace) (joint-transform transformq))
  "cspace callback for joint-mod-spinner. Update the cspace's bone from the parent transformq, plus the rotation from this spinner."
  (let ((gp-0 (the-as joint-mod-spinner (-> bone-cspace param1))))
    (when (-> gp-0 enable)
      (let ((f30-0
              (the float (sar (shl (the int (+ (-> gp-0 angle) (* (-> gp-0 spin-rate) (seconds-per-frame)))) 48) 48))
              )
            )
        (quaternion-vector-angle! (-> joint-transform quat) (-> gp-0 spin-axis) f30-0)
        (set! (-> gp-0 angle) f30-0)
        )
      )
    )
  (cspace<-parented-transformq-joint! bone-cspace joint-transform)
  (none)
  )

(defmethod new joint-mod-spinner ((allocation symbol) (type-to-make type) (proc process-drawable) (bone-idx int) (axis vector) (rate float))
  (let ((v0-0 (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
    (set! (-> v0-0 spin-axis quad) (-> axis quad))
    (set! (-> v0-0 spin-rate) rate)
    (set! (-> v0-0 enable) #t)
    (set! (-> v0-0 angle) 0.0)
    (let ((a0-3 (-> proc node-list data bone-idx)))
      (set! (-> a0-3 param0) joint-mod-spinner-callback)
      (set! (-> a0-3 param1) v0-0)
      )
    v0-0
    )
  )

(deftype joint-mod-blend-world (basic)
  "Blend the _bone_ transform between this one and the animated one."
  ((transform        transformq  :inline)
   (blend-transform  transformq  :inline)
   (blend-flags      joint-mod-blend-flags)
   (node-index       int32)
   (blend            float)
   (enable           symbol)
   )
  (:methods
    (new (symbol type process-drawable int symbol float) _type_)
    )
  )


;; WARN: Return type mismatch object vs none.
(defun joint-mod-blend-world-callback ((arg0 cspace) (arg1 transformq))
  "Callback for joint-mod-blend-local. See comment on that type."
  (let ((gp-0 (the-as joint-mod-blend-world (-> arg0 param1))))
    (cond
      ((-> gp-0 enable)
       (let ((f30-0 (if (logtest? (-> gp-0 blend-flags) (joint-mod-blend-flags trans))
                        (-> gp-0 blend)
                        0.0
                        )
                    )
             (f28-0 (if (logtest? (-> gp-0 blend-flags) (joint-mod-blend-flags scale))
                        (-> gp-0 blend)
                        0.0
                        )
                    )
             (f26-0 (if (logtest? (-> gp-0 blend-flags) (joint-mod-blend-flags rotation))
                        (-> gp-0 blend)
                        0.0
                        )
                    )
             (s0-0 (new 'stack-no-clear 'matrix))
             (s2-0 (new 'stack-no-clear 'matrix))
             (s3-0 (new 'stack-no-clear 'quaternion))
             )
         (let ((s1-0 (new 'stack-no-clear 'vector)))
           (if (= (-> arg0 parent bone scale w) 0.0)
               (matrix<-transformq! s0-0 arg1)
               (matrix<-parented-transformq! s0-0 arg1 (-> arg0 parent bone scale))
               )
           (matrix*! s2-0 s0-0 (-> arg0 parent bone transform))
           (set-vector!
             s1-0
             (vector-length (the-as vector (-> s2-0 vector)))
             (vector-length (-> s2-0 vector 1))
             (vector-length (-> s2-0 vector 2))
             1.0
             )
           (vector*! (-> gp-0 blend-transform scale) s1-0 (-> arg1 scale))
           )
         (vector-lerp!
           (the-as vector (-> gp-0 blend-transform))
           (-> s2-0 trans)
           (the-as vector (-> gp-0 transform))
           f30-0
           )
         (vector-lerp! (-> gp-0 blend-transform scale) (-> gp-0 blend-transform scale) (-> gp-0 transform scale) f28-0)
         (quaternion-slerp!
           (-> gp-0 blend-transform quat)
           (matrix->quaternion s3-0 s2-0)
           (-> gp-0 transform quat)
           f26-0
           )
         )
       (cspace<-transformq! arg0 (-> gp-0 blend-transform))
       )
      (else
        (cspace<-parented-transformq-joint! arg0 arg1)
        )
      )
    )
  (none)
  )

(defmethod new joint-mod-blend-world ((allocation symbol) (type-to-make type) (arg0 process-drawable) (arg1 int) (arg2 symbol) (arg3 float))
  (let ((v0-0 (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
    (set! (-> v0-0 node-index) arg1)
    (set! (-> v0-0 enable) arg2)
    (set! (-> v0-0 blend) arg3)
    (set! (-> v0-0 transform trans quad) (-> *null-vector* quad))
    (set! (-> v0-0 transform quat quad) (-> *null-vector* quad))
    (set! (-> v0-0 transform scale quad) (-> *identity-vector* quad))
    (let ((a0-8 (-> arg0 node-list data arg1)))
      (set! (-> a0-8 param0) joint-mod-blend-world-callback)
      (set! (-> a0-8 param1) v0-0)
      )
    v0-0
    )
  )

(deftype joint-mod-rotate-local (basic)
  ((enable    symbol)
   (rotation  quaternion  :inline)
   )
  (:methods
    (new (symbol type process-drawable int symbol) _type_)
    )
  )


(defun joint-mod-rotate-local-callback ((arg0 cspace) (arg1 transformq))
  "Apply an additional rotation to the transform (left side quaternion multiplication"
  (let ((v1-0 (the-as joint-mod-rotate-local (-> arg0 param1))))
    (new 'stack-no-clear 'vector)
    (if (-> v1-0 enable)
        (quaternion*! (-> arg1 quat) (-> arg1 quat) (-> v1-0 rotation))
        )
    )
  (cspace<-parented-transformq-joint! arg0 arg1)
  (none)
  )

(defmethod new joint-mod-rotate-local ((allocation symbol) (type-to-make type) (arg0 process-drawable) (arg1 int) (arg2 symbol))
  (let ((gp-0 (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
    (set! (-> gp-0 enable) arg2)
    (quaternion-set! (-> gp-0 rotation) 0.0 0.0 0.0 1.0)
    (let ((a0-3 (-> arg0 node-list data arg1)))
      (set! (-> a0-3 param0) joint-mod-rotate-local-callback)
      (set! (-> a0-3 param1) gp-0)
      )
    gp-0
    )
  )

(deftype joint-mod-ik (basic)
  ((flags                   joint-mod-ik-flags)
   (process                 process-drawable)
   (hand-dist               float)
   (handle-pos              vector  :inline)
   (elbow-pole-vector-axis  uint32)
   (elbow-rotation-axis     uint32)
   (user-position           vector  :inline)
   (user-normal             vector  :inline)
   (user-blend              float)
   (user-float              float)
   (callback                (function joint-mod-ik matrix matrix vector object))
   (shoulder-matrix-no-ik   matrix  :inline)
   (elbow-matrix-no-ik      matrix  :inline)
   (blend                   float)
   (blend-interp            float)
   )
  (:methods
    (new (symbol type process-drawable int float) _type_)
    (handle-copy! (_type_ vector) none)
    (enable-set! (_type_ symbol) none)
    )
  )

;; og:preserve-this added macro
(defmacro target-look-at-me! (&key trans &key (message 'nothing-special))
  "make target look at a trans in self. PC PORT NOTE : added check to see if lods have been set"
  `(if (and (not (logtest? (-> self draw status) (draw-control-status uninited no-draw-temp))) *target*)
      (look-at! (-> *target* neck) ,trans ,message self)))

