;;-*-Lisp-*-
(in-package goal)
(bundles "ENGINE.CGO" "GAME.CGO")
(require "engine/gfx/hw/display.gc")
(require "engine/gfx/sprite/sparticle/sparticle-launcher.gc")

;; This file contains the interface between sparticle and sprite as well as the actual particle updates.

;; DECOMP BEGINS

(defmethod print ((this sparticle-cpuinfo))
  (format #t "<sparticle-cpuinfo>~%")
  (dotimes (s5-0 16)
    (format #t "~D:~F~%" s5-0 (the-as float (-> this data s5-0))))
  (format #t "TIMER:~D~%" (-> this timer))
  (the-as sparticle-cpuinfo (format #t "FLAGS:~X~%" (-> this flags))))

(defun sp-particle-copy! ((arg0 sparticle-cpuinfo) (arg1 sparticle-cpuinfo))
  (let ((v1-1 (-> arg1 sprite x-y-z-sx quad))) (set! (-> arg0 sprite x-y-z-sx quad) v1-1))
  (let ((v1-3 (-> arg1 sprite flag-rot-sy quad))) (set! (-> arg0 sprite flag-rot-sy quad) v1-3))
  (let ((v1-5 (-> arg1 sprite color quad))) (set! (-> arg0 sprite color quad) v1-5))
  (dotimes (v1-6 10)
    (set! (-> arg0 adgif prims v1-6) (-> arg1 adgif prims v1-6)))
  (set! (-> arg0 vel-sxvel quad) (-> arg1 vel-sxvel quad))
  (set! (-> arg0 rot-syvel quad) (-> arg1 rot-syvel quad))
  (set! (-> arg0 fade quad) (-> arg1 fade quad))
  (set! (-> arg0 acc quad) (-> arg1 acc quad))
  (set! (-> arg0 friction) (-> arg1 friction))
  (set! (-> arg0 timer) (-> arg1 timer))
  (set! (-> arg0 flags) (-> arg1 flags))
  (set! (-> arg0 user-float) (-> arg1 user-float))
  (set! (-> arg0 func) (-> arg1 func))
  (none))

(defmethod new sparticle-system ((allocation symbol)
            (type-to-make type)
            (arg0 int)
            (arg1 int)
            (arg2 symbol)
            (arg3 pointer)
            (arg4 (inline-array adgif-shader)))
  "allocate a particle system.
  arg0: number of group0's
  arg1: number of group1's
  arg2: is 3d?
  arg3: pointer to sprite allocations
  arg4: pointer to adgif allocation"
  (#when PC_BIG_MEMORY
    (*! arg0 SPRITE_MAX_AMOUNT_MULT)
    ; (*! arg1 SPRITE_MAX_AMOUNT_MULT)
    )
  (let ((gp-0 (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
    (let* ((v1-3 (/ (+ arg0 63) 64)) ;; num blocks
           (a0-2 (/ (+ arg1 63) 64))
           (a1-2 (* v1-3 64)) ;; length
           (a2-2 (* a0-2 64))
           (s2-1 (+ v1-3 a0-2)) ;; total blocks
           (s5-1 (+ a1-2 a2-2)) ;; total length
           )
      (set! (-> gp-0 blocks 0) v1-3)
      (set! (-> gp-0 length 0) a1-2)
      (set! (-> gp-0 num-alloc 0) 0)
      (set! (-> gp-0 blocks 1) a0-2)
      (set! (-> gp-0 length 1) a2-2)
      (set! (-> gp-0 num-alloc 1) 0)
      (set! (-> gp-0 is-3d) arg2)
      ;; per block uint64 (bit per-sprite)
      (set! (-> gp-0 alloc-table) (the-as (pointer uint64) (malloc 'global (* s2-1 8))))
      ;; per particle
      (set! (-> gp-0 cpuinfo-table) (the-as (inline-array sparticle-cpuinfo) (malloc 'global (* 144 s5-1))))
      ;; sprite data
      (set! (-> gp-0 vecdata-table) arg3)
      ;; adgifs
      (set! (-> gp-0 adgifdata-table) arg4)
      (dotimes (v1-5 s2-1)
        ;; fill alloc-table with 1's
        (set! (-> gp-0 alloc-table v1-5) (the-as uint -1)))
      (dotimes (s4-1 s5-1)
        (set! (-> gp-0 cpuinfo-table s4-1 valid) #f)
        ;; link cpuinfos to sprite data
        (set! (-> gp-0 cpuinfo-table s4-1 sprite) (the-as sprite-vec-data-2d (&+ (-> gp-0 vecdata-table) (* 48 s4-1))))
        ;; and to adgif
        (set! (-> gp-0 cpuinfo-table s4-1 adgif) (-> gp-0 adgifdata-table s4-1))
        ;; default init adgifs.
        (adgif-shader<-texture-simple! (-> gp-0 adgifdata-table s4-1) (the-as texture #f))
        (set! (-> gp-0 adgifdata-table s4-1 alpha) (new 'static 'gs-alpha :b #x2 :d #x1))))
    gp-0))

;;;;;;;;;;;;;;;;;;;;
;; particle systems
;;;;;;;;;;;;;;;;;;;;

;; note: these constants must match the sizes of the sprite arrays.

(define *sp-particle-system-2d*
  (new 'global 'sparticle-system 1920 128 #f (-> *sprite-array-2d* vec-data) (-> *sprite-array-2d* adgif-data)))

(define *sp-particle-system-3d*
  (new 'global 'sparticle-system 256 0 #t (-> *sprite-array-3d* vec-data) (-> *sprite-array-3d* adgif-data)))

;;;;;;;;;;;;;;;;;;;;
;; alloc and block
;;;;;;;;;;;;;;;;;;;;

(defun sp-get-block-size ((arg0 sparticle-system) (arg1 int))
  "get the index of the highest used block for the given group (0 or 1)"
  (let ((v0-0 0))
    (let ((v1-0 0)
          (a2-0 (-> arg0 blocks 0)))
      (when (= arg1 1)
        (set! v1-0 a2-0)
        (set! a2-0 (-> arg0 blocks 1)))
      (dotimes (a1-3 a2-0)
        (if (!= (-> arg0 alloc-table (+ v1-0 a1-3)) -1) (set! v0-0 (+ a1-3 1)))))
    v0-0))

(defun sp-get-approx-alloc-size ((arg0 sparticle-system) (arg1 int))
  "get approximately the amount of sprite memory used."
  ;; this is just (* 64 (sp-get-block-size arg0 arg1))
  (let ((a3-0 arg1)
        (v1-0 0))
    (let ((a1-1 0)
          (a2-0 (-> arg0 blocks 0)))
      (when (= a3-0 1)
        (set! a1-1 a2-0)
        (set! a2-0 (-> arg0 blocks 1)))
      (dotimes (a3-3 a2-0)
        (if (!= (-> arg0 alloc-table (+ a1-1 a3-3)) -1) (set! v1-0 (+ a3-3 1)))))
    (* v1-0 64)))

(defun sp-free-particle ((arg0 sparticle-system) (arg1 int) (arg2 sparticle-cpuinfo) (arg3 sprite-vec-data-2d))
  "free a particle"
  ;; clear flags on our launch state.
  (if (and (-> arg2 binding) (nonzero? (-> arg2 binding)))
    (logclear! (-> arg2 binding flags) (sp-launch-state-flags launcher-active particles-active)))
  ;; clear the bit indicating that we're alive.
  (let ((v1-6 (/ arg1 64))
        (t0-4 (logand arg1 63)))
    (logior! (-> arg0 alloc-table v1-6) (ash 1 t0-4)))
  ;; decrease alloc count for the appropriate group
  (if (< arg1 (-> arg0 length 0)) (+! (-> arg0 num-alloc 0) -1) (+! (-> arg0 num-alloc 1) -1))
  (set! (-> arg2 valid) #f)
  (set! (-> arg3 a) 0.0)
  0
  (none))

(defun sp-get-particle ((arg0 sparticle-system) (arg1 int) (arg2 sparticle-launch-state))
  "alloc a particle"
  ;; og:preserve-this
  ;; pc port note : this function originally attempted to find a free sparticle by picking a "random"
  ;; (actually incremental) starting block and going from there. we disable that completely for pc
  ;; because it is actually detrimental since we may alloc sparticles spread over way too many blocks
  ;; which can have a negative impact on our performance.
  (let ((v1-0 0)
        (t0-0 (-> arg0 blocks 0))
        (a3-0 0))
    (when (= arg1 1)
      (set! v1-0 t0-0)
      (set! t0-0 (-> arg0 blocks 1)))
    (dotimes (blk t0-0)
      (when (nonzero? (-> arg0 alloc-table (+ v1-0 a3-0)))
        (dotimes (bit 64)
          (when (nonzero? (logand (ash 1 bit) (-> arg0 alloc-table (+ v1-0 a3-0))))
            (logxor! (-> arg0 alloc-table (+ v1-0 a3-0)) (ash 1 bit))
            (+! (-> arg0 num-alloc arg1) 1)
            (let ((v1-9 (-> arg0 cpuinfo-table (+ bit (* (+ v1-0 a3-0) 64))))) (set! (-> v1-9 valid) #t) (return v1-9)))))
      (+! a3-0 1)
      (if (= a3-0 t0-0) (set! a3-0 0))))
  (the-as sparticle-cpuinfo #f))

(defun sp-kill-particle ((arg0 sparticle-system) (arg1 sparticle-cpuinfo))
  "kill the given particle"
  (cond
    ;; modified to check the end of the spad too.
    ((in-scratchpad? arg1) ;;(>= (the-as int arg1) #x70000000) ;; TODO PC port
     ;; if in the sratchpad, just set the timer to 0.
     (set! (-> arg1 timer) 0)
     0)
    (else
     (let ((a2-1 (/ (the-as int (- (the-as uint arg1) (the-as uint (-> arg0 cpuinfo-table 0)))) 144)))
       (when (or (< a2-1 0) (>= a2-1 (+ (-> arg0 length 0) (-> arg0 length 1))))
         ;; invalid location for particle, error
         (format 0 "Tried to release particle ~D~%" a2-1)
         (return #f))
       ;; free it.
       (sp-free-particle arg0 a2-1 arg1 (-> arg1 sprite)))))
  #t
  (none))

;;;;;;;;;;;;;;;;
;; effects
;;;;;;;;;;;;;;;;

(defun sp-orbiter ((arg0 sparticle-system) (arg1 sparticle-cpuinfo) (arg2 vector))
  (let* ((f2-0 (-> arg1 omega))
         (f0-0 (-> arg1 radius))
         (f4-0 (-> arg1 vel-sxvel x))
         (f24-0 (-> arg1 vel-sxvel y))
         (f1-0 (-> arg1 vel-sxvel z))
         (f3-0 (-> *sp-frame-time* y))
         (f28-0 (+ f2-0 (* f4-0 f3-0))))
    (set! (-> arg1 omega) f28-0)
    (let ((f30-0 (+ f0-0 (* f1-0 f3-0))))
      (set! (-> arg1 radius) f30-0)
      (let ((f26-0 (sin f28-0))
            (f28-1 (cos f28-0))
            (f22-0 (sin (* 0.5 f24-0)))
            (f0-5 (cos (* 0.5 f24-0)))
            (a1-1 (new 'stack-no-clear 'vector))
            (s4-0 (new 'stack-no-clear 'vector)))
        (let ((s3-0 (new 'stack-no-clear 'matrix)))
          (set-vector! a1-1 (* f22-0 f28-1) 0.0 (* f22-0 f26-0) f0-5)
          (quaternion*! (-> arg1 rotvel3d) (the-as quaternion a1-1) (-> arg1 rotvel3d))
          (quaternion-normalize! (-> arg1 rotvel3d))
          (set-vector! s4-0 (* f26-0 f30-0) 0.0 (* f28-1 f30-0) 1.0)
          (quaternion->matrix s3-0 (-> arg1 rotvel3d))
          (vector-matrix*! s4-0 s4-0 s3-0))
        (let ((v1-3 (the-as object (-> arg1 user-float))))
          (set! (-> arg2 x) (+ (-> s4-0 x) (-> (the-as sprite-vec-data-2d v1-3) x)))
          (set! (-> arg2 y) (+ (-> s4-0 y) (-> (the-as sprite-vec-data-2d v1-3) y)))
          (set! (-> arg2 z) (+ (-> s4-0 z) (-> (the-as sprite-vec-data-2d v1-3) z)))))))
  0
  (none))

;;;;;;;;;;;;;;;;;;;;;
;; particle update
;;;;;;;;;;;;;;;;;;;;;

(def-mips2c sp-process-block-2d (function sparticle-system int int int int symbol none))

(def-mips2c sp-process-block-3d (function sparticle-system int int int int symbol none))

(defun sp-copy-to-spr ((arg0 int) (arg1 pointer) (arg2 int))
  ;; modified to use fake spad
  (let ((a2-1 (/ (+ arg2 15) 16)))
    (__mem-move (&+ (scratchpad-start) arg0) arg1 (the uint (* a2-1 16)))
    ;; (dma-send-to-spr-no-flush (the-as uint arg0) (the-as uint arg1) (the-as uint a2-1) #t)
    )
  0
  (none))

(defun sp-copy-from-spr ((arg0 int) (arg1 pointer) (arg2 int))
  ;; modified to use fake spad.
  (let ((a2-1 (/ (+ arg2 15) 16)))
    (__mem-move arg1 (&+ (scratchpad-start) arg0) (the uint (* a2-1 16)))
    ;;(dma-send-from-spr-no-flush (the-as uint arg1) (the-as uint arg0) (the-as uint a2-1) #t)
    )
  0
  (none))

;; TODO memcpy (unused)

(defun sp-process-block ((arg0 sparticle-system) (arg1 int) (arg2 sprite-array-2d) (arg3 int))
  "update the given block of particles"
  (local-vars (sv-16 int) (sv-32 int) (sv-80 int) (sv-96 int))
  (let ((s3-0 16)
        (s2-0 (* 144 arg3))
        (s5-0 (* 48 arg3)))
    (set! sv-32 (* 80 arg3))
    (let ((s1-0 (+ s3-0 s2-0)))
      (set! sv-16 (+ s1-0 s5-0))
      (sp-copy-to-spr s3-0 (the-as pointer (-> arg0 cpuinfo-table arg1)) s2-0)
      (sp-copy-to-spr s1-0 (&+ (-> arg0 vecdata-table) (* 48 arg1)) s5-0)
      (let ((t9-2 sp-copy-to-spr)
            (a1-7 (-> arg0 adgifdata-table arg1)))
        (t9-2 sv-16 (the-as pointer a1-7) sv-32))
      ;; alternate to run without doing scratchpad (will change behavior of kill, but at least works for hud)
      ; (set! sv-80 (the-as int (-> arg0 cpuinfo-table arg1)))
      ; (set! sv-96 (the-as int (&+ (-> arg0 vecdata-table) (* 48 arg1))))
      (set! sv-80 (+ (the int (scratchpad-start)) s3-0)) ;; spad
      (set! sv-96 (+ (the int (scratchpad-start)) s1-0)) ;; spad
      (cond
        ((-> arg0 is-3d) (sp-process-block-3d arg0 sv-80 sv-96 arg1 arg3 (paused?)))
        (else (sp-process-block-2d arg0 sv-80 sv-96 arg1 arg3 (paused?))))
      (sp-copy-from-spr s3-0 (the-as pointer (-> arg0 cpuinfo-table arg1)) s2-0)
      (sp-copy-from-spr s1-0 (&+ (-> arg0 vecdata-table) (* 48 arg1)) s5-0)))
  0
  (none))

(defun sp-process-particle-system ((arg0 sparticle-system) (arg1 int) (arg2 sprite-array-2d))
  "update an entire particle system."
  (let* ((v1-0 16)
         (s1-0 (/ (- #x4000 v1-0) 272)) ;; offset in spad of vecdata, max sprite data in spad
         (s3-0 0) ;; mem offset
         (s4-0 (sp-get-approx-alloc-size arg0 arg1)) ;; remaining size.
         )
    ;; group 1 starts after group 0
    (if (= arg1 1) (set! s3-0 (* (-> arg0 blocks 0) 64)))
    (set! (-> arg2 num-valid arg1) s4-0)
    ;; flush before processing
    (flush-cache 0)
    (while (>= s4-0 s1-0) ;; there are full blocks remaining...
      (sp-process-block arg0 s3-0 arg2 s1-0) ;; process it
      (set! s4-0 (- s4-0 s1-0))
      (+! s3-0 s1-0) ;; next data
      )
    ;; if there's anything leftover, don't forget that.
    (if (> s4-0 0) (sp-process-block arg0 s3-0 arg2 s4-0)))
  0
  (none))

(define-perm *particles-flag* symbol #t)

(defun forall-particles-with-key-runner ((arg0 sparticle-launch-control) (arg1 (function sparticle-system sparticle-cpuinfo none)) (arg2 sparticle-system))
  "call the given function on all particles with the given key (arg0)"
  (local-vars (sv-16 int))
  (let ((s3-0 (the-as object (-> arg2 cpuinfo-table 0)))
        (s2-0 (&+ (-> arg2 vecdata-table) 0))
        (s1-0 (+ (-> arg2 blocks 0) (-> arg2 blocks 1))))
    (dotimes (s0-0 s1-0) ;; loop over blocks
      (cond
        ((!= (-> arg2 alloc-table s0-0) -1) ;; block has something allocated.
         (set! sv-16 0)
         (while (< sv-16 64)
           (if (and (-> (the-as sparticle-cpuinfo s3-0) valid) ;; is allocated
                    (= (-> (the-as sparticle-cpuinfo s3-0) key) arg0) ;; matches the key
                    )
             (arg1 arg2 (the-as sparticle-cpuinfo s3-0)) ;; run the callback!
             )
           (set! s3-0 (-> (the-as (inline-array sparticle-cpuinfo) s3-0) 1)) ;; next cpu
           (&+! s2-0 48) ;; next vec
           (set! sv-16 (+ sv-16 1)) ;; next idx in block
           ))
        (else
         ;; skip the whole block!
         (set! s3-0 (-> (the-as (inline-array sparticle-cpuinfo) s3-0) 64))
         (&+! s2-0 3072)))))
  0
  (none))

(defun forall-particles-with-key ((arg0 sparticle-launch-control) (arg1 (function sparticle-system sparticle-cpuinfo none)) (arg2 symbol) (arg3 symbol))
  "call the given function on all particles with the given key. arg2 is 2d, arg3 is 3d."
  (if arg2 (forall-particles-with-key-runner arg0 arg1 *sp-particle-system-2d*))
  (if arg3 (forall-particles-with-key-runner arg0 arg1 *sp-particle-system-3d*))
  0
  (none))

(defun sparticle-kill-it ((arg0 sparticle-system) (arg1 sparticle-cpuinfo))
  "kill a particle without freeing it."
  (set! (-> arg1 timer) 0) ;; advance timer to zero
  (set! (-> arg1 func) (the-as basic 0)) ;; don't let the callback run and save the timer
  (when (and (-> arg1 binding) (nonzero? (-> arg1 binding)))
    ;; kill the launcher too.
    (logclear! (-> arg1 binding flags) (sp-launch-state-flags launcher-active particles-active))
    ;; and forget it, I guess it could be unloaded
    (set! (-> arg1 binding) #f))
  0
  (none))

(defun sparticle-kill-it-level0 ((arg0 sparticle-system) (arg1 sparticle-cpuinfo))
  "kill all particles belonging to level 0"
  (if (logtest? (-> arg1 flags) (sp-cpuinfo-flag level0)) (sparticle-kill-it arg0 arg1))
  0
  (none))

(defun sparticle-kill-it-level1 ((arg0 sparticle-system) (arg1 sparticle-cpuinfo))
  "kill all particles belonging to level 1."
  (if (logtest? (-> arg1 flags) (sp-cpuinfo-flag level1)) (sparticle-kill-it arg0 arg1))
  0
  (none))

(defun sparticle-60-to-50 ((arg0 sparticle-system) (arg1 sparticle-cpuinfo) (arg2 pointer))
  "convert from 60hz to 50hz particles"
  (let ((gp-0 (-> arg1 rotvel3d))
        (s5-0 (new 'stack-no-clear 'quaternion)))
    (vector-angle<-quaternion! (the-as vector s5-0) gp-0)
    (set! (-> s5-0 w) (* 12516.455 (-> s5-0 w)))
    (quaternion-vector-angle! gp-0 (the-as vector s5-0) (-> s5-0 w)))
  0
  (none))

(defun sparticle-50-to-60 ((arg0 sparticle-system) (arg1 sparticle-cpuinfo) (arg2 pointer))
  "convert from 50hz to 60hz particles"
  (let ((gp-0 (-> arg1 rotvel3d))
        (s5-0 (new 'stack-no-clear 'quaternion)))
    (vector-angle<-quaternion! (the-as vector s5-0) gp-0)
    (set! (-> s5-0 w) (* 8691.982 (-> s5-0 w)))
    (quaternion-vector-angle! gp-0 (the-as vector s5-0) (-> s5-0 w)))
  0
  (none))

(defun kill-all-particles-with-key ((arg0 sparticle-launch-control))
  (forall-particles-with-key arg0 sparticle-kill-it #t #t)
  0
  (none))

(defun forall-particles-runner ((arg0 (function sparticle-system sparticle-cpuinfo pointer none)) (arg1 sparticle-system))
  "run function on all particles in the system."
  (let ((s4-0 (the-as object (-> arg1 cpuinfo-table 0)))
        (s3-0 (&+ (-> arg1 vecdata-table) 0))
        (s2-0 (+ (-> arg1 blocks 0) (-> arg1 blocks 1))))
    (dotimes (s1-0 s2-0)
      (cond
        ((!= (-> arg1 alloc-table s1-0) -1)
         (dotimes (s0-0 64)
           (if (-> (the-as sparticle-cpuinfo s4-0) valid) (arg0 arg1 (the-as sparticle-cpuinfo s4-0) s3-0))
           (set! s4-0 (+ (the-as uint s4-0) 144))
           (&+! s3-0 48)))
        (else (set! s4-0 (&+ (the-as pointer s4-0) 9216)) (&+! s3-0 3072)))))
  0
  (none))

(defun forall-particles ((arg0 function) (arg1 symbol) (arg2 symbol))
  "run function on all particles. arg1 for 2d, arg2 for 3d."
  (if arg1
    (forall-particles-runner (the-as (function sparticle-system sparticle-cpuinfo pointer none) arg0)
                             *sp-particle-system-2d*))
  (if arg2
    (forall-particles-runner (the-as (function sparticle-system sparticle-cpuinfo pointer none) arg0)
                             *sp-particle-system-3d*))
  0
  (none))

(defun kill-all-particles-in-level ((arg0 level))
  "kill all particles belonging to the given level."
  (forall-particles (if (zero? (-> arg0 index)) sparticle-kill-it-level0 sparticle-kill-it-level1) #t #t)
  0)

(defun all-particles-50-to-60 ()
  (forall-particles-runner sparticle-50-to-60 *sp-particle-system-3d*)
  (none))

(defun all-particles-60-to-50 ()
  (forall-particles-runner sparticle-60-to-50 *sp-particle-system-3d*)
  (none))

(defun set-particle-frame-time ((scaled-seconds int))
  "Adjusts particle frame time based on the frame-rate (scaled-seconds). Note: This used to be a case statement and has been rewritten as a more generic formula"
  (set-vector! *sp-frame-time*
               (the-as float (logior #x800000 scaled-seconds))
               (the float scaled-seconds)
               (* 0.2 scaled-seconds)
               (* 0.2 scaled-seconds))
  (none))

(defun process-particles ()
  "main particle system update."
  (local-vars (v1-29 int) (gp-0 int))
  (when *particles-flag*
    0
    0
    ;;(.mfc0 gp-0 Count)
    (set! *sp-launcher-lock* #t)
    ;; start a profile frame.
    (if *debug-segment*
      (add-frame (-> *display* frames (-> *display* on-screen) frame profile-bar 0)
                 'draw
                 (new 'static 'rgba :r #x40 :b #x40 :a #x80)))
    ;; update timer
    (let ((v1-14 (logand (the-as int (-> *sp-frame-time* x)) 255)))
      (set! *particle-300hz-timer* (+ *particle-300hz-timer* v1-14))
      (cond
        (*sp-60-hz* (when (or (= v1-14 6) (= v1-14 12)) (set! *sp-60-hz* #f) (all-particles-60-to-50)))
        (else (when (or (= v1-14 5) (= v1-14 10)) (set! *sp-60-hz* #t) (all-particles-50-to-60)))))
    ;; process the particles
    ;; og:preserve-this made this not run when paused, since aux sprites wont get added.
    (unless (paused?)
      (clear-sprite-aux-list))
    (sp-process-particle-system *sp-particle-system-2d* 0 *sprite-array-2d*)
    (sp-process-particle-system *sp-particle-system-2d* 1 *sprite-array-2d*)
    (sp-process-particle-system *sp-particle-system-3d* 0 (the-as sprite-array-2d *sprite-array-3d*))
    (if *debug-segment*
      (add-frame (-> *display* frames (-> *display* on-screen) frame profile-bar 0)
                 'draw
                 (new 'static 'rgba :r #x80 :g #x80 :b #xff :a #x80)))
    (set! *sp-launcher-lock* #f)
    ;; launch queued particles
    (sp-clear-queue)
    ;;(.mfc0 v1-29 Count)
    (let ((a2-5 (- v1-29 gp-0)))
      (if *display-sprite-info*
        (format *stdcon*
                "Particle time = ~D cycles for ~D 2D [~D warp] and ~D HUD and ~D 3D~%"
                a2-5
                (-> *sp-particle-system-2d* num-alloc 0)
                (-> *sprite-aux-list* entry)
                (-> *sp-particle-system-2d* num-alloc 1)
                (-> *sp-particle-system-3d* num-alloc 0)))))
  0
  (none))
