jak-project/goal_src/engine/util/sync-info.gc
water111 c2c6d3ba46
[decompiler] detect res-lump macros (#796)
* support res lump float

* handle data macro

* rest of res macros

* add vi3 to the build system

* missing close paren
2021-08-31 15:01:54 -04:00

660 lines
22 KiB
Common Lisp

;;-*-Lisp-*-
(in-package goal)
;; name: sync-info.gc
;; name in dgo: sync-info
;; dgos: GAME, ENGINE
(defmethod setup-params! sync-info ((obj sync-info) (period uint) (phase float) (arg2 float) (arg3 float))
"Setup a sync-info.
period is the duration of the pattern.
phase is the offset relative to the global clock, specified as a fraction of period."
(set! (-> obj period) period)
(let* ((period-float (the float period))
(value (* phase period-float))
)
;; this is like (fmod value period-float)
(set! (-> obj offset)
(- value (* (the float (the int (/ value period-float))) period-float))
)
)
(none)
)
(defmethod setup-params! sync-info-eased ((obj sync-info-eased) (period uint) (phase float) (out-param float) (in-param float))
"Setup a sync-info-eased. The out-param and in-param are related to the smoothing at the beginning/end.
it looks like the easing is cubic so the first derivative is continuous."
(set! (-> obj period) period)
;; set the offset from the phase
(let* ((period-float (the float period))
(value (* phase period-float))
)
(set! (-> obj offset) (- value (* (the float (the int (/ value period-float))) period-float)))
)
;; saturate the params
(if (< out-param 0.0)
(set! out-param 0.0)
)
(if (< 1.0 out-param)
(set! out-param 1.0)
)
(if (< in-param 0.001)
(set! in-param 0.001)
)
(if (< 1.0 in-param)
(set! in-param 1.0)
)
(let ((total-easing-phase (+ out-param in-param)))
(when (< 1.0 total-easing-phase)
(set! total-easing-phase 1.0)
(set! out-param (- 1.0 in-param))
)
(let* ((total-normal-phase (- 1.0 total-easing-phase))
(f0-10 out-param)
(f1-12 (+ out-param total-normal-phase))
(f2-5 (* f0-10 f0-10))
(f3-3 (+ (* (* 2.0 f0-10) (- f1-12 f0-10)) f2-5))
(f4-3 (/ f0-10 (- 1.0 f1-12)))
(y-end (+ (* (* (- 1.0 f1-12) (- 1.0 f1-12)) f4-3) f3-3))
)
(set! (-> obj tlo) f0-10)
(set! (-> obj thi) f1-12)
(set! (-> obj ylo) f2-5)
(set! (-> obj m2) f4-3)
(set! (-> obj yend) y-end)
)
)
(none)
)
(defmethod setup-params! sync-info-paused ((obj sync-info-paused) (period uint) (phase float) (out-param float) (in-param float))
"Setup a sync-info-paused. The params are delays for the pause, specified in a fraction of period."
(set! (-> obj period) period)
;; set phase.
(let* ((f0-1 (the float period))
(f1-1 (* phase f0-1))
)
(set! (-> obj offset) (- f1-1 (* (the float (the int (/ f1-1 f0-1))) f0-1)))
)
;; saturate params
(cond
((< out-param 0.0)
(set! out-param 0.0)
)
((< 1.0 out-param)
(set! out-param 1.0)
)
)
(cond
((< in-param 0.0)
(set! in-param 0.0)
)
;; note: makes sure pauses don't overlap
((< (- 1.0 out-param) in-param)
(set! in-param (- 1.0 out-param))
)
)
(set! (-> obj pause-after-in) in-param)
(set! (-> obj pause-after-out) out-param)
(none)
)
(defmethod load-params! sync-info ((obj sync-info) (proc process) (default-period uint) (default-phase float) (default-out float) (default-in float))
"Load params from the res of a process, and set them up. If the res lookup fails, returns #f and uses
the specified defaults."
(local-vars (sv-16 res-tag))
(set! sv-16 (new 'static 'res-tag))
(let ((v1-1 (res-lump-data (-> proc entity) 'sync pointer :tag-ptr (& sv-16))))
(cond
(v1-1
;; res lookup succeeded, we should have two values: a period (not yet in seconds) and a phase.
(setup-params!
obj
(the-as uint (the int (* 300.0 (-> (the-as (pointer float) v1-1) 0))))
(-> (the-as (pointer float) v1-1) 1)
0.15
0.15
)
#t
)
(else
;; failed, set defaults
(setup-params! obj default-period default-phase 0.15 0.15)
#f
)
)
)
)
(defmethod load-params! sync-info-eased ((obj sync-info-eased) (proc process) (default-period uint) (default-phase float) (default-out float) (default-in float))
"Load settings from a res. Can load settings from just a sync-info and uses defaults.
If res lookup totally fails, will return #f and use all defaults."
(local-vars (sv-16 res-tag))
(set! sv-16 (new 'static 'res-tag))
(let ((v1-1 (res-lump-data (-> proc entity) 'sync pointer :tag-ptr (& sv-16))))
(cond
(v1-1
;; we may not get all the parameters
(if (>= (-> sv-16 elt-count) (the-as uint 4))
(setup-params!
obj
(the-as uint (the int (* 300.0 (-> (the-as (pointer float) v1-1) 0))))
(-> (the-as (pointer float) v1-1) 1)
(-> (the-as (pointer float) v1-1) 2)
(-> (the-as (pointer float) v1-1) 3)
)
(setup-params!
obj
(the-as uint (the int (* 300.0 (-> (the-as (pointer float) v1-1) 0))))
(-> (the-as (pointer float) v1-1) 1)
default-out
default-in
)
)
#t
)
(else
(setup-params! obj default-period default-phase default-out default-in)
#f
)
)
)
)
(defmethod load-params! sync-info-paused ((obj sync-info-paused) (proc process) (default-period uint) (default-phase float) (default-out float) (default-in float))
"Load and setup a sync-info-paused."
(local-vars (sv-16 res-tag))
(set! sv-16 (new 'static 'res-tag))
(let ((v1-1 (res-lump-data (-> proc entity) 'sync pointer :tag-ptr (& sv-16))))
(cond
(v1-1
(if (>= (-> sv-16 elt-count) (the-as uint 4))
(setup-params!
obj
(the-as uint (the int (* 300.0 (-> (the-as (pointer float) v1-1) 0))))
(-> (the-as (pointer float) v1-1) 1)
(-> (the-as (pointer float) v1-1) 2)
(-> (the-as (pointer float) v1-1) 3)
)
(setup-params!
obj
(the-as uint (the int (* 300.0 (-> (the-as (pointer float) v1-1) 0))))
(-> (the-as (pointer float) v1-1) 1)
default-out
default-in
)
)
#t
)
(else
(setup-params! obj default-period default-phase default-out default-in)
#f
)
)
)
)
(defmethod get-current-phase-no-mod sync-info ((obj sync-info))
"Based on the current frame, get the current phase. Does not apply any modifications
like pauses or eases."
(let* ((period (-> obj period))
(period-float (the float period))
;; now + offset
(current-time
(+ (the float (mod (the-as uint (-> *display* base-frame-counter)) period))
(-> obj offset)
)
)
)
;; compute wrapped phase from current-time
(/ (- current-time
(* (the float (the int (/ current-time period-float))) period-float))
period-float
)
)
)
(defmethod get-phase-offset sync-info ((obj sync-info))
"Get the offset, as a fraction of period"
(/ (-> obj offset) (the float (-> obj period)))
)
(defmethod sync-now! sync-info ((obj sync-info) (user-phase-offset float))
"Adjusts our offset so we are at phase user-phase-offset now"
(let* ((period (-> obj period))
(period-float (the float period))
;; in (0, 1)
(wrapped-user-offset
(- user-phase-offset
(* (the float (the int (/ user-phase-offset period-float)))
period-float
)
)
)
;; with the current offset, what is the time (0, period)?
(current-time
(+ (the float (mod (the-as uint (-> *display* base-frame-counter)) period))
(-> obj offset)
)
)
;; current period in (0, 1)
(current-period-wrapped
(/ (- current-time (* (the float (the int (/ current-time period-float))) period-float))
period-float
)
)
;; a time
(combined-offset
(+ (+ (* (- wrapped-user-offset current-period-wrapped) period-float)
period-float
)
(-> obj offset)
)
)
;; wrap it
(combined-offset-wrapped
(- combined-offset
(* (the float (the int (/ combined-offset period-float))) period-float)
)
)
)
(set! (-> obj offset) combined-offset-wrapped)
combined-offset-wrapped
)
)
(defmethod get-current-phase sync-info ((obj sync-info))
"Get the current phase."
(let* ((period (-> obj period))
(period-float (the float period))
(current-time (+ (the float (mod (the-as uint (-> *display* base-frame-counter)) period)) (-> obj offset)))
)
;; don't need to wrap this again.
(/ (- current-time (* (the float (the int (/ current-time period-float))) period-float))
period-float
)
)
)
(defmethod get-current-phase sync-info-paused ((obj sync-info-paused))
"Get the current phase. this only uses the pause-after-out - use the mirrored version
for pauses on both ends"
(let* ((period (-> obj period))
(period-float (the float period))
(max-phase 1.0)
(current-time (+ (the float (mod (the-as uint (-> *display* base-frame-counter)) period)) (-> obj offset)))
)
(fmin max-phase
(/ (- current-time (* (the float (the int (/ current-time period-float))) period-float))
(* period-float (- 1.0 (-> obj pause-after-out))) ;; just tweak the effective period so we end early
)
)
)
)
(defmethod get-current-value sync-info ((obj sync-info) (max-val float))
"This is just get-current-phase multiplied by max-val"
(let* ((period (-> obj period))
(period-float (the float period))
(current-time (+ (the float (mod (the-as uint (-> *display* base-frame-counter)) period)) (-> obj offset)))
)
(* (/ (- current-time (* (the float (the int (/ current-time period-float))) period-float))
period-float
)
max-val
)
)
)
(defmethod get-current-value sync-info-paused ((obj sync-info-paused) (arg0 float))
"This is just get-current-phase multiplied by max-val"
(* (get-current-phase obj) arg0)
)
(defmethod get-current-phase-with-mirror sync-info ((obj sync-info))
"Gets the phase that goes from 0 to 1 back to 0 every period."
(let* ((period (-> obj period))
(period-float (the float period))
(max-val 2.0)
(current-time (+ (the float (mod (the-as uint (-> *display* base-frame-counter)) period)) (-> obj offset)))
(phase-out-of-2
(* max-val
(/ (- current-time (* (the float (the int (/ current-time period-float))) period-float))
period-float
)
)
)
)
(if (>= phase-out-of-2 1.0)
(set! phase-out-of-2 (- 2.0 phase-out-of-2))
)
phase-out-of-2
)
)
(defmethod get-current-phase-with-mirror sync-info-eased ((obj sync-info-eased))
"Get the phase that goes from 0 to 1 back to 0 every period.
Note that sync-info-eased only does easing on this mirrored version."
(let* ((period (-> obj period))
(period-float (the float period))
(max-val 2.0)
(current-time (+ (the float (mod (the-as uint (-> *display* base-frame-counter)) period)) (-> obj offset)))
(current-val
(* max-val
(/ (- current-time (* (the float (the int (/ current-time period-float))) period-float))
period-float
)
)
)
(in-mirror? #f)
)
;; the input to the eased-phase calculation is un-mirrored, then mirrored after
(when (>= current-val 1.0)
(set! in-mirror? #t)
(set! current-val (+ -1.0 current-val))
)
(let* ((tlo (-> obj tlo))
(eased-phase (/ (cond
((< current-val tlo)
;; quadratic ramp in
(* current-val current-val)
)
((< current-val (-> obj thi))
;; linear part
(+ (* (* 2.0 tlo) (- current-val tlo)) (-> obj ylo))
)
(else
(let ((f1-7 (- 1.0 current-val)))
;; quadratic ramp out
(- (-> obj yend) (* (* f1-7 f1-7) (-> obj m2)))
)
)
)
(-> obj yend)
)
)
)
;; flip again
(if in-mirror?
(set! eased-phase (- 1.0 eased-phase))
)
eased-phase
)
)
)
(defmethod get-current-phase-with-mirror sync-info-paused ((obj sync-info-paused))
"Get the phase that goes from 0 to 1 to 0 in one period."
(let* ((v1-0 (-> obj period))
(f1-0 (the float v1-0))
;; max val
(f0-1 2.0)
;; current-time
(f2-2 (+ (the float (mod (the-as uint (-> *display* base-frame-counter)) v1-0)) (-> obj offset)))
;; phase
(f0-2 (* f0-1 (/ (- f2-2 (* (the float (the int (/ f2-2 f1-0))) f1-0)) f1-0)))
;; offset for pause
(f1-3 (- 1.0 (* 2.0 (-> obj pause-after-in))))
(f2-7 (- 1.0 (* 2.0 (-> obj pause-after-out))))
)
(cond
((>= f0-2 (+ 1.0 f1-3))
0.0 ;; before pause ends
)
((< 1.0 f0-2)
;; in moving part
(- 1.0 (/ (+ -1.0 f0-2) f1-3))
)
((>= f0-2 f2-7)
;; after end pause
1.0
)
(else
;; in moving part
(/ f0-2 f2-7)
)
)
)
)
(defmethod get-current-value-with-mirror sync-info ((obj sync-info) (max-out-val float))
"Get the phase that goes from 0 to max-out-val to 0 in each period."
(let* ((period (-> obj period))
(period-float (the float period))
(max-val 2.0)
(current-time (+ (the float (mod (the-as uint (-> *display* base-frame-counter)) period)) (-> obj offset)))
(current-val
(* max-val
(/ (- current-time (* (the float (the int (/ current-time period-float))) period-float))
period-float
)
)
)
)
(if (>= current-val 1.0)
(set! current-val (- 2.0 current-val))
)
(* current-val max-out-val)
)
)
(defmethod get-current-value-with-mirror sync-info-eased ((obj sync-info-eased) (max-out-val float))
"Get phase that goes from 0 to max-out-val to 0 in each period"
(* (get-current-phase-with-mirror obj) max-out-val)
)
(defmethod get-current-value-with-mirror sync-info-paused ((obj sync-info-paused) (max-out-val float))
"Get phase that goes from 0 to max-out-val to 0 in each period"
(* (get-current-phase-with-mirror obj) max-out-val)
)
(defmethod set-params! delayed-rand-float ((obj delayed-rand-float) (min-tim int) (max-time int) (max-times-two float))
"Float that changes randomly:
min-time: minimum time between changes
max-time: maximum time between changes
max-times-two: maximum range. result is centered around zero."
(set! (-> obj min-time) min-tim)
(set! (-> obj max-time) max-time)
(set! (-> obj max-val) (* 0.5 max-times-two))
(set! (-> obj start-time) 0)
(set! (-> obj timer) 0)
(set! (-> obj value) 0.0)
(-> obj value)
)
(defmethod update! delayed-rand-float ((obj delayed-rand-float))
"Get the value."
(when (>= (- (-> *display* base-frame-counter) (-> obj start-time)) (-> obj timer))
;; only update if enough time has passed.
(set! (-> obj start-time) (-> *display* base-frame-counter))
;; come up with a random end time.
(set! (-> obj timer) (rand-vu-int-range (-> obj min-time) (-> obj max-time)))
;; come up with a random value in (-max, max)
(set! (-> obj value) (rand-vu-float-range (- (-> obj max-val)) (-> obj max-val)))
)
(-> obj value)
)
(defmethod set-params! oscillating-float ((obj oscillating-float) (init-val float) (accel float) (max-vel float) (damping float))
"Setup an oscillating-float. It will head toward the target, but will overshoot and oscillate before
eventually reaching the target.
init-val: the initial value and target
max-vel: velocity limit
damping: this is 1 - damping really. 0 means don't move, 1 means oscillate forever.
accel: gain."
(set! (-> obj value) init-val)
(set! (-> obj target) init-val)
(set! (-> obj vel) 0.0)
(set! (-> obj max-vel) max-vel)
(set! (-> obj damping) damping)
(set! (-> obj accel) accel)
(-> obj value)
)
(defmethod update! oscillating-float ((obj oscillating-float) (target-offset float))
;; first compute desired acceleration
(let ((acc (* (- (+ (-> obj target) target-offset) (-> obj value))
(* (-> obj accel) (-> *display* time-adjust-ratio))
)
)
)
;; integrate and update velocity
(+! (-> obj vel) acc)
)
;; limit velocity
(set! (-> obj vel) (fmin (-> obj max-vel) (fmax (- (-> obj max-vel)) (-> obj vel))))
;; apply damping
(set! (-> obj vel) (* (-> obj vel) (-> obj damping)))
;; integrate and update position
(+! (-> obj value) (* (-> obj vel) (-> *display* time-adjust-ratio)))
(-> obj value)
)
(defmethod set-params! bouncing-float ((obj bouncing-float) (init-val float) (max-val float) (min-val float) (elast float) (accel float) (max-vel float) (damping float))
"Float that bounces. It's an oscillating float, but you can add a floor/ceiling that has an
elastic collision.
init-val: intial value and target.
max-val: ceiling to bounce off of
min-val: floor to bounce off of
elast: elasticity
accel: gain
max-vel: maximum velocity, not in elastic part.
damping: damping for the non-elastic part."
(set-params! (-> obj osc) init-val accel max-vel damping)
(set! (-> obj max-value) max-val)
(set! (-> obj min-value) min-val)
(set! (-> obj elasticity) elast)
(set! (-> obj state) 0)
(-> obj osc value)
)
(defmethod update! bouncing-float ((obj bouncing-float) (arg0 float))
;; first, update the oscillator and assume we aren't in a bouncing part
(update! (-> obj osc) arg0)
(set! (-> obj state) 0)
(when (>= (-> obj osc value) (-> obj max-value))
;; boucing off of the ceiling. first, saturate to max-val
(set! (-> obj osc value) (-> obj max-value))
(if (< 0.0 (-> obj osc vel))
;; then update our velocity for the elastic collision
(set! (-> obj osc vel) (* (-> obj osc vel) (- (-> obj elasticity))))
)
;; and remember we did this, so at-min/at-max work
(set! (-> obj state) 1)
)
;; same for bouncing off of the floor
(when (>= (-> obj min-value) (-> obj osc value))
(set! (-> obj osc value) (-> obj min-value))
(if (< (-> obj osc vel) 0.0)
(set! (-> obj osc vel) (* (-> obj osc vel) (- (-> obj elasticity))))
)
(set! (-> obj state) -1)
)
(-> obj osc value)
)
(defmethod at-min? bouncing-float ((obj bouncing-float))
"Did the last update it the minimum value?"
(= (-> obj state) -1)
)
(defmethod at-max? bouncing-float ((obj bouncing-float))
"Did the last update hit the maximum value?"
(= (-> obj state) 1)
)
(defmethod set-params! delayed-rand-vector ((obj delayed-rand-vector) (min-time int) (max-time int) (xz-range float) (y-range float))
"Set up a delayed-rand-vector. This vector randomly changes at random times.
min-time: minimum time between changes
max-time: maximum time between changes
xz-range: xz results in (-range/2, range/2)
y-range: y results in (-range/2, range/2)"
(set! (-> obj min-time) min-time)
(set! (-> obj max-time) max-time)
(set! (-> obj xz-max) (* 0.5 xz-range))
(set! (-> obj y-max) (* 0.5 y-range))
(set! (-> obj start-time) 0)
(set! (-> obj timer) 0)
(vector-reset! (-> obj value))
(-> obj value)
)
(defmethod update-now! delayed-rand-vector ((obj delayed-rand-vector))
"update to a random value now"
(set! (-> obj start-time) (-> *display* base-frame-counter))
(set! (-> obj timer) (rand-vu-int-range (-> obj min-time) (-> obj max-time)))
(set! (-> obj value x) (rand-vu-float-range (- (-> obj xz-max)) (-> obj xz-max)))
(set! (-> obj value y) (rand-vu-float-range (- (-> obj y-max)) (-> obj y-max)))
(set! (-> obj value z) (rand-vu-float-range (- (-> obj xz-max)) (-> obj xz-max)))
(-> obj value)
)
(defmethod update-with-delay! delayed-rand-vector ((obj delayed-rand-vector))
"Update, if enough time has passed"
(if (>= (- (-> *display* base-frame-counter) (-> obj start-time)) (-> obj timer))
(update-now! obj)
)
(-> obj value)
)
(defmethod update-with-delay-or-reset! delayed-rand-vector ((obj delayed-rand-vector))
"Update, if enough time has passed. Otherwise reset to zero."
(if (>= (- (-> *display* base-frame-counter) (-> obj start-time)) (-> obj timer))
(update-now! obj)
(vector-reset! (-> obj value))
)
(-> obj value)
)
(defmethod set-params! oscillating-vector ((obj oscillating-vector) (init-val vector) (accel float) (max-vel float) (damping float))
"Works just like oscillating-float, but does a whole vector.
init-val can be #f to reset to 0."
(cond
(init-val
(set! (-> obj value quad) (-> init-val quad))
(set! (-> obj target quad) (-> init-val quad))
)
(else
(vector-reset! (-> obj value))
(vector-reset! (-> obj target))
)
)
(vector-reset! (-> obj vel))
(set! (-> obj max-vel) max-vel)
(set! (-> obj damping) damping)
(set! (-> obj accel) accel)
(-> obj value)
)
(defmethod update! oscillating-vector ((obj oscillating-vector) (target-offset vector))
"target-offset can be #f, acts like 0"
(let ((s5-0 (new 'stack-no-clear 'vector)))
(cond
(target-offset
(vector+! s5-0 (-> obj target) target-offset)
(vector-! s5-0 s5-0 (-> obj value))
)
(else
(vector-! s5-0 (-> obj target) (-> obj value))
)
)
(vector-float*! s5-0 s5-0 (* (-> obj accel) (-> *display* time-adjust-ratio)))
(vector+! (-> obj vel) (-> obj vel) s5-0)
(let ((vel (vector-length (-> obj vel))))
(if (< (-> obj max-vel) vel)
(vector-float*! (-> obj vel) (-> obj vel) (/ (-> obj max-vel) vel))
)
)
(vector-float*! (-> obj vel) (-> obj vel) (-> obj damping))
(vector-float*! s5-0 (-> obj vel) (-> *display* time-adjust-ratio))
(vector+! (-> obj value) (-> obj value) s5-0)
)
(-> obj value)
)