mirror of
https://github.com/open-goal/jak-project
synced 2026-09-10 04:22:13 -04:00
up to settings
This commit is contained in:
@@ -21912,7 +21912,7 @@ a positive implicit w, and convert 300 Hz angular velocity to a per-display-fram
|
||||
(define-extern sp-rotate-system
|
||||
"Rotate a particle's launch offset, velocity, and local acceleration by the owning system's
|
||||
xyz-only quaternion."
|
||||
(function sparticle-launchinfo sparticle-cpuinfo transformq none))
|
||||
(function sparticle-launchinfo sparticle-cpuinfo sprite-vec-data-3d none))
|
||||
(define-extern sp-launch-particles-death
|
||||
"Launch the enemy-death particle as a single 2D sprite, initialize its sprite and CPU fields,
|
||||
apply current time-of-day color, copy the shared death shader, and mark it just launched."
|
||||
@@ -22261,10 +22261,10 @@ record as user-modified."
|
||||
:flag-assert #xc00000010
|
||||
:pack-me
|
||||
(:methods
|
||||
(ambient-control-method-9
|
||||
(reset-ambient-cooldown!
|
||||
"Restart the ambient-chatter cooldown at the current gameplay frame."
|
||||
(_type_) none) ;; 9
|
||||
(ambient-control-method-10
|
||||
(ambient-chatter-ready?
|
||||
"Fill out-vector with the displacement from the listener to speaker and return it only when
|
||||
cooldown ticks have elapsed and the speaker is within max-distance. Return #f otherwise."
|
||||
(_type_ vector time-frame float process-drawable) vector) ;; 10
|
||||
|
||||
@@ -1115,9 +1115,11 @@
|
||||
;; at some point, this could be more optimized.
|
||||
;; MIPS has an explicit abs.s instruction, but x86-64 doesn't.
|
||||
;; modern clang on O3 does a comiss/branch and this is probably pretty close.
|
||||
`(if (< (the float ,x) 0)
|
||||
(- (the float ,x))
|
||||
(the float ,x))
|
||||
(with-gensyms (value)
|
||||
`(let ((,value (the float ,x)))
|
||||
(if (< ,value 0)
|
||||
(- ,value)
|
||||
,value)))
|
||||
)
|
||||
|
||||
(defmacro sqrtf (x)
|
||||
|
||||
@@ -723,7 +723,7 @@
|
||||
(set! (-> this been-kicked) #f)
|
||||
(set! (-> this neck-joint-index) neck-joint)
|
||||
(set! (-> this cur-trans-hook) nothing)
|
||||
(ambient-control-method-9 (-> this ambient))
|
||||
(reset-ambient-cooldown! (-> this ambient))
|
||||
(set! (-> this event-hook) (-> (method-of-object this idle) event))
|
||||
(set! (-> this draw shadow-ctrl) (new 'process 'shadow-control 0.0 0.0 614400.0 (the-as float 60) 245760.0))
|
||||
(setup-shadow-settings! this)
|
||||
@@ -743,13 +743,13 @@
|
||||
"Try to play character-specific ambient speech. The base implementation does nothing."
|
||||
(the-as symbol 0))
|
||||
|
||||
(defmethod ambient-control-method-9 ((this ambient-control))
|
||||
(defmethod reset-ambient-cooldown! ((this ambient-control))
|
||||
"Restart the ambient-chatter cooldown at the current gameplay frame."
|
||||
(set! (-> this last-ambient-time) (-> *display* game-frame-counter))
|
||||
0
|
||||
(none))
|
||||
|
||||
(defmethod ambient-control-method-10 ((this ambient-control) (out-vector vector) (cooldown time-frame) (max-distance float) (speaker process-drawable))
|
||||
(defmethod ambient-chatter-ready? ((this ambient-control) (out-vector vector) (cooldown time-frame) (max-distance float) (speaker process-drawable))
|
||||
"Fill out-vector with the displacement from the listener to speaker and return it only when
|
||||
cooldown ticks have elapsed and the speaker is within max-distance. Return #f otherwise."
|
||||
(when (< (- (-> *display* game-frame-counter) (-> this last-ambient-time)) cooldown)
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
;; :heap-size the required size for the process heap
|
||||
|
||||
;; This table was much larger in the demo and was reduced for the final game. The actor birth path
|
||||
;; uses heap-size to size each process heap; the other fields retain the package, art, and pool
|
||||
;; requirements that go with the process type.
|
||||
;; uses heap-size to size each process heap; the other fields seem unused.
|
||||
|
||||
(define *entity-info*
|
||||
(new 'static
|
||||
|
||||
@@ -29,9 +29,7 @@
|
||||
(defmethod debug-draw! ((this border-plane))
|
||||
"Draw this plane's name, origin, and normal, using green for a load action and red otherwise."
|
||||
(let* ((action (-> this action))
|
||||
;; pick color based on action
|
||||
(color (if (= action 'load) (new 'static 'rgba :g #xff :a #x80) (new 'static 'rgba :r #xff :a #x80))))
|
||||
;; add text and vector
|
||||
(color (if (= action 'load) (static-rgba 0 #xff 0 #x80) (static-rgba #xff 0 0 #x80))))
|
||||
(add-debug-text-sphere #t (bucket-id debug-no-zbuf) (-> this trans) 819.2 (symbol->string (-> this name)) color)
|
||||
(add-debug-vector #t (bucket-id debug-no-zbuf) (-> this trans) (-> this normal) (meters 2) color))
|
||||
0
|
||||
@@ -131,10 +129,10 @@
|
||||
(-> this task-perm-list data task))
|
||||
|
||||
(defmethod initialize! ((this game-info) (cause symbol) (save-to-load game-save) (continue-point-override string))
|
||||
"Reset or reload game state for cause. dead records death statistics and becomes life or try
|
||||
in play mode; game clears all persistent progress and selects a starting checkpoint; try and
|
||||
game restore lives. In play mode, restart actors asynchronously at the selected checkpoint and
|
||||
optionally load save-to-load. continue-point-override applies only to a game reset."
|
||||
"Reset or reload game state for cause:
|
||||
- dead : count a death. If in 'play mode, then continue with 'life/'try logic.
|
||||
- game : reset all persistent information, restart at continue-point-override
|
||||
- life : out of lives (unused feature), reset life counter."
|
||||
(local-vars (death-count int))
|
||||
(case cause
|
||||
(('dead)
|
||||
@@ -199,6 +197,7 @@
|
||||
(set! (-> this in-level-time i) 0)
|
||||
(set! (-> this level-opened i) (the-as uint 0))
|
||||
(nop!))))
|
||||
;; reset lives
|
||||
(case cause
|
||||
(('game 'try)
|
||||
;; full restart, or ran out of lives
|
||||
@@ -276,7 +275,8 @@
|
||||
;; increment our total money in the game (out of the 2000 max orbs)
|
||||
(+! (-> this money-total) amount)
|
||||
;; if we have all the money in our level, display the all orbs graphic
|
||||
(if (= (-> this money-per-level level-idx) (-> (get-game-count level-idx) money-count)) (activate-orb-all level-idx)))))))
|
||||
(if (= (-> this money-per-level level-idx) (-> (get-game-count level-idx) money-count))
|
||||
(activate-orb-all level-idx)))))))
|
||||
;; increment our current money count
|
||||
(+! (-> this money) amount))
|
||||
(('fuel-cell)
|
||||
@@ -544,14 +544,14 @@
|
||||
(else ((method-of-type fact-info pickup-collectable!) this kind amount source-handle))))
|
||||
|
||||
(defmethod lookup-entity-perm-by-aid ((this game-info) (aid actor-id))
|
||||
"Return the saved entity permission record with actor ID aid, or #f when absent."
|
||||
"Return the saved entity permanent records with actor ID aid, or #f when absent."
|
||||
(let ((perms (-> this perm-list)))
|
||||
(countdown (i (-> perms length))
|
||||
(if (= aid (-> perms data i aid)) (return (-> perms data i)))))
|
||||
(the-as entity-perm #f))
|
||||
|
||||
(defmethod copy-perms-from-level! ((this game-info) (lev level))
|
||||
"Copy task-bearing entity permission records from lev into persistent game state, replacing
|
||||
"Copy entity permanent records from lev into persistent game state, replacing
|
||||
records with matching actor IDs and appending new records while capacity remains."
|
||||
(let ((perms (-> this perm-list)) ;; our perms
|
||||
(lev-entities (-> lev bsp level entity)) ;; entities in the level
|
||||
@@ -576,7 +576,7 @@
|
||||
(none))
|
||||
|
||||
(defmethod copy-perms-to-level! ((this game-info) (lev level))
|
||||
"Copy matching persistent entity permission records into lev, then clear transient try-reset
|
||||
"Copy matching persistent entity permanent records into lev, then clear transient try-reset
|
||||
status bits before the entities are used."
|
||||
(let ((lev-entities (-> lev bsp level entity)))
|
||||
(dotimes (lev-entity-idx (-> lev-entities length))
|
||||
@@ -659,7 +659,7 @@
|
||||
(enum->string game-task task))
|
||||
|
||||
(defmethod debug-print ((this game-info) (category symbol))
|
||||
"Inspect game state and optionally print completed game tasks or saved entity permissions.
|
||||
"Inspect game state and optionally print completed game tasks or saved entity permanents.
|
||||
Pass #f for both categories."
|
||||
(inspect this)
|
||||
(when (or (not category) (= category 'game-task))
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
|
||||
(define-extern othercam-init-by-other (function process-taskable symbol symbol symbol none :behavior othercam))
|
||||
|
||||
;; Each task-control holds an ordered list of progression stages. A game task may have several
|
||||
;; stages, each identified by a monotonically increasing status. current-stage selects the first
|
||||
;; available stage or is -1 when none is active. The complete game-task list lives in game-info-h.
|
||||
;; Each task-control holds an ordered list of progression stages (cstages).
|
||||
;; A game task may have several stages, each identified by a monotonically increasing status.
|
||||
;; current-stage selects the first available stage or is -1 when none is active.
|
||||
;; The complete game-task list lives in game-info-h.
|
||||
;; Most tasks award a power cell, while special tasks cover interactions such as the levitator.
|
||||
;; Stage conditions run as process-taskable behaviors so they can use the current character context
|
||||
;; while receiving the task-control whose sibling stages and reminders they inspect.
|
||||
;; Stage conditions run as process-taskable behaviors.
|
||||
|
||||
(defenum task-status
|
||||
:type uint64
|
||||
(invalid 0)
|
||||
@@ -37,35 +38,36 @@
|
||||
|
||||
;; DECOMP BEGINS
|
||||
|
||||
;; A stage that a task can be at
|
||||
(deftype task-cstage (structure)
|
||||
((game-task game-task)
|
||||
(status task-status)
|
||||
(flags task-flags)
|
||||
(condition (function task-control symbol :behavior process-taskable)))
|
||||
(:methods
|
||||
(get-task "Return this stage's game task." (_type_) game-task)
|
||||
(get-status "Return this stage's progress status." (_type_) task-status)
|
||||
(task-available? "Return whether this stage may become current, closing it when persistent
|
||||
progress has already passed it." (_type_ task-control) symbol)
|
||||
(closed? "Return whether this stage is currently closed." (_type_) symbol)
|
||||
(closed-by-default? "Return whether non-game resets leave this stage closed." (_type_) symbol)
|
||||
(close-task! "Close this stage and advance its associated persistent task status when present." (_type_) int)
|
||||
(open-task! "Clear this stage's transient closed flag." (_type_) int)))
|
||||
(get-task (_type_) game-task)
|
||||
(get-status (_type_) task-status)
|
||||
(task-available? (_type_ task-control) symbol)
|
||||
(closed? (_type_) symbol)
|
||||
(closed-by-default? (_type_) symbol)
|
||||
(close-task! (_type_) int)
|
||||
(open-task! (_type_) int)))
|
||||
|
||||
;; The array of stages for a single task.
|
||||
(deftype task-control (basic)
|
||||
((current-stage int16)
|
||||
(stage (array task-cstage)))
|
||||
(:methods
|
||||
(current-task "Return the current task or game-task.none." (_type_) game-task)
|
||||
(current-status "Return the current status or task-status.invalid." (_type_) task-status)
|
||||
(close-current! "Close the current stage and select the first available task." (_type_) game-task)
|
||||
(close-status! "Close a status for the current task and select the first available task." (_type_ task-status) game-task)
|
||||
(first-any "Select and return the first available task in stage-array order." (_type_ symbol) game-task)
|
||||
(reset! "Reset stage closure according to reset mode and closed-by-default flags." (_type_ symbol symbol) int)
|
||||
(closed? "Return whether the matching task and status stage is closed." (_type_ game-task task-status) symbol)
|
||||
(get-reminder "Read an indexed reminder byte from the task's persistent record." (_type_ int) int)
|
||||
(save-reminder "Write an indexed reminder byte to the task's persistent record." (_type_ int int) int)
|
||||
(exists? "Return whether a stage with the given task and status exists." (_type_ game-task task-status) symbol)))
|
||||
(current-task (_type_) game-task)
|
||||
(current-status (_type_) task-status)
|
||||
(close-current! (_type_) game-task)
|
||||
(close-status! (_type_ task-status) game-task)
|
||||
(first-any (_type_ symbol) game-task)
|
||||
(reset! (_type_ symbol symbol) int)
|
||||
(closed? (_type_ game-task task-status) symbol)
|
||||
(get-reminder (_type_ int) int)
|
||||
(save-reminder (_type_ int int) int)
|
||||
(exists? (_type_ game-task task-status) symbol)))
|
||||
|
||||
;; Tracks cooldown and repetition for positional NPC chatter.
|
||||
(deftype ambient-control (structure)
|
||||
@@ -74,11 +76,9 @@
|
||||
(last-ambient-id sound-id))
|
||||
:pack-me
|
||||
(:methods
|
||||
(ambient-control-method-9 "Restart the ambient-chatter cooldown." (_type_) none)
|
||||
(ambient-control-method-10 "Return the listener-to-speaker displacement after the cooldown when
|
||||
the speaker is within the supplied distance." (_type_ vector time-frame float process-drawable) vector)
|
||||
(play-ambient "Play a nonrepeating positional ambient line when hint and level conditions permit
|
||||
it; force bypasses the hint-availability test." (_type_ string symbol vector) symbol)))
|
||||
(reset-ambient-cooldown! (_type_) none)
|
||||
(ambient-chatter-ready? (_type_ vector time-frame float process-drawable) vector)
|
||||
(play-ambient (_type_ string symbol vector) symbol)))
|
||||
|
||||
;; Shared interaction, animation, camera, audio, and shadow state for task-giving characters.
|
||||
(deftype process-taskable (process-drawable)
|
||||
@@ -121,47 +121,32 @@
|
||||
(be-clone handle)
|
||||
idle)
|
||||
(:methods
|
||||
(get-art-elem "Return the art element on the active root animation channel." (_type_) art-element)
|
||||
(play-anim! "Select the current task animation; commit? lets subtype implementations apply
|
||||
progression changes when playback begins." (_type_ symbol) basic)
|
||||
(prefetch-play-anim! "Select the current task animation without committing progression and
|
||||
request it for streaming." (_type_) none)
|
||||
(get-accept-anim "Return the task-accept animation, optionally committing the accepted choice." (_type_ symbol) spool-anim)
|
||||
(push-accept-anim "Select the task-accept animation without committing and request it for
|
||||
streaming." (_type_) none)
|
||||
(get-reject-anim "Return the task-reject animation, optionally committing the rejected choice." (_type_ symbol) spool-anim)
|
||||
(push-reject-anim "Select the task-reject animation without committing and request it for
|
||||
streaming." (_type_) none)
|
||||
(goto-post-anim-state! "Enter give-cell when a reward is owed, otherwise enter release." (_type_) none)
|
||||
(should-display? "Return whether this taskable character should be visible." (_type_) symbol)
|
||||
(init-as-taskable! "Initialize a taskable character's collision, skeleton, interaction, audio,
|
||||
and shadow state." (_type_ object skeleton-group int int vector int) none)
|
||||
(initialize-collision "Build and install this character's solid collision sphere at the supplied
|
||||
center joint." (_type_ int vector) none)
|
||||
(goto-initial-state! "Enter this character's initial hidden, reward, or idle state." (_type_) none)
|
||||
(try-play-ambient-chatter "Try to play character-specific ambient speech; the base method does
|
||||
nothing." (_type_) symbol)
|
||||
(play-reminder "Try to play a reminder for the current task stage." (_type_) symbol)
|
||||
(get-art-elem (_type_) art-element)
|
||||
(play-anim! (_type_ symbol) basic)
|
||||
(prefetch-play-anim! (_type_) none)
|
||||
(get-accept-anim (_type_ symbol) spool-anim)
|
||||
(push-accept-anim (_type_) none)
|
||||
(get-reject-anim (_type_ symbol) spool-anim)
|
||||
(push-reject-anim (_type_) none)
|
||||
(goto-post-anim-state! (_type_) none)
|
||||
(should-display? (_type_) symbol)
|
||||
(init-as-taskable! (_type_ object skeleton-group int int vector int) none)
|
||||
(initialize-collision (_type_ int vector) none)
|
||||
(goto-initial-state! (_type_) none)
|
||||
(try-play-ambient-chatter (_type_) symbol)
|
||||
(play-reminder (_type_) symbol)
|
||||
(process-taskable-method-45 (_type_) symbol)
|
||||
(update-music-and-flava! "Update proximity-controlled music and sound flavor settings, weighting
|
||||
vertical separation fourfold." (_type_) none)
|
||||
(target-above-threshold? "Return whether the target is above the interaction threshold." (_type_) symbol)
|
||||
(draw-npc-shadow "Update the NPC shadow when its highest-detail geometry was drawn, otherwise
|
||||
disable it." (_type_) none)
|
||||
(update-music-and-flava! (_type_) none)
|
||||
(target-above-threshold? (_type_) symbol)
|
||||
(draw-npc-shadow (_type_) none)
|
||||
(hidden-other () _type_ :state)
|
||||
(target-far-away? "Return whether the target, or the camera when no target exists, is far enough
|
||||
away to leave hidden-other." (_type_) symbol)
|
||||
(close-anim-file! "Return the streaming state of the current play animation." (_type_) symbol)
|
||||
(setup-shadow-settings! "Set the character's default shadow clipping planes." (_type_) none)))
|
||||
(target-far-away? (_type_) symbol)
|
||||
(close-anim-file! (_type_) symbol)
|
||||
(setup-shadow-settings! (_type_) none)))
|
||||
|
||||
(defun-extern task-known? game-task symbol)
|
||||
|
||||
(defun-extern task-control-reset symbol none)
|
||||
|
||||
(define-extern task-closed? (function game-task task-status symbol))
|
||||
|
||||
(define-extern get-task-status (function game-task task-status))
|
||||
|
||||
(define-extern get-task-control (function game-task task-control))
|
||||
|
||||
(define-extern close-specific-task! (function game-task task-status game-task))
|
||||
|
||||
@@ -0,0 +1,690 @@
|
||||
(#unless PC_PORT
|
||||
(defun sp-get-particle ((sys sparticle-system) (group int) (binding sparticle-launch-state))
|
||||
"Allocate a free particle in group, beginning at binding's rotating block index when supplied.
|
||||
Clear its bitmap bit, mark its CPU record valid, and return it; return false when full."
|
||||
(let ((block-offset 0)
|
||||
(num-blocks (-> sys blocks 0))
|
||||
(block-index 0))
|
||||
(when (= group 1)
|
||||
(set! block-offset num-blocks)
|
||||
(set! num-blocks (-> sys blocks 1)))
|
||||
;; Rotate the first block examined so repeated launchers spread their allocations.
|
||||
(when binding
|
||||
(set! block-index (the int (-> binding randomize)))
|
||||
(+! (-> binding randomize) 1)
|
||||
(when (= (-> binding randomize) num-blocks)
|
||||
(set! (-> binding randomize) (the uint 0))))
|
||||
(dotimes (i num-blocks)
|
||||
(let ((free-bits (-> sys alloc-table (+ block-offset block-index))))
|
||||
(when (nonzero? free-bits)
|
||||
;; Find the least-significant set bit in six branchless halves. Shifting a candidate
|
||||
;; half to the sign end lets mov.nz keep it when that half contains a free slot;
|
||||
;; mov.z advances the bit index only when the candidate half is empty.
|
||||
(let ((free-bit (rlet ((bits :reg t1)
|
||||
(shifted :reg t2)
|
||||
(candidate-index :reg t3)
|
||||
(bit-index :reg a2))
|
||||
(m! bits free-bits)
|
||||
(m! bit-index 0)
|
||||
(sll32 shifted bits)
|
||||
(add.i candidate-index bit-index 32)
|
||||
(mov.nz bits shifted shifted)
|
||||
(mov.z bit-index candidate-index shifted)
|
||||
(sll shifted bits 16)
|
||||
(add.i candidate-index bit-index 16)
|
||||
(mov.nz bits shifted shifted)
|
||||
(mov.z bit-index candidate-index shifted)
|
||||
(sll shifted bits 8)
|
||||
(add.i candidate-index bit-index 8)
|
||||
(mov.nz bits shifted shifted)
|
||||
(mov.z bit-index candidate-index shifted)
|
||||
(sll shifted bits 4)
|
||||
(add.i candidate-index bit-index 4)
|
||||
(mov.nz bits shifted shifted)
|
||||
(mov.z bit-index candidate-index shifted)
|
||||
(sll shifted bits 2)
|
||||
(add.i candidate-index bit-index 2)
|
||||
(mov.nz bits shifted shifted)
|
||||
(mov.z bit-index candidate-index shifted)
|
||||
(sll shifted bits 1)
|
||||
(add.i candidate-index bit-index 1)
|
||||
(mov.nz bits shifted shifted)
|
||||
(mov.z bit-index candidate-index shifted)
|
||||
bit-index)))
|
||||
(let ((slot (+ free-bit (* (+ block-offset block-index) 64))))
|
||||
(logxor! (-> sys alloc-table (+ block-offset block-index)) (ash 1 free-bit))
|
||||
(+! (-> sys num-alloc group) 1)
|
||||
(let ((cpuinfo (-> sys cpuinfo-table slot))) (set! (-> cpuinfo valid) #t) (return cpuinfo))))))
|
||||
(+! block-index 1)
|
||||
(if (= block-index num-blocks) (set! block-index 0))))
|
||||
(the-as sparticle-cpuinfo #f)))
|
||||
|
||||
(#unless PC_PORT
|
||||
(defun sp-process-block-2d ((sys sparticle-system)
|
||||
(scratchpad-cpuinfo int)
|
||||
(scratchpad-sprite-data int)
|
||||
(start-slot int)
|
||||
(count int)
|
||||
(paused? symbol))
|
||||
"Update count 2D particle records in scratchpad: age, integrate, fade, invoke callbacks, handle
|
||||
death or relaunch, and write their billboard sprite data."
|
||||
(declare (asm-func none))
|
||||
;; Each iteration walks one 144-byte CPU record beside one 48-byte sprite record. The sprite
|
||||
;; record is three quadwords:
|
||||
;;
|
||||
;; +0 position.xyz, x-scale
|
||||
;; +16 sprite flags/matrix mode, rotation, y-scale
|
||||
;; +32 color.rgba
|
||||
;;
|
||||
;; The matching CPU record keeps velocity.xyz/x-scale velocity at +16, rotation/y-scale
|
||||
;; velocity at +32, color fade at +48, acceleration at +64, friction at +96, timer at +100,
|
||||
;; flags at +104, the per-particle update function at +112, relaunch state at +116/+120, cached
|
||||
;; launch alpha at +124, and validity at +128. Keeping these records separate lets the sprite
|
||||
;; record remain ready for the renderer while simulation-only state stays in the CPU record.
|
||||
;;
|
||||
;; *sp-frame-time*.x carries the integer 300 Hz tick count in its low byte. Its y lane scales
|
||||
;; position, rotation, scale, and color increments, while z and w express the same interval in
|
||||
;; 60 Hz frames for acceleration and friction. Invalid slots are skipped. Pausing freezes a
|
||||
;; particle unless run-while-paused is set. A timer of -1 never expires; zero frees the particle.
|
||||
;; On the first visible update, just-launched restores the intended alpha from cache-alpha.
|
||||
(rlet ((sys :reg gp)
|
||||
(cpuinfo :reg s5 :type sparticle-cpuinfo)
|
||||
(sprite-data :reg s4 :type sprite-vec-data-2d)
|
||||
(slot :reg s1)
|
||||
(remaining :reg s3)
|
||||
(paused? :reg s2)
|
||||
(ticks :reg s0)
|
||||
(frame-time :reg vf9)
|
||||
(sprite-pos-scale :reg vf1)
|
||||
(sprite-rot-scale :reg vf2)
|
||||
(sprite-color :reg vf3)
|
||||
(velocity-scale-rate :reg vf4)
|
||||
(rotation-scale-rate :reg vf5)
|
||||
(color-fade-rate :reg vf6)
|
||||
(acceleration :reg vf7)
|
||||
(friction-factor :reg vf8)
|
||||
(position-step :reg vf10)
|
||||
(rotation-scale-step :reg vf11)
|
||||
(color-step :reg vf12))
|
||||
;; Establish the scratchpad cursors and recover this frame's integer tick count.
|
||||
(asm-block setup-2d
|
||||
(label particle-update-2d-entry)
|
||||
(add.i sp sp -128)
|
||||
(s.d ra sp)
|
||||
(s.q s0 sp 16)
|
||||
(s.q s1 sp 32)
|
||||
(s.q s2 sp 48)
|
||||
(s.q s3 sp 64)
|
||||
(s.q s4 sp 80)
|
||||
(s.q s5 sp 96)
|
||||
(s.q gp sp 112)
|
||||
(m sys a0)
|
||||
(m cpuinfo a1)
|
||||
(m sprite-data a2)
|
||||
(m slot a3)
|
||||
(m remaining t0)
|
||||
(m paused? t1)
|
||||
(m! v1 *sp-frame-time*)
|
||||
(l.vf frame-time v1)
|
||||
(m v1 frame-time)
|
||||
(and.i ticks v1 255))
|
||||
;; Reject unused slots, then decide whether a paused particle is allowed to simulate.
|
||||
(asm-block particle-lifetime-2d
|
||||
(label particle-loop)
|
||||
(l.w.field v1 cpuinfo (valid))
|
||||
(b.eq v1 s7 next-particle :delay (nop!))
|
||||
(b.eq paused? s7 simulate-particle :delay (l.w.field v1 cpuinfo (flags)))
|
||||
(and.i v1 v1 #x2000)
|
||||
(b.nz v1 simulate-particle :delay (nop!))
|
||||
(l.w.field v1 cpuinfo (timer))
|
||||
(add.i a0 r0 -1)
|
||||
(b.eq v1 a0 reveal-new-particle :delay (nop!))
|
||||
(b.z v1 free-particle :delay (nop!))
|
||||
;; A newly launched particle is hidden with alpha zero. Reveal it without advancing time when
|
||||
;; the rest of its simulation is frozen.
|
||||
(label reveal-new-particle)
|
||||
(l.w.field a0 cpuinfo (flags))
|
||||
(and.i v1 a0 64)
|
||||
(xor a0 a0 v1)
|
||||
(b.z v1 next-particle :delay (s.w.field a0 cpuinfo (flags)))
|
||||
(l.w.field v1 cpuinfo (cache-alpha))
|
||||
(b next-particle :delay (s.w.field v1 sprite-data (a)))
|
||||
;; Age finite particles in 300 Hz ticks. The packed maximum clamps an overshoot to zero so the
|
||||
;; next iteration follows the ordinary zero-timer death path.
|
||||
(label simulate-particle)
|
||||
(l.w.field v1 cpuinfo (timer))
|
||||
(add.i a0 r0 -1)
|
||||
(b.eq v1 a0 run-callback :delay (sub a0 v1 ticks))
|
||||
(b.z v1 free-particle :delay (max.w v1 a0 r0))
|
||||
(s.w.field v1 cpuinfo (timer))
|
||||
;; Restore the launch alpha before callbacks so custom particle logic sees the visible state.
|
||||
(label run-callback)
|
||||
(l.w.field a0 cpuinfo (flags))
|
||||
(and.i v1 a0 64)
|
||||
(xor a0 a0 v1)
|
||||
(b.z v1 call-update-function :delay (s.w.field a0 cpuinfo (flags)))
|
||||
(l.w.field v1 cpuinfo (cache-alpha))
|
||||
(s.w.field v1 sprite-data (a)))
|
||||
;; The callback may edit either the CPU record or its renderer-facing sprite record.
|
||||
(asm-block callback-and-relaunch-2d
|
||||
(label call-update-function)
|
||||
(l.w.field t9 cpuinfo (func))
|
||||
(b.z t9 check-relaunch :delay (nop!))
|
||||
(add.i sp sp -80)
|
||||
(s.q sys sp)
|
||||
(s.q cpuinfo sp 16)
|
||||
(s.q sprite-data sp 32)
|
||||
(s.q slot sp 48)
|
||||
(m a0 sys)
|
||||
(m a1 cpuinfo)
|
||||
(m a2 sprite-data)
|
||||
(jalr ra t9 :delay (s.q remaining sp 64))
|
||||
(l.q sys sp)
|
||||
(l.q cpuinfo sp 16)
|
||||
(l.q sprite-data sp 32)
|
||||
(l.q slot sp 48)
|
||||
(l.q remaining sp 64)
|
||||
(add.i sp sp 80)
|
||||
;; A nonfalse next-launcher owns a countdown independent of the particle lifetime. Relaunch as
|
||||
;; soon as subtracting this frame's ticks carries it below zero.
|
||||
(label check-relaunch)
|
||||
(l.w.field a1 cpuinfo (next-launcher))
|
||||
(l.w.field v1 cpuinfo (next-time))
|
||||
(b.z a1 integrate :delay (sub a0 v1 ticks))
|
||||
(add.i v1 a0 -1)
|
||||
(b.ge v1 r0 integrate :delay (s.w.field a0 cpuinfo (next-time)))
|
||||
(add.i sp sp -96)
|
||||
(s.q sys sp)
|
||||
(s.q cpuinfo sp 16)
|
||||
(s.q sprite-data sp 32)
|
||||
(s.q slot sp 48)
|
||||
(s.q remaining sp 64)
|
||||
(s.q paused? sp 80)
|
||||
(m a0 sys)
|
||||
(m a3 sprite-data)
|
||||
(m a2 cpuinfo)
|
||||
(m! t9 sp-relaunch-particle-2d)
|
||||
(jalr ra t9 :delay (sll v0 ra 0))
|
||||
(l.q sys sp)
|
||||
(l.q cpuinfo sp 16)
|
||||
(l.q sprite-data sp 32)
|
||||
(l.q slot sp 48)
|
||||
(l.q remaining sp 64)
|
||||
(l.q paused? sp 80)
|
||||
(add.i sp sp 96))
|
||||
;; Integrate the four adjacent CPU vectors directly into the three sprite quadwords.
|
||||
(asm-block integrate-motion-2d
|
||||
(label integrate)
|
||||
(l.vf.field sprite-pos-scale sprite-data (x-y-z-sx))
|
||||
(l.vf.field sprite-rot-scale sprite-data (flag-rot-sy))
|
||||
(l.vf.field sprite-color sprite-data (r-g-b-a))
|
||||
(l.vf.field velocity-scale-rate cpuinfo (vel-sxvel))
|
||||
(l.vf.field rotation-scale-rate cpuinfo (rot-syvel))
|
||||
(l.vf.field color-fade-rate cpuinfo (fade))
|
||||
(l.vf.field acceleration cpuinfo (acc))
|
||||
(l.s.field f0 cpuinfo (friction))
|
||||
(m v1 f0)
|
||||
;; Acceleration uses 60 Hz frame units. Nonzero friction is converted to a retained-velocity
|
||||
;; factor for this interval, then applied to xyz without disturbing x-scale velocity in w.
|
||||
(mul.z.vf acceleration acceleration frame-time)
|
||||
(b.z v1 no-friction :delay (add.vf.xyz velocity-scale-rate velocity-scale-rate acceleration))
|
||||
(m friction-factor v1)
|
||||
(sub.x.vf.w friction-factor vf0 friction-factor)
|
||||
(mul.w.vf friction-factor friction-factor frame-time)
|
||||
(sub.w.vf.w friction-factor vf0 friction-factor)
|
||||
(mul.w.vf.xyz velocity-scale-rate velocity-scale-rate friction-factor)
|
||||
(label no-friction)
|
||||
;; Apply velocity, rotation/scale velocity, and fade with the full 300 Hz tick count. Clamp
|
||||
;; color to zero so the packed death tests below can compare its float bits as integers.
|
||||
(mul.y.vf position-step velocity-scale-rate frame-time)
|
||||
(mul.y.vf rotation-scale-step rotation-scale-rate frame-time)
|
||||
(mul.y.vf color-step color-fade-rate frame-time)
|
||||
(add.vf sprite-pos-scale sprite-pos-scale position-step)
|
||||
(add.vf.zw sprite-rot-scale sprite-rot-scale rotation-scale-step)
|
||||
(add.vf sprite-color sprite-color color-step)
|
||||
(max.x.vf sprite-color sprite-color vf0)
|
||||
(s.vf.field velocity-scale-rate cpuinfo (vel-sxvel))
|
||||
(s.vf.field sprite-pos-scale sprite-data (x-y-z-sx))
|
||||
(s.vf.field sprite-rot-scale sprite-data (flag-rot-sy))
|
||||
(s.vf.field sprite-color sprite-data (r-g-b-a)))
|
||||
;; 2D rotation is kept as an integer-valued float, matching the sprite packet format.
|
||||
(asm-block billboard-post-update
|
||||
(l.s.field f0 sprite-data (rot))
|
||||
(cvt.w.s f0 f0)
|
||||
(m v1 f0)
|
||||
(sll32 v1 v1 16)
|
||||
(sra32 v1 v1 16)
|
||||
(m f0 v1)
|
||||
(cvt.s.w f0 f0)
|
||||
(s.s.field f0 sprite-data (rot))
|
||||
;; Orbiting is a post-integration position adjustment around the particle's stored center.
|
||||
(l.w.field v1 cpuinfo (flags))
|
||||
(and.i v1 v1 128)
|
||||
(b.z v1 test-death-flags :delay (m! t9 sp-orbiter))
|
||||
(add.i sp sp -96)
|
||||
(s.q sys sp)
|
||||
(s.q cpuinfo sp 16)
|
||||
(s.q sprite-data sp 32)
|
||||
(s.q slot sp 48)
|
||||
(s.q remaining sp 64)
|
||||
(m a0 sys)
|
||||
(m a1 cpuinfo)
|
||||
(m a2 sprite-data)
|
||||
(jalr ra t9 :delay (s.q paused? sp 80))
|
||||
(l.q sys sp)
|
||||
(l.q cpuinfo sp 16)
|
||||
(l.q sprite-data sp 32)
|
||||
(l.q slot sp 48)
|
||||
(l.q remaining sp 64)
|
||||
(l.q paused? sp 80)
|
||||
(add.i sp sp 96))
|
||||
;; Packed extracts test RGB together, then alpha, then x- and y-scale. These correspond to
|
||||
;; die-when-color-0, die-when-faded, and die-when-scale-0.
|
||||
(asm-block death-tests-2d
|
||||
(label test-death-flags)
|
||||
(l.q.field v1 sprite-data (r-g-b-a))
|
||||
(l.w.field a0 cpuinfo (flags))
|
||||
(and.i a1 a0 2)
|
||||
(b.z a1 test-fade-death :delay (and.i a1 a0 4))
|
||||
(b.nz v1 test-fade-death :delay (pextuw t4 v1 r0))
|
||||
(b.z t4 free-particle :delay (nop!))
|
||||
(label test-fade-death)
|
||||
(b.z a1 test-scale-death :delay (and.i a0 a0 1))
|
||||
(pcpyud t4 v1 r0)
|
||||
(pexew t4 t4)
|
||||
(b.le t4 r0 free-particle :delay (nop!))
|
||||
(label test-scale-death)
|
||||
(b.z a0 next-particle :delay (nop!))
|
||||
(m v1 sprite-pos-scale)
|
||||
(pcpyud v1 v1 r0)
|
||||
(pexew v1 v1)
|
||||
(b.lt v1 r0 free-particle :delay (m v1 sprite-rot-scale))
|
||||
(pcpyud v1 v1 r0)
|
||||
(pexew v1 v1)
|
||||
(b.ge v1 r0 next-particle :delay (nop!))
|
||||
;; Return dead particles to the bitmap-managed pool.
|
||||
(label free-particle)
|
||||
(m! t9 sp-free-particle)
|
||||
(m a0 sys)
|
||||
(m a1 slot)
|
||||
(m a2 cpuinfo)
|
||||
(m a3 sprite-data)
|
||||
(jalr ra t9 :delay (sll v0 ra 0)))
|
||||
;; Advance every parallel cursor even when this slot was invalid.
|
||||
(asm-block advance-and-return-2d
|
||||
(label next-particle)
|
||||
(add.i remaining remaining -1)
|
||||
(add.i cpuinfo cpuinfo 144)
|
||||
(add.i sprite-data sprite-data 48)
|
||||
(b.nz remaining particle-loop :delay (add.i slot slot 1))
|
||||
;; Return the first slot after the processed block.
|
||||
(m v0 slot)
|
||||
(l.d ra sp)
|
||||
(l.q sys sp 112)
|
||||
(l.q cpuinfo sp 96)
|
||||
(l.q sprite-data sp 80)
|
||||
(l.q remaining sp 64)
|
||||
(l.q paused? sp 48)
|
||||
(l.q slot sp 32)
|
||||
(l.q ticks sp 16)
|
||||
(jr ra :delay (add.i sp sp 128))
|
||||
(nop! :count 3)))))
|
||||
|
||||
(#unless PC_PORT
|
||||
(defun sp-process-block-3d ((sys sparticle-system)
|
||||
(scratchpad-cpuinfo int)
|
||||
(scratchpad-sprite-data int)
|
||||
(start-slot int)
|
||||
(count int)
|
||||
(paused? symbol))
|
||||
"Update count 3D particle records in scratchpad: age, integrate, rotate, fade, invoke callbacks,
|
||||
handle death or relaunch, and write their oriented sprite data."
|
||||
(declare (asm-func none))
|
||||
;; The 3D path shares the 144-byte CPU record and 48-byte renderer record with the 2D path. The
|
||||
;; renderer record still stores position.xyz/x-scale at +0 and color.rgba at +32, but its middle
|
||||
;; quadword holds quaternion.xyz/y-scale rather than a billboard rotation. The complete
|
||||
;; rotational velocity quaternion is kept in the CPU record at +80.
|
||||
;;
|
||||
;; Quaternion w is omitted from the renderer record because a unit quaternion has only three
|
||||
;; independent components. Rebuild |w| from xyz, compose the rotational step, then choose the
|
||||
;; sign of the stored xyz so the omitted w can remain nonnegative. q and -q represent the same
|
||||
;; rotation, so the renderer can reconstruct an equivalent orientation without spending a sign
|
||||
;; bit.
|
||||
;;
|
||||
;; Lifetime, pause, callback, relaunch, integration, and packed death tests otherwise match the
|
||||
;; 2D update. A long frame of at least ten 300 Hz ticks composes the rotation step twice.
|
||||
(rlet ((sys :reg gp)
|
||||
(cpuinfo :reg s5 :type sparticle-cpuinfo)
|
||||
(sprite-data :reg s4 :type sprite-vec-data-3d)
|
||||
(slot :reg s0)
|
||||
(remaining :reg s3)
|
||||
(paused? :reg s2)
|
||||
(rotation :reg s1)
|
||||
(frame-time :reg vf16)
|
||||
(sprite-pos-scale :reg vf8)
|
||||
(sprite-quat-scale :reg vf9)
|
||||
(sprite-color :reg vf10)
|
||||
(velocity-scale-rate :reg vf11)
|
||||
(rotation-scale-rate :reg vf12)
|
||||
(color-fade-rate :reg vf13)
|
||||
(acceleration :reg vf14)
|
||||
(friction-factor :reg vf15)
|
||||
(position-step :reg vf17)
|
||||
(rotation-scale-step :reg vf18)
|
||||
(color-step :reg vf19)
|
||||
(stored-quat-scale :reg vf1)
|
||||
(full-quaternion :reg vf2))
|
||||
;; Establish the scratchpad cursors, reserve a complete quaternion, and cache the tick count.
|
||||
(asm-block setup-3d
|
||||
(label particle-update-3d-entry)
|
||||
(add.i sp sp -160)
|
||||
(s.d ra sp)
|
||||
(s.d fp sp 8)
|
||||
(m fp t9)
|
||||
(s.q s0 sp 48)
|
||||
(s.q s1 sp 64)
|
||||
(s.q s2 sp 80)
|
||||
(s.q s3 sp 96)
|
||||
(s.q s4 sp 112)
|
||||
(s.q s5 sp 128)
|
||||
(s.q gp sp 144)
|
||||
(m sys a0)
|
||||
(m cpuinfo a1)
|
||||
(m sprite-data a2)
|
||||
(m slot a3)
|
||||
(m remaining t0)
|
||||
(m paused? t1)
|
||||
(add.i rotation sp 16)
|
||||
(s.q r0 rotation)
|
||||
(m! v1 *sp-frame-time*)
|
||||
(l.vf frame-time v1)
|
||||
(m v1 frame-time)
|
||||
(and.i v1 v1 255)
|
||||
(s.q v1 sp 32)
|
||||
(nop!))
|
||||
;; Reject unused slots, then decide whether a paused particle is allowed to simulate.
|
||||
(asm-block particle-lifetime-3d
|
||||
(label particle-loop-3d)
|
||||
(l.w.field v1 cpuinfo (valid))
|
||||
(b.eq v1 s7 next-particle-3d :delay (nop!))
|
||||
(b.eq paused? s7 simulate-particle-3d :delay (l.w.field v1 cpuinfo (flags)))
|
||||
(and.i v1 v1 #x2000)
|
||||
(b.nz v1 simulate-particle-3d :delay (nop!))
|
||||
(l.w.field v1 cpuinfo (timer))
|
||||
(add.i a0 r0 -1)
|
||||
(b.eq v1 a0 reveal-new-particle-3d :delay (nop!))
|
||||
(b.z v1 free-particle-3d :delay (nop!))
|
||||
;; Reveal a newly launched particle without advancing it when simulation is frozen.
|
||||
(label reveal-new-particle-3d)
|
||||
(l.w.field a0 cpuinfo (flags))
|
||||
(and.i v1 a0 64)
|
||||
(xor a0 a0 v1)
|
||||
(b.z v1 next-particle-3d :delay (s.w.field a0 cpuinfo (flags)))
|
||||
(l.w.field v1 cpuinfo (cache-alpha))
|
||||
(b next-particle-3d :delay (s.w.field v1 sprite-data (a)))
|
||||
;; Age finite particles in 300 Hz ticks and clamp an overshoot to zero.
|
||||
(label simulate-particle-3d)
|
||||
(l.w.field v1 cpuinfo (timer))
|
||||
(add.i a0 r0 -1)
|
||||
(b.eq v1 a0 run-callback-3d :delay (l.q a0 sp 32))
|
||||
(sub a0 v1 a0)
|
||||
(b.z v1 free-particle-3d :delay (max.w v1 a0 r0))
|
||||
(s.w.field v1 cpuinfo (timer))
|
||||
;; Restore the launch alpha before custom particle logic runs.
|
||||
(label run-callback-3d)
|
||||
(l.w.field a0 cpuinfo (flags))
|
||||
(and.i v1 a0 64)
|
||||
(xor a0 a0 v1)
|
||||
(b.z v1 call-update-function-3d :delay (s.w.field a0 cpuinfo (flags)))
|
||||
(l.w.field v1 cpuinfo (cache-alpha))
|
||||
(s.w.field v1 sprite-data (a)))
|
||||
;; The callback may edit either the CPU record or its renderer-facing sprite record.
|
||||
(asm-block callback-3d
|
||||
(label call-update-function-3d)
|
||||
(l.w.field t9 cpuinfo (func))
|
||||
(b.z t9 check-relaunch-3d :delay (nop!))
|
||||
(add.i sp sp -96)
|
||||
(s.q sys sp)
|
||||
(s.q cpuinfo sp 16)
|
||||
(s.q sprite-data sp 32)
|
||||
(s.q slot sp 48)
|
||||
(s.q remaining sp 64)
|
||||
(m a0 sys)
|
||||
(m a1 cpuinfo)
|
||||
(m a2 sprite-data)
|
||||
(jalr ra t9 :delay (s.q paused? sp 80))
|
||||
(l.q sys sp)
|
||||
(l.q cpuinfo sp 16)
|
||||
(l.q sprite-data sp 32)
|
||||
(l.q slot sp 48)
|
||||
(l.q remaining sp 64)
|
||||
(l.q paused? sp 80)
|
||||
(add.i sp sp 96))
|
||||
;; Relaunch on the first frame that carries the independent next-time countdown below zero.
|
||||
(asm-block relaunch-3d
|
||||
(label check-relaunch-3d)
|
||||
(l.w.field a1 cpuinfo (next-launcher))
|
||||
(l.w.field v1 cpuinfo (next-time))
|
||||
(b.z a1 integrate-3d :delay (l.q a0 sp 32))
|
||||
(sub v1 v1 a0)
|
||||
(b.ge v1 r0 integrate-3d :delay (s.w.field v1 cpuinfo (next-time)))
|
||||
(add.i sp sp -96)
|
||||
(s.q sys sp)
|
||||
(s.q cpuinfo sp 16)
|
||||
(s.q sprite-data sp 32)
|
||||
(s.q slot sp 48)
|
||||
(s.q remaining sp 64)
|
||||
(s.q paused? sp 80)
|
||||
(m a0 sys)
|
||||
(m a3 sprite-data)
|
||||
(m a2 cpuinfo)
|
||||
(m! t9 sp-relaunch-particle-3d)
|
||||
(jalr ra t9 :delay (sll v0 ra 0))
|
||||
(l.q sys sp)
|
||||
(l.q cpuinfo sp 16)
|
||||
(l.q sprite-data sp 32)
|
||||
(l.q slot sp 48)
|
||||
(l.q remaining sp 64)
|
||||
(l.q paused? sp 80)
|
||||
(add.i sp sp 96))
|
||||
;; Integrate the four adjacent CPU vectors directly into the three sprite quadwords.
|
||||
(asm-block integrate-motion-3d
|
||||
(label integrate-3d)
|
||||
(l.vf.field sprite-pos-scale sprite-data (x-y-z-sx))
|
||||
(l.vf.field sprite-quat-scale sprite-data (qx-qy-qz-sy))
|
||||
(l.vf.field sprite-color sprite-data (r-g-b-a))
|
||||
(l.vf.field velocity-scale-rate cpuinfo (vel-sxvel))
|
||||
(l.vf.field rotation-scale-rate cpuinfo (rot-syvel))
|
||||
(l.vf.field color-fade-rate cpuinfo (fade))
|
||||
(l.vf.field acceleration cpuinfo (acc))
|
||||
(l.s.field f0 cpuinfo (friction))
|
||||
(m v1 f0)
|
||||
;; Acceleration uses 60 Hz frame units. Nonzero friction becomes a retained-velocity factor
|
||||
;; for this interval and applies to xyz without disturbing x-scale velocity in w.
|
||||
(mul.z.vf acceleration acceleration frame-time)
|
||||
(b.z v1 no-friction-3d :delay (add.vf.xyz velocity-scale-rate velocity-scale-rate acceleration))
|
||||
(m friction-factor v1)
|
||||
(sub.x.vf.w friction-factor vf0 friction-factor)
|
||||
(mul.w.vf friction-factor friction-factor frame-time)
|
||||
(sub.w.vf.w friction-factor vf0 friction-factor)
|
||||
(mul.w.vf.xyz velocity-scale-rate velocity-scale-rate friction-factor)
|
||||
(label no-friction-3d)
|
||||
;; Apply velocity, y-scale velocity, and fade with the full tick count. The xyz lanes of
|
||||
;; rotation-scale-rate are not used here; orientation advances from rotvel3d below.
|
||||
(mul.y.vf position-step velocity-scale-rate frame-time)
|
||||
(mul.y.vf rotation-scale-step rotation-scale-rate frame-time)
|
||||
(mul.y.vf color-step color-fade-rate frame-time)
|
||||
(add.vf sprite-pos-scale sprite-pos-scale position-step)
|
||||
(add.w.vf.w sprite-quat-scale sprite-quat-scale rotation-scale-step)
|
||||
(add.vf sprite-color sprite-color color-step)
|
||||
(max.x.vf sprite-color sprite-color vf0)
|
||||
(s.vf.field velocity-scale-rate cpuinfo (vel-sxvel))
|
||||
(s.vf.field sprite-pos-scale sprite-data (x-y-z-sx))
|
||||
(s.vf.field sprite-quat-scale sprite-data (qx-qy-qz-sy))
|
||||
(s.vf.field sprite-color sprite-data (r-g-b-a)))
|
||||
;; Rebuild w = sqrt(1-x^2-y^2-z^2) in the stack quaternion. The constant is loaded relative to
|
||||
;; this function because the hand-written function keeps 1.0 in its local literal pool.
|
||||
(asm-block update-quaternion
|
||||
(m v1 rotation)
|
||||
(m a0 sprite-data)
|
||||
(l.s f0 a0 16)
|
||||
(l.s f1 a0 20)
|
||||
(l.s f3 a0 24)
|
||||
(s.s f0 v1)
|
||||
(s.s f1 v1 4)
|
||||
(s.s f3 v1 8)
|
||||
(l.s f2 fp L157)
|
||||
(mul.s f3 f3 f3)
|
||||
(sub.s f2 f2 f3)
|
||||
(mul.s f1 f1 f1)
|
||||
(sub.s f1 f2 f1)
|
||||
(mul.s f0 f0 f0)
|
||||
(sub.s f0 f1 f0)
|
||||
(sqrt.s f0 f0)
|
||||
(s.s f0 v1 12)
|
||||
(m a0 f0)
|
||||
;; Compose the particle's rotational velocity once, or twice when a long frame crosses the
|
||||
;; ten-tick threshold.
|
||||
(m! v1 *sp-frame-time*)
|
||||
(l.s f0 v1)
|
||||
(m v1 f0)
|
||||
(and.i v1 v1 255)
|
||||
(add.i v1 v1 -10)
|
||||
(b.lt v1 r0 apply-rotation-step :delay (nop!))
|
||||
(m! t9 quaternion*!)
|
||||
(m a0 rotation)
|
||||
(m a1 rotation)
|
||||
(lea.field a2 cpuinfo (rotvel3d))
|
||||
(jalr ra t9 :delay (sll v0 ra 0))
|
||||
(label apply-rotation-step)
|
||||
(m! t9 quaternion*!)
|
||||
(m a0 rotation)
|
||||
(m a1 rotation)
|
||||
(lea.field a2 cpuinfo (rotvel3d))
|
||||
(jalr ra t9 :delay (sll v0 ra 0)))
|
||||
;; q and -q are equivalent. Store the sign whose omitted w is nonnegative so the renderer can
|
||||
;; reconstruct the same convention from xyz alone; preserve y-scale in the untouched w lane.
|
||||
(asm-block store-compact-quaternion
|
||||
(m a0 sprite-data)
|
||||
(m v1 rotation)
|
||||
(l.s f0 v1 12)
|
||||
(m f1 r0)
|
||||
(c.lt.s f0 f1)
|
||||
(b.fpf store-positive-quaternion :delay (nop!))
|
||||
(l.vf stored-quat-scale a0 16)
|
||||
(l.vf full-quaternion v1)
|
||||
(sub.vf.xyz stored-quat-scale vf0 full-quaternion)
|
||||
(s.vf stored-quat-scale a0 16)
|
||||
(m a0 stored-quat-scale)
|
||||
(b test-death-flags-3d :delay (nop!))
|
||||
(label store-positive-quaternion)
|
||||
(l.vf stored-quat-scale a0 16)
|
||||
(l.vf full-quaternion v1)
|
||||
(add.vf.xyz stored-quat-scale vf0 full-quaternion)
|
||||
(s.vf stored-quat-scale a0 16)
|
||||
(m a0 stored-quat-scale))
|
||||
;; Packed extracts test RGB together, then alpha, then x- and y-scale. These correspond to
|
||||
;; die-when-color-0, die-when-faded, and die-when-scale-0.
|
||||
(asm-block death-tests-3d
|
||||
(label test-death-flags-3d)
|
||||
(m v1 sprite-color)
|
||||
(l.w.field a0 cpuinfo (flags))
|
||||
(and.i a1 a0 2)
|
||||
(b.z a1 test-fade-death-3d :delay (and.i a1 a0 4))
|
||||
(b.nz v1 test-fade-death-3d :delay (pextuw a2 v1 r0))
|
||||
(b.z a2 free-particle-3d :delay (nop!))
|
||||
(label test-fade-death-3d)
|
||||
(b.z a1 test-scale-death-3d :delay (and.i a0 a0 1))
|
||||
(pcpyud v1 v1 r0)
|
||||
(pexew v1 v1)
|
||||
(b.le v1 r0 free-particle-3d :delay (nop!))
|
||||
(label test-scale-death-3d)
|
||||
(b.z a0 next-particle-3d :delay (nop!))
|
||||
(m v1 sprite-pos-scale)
|
||||
(pcpyud v1 v1 r0)
|
||||
(pexew v1 v1)
|
||||
(b.lt v1 r0 free-particle-3d :delay (m v1 sprite-quat-scale))
|
||||
(pcpyud v1 v1 r0)
|
||||
(pexew v1 v1)
|
||||
(b.ge v1 r0 next-particle-3d :delay (nop!))
|
||||
;; Return dead particles to the bitmap-managed pool.
|
||||
(label free-particle-3d)
|
||||
(m! t9 sp-free-particle)
|
||||
(m a0 sys)
|
||||
(m a1 slot)
|
||||
(m a2 cpuinfo)
|
||||
(m a3 sprite-data)
|
||||
(jalr ra t9 :delay (sll v0 ra 0)))
|
||||
;; Advance every parallel cursor even when this slot was invalid.
|
||||
(asm-block advance-and-return-3d
|
||||
(label next-particle-3d)
|
||||
(add.i remaining remaining -1)
|
||||
(add.i cpuinfo cpuinfo 144)
|
||||
(add.i sprite-data sprite-data 48)
|
||||
(b.nz remaining particle-loop-3d :delay (add.i slot slot 1))
|
||||
;; Return the first slot after the processed block.
|
||||
(m v0 slot)
|
||||
(l.d ra sp)
|
||||
(l.d fp sp 8)
|
||||
(l.q sys sp 144)
|
||||
(l.q cpuinfo sp 128)
|
||||
(l.q sprite-data sp 112)
|
||||
(l.q remaining sp 96)
|
||||
(l.q paused? sp 80)
|
||||
(l.q rotation sp 64)
|
||||
(l.q slot sp 48)
|
||||
(jr ra :delay (add.i sp sp 160))
|
||||
(nop! :count 3)))))
|
||||
|
||||
(#unless PC_PORT
|
||||
(defun memcpy ((dst pointer) (src pointer) (size int))
|
||||
"Copy size rounded-up bytes from src to dst in 128-byte bursts followed by 16-byte quadwords."
|
||||
(declare (asm-func none))
|
||||
;; Particle scratchpad records are all quadword-padded, so this copier deliberately rounds size
|
||||
;; upward. Large transfers use eight quadword loads followed by eight stores per iteration.
|
||||
(rlet ((dst :reg a0)
|
||||
(src :reg a1)
|
||||
(size :reg a2)
|
||||
(quadwords :reg v1)
|
||||
(data0 :reg a2 :type i128)
|
||||
(data1 :reg a3 :type i128)
|
||||
(data2 :reg t0 :type i128)
|
||||
(data3 :reg t1 :type i128)
|
||||
(data4 :reg t2 :type i128)
|
||||
(data5 :reg t3 :type i128)
|
||||
(data6 :reg t4 :type i128)
|
||||
(data7 :reg t5 :type i128))
|
||||
(add.i quadwords size 15)
|
||||
(sra quadwords quadwords 4)
|
||||
(add.i quadwords quadwords -8)
|
||||
(b.lt quadwords r0 finish-bursts :delay (at-label copy-burst (l.q data0 src)))
|
||||
(l.q data1 src 16)
|
||||
(l.q data2 src 32)
|
||||
(l.q data3 src 48)
|
||||
(l.q data4 src 64)
|
||||
(l.q data5 src 80)
|
||||
(l.q data6 src 96)
|
||||
(l.q data7 src 112)
|
||||
(s.q data0 dst)
|
||||
(s.q data1 dst 16)
|
||||
(s.q data2 dst 32)
|
||||
(s.q data3 dst 48)
|
||||
(s.q data4 dst 64)
|
||||
(s.q data5 dst 80)
|
||||
(s.q data6 dst 96)
|
||||
(s.q data7 dst 112)
|
||||
(add.i src src 128)
|
||||
(add.i quadwords quadwords -8)
|
||||
(b.ge quadwords r0 copy-burst :delay (add.i dst dst 128))
|
||||
(label finish-bursts)
|
||||
(add.i quadwords quadwords 8)
|
||||
(b.z quadwords done-copying :delay (at-label copy-quadword (l.q data0 src)))
|
||||
(add.i src src 16)
|
||||
(s.q data0 dst)
|
||||
(add.i quadwords quadwords -1)
|
||||
(nop!)
|
||||
(b.nz quadwords copy-quadword :delay (add.i dst dst 16))
|
||||
(label done-copying)
|
||||
(m v0 r0)
|
||||
(jr ra :delay (add sp sp r0))
|
||||
(nop! :count 3))))
|
||||
@@ -0,0 +1,899 @@
|
||||
(#unless PC_PORT
|
||||
;; Each init spec is one 16-byte record:
|
||||
;;
|
||||
;; +0 uint16 field ID +2 uint16 sp-flag
|
||||
;; +4 initial value +8 random range
|
||||
;; +12 random multiplier
|
||||
;;
|
||||
;; The last three words are interpreted as floats, integers, symbols, or pointers according to
|
||||
;; the flag. Specs are sorted by field ID, so adjacent field ranges can share one forward cursor.
|
||||
;; Float and integer-random forms advance the VU random register; copy and reference forms avoid
|
||||
;; that arithmetic and resolve their value directly.
|
||||
(defun sp-init-fields! ((dest (pointer float))
|
||||
(specs (inline-array sp-field-init-spec))
|
||||
(field-id sp-field-id)
|
||||
(end-field-id sp-field-id)
|
||||
(write-missing-fields symbol))
|
||||
"Initialize consecutive particle fields from the sorted init-spec stream, returning its next
|
||||
unconsumed entry. Missing fields may be cleared for a new particle or retained for a relaunch."
|
||||
(declare (asm-func (inline-array sp-field-init-spec)))
|
||||
(rlet ((dest-reg :reg a0)
|
||||
(field-reg :reg a2)
|
||||
(end-field-reg :reg a3)
|
||||
(write-missing-reg :reg t0)
|
||||
(spec-reg :reg v0))
|
||||
(label entry)
|
||||
(m v1 dest-reg)
|
||||
(m v1 field-reg)
|
||||
(m v1 end-field-reg)
|
||||
(m v1 write-missing-reg)
|
||||
(nop!)
|
||||
(add.i field-reg field-reg 1)
|
||||
(m spec-reg a1)
|
||||
;; Init specs are sorted by field ID. Skip any earlier range that a previous call consumed.
|
||||
(label scan-specs)
|
||||
(l.h a1 spec-reg)
|
||||
(nop!)
|
||||
(sub a1 a1 field-reg)
|
||||
(nop! :count 3)
|
||||
(b.ltl a1 r0 scan-specs :delay (add.i spec-reg spec-reg 16))
|
||||
|
||||
(sub a1 field-reg end-field-reg)
|
||||
(nop!)
|
||||
(b.ge a1 r0 done :delay (nop!))
|
||||
|
||||
;; vf2 receives one random value before dispatch. The selected field kind decides whether to
|
||||
;; use it as a float, quantize it, ignore it, or replace it with a pointer-like value.
|
||||
(label field-loop)
|
||||
(l.h a1 spec-reg)
|
||||
(nop!)
|
||||
(b.ne a1 field-reg missing-field :delay (vrget.xyzw vf1))
|
||||
(sqrt.x Q vf1)
|
||||
(l.h a1 spec-reg 2)
|
||||
(vaddq.x vf2 vf0 Q)
|
||||
(l.w t2 spec-reg 8)
|
||||
(add.i v1 r0 7)
|
||||
(b.eq field-reg v1 store-sound :delay (add.i t1 r0 1))
|
||||
(b.eq a1 t1 store-float :delay (add.i t1 r0 2))
|
||||
(b.eq a1 t1 store-quantized-float :delay (add.i t1 r0 3))
|
||||
(b.eq a1 t1 copy-field :delay (add.i t1 r0 5))
|
||||
(b.eq a1 t1 load-symbol :delay (add.i t1 r0 6))
|
||||
(b.eq a1 t1 lookup-launcher :delay (add.i t1 r0 4))
|
||||
(b.eq a1 t1 store-object :delay (nop!))
|
||||
(b.z t2 store-integer :delay (nop!))
|
||||
|
||||
;; Mix the square root of the current VU random value back into R, then use the next value in
|
||||
;; [0, 1). The common integer path truncates the scaled random term before adding the initial
|
||||
;; value.
|
||||
(vrxorw vf2)
|
||||
(l.w t1 spec-reg 12)
|
||||
(vrnext.xyzw vf1)
|
||||
(l.w t3 spec-reg 4)
|
||||
(sub.w.vf vf1 vf1 vf0)
|
||||
(nop!)
|
||||
(m vf2 t2)
|
||||
(nop!)
|
||||
(itof.vf vf2 vf2)
|
||||
(nop!)
|
||||
(mul.vf vf1 vf1 vf2)
|
||||
(nop!)
|
||||
(ftoi.vf vf1 vf1)
|
||||
(nop!)
|
||||
(m t2 vf1)
|
||||
(nop!)
|
||||
(mult3 t2 t2 t1)
|
||||
(nop!)
|
||||
(add t2 t2 t3)
|
||||
(add.i field-reg field-reg 1)
|
||||
(s.w t2 dest-reg)
|
||||
(add.i dest-reg dest-reg 4)
|
||||
(b.ne field-reg end-field-reg field-loop :delay (add.i spec-reg spec-reg 16))
|
||||
(jr ra :delay (nop!))
|
||||
|
||||
;; Integer fields use initial + trunc(random * range) * multiplier.
|
||||
(label store-integer)
|
||||
(l.w t3 spec-reg 4)
|
||||
(add.i field-reg field-reg 1)
|
||||
(s.w t3 dest-reg)
|
||||
(add.i dest-reg dest-reg 4)
|
||||
(b.ne field-reg end-field-reg field-loop :delay (add.i spec-reg spec-reg 16))
|
||||
(jr ra :delay (nop!))
|
||||
|
||||
(label store-sound)
|
||||
(l.w t3 spec-reg 4)
|
||||
(add.i field-reg field-reg 1)
|
||||
(s.w t3 dest-reg)
|
||||
(add.i dest-reg dest-reg 4)
|
||||
(b.ne field-reg end-field-reg field-loop :delay (add.i spec-reg spec-reg 16))
|
||||
(jr ra :delay (nop!))
|
||||
|
||||
;; Floating fields use initial + random * range * multiplier.
|
||||
(label store-float)
|
||||
(b.z t2 store-plain-float :delay (vrxorw vf2))
|
||||
(vrnext.xyzw vf1)
|
||||
(l.w t1 spec-reg 12)
|
||||
(sub.w.vf vf1 vf1 vf0)
|
||||
(l.w t3 spec-reg 4)
|
||||
(m vf2 t2)
|
||||
(nop!)
|
||||
(mul.vf vf1 vf1 vf2)
|
||||
(nop!)
|
||||
(m vf2 t1)
|
||||
(nop!)
|
||||
(mul.vf vf1 vf1 vf2)
|
||||
(nop!)
|
||||
(m vf2 t3)
|
||||
(nop!)
|
||||
(add.vf vf1 vf1 vf2)
|
||||
(nop!)
|
||||
(m t2 vf1)
|
||||
(add.i field-reg field-reg 1)
|
||||
(s.w t2 dest-reg)
|
||||
(add.i dest-reg dest-reg 4)
|
||||
(b.ne field-reg end-field-reg field-loop :delay (add.i spec-reg spec-reg 16))
|
||||
(jr ra :delay (nop!))
|
||||
|
||||
(label store-plain-float)
|
||||
(l.w t3 spec-reg 4)
|
||||
(add.i field-reg field-reg 1)
|
||||
(s.w t3 dest-reg)
|
||||
(add.i dest-reg dest-reg 4)
|
||||
(b.ne field-reg end-field-reg field-loop :delay (add.i spec-reg spec-reg 16))
|
||||
(jr ra :delay (nop!))
|
||||
|
||||
;; This form chooses one of range+1 integer steps, then stores the scaled result as a float.
|
||||
(label store-quantized-float)
|
||||
(b.z t2 store-plain-float :delay (vrxorw vf2))
|
||||
(vrnext.xyzw vf1)
|
||||
(l.w t1 spec-reg 12)
|
||||
(sub.w.vf vf1 vf1 vf0)
|
||||
(add.i t2 t2 1)
|
||||
(m vf2 t2)
|
||||
(l.w t3 spec-reg 4)
|
||||
(itof.vf vf2 vf2)
|
||||
(nop!)
|
||||
(mul.vf vf1 vf1 vf2)
|
||||
(nop!)
|
||||
(ftoi.vf vf1 vf1)
|
||||
(nop!)
|
||||
(itof.vf vf1 vf1)
|
||||
(nop!)
|
||||
(m vf2 t1)
|
||||
(nop!)
|
||||
(mul.vf vf1 vf1 vf2)
|
||||
(nop!)
|
||||
(m vf2 t3)
|
||||
(nop!)
|
||||
(add.vf vf1 vf1 vf2)
|
||||
(nop!)
|
||||
(m t2 vf1)
|
||||
(add.i field-reg field-reg 1)
|
||||
(s.w t2 dest-reg)
|
||||
(add.i dest-reg dest-reg 4)
|
||||
(b.ne field-reg end-field-reg field-loop :delay (add.i spec-reg spec-reg 16))
|
||||
(jr ra :delay (nop!))
|
||||
|
||||
;; initial-value is a negative field offset, relative to the destination currently being written.
|
||||
(label copy-field)
|
||||
(l.w t1 spec-reg 4)
|
||||
(nop!)
|
||||
(sll t1 t1 2)
|
||||
(nop!)
|
||||
(add t1 t1 dest-reg)
|
||||
(nop!)
|
||||
(l.w t3 t1)
|
||||
(add.i field-reg field-reg 1)
|
||||
(s.w t3 dest-reg)
|
||||
(add.i dest-reg dest-reg 4)
|
||||
(b.ne field-reg end-field-reg field-loop :delay (add.i spec-reg spec-reg 16))
|
||||
(jr ra :delay (nop!))
|
||||
|
||||
(label load-symbol)
|
||||
(l.w t1 spec-reg 4)
|
||||
(nop!)
|
||||
(l.w t3 t1)
|
||||
(add.i field-reg field-reg 1)
|
||||
(s.w t3 dest-reg)
|
||||
(add.i dest-reg dest-reg 4)
|
||||
(b.ne field-reg end-field-reg field-loop :delay (add.i spec-reg spec-reg 16))
|
||||
(jr ra :delay (nop!))
|
||||
|
||||
(label lookup-launcher)
|
||||
(m! t1 *part-id-table*)
|
||||
(nop!)
|
||||
(l.w t3 spec-reg 4)
|
||||
(nop!)
|
||||
(sll t3 t3 2)
|
||||
(add.i t1 t1 12)
|
||||
(add t3 t3 t1)
|
||||
(nop!)
|
||||
(l.w t2 t3)
|
||||
(add.i field-reg field-reg 1)
|
||||
(s.w t2 dest-reg)
|
||||
(add.i dest-reg dest-reg 4)
|
||||
(b.ne field-reg end-field-reg field-loop :delay (add.i spec-reg spec-reg 16))
|
||||
(jr ra :delay (nop!))
|
||||
|
||||
(label store-object)
|
||||
(l.w t3 spec-reg 4)
|
||||
(add.i field-reg field-reg 1)
|
||||
(s.w t3 dest-reg)
|
||||
(add.i dest-reg dest-reg 4)
|
||||
(b.ne field-reg end-field-reg field-loop :delay (add.i spec-reg spec-reg 16))
|
||||
(jr ra :delay (nop!))
|
||||
;; Missing fields are either cleared or left intact. The branch-likely keeps the store in the
|
||||
;; taken path, which is needed by relaunches that preserve unspecified fields.
|
||||
(label missing-field)
|
||||
(b.nel write-missing-reg s7 advance-field :delay (s.w r0 dest-reg))
|
||||
(label advance-field)
|
||||
(add.i field-reg field-reg 1)
|
||||
(add.i dest-reg dest-reg 4)
|
||||
(b.ne field-reg end-field-reg field-loop :delay (nop!))
|
||||
(label done)
|
||||
(jr ra :delay (nop!))
|
||||
(jr ra :delay (add sp sp r0))
|
||||
(nop! :count 3))))
|
||||
|
||||
(#unless PC_PORT
|
||||
;; Cache layout:
|
||||
;;
|
||||
;; +0 number of populated entries
|
||||
;; +4 hash of the last texture
|
||||
;; +8 shader for the last texture
|
||||
;; +12 eighty uint16 texture hashes
|
||||
;; +172 eighty 80-byte adgif shaders
|
||||
;;
|
||||
;; The last pair handles consecutive uses without a scan. On a general hit, the hash and shader
|
||||
;; cursors advance together. A miss fills the next pair of slots; after all eighty slots are used,
|
||||
;; the shader is built directly in the caller's output instead of evicting an entry.
|
||||
(defun particle-adgif ((shader-out adgif-shader) (tex-id texture-id))
|
||||
"Copy the particle shader for tex-id to shader-out, filling a free cache entry when possible."
|
||||
(declare (asm-func none))
|
||||
(rlet ((shader-out :reg a0)
|
||||
(cache :reg t1)
|
||||
(remaining :reg t2)
|
||||
(hash-cursor :reg t3)
|
||||
(shader-cursor :reg t4)
|
||||
(texture-hash :reg a3)
|
||||
(shader-qw0 :reg vf16)
|
||||
(shader-qw1 :reg vf17)
|
||||
(shader-qw2 :reg vf18)
|
||||
(shader-qw3 :reg vf19)
|
||||
(shader-qw4 :reg vf20))
|
||||
(label entry)
|
||||
(m v1 shader-out)
|
||||
(m v1 a1)
|
||||
(nop!)
|
||||
|
||||
;; Fold the texture ID's upper fields into a compact key. The cache keeps the full expanded
|
||||
;; shader, so a hit needs only five quadword loads and stores.
|
||||
(sra texture-hash a1 20)
|
||||
(m! cache *particle-adgif-cache*)
|
||||
(sra t0 a1 8)
|
||||
(l.w remaining cache)
|
||||
(xor texture-hash texture-hash t0)
|
||||
(l.hu v1 cache 4)
|
||||
(and.i texture-hash texture-hash #xffff)
|
||||
(l.w shader-cursor cache 8)
|
||||
(b.eq v1 texture-hash cache-hit :delay (add.i hash-cursor cache 12))
|
||||
(b.z remaining cache-miss :delay (add.i shader-cursor cache 172))
|
||||
;; tidhash and spadgif advance in parallel. The folded 16-bit key avoids comparing or
|
||||
;; dereferencing texture objects during the common lookup path.
|
||||
(label scan-cache)
|
||||
(l.hu v1 hash-cursor)
|
||||
(add.i hash-cursor hash-cursor 2)
|
||||
(nop! :count 2)
|
||||
(b.eq v1 texture-hash cache-hit :delay (add.i remaining remaining -1))
|
||||
(b.nz remaining scan-cache :delay (add.i shader-cursor shader-cursor 80))
|
||||
|
||||
;; Fill the next free slot until all eighty entries are used. Once full, initialize the
|
||||
;; caller's shader directly without changing the cache.
|
||||
(label cache-miss)
|
||||
(add.i sp sp -16)
|
||||
(l.w v1 cache)
|
||||
(add.i v1 v1 -80)
|
||||
(s.w shader-out sp)
|
||||
(b.z v1 uncached-miss :delay (add.i v1 v1 81))
|
||||
(s.h texture-hash hash-cursor)
|
||||
(nop!)
|
||||
(s.w shader-cursor sp 4)
|
||||
(m shader-out shader-cursor)
|
||||
(m! t9 particle-setup-adgif)
|
||||
(nop!)
|
||||
(s.w ra sp 8)
|
||||
(nop!)
|
||||
(jalr ra t9 :delay (s.w v1 cache))
|
||||
(l.w shader-out sp)
|
||||
(nop!)
|
||||
(l.w shader-cursor sp 4)
|
||||
(nop!)
|
||||
(l.w ra sp 8)
|
||||
(add.i sp sp 16)
|
||||
|
||||
;; A shader is five quadwords; copy it directly and make this entry the next fast-path hit.
|
||||
(label cache-hit)
|
||||
(l.vf shader-qw0 shader-cursor)
|
||||
(l.vf shader-qw1 shader-cursor 16)
|
||||
(l.vf shader-qw2 shader-cursor 32)
|
||||
(l.vf shader-qw3 shader-cursor 48)
|
||||
(l.vf shader-qw4 shader-cursor 64)
|
||||
(s.vf shader-qw0 shader-out)
|
||||
(s.vf shader-qw1 shader-out 16)
|
||||
(s.vf shader-qw2 shader-out 32)
|
||||
(s.vf shader-qw3 shader-out 48)
|
||||
(s.vf shader-qw4 shader-out 64)
|
||||
(s.w shader-cursor cache 8)
|
||||
(s.h texture-hash cache 4)
|
||||
(jr ra :delay (nop!))
|
||||
|
||||
;; A full cache still uses particle-setup-adgif; only the destination changes.
|
||||
(label uncached-miss)
|
||||
(s.w shader-cursor sp 4)
|
||||
(nop!)
|
||||
(m! t9 particle-setup-adgif)
|
||||
(nop!)
|
||||
(s.w ra sp 8)
|
||||
(nop!)
|
||||
(jalr ra t9 :delay (nop!))
|
||||
(l.w shader-cursor sp 4)
|
||||
(nop!)
|
||||
(l.w ra sp 8)
|
||||
(add.i sp sp 16)
|
||||
(jr ra :delay (nop!))
|
||||
(jr ra :delay (add sp sp r0))
|
||||
(nop! :count 2))))
|
||||
|
||||
|
||||
(#unless PC_PORT
|
||||
;; The launcher decodes its definition into two stack records before entering the particle loop:
|
||||
;;
|
||||
;; +96 sparticle-birthinfo
|
||||
;; texture, animation, birth callback, count, and sound shared by this launch
|
||||
;;
|
||||
;; +128 three sprite quadwords
|
||||
;; position.xyz / scale-x
|
||||
;; 2D: flags / matrix mode / angle / scale-y
|
||||
;; 3D: quaternion.xyz / scale-y
|
||||
;; color.rgba
|
||||
;;
|
||||
;; The first two sprite quadwords also have the sparticle-launchinfo layout while launch rotation
|
||||
;; is being applied: position is the launch offset and the second quadword holds its Euler angles.
|
||||
;; A 3D particle converts those angles to the renderer's xyz-only quaternion before publication.
|
||||
;; Keeping the packet in scratch until the end lets bindings, callbacks, level ownership, and
|
||||
;; time-of-day color finish without exposing a half-initialized sprite to the update pass.
|
||||
;;
|
||||
;; Birth counts use a floating accumulator, either on launch-state for an instanced group item or
|
||||
;; on the launcher itself. Each completed particle consumes one whole unit, leaving the fraction
|
||||
;; for the next call.
|
||||
(defun sp-launch-particles-var ((sys sparticle-system)
|
||||
(launcher sparticle-launcher)
|
||||
(pos vector)
|
||||
(launch-state sparticle-launch-state)
|
||||
(launch-control sparticle-launch-control)
|
||||
(rate float))
|
||||
"Birth rate-scaled particles, initialize their renderer and simulation state, and attach them to
|
||||
the optional launch control."
|
||||
(declare (asm-func none) (allow-saved-regs))
|
||||
(rlet ((system-reg :reg s3)
|
||||
(launcher-reg :reg s4)
|
||||
(launch-state-reg :reg s0)
|
||||
(launch-control-reg :reg s1)
|
||||
(cpuinfo-reg :reg s2)
|
||||
(particle-flags :reg s5)
|
||||
(is-3d :reg s6)
|
||||
(origin-vf :reg vf30)
|
||||
(max-color-vf :reg vf31))
|
||||
(label entry)
|
||||
(m v1 a0)
|
||||
(m v1 a1)
|
||||
(m v1 a2)
|
||||
(m v1 a3)
|
||||
(m v1 t0)
|
||||
(m v1 t1)
|
||||
(nop!)
|
||||
|
||||
;; Save the persistent arguments and check whether this call can launch immediately.
|
||||
(add.i sp sp -256)
|
||||
(nop!)
|
||||
(m! v1 *sp-launcher-enable*)
|
||||
(s.w ra sp)
|
||||
(b.eq v1 s7 finish-disabled :delay (s.q launch-state-reg sp 16))
|
||||
(m! v1 *sp-launcher-lock*)
|
||||
(s.q launch-control-reg sp 32)
|
||||
(b.eq v1 s7 begin-launch :delay (s.q cpuinfo-reg sp 48))
|
||||
(b.ne a3 s7 bad-nested-launch :delay (lui v1 #x3f80))
|
||||
(b.ne t0 s7 bad-nested-launch :delay (nop!))
|
||||
(b.ne t1 v1 bad-nested-launch :delay (nop!))
|
||||
(m! t9 sp-queue-launch)
|
||||
(nop!)
|
||||
(jalr ra t9 :delay (nop!))
|
||||
(l.w ra sp)
|
||||
(nop!)
|
||||
|
||||
(label finish-disabled)
|
||||
(jr ra :delay (add.i sp sp 256))
|
||||
|
||||
;; Reentrant launches may only use the default state, control, and rate because the deferred
|
||||
;; queue stores only system, launcher, and position. Trap instead of silently discarding state.
|
||||
(label bad-nested-launch)
|
||||
(s.d r0 r0 2)
|
||||
(nop!)
|
||||
|
||||
;; Decode the misc range once. birthinfo holds the texture, animation, callback, count, and sound
|
||||
;; shared by all particles born in this call.
|
||||
(label begin-launch)
|
||||
(s.q system-reg sp 176)
|
||||
(nop!)
|
||||
(s.q launcher-reg sp 192)
|
||||
(nop!)
|
||||
(s.q particle-flags sp 208)
|
||||
(nop!)
|
||||
(s.q is-3d sp 224)
|
||||
(nop!)
|
||||
(l.vf origin-vf a2)
|
||||
(m f1 t1)
|
||||
(s.vf origin-vf sp 64)
|
||||
(lui v1 #x437f)
|
||||
(m system-reg a0)
|
||||
(m max-color-vf v1)
|
||||
(m launcher-reg a1)
|
||||
(l.w is-3d system-reg 24) ;; sparticle-system.is-3d
|
||||
(m launch-state-reg a3)
|
||||
(m launch-control-reg t0)
|
||||
(l.w a1 a1 8)
|
||||
(add.i a0 sp 96)
|
||||
(add.i a2 r0 (sp-field-id misc-fields-start))
|
||||
(add.i a3 r0 (sp-field-id misc-fields-end))
|
||||
(m! t9 sp-init-fields!)
|
||||
(add.i t0 s7 8)
|
||||
(jalr ra t9 :delay (nop!))
|
||||
(s.w v0 sp 12)
|
||||
(nop!)
|
||||
(l.s f2 sp 116) ;; sparticle-birthinfo.num-to-birth
|
||||
(nop!)
|
||||
|
||||
;; Scale the launcher's requested count by rate, then add it to the selected accumulator.
|
||||
(b.eq launch-state-reg s7 use-launcher-accum :delay (mul.s f1 f2 f1))
|
||||
(l.s f2 launch-state-reg 24)
|
||||
(nop!)
|
||||
(add.s f2 f2 f1)
|
||||
(nop!)
|
||||
(s.s f2 launch-state-reg 24)
|
||||
(nop!)
|
||||
(cvt.w.s f2 f2)
|
||||
(nop!)
|
||||
(m v1 f2)
|
||||
(nop!)
|
||||
(b.z v1 finish :delay (nop!))
|
||||
(b particle-loop :delay (nop!))
|
||||
|
||||
(label use-launcher-accum)
|
||||
(nop!)
|
||||
(l.s f2 launcher-reg)
|
||||
(nop!)
|
||||
(add.s f2 f2 f1)
|
||||
(nop!)
|
||||
(s.s f2 launcher-reg)
|
||||
(nop!)
|
||||
(cvt.w.s f2 f2)
|
||||
(nop!)
|
||||
(m v1 f2)
|
||||
(nop!)
|
||||
(b.z v1 finish :delay (nop!))
|
||||
|
||||
;; The integer part of the accumulator is the number to birth now. Each successful iteration
|
||||
;; subtracts one, leaving the fractional remainder for the next call.
|
||||
(label particle-loop)
|
||||
(add.i a1 r0 0)
|
||||
(m a2 s7)
|
||||
(b.eq launch-control-reg s7 allocate-particle :delay (l.w v1 launch-control-reg 12))
|
||||
(b.eq v1 s7 allocate-particle :delay (l.h v1 v1 6))
|
||||
(and.i a0 v1 (sp-group-flag screen-space))
|
||||
(add.i a3 r0 1)
|
||||
(movn a1 a3 a0)
|
||||
;; Group flag bit 3 passes the item's launch-state to the allocator.
|
||||
(and.i a0 v1 8)
|
||||
(movn a2 launch-state-reg a0)
|
||||
(nop!)
|
||||
|
||||
;; Screen-space groups allocate from the alternate particle group. A binding can also supply the
|
||||
;; launch-state pointer used by sp-get-particle.
|
||||
(label allocate-particle)
|
||||
(m! t9 sp-get-particle)
|
||||
(m a0 system-reg)
|
||||
(jalr ra t9 :delay (nop!))
|
||||
(b.eq v0 s7 finish :delay (m cpuinfo-reg v0))
|
||||
(add.i a0 sp 128)
|
||||
(l.w a1 sp 12)
|
||||
(add.i a2 r0 (sp-field-id sprite-fields-start))
|
||||
(add.i a3 r0 (sp-field-id sprite-fields-end))
|
||||
(m! t9 sp-init-fields!)
|
||||
(add.i t0 s7 8)
|
||||
(jalr ra t9 :delay (nop!))
|
||||
(add.i a0 cpuinfo-reg 12)
|
||||
(m a1 v0)
|
||||
(add.i a2 r0 (sp-field-id cpu-fields-start))
|
||||
(add.i a3 r0 (sp-field-id cpu-fields-end))
|
||||
(jalr ra t9 :delay (add.i t0 s7 8))
|
||||
(s.w v0 sp 80)
|
||||
(nop!)
|
||||
(l.w particle-flags cpuinfo-reg 104) ;; sparticle-cpuinfo.flags
|
||||
(nop!)
|
||||
|
||||
;; The second sprite quadword has different meanings for 2D and 3D particles. Normalize the
|
||||
;; matrix selector and signed 16-bit angle only for the 2D layout.
|
||||
(b.ne is-3d s7 clamp-color :delay (nop!))
|
||||
(b.eq launch-control-reg s7 default-matrix-mode :delay (l.w v1 launch-control-reg 12))
|
||||
(b.eq v1 s7 default-matrix-mode :delay (l.h v1 v1 6))
|
||||
(and.i v1 v1 (sp-group-flag screen-space))
|
||||
(m a0 particle-flags)
|
||||
(b.z v1 default-matrix-mode :delay (l.w v1 launch-control-reg 28))
|
||||
(and.i a0 a0 (sp-cpuinfo-flag aux-list))
|
||||
(add.i a1 r0 1)
|
||||
(movn v1 a1 a0)
|
||||
(nop!)
|
||||
(b normalize-matrix-mode :delay (s.w v1 sp 148))
|
||||
(label default-matrix-mode)
|
||||
(and.i v1 particle-flags (sp-cpuinfo-flag no-fog))
|
||||
(nop!)
|
||||
(sra v1 v1 14)
|
||||
(nop!)
|
||||
(s.w v1 sp 148)
|
||||
(nop!)
|
||||
;; Screen-space controls supply their selected matrix mode, with auxiliary sprites forced to
|
||||
;; screen mode. Otherwise the no-fog convention selects screen instead of camera space.
|
||||
;; Truncate the packed rotation field to the signed 16-bit angle used by the renderer.
|
||||
(label normalize-matrix-mode)
|
||||
(l.s f2 sp 152)
|
||||
(nop!)
|
||||
(cvt.w.s f2 f2)
|
||||
(nop!)
|
||||
(m v1 f2)
|
||||
(nop!)
|
||||
(dsll32 v1 v1 16)
|
||||
(nop!)
|
||||
(sra32 v1 v1 16)
|
||||
(nop!)
|
||||
(m f2 v1)
|
||||
(nop!)
|
||||
(cvt.s.w f2 f2)
|
||||
(nop!)
|
||||
(s.s f2 sp 152)
|
||||
(nop!)
|
||||
|
||||
(label clamp-color)
|
||||
(and.i v1 particle-flags (sp-cpuinfo-flag aux-list))
|
||||
(rlet ((particle-color :reg vf4))
|
||||
(l.vf particle-color sp 160)
|
||||
(b.nz v1 make-axis-rotation :delay (vminix.xyz particle-color particle-color max-color-vf))
|
||||
(s.vf particle-color sp 160)
|
||||
(nop!))
|
||||
|
||||
;; Auxiliary-list particles retain their original RGB; ordinary particles clamp it to 255.
|
||||
;; For an orbiting particle, x selects a horizontal axis (cos x, 0, -sin x), y is the angle
|
||||
;; about that axis, and z becomes the orbit radius.
|
||||
(label make-axis-rotation)
|
||||
(b.eq launch-control-reg s7 bind-particle :delay (and.i v1 particle-flags (sp-cpuinfo-flag orbit)))
|
||||
(b.z v1 bind-particle :delay (l.w a0 sp 128))
|
||||
(m! t9 cos)
|
||||
(nop!)
|
||||
(jalr ra t9 :delay (nop!))
|
||||
(m a1 v0)
|
||||
(l.w a0 sp 128)
|
||||
(m! t9 sin)
|
||||
(nop!)
|
||||
(jalr ra t9 :delay (nop!))
|
||||
(ori v1 r0 #x8000)
|
||||
(sll v1 v1 16)
|
||||
(l.w a0 sp 136)
|
||||
(xor a3 v0 v1)
|
||||
(s.w a0 cpuinfo-reg 8)
|
||||
(m a2 r0)
|
||||
(l.w t0 sp 132)
|
||||
(m! t9 quaternion-axis-angle!)
|
||||
(add.i a0 cpuinfo-reg 80)
|
||||
(jalr ra t9 :delay (nop!))
|
||||
(l.w v1 launch-state-reg 16)
|
||||
(nop!)
|
||||
(l.w v1 v1)
|
||||
(nop!)
|
||||
(s.w v1 cpuinfo-reg 108)
|
||||
(nop!)
|
||||
|
||||
(label bind-particle)
|
||||
(s.w s7 cpuinfo-reg 136)
|
||||
(nop!)
|
||||
(b.eq launch-state-reg s7 apply-launch-fields :delay (l.w a0 launch-state-reg))
|
||||
(l.w a1 a0 24)
|
||||
(nop!)
|
||||
(b.z a1 apply-launch-fields :delay (nop!))
|
||||
(l.w a2 launch-control-reg)
|
||||
(add.i a3 launch-control-reg 60)
|
||||
;; A launch-control binding records this particle in the first matching free state slot. The state
|
||||
;; then owns the CPU record and, for ordinary births, the allocated sprite.
|
||||
(label scan-bindings)
|
||||
(l.w v1 a3)
|
||||
(add.i a2 a2 -1)
|
||||
(l.w v1 v1)
|
||||
(nop!)
|
||||
(b.ne v1 a1 next-binding :delay (l.h v1 a3 4))
|
||||
(and.i a0 v1 (sp-launch-state-flags enabled))
|
||||
(ori v1 v1 (sp-launch-state-flags enabled))
|
||||
(b.nz a0 next-binding :delay (nop!))
|
||||
(s.h v1 a3 4)
|
||||
(nop!)
|
||||
(l.w v1 cpuinfo-reg)
|
||||
(nop!)
|
||||
(s.w v1 a3 8)
|
||||
(m a0 s7)
|
||||
(b.nel is-3d s7 install-binding :delay (l.w a0 cpuinfo-reg))
|
||||
|
||||
(label install-binding)
|
||||
(s.w a0 a3 12)
|
||||
(nop!)
|
||||
(s.w cpuinfo-reg a3 16)
|
||||
(nop!)
|
||||
(s.w a3 cpuinfo-reg 136)
|
||||
(nop!)
|
||||
(b apply-launch-fields :delay (nop!))
|
||||
|
||||
(label next-binding)
|
||||
(b.nz a2 scan-bindings :delay (add.i a3 a3 32))
|
||||
|
||||
;; Launch-field specs follow the CPU range. Apply them only when that range is present, then fold
|
||||
;; in 3D Euler rotation and the owning launch-control transform.
|
||||
(label apply-launch-fields)
|
||||
(l.w a2 sp 80)
|
||||
(add.i a0 sp 128)
|
||||
(l.h v1 a2)
|
||||
(m a1 cpuinfo-reg)
|
||||
(m! t9 sp-adjust-launch)
|
||||
(add.i v1 v1 -64)
|
||||
(b.ge v1 r0 after-launch-adjust :delay (nop!))
|
||||
(jalr ra t9 :delay (nop!))
|
||||
|
||||
(label after-launch-adjust)
|
||||
(b.eq is-3d s7 after-euler-convert :delay (nop!))
|
||||
(m! t9 sp-euler-convert)
|
||||
(add.i a0 sp 128)
|
||||
(jalr ra t9 :delay (m a1 cpuinfo-reg))
|
||||
|
||||
(label after-euler-convert)
|
||||
(b.eq launch-state-reg s7 after-system-rotation :delay (l.w a2 launch-state-reg 12))
|
||||
(b.eq a2 s7 after-system-rotation :delay (add.i a0 sp 128))
|
||||
(b.z a2 after-system-rotation :delay (m a1 cpuinfo-reg))
|
||||
(m! t9 sp-rotate-system)
|
||||
(nop!)
|
||||
(jalr ra t9 :delay (nop!))
|
||||
|
||||
;; Offset the initialized position by the requested origin. The shader cache expands the compact
|
||||
;; texture ID into the five-quadword ADGIF used by the sprite renderer.
|
||||
(label after-system-rotation)
|
||||
(rlet ((launch-position :reg vf4))
|
||||
(l.vf launch-position sp 128)
|
||||
(nop!)
|
||||
(add.vf.xyz launch-position launch-position origin-vf)
|
||||
(l.w a0 cpuinfo-reg 4)
|
||||
(l.w a1 sp 96)
|
||||
(nop!)
|
||||
(m! t9 particle-adgif)
|
||||
(nop!)
|
||||
(s.vf launch-position sp 128)
|
||||
(nop!)
|
||||
(jalr ra t9 :delay (nop!)))
|
||||
|
||||
;; Override the texture's ALPHA register for erase-destination, subtractive, or additive
|
||||
;; blending, in that priority order. write-depth replaces the ZBUF setting.
|
||||
(l.w a2 cpuinfo-reg 4)
|
||||
(lui a3 256)
|
||||
(l.w v1 a2 64)
|
||||
(and.i a0 particle-flags (sp-cpuinfo-flag blend-erase-dest))
|
||||
(add.i a1 r0 134)
|
||||
(nop!)
|
||||
(movn v1 a1 a0)
|
||||
(and.i a0 particle-flags (sp-cpuinfo-flag subtract-blend))
|
||||
(add.i a1 r0 66)
|
||||
(nop!)
|
||||
(movn v1 a1 a0)
|
||||
(and.i a0 particle-flags (sp-cpuinfo-flag glow))
|
||||
(add.i a1 r0 72)
|
||||
(nop!)
|
||||
(movn v1 a1 a0)
|
||||
(and.i a0 particle-flags (sp-cpuinfo-flag write-depth))
|
||||
(s.w v1 a2 64)
|
||||
(ori a1 a3 448)
|
||||
(b.nzl a0 after-blend-mode :delay (s.d a1 a2 48))
|
||||
|
||||
;; The optional birth callback runs after all standard fields and transforms, so it can make
|
||||
;; effect-specific final changes to both the CPU record and the scratch sprite packet.
|
||||
(label after-blend-mode)
|
||||
(l.w t9 sp 108) ;; sparticle-birthinfo.birth-func
|
||||
(m a1 cpuinfo-reg)
|
||||
(m a0 system-reg)
|
||||
(m t0 launch-state-reg)
|
||||
(b.z t9 after-birth-callback :delay (add.i a2 sp 128))
|
||||
(jalr ra t9 :delay (m a3 launcher-reg))
|
||||
|
||||
(label after-birth-callback)
|
||||
(l.w a0 sp 120) ;; sparticle-birthinfo.sound
|
||||
(nop!)
|
||||
(l.s f2 launcher-reg 4)
|
||||
(lui a1 #x3f80)
|
||||
(b.z a0 after-sound :delay (nop!))
|
||||
(l.s f3 a0 4)
|
||||
(nop!)
|
||||
(add.s f2 f2 f3)
|
||||
(nop!)
|
||||
(s.s f2 launcher-reg 4)
|
||||
(m f3 a1)
|
||||
(sub.s f2 f2 f3)
|
||||
(nop!)
|
||||
(m a1 f2)
|
||||
(nop!)
|
||||
(b.lt a1 r0 after-sound :delay (m! t9 new-sound-id))
|
||||
(s.w a1 launcher-reg 4)
|
||||
(nop!)
|
||||
(jalr ra t9 :delay (nop!))
|
||||
(l.w a0 sp 120)
|
||||
(m a1 v0)
|
||||
(m! t9 sound-play-by-spec)
|
||||
(add.i a2 sp 64)
|
||||
(jalr ra t9 :delay (nop!))
|
||||
|
||||
;; Auxiliary-list particles use a separate update callback and minimum lifetime. Level ownership
|
||||
;; bits select the heap range that contains the launcher so relaunch pointers remain valid.
|
||||
(label after-sound)
|
||||
(add.i a0 r0 4)
|
||||
(and.i v1 particle-flags (sp-cpuinfo-flag aux-list))
|
||||
(b.z v1 after-aux-list :delay (sub a0 r0 a0))
|
||||
(m! v1 add-to-sprite-aux-list)
|
||||
(add.i a0 a0 -1)
|
||||
(s.w v1 cpuinfo-reg 112)
|
||||
(and particle-flags particle-flags a0)
|
||||
(s.w r0 sp 172)
|
||||
(nop!)
|
||||
(s.w r0 cpuinfo-reg 60)
|
||||
(nop!)
|
||||
(l.w v1 sp 144)
|
||||
(add.i a0 r0 3)
|
||||
(max.w v1 v1 a0)
|
||||
(add.i a0 r0 11)
|
||||
(pminw v1 v1 a0)
|
||||
(nop!)
|
||||
(s.w v1 sp 144)
|
||||
(nop!)
|
||||
|
||||
(label after-aux-list)
|
||||
(m! a0 *level*)
|
||||
(nop!)
|
||||
;; A statically defined launcher belongs to a level when its address lies between that
|
||||
;; level's heap base and top-base. Tag it so level unload can kill or relaunch it safely.
|
||||
(l.w v1 a0 124) ;; level0.heap.base
|
||||
(nop!)
|
||||
(sub v1 launcher-reg v1)
|
||||
(nop!)
|
||||
(b.lt v1 r0 test-level1 :delay (nop!))
|
||||
(l.w v1 a0 128) ;; level0.heap.top-base
|
||||
(nop!)
|
||||
(sub v1 launcher-reg v1)
|
||||
(nop!)
|
||||
(b.ge v1 r0 test-level1 :delay (nop!))
|
||||
(b apply-time-of-day :delay (ori particle-flags particle-flags (sp-cpuinfo-flag level0)))
|
||||
|
||||
(label test-level1)
|
||||
(l.w v1 a0 2732) ;; level1.heap.base
|
||||
(nop!)
|
||||
(sub v1 launcher-reg v1)
|
||||
(nop!)
|
||||
(b.lt v1 r0 apply-time-of-day :delay (nop!))
|
||||
(l.w v1 a0 2736) ;; level1.heap.top-base
|
||||
(nop!)
|
||||
(sub v1 launcher-reg v1)
|
||||
(nop!)
|
||||
(b.ge v1 r0 apply-time-of-day :delay (nop!))
|
||||
(ori particle-flags particle-flags (sp-cpuinfo-flag level1))
|
||||
(nop!)
|
||||
|
||||
;; Time-of-day-enabled particles outside the auxiliary list are tinted. Apply the same RGB
|
||||
;; scale to both the initial color and its fade vector.
|
||||
(label apply-time-of-day)
|
||||
(and.i v1 particle-flags (sp-cpuinfo-flag aux-list use-time-of-day-color))
|
||||
(add.i a0 r0 (sp-cpuinfo-flag use-time-of-day-color))
|
||||
(b.ne v1 a0 write-sprite :delay (m! a0 *time-of-day-context*))
|
||||
(rlet ((time-color :reg vf4)
|
||||
(particle-color :reg vf5)
|
||||
(particle-fade :reg vf6))
|
||||
(l.vf time-color a0 108)
|
||||
(nop!)
|
||||
(l.vf particle-color sp 160)
|
||||
(nop!)
|
||||
(l.vf particle-fade cpuinfo-reg 48)
|
||||
(nop!)
|
||||
(mul.vf.xyz particle-color particle-color time-color)
|
||||
(nop!)
|
||||
(mul.vf.xyz particle-fade particle-fade time-color)
|
||||
(nop!)
|
||||
(s.vf particle-color sp 160)
|
||||
(nop!)
|
||||
(s.vf particle-fade cpuinfo-reg 48)
|
||||
(nop!))
|
||||
|
||||
;; Publish the three sprite vectors last. Alpha starts at zero; cache-alpha retains the requested
|
||||
;; value until the update pass processes just-launched.
|
||||
(label write-sprite)
|
||||
(m v1 launch-control-reg)
|
||||
(sub a0 launch-control-reg s7)
|
||||
(mov.z v1 r0 a0)
|
||||
(nop!)
|
||||
(s.w v1 cpuinfo-reg 132)
|
||||
(nop!)
|
||||
(l.w v1 cpuinfo-reg)
|
||||
(nop!)
|
||||
(rlet ((sprite-position :reg vf4)
|
||||
(sprite-orientation :reg vf5)
|
||||
(sprite-color :reg vf6))
|
||||
(l.vf sprite-position sp 128)
|
||||
(nop!)
|
||||
(l.vf sprite-orientation sp 144)
|
||||
(nop!)
|
||||
(l.vf sprite-color sp 160)
|
||||
(nop!)
|
||||
(s.vf sprite-position v1)
|
||||
(nop!)
|
||||
(s.vf sprite-orientation v1 16)
|
||||
(nop!)
|
||||
(sub.w.vf.w sprite-color vf0 vf0)
|
||||
(nop!)
|
||||
(s.vf sprite-color v1 32)
|
||||
(nop!))
|
||||
(ori particle-flags particle-flags (sp-cpuinfo-flag just-launched))
|
||||
(l.w a0 sp 172)
|
||||
(s.w particle-flags cpuinfo-reg 104)
|
||||
(nop!)
|
||||
(s.w a0 cpuinfo-reg 124)
|
||||
(nop!)
|
||||
|
||||
;; Consume one birth and continue while the selected accumulator still has a whole unit.
|
||||
(b.eq launch-state-reg s7 decrement-launch-state :delay (lui v1 #x3f80))
|
||||
(l.s f2 launch-state-reg 24)
|
||||
(m f3 v1)
|
||||
(sub.s f2 f2 f3)
|
||||
(nop!)
|
||||
(s.s f2 launch-state-reg 24)
|
||||
(cvt.w.s f2 f2)
|
||||
(m v1 f2)
|
||||
(nop!)
|
||||
(b.nz v1 particle-loop :delay (nop!))
|
||||
(b finish :delay (nop!))
|
||||
|
||||
(label decrement-launch-state)
|
||||
(l.s f2 launcher-reg)
|
||||
(m f3 v1)
|
||||
(sub.s f2 f2 f3)
|
||||
(nop!)
|
||||
(s.s f2 launcher-reg)
|
||||
(cvt.w.s f2 f2)
|
||||
(m v1 f2)
|
||||
(nop!)
|
||||
(b.nz v1 particle-loop :delay (nop!))
|
||||
|
||||
(label finish)
|
||||
(l.w ra sp)
|
||||
(nop!)
|
||||
(l.q launch-state-reg sp 16)
|
||||
(nop!)
|
||||
(l.q launch-control-reg sp 32)
|
||||
(nop!)
|
||||
(l.q cpuinfo-reg sp 48)
|
||||
(nop!)
|
||||
(l.q system-reg sp 176)
|
||||
(nop!)
|
||||
(l.q launcher-reg sp 192)
|
||||
(nop!)
|
||||
(l.q particle-flags sp 208)
|
||||
(nop!)
|
||||
(l.q is-3d sp 224)
|
||||
(nop!)
|
||||
(jr ra :delay (add.i sp sp 256))
|
||||
(jr ra :delay (add sp sp r0))
|
||||
(nop! :count 2))))
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,9 +18,9 @@
|
||||
(defun sp-particle-copy! ((dst sparticle-cpuinfo) (src sparticle-cpuinfo))
|
||||
"Copy src's rendered sprite, shader, and simulated CPU state to dst without replacing dst's
|
||||
pool bookkeeping, ownership key, binding, or cached record pointers."
|
||||
(let ((position-and-scale (-> src sprite x-y-z-sx quad))) (set! (-> dst sprite x-y-z-sx quad) position-and-scale))
|
||||
(let ((rotation-and-scale (-> src sprite flag-rot-sy quad))) (set! (-> dst sprite flag-rot-sy quad) rotation-and-scale))
|
||||
(let ((color (-> src sprite color quad))) (set! (-> dst sprite color quad) color))
|
||||
(vector-copy! (-> dst sprite x-y-z-sx) (-> src sprite x-y-z-sx))
|
||||
(vector-copy! (-> dst sprite flag-rot-sy) (-> src sprite flag-rot-sy))
|
||||
(vector-copy! (-> dst sprite color) (-> src sprite color))
|
||||
(dotimes (i 10)
|
||||
(set! (-> dst adgif prims i) (-> src adgif prims i)))
|
||||
(vector-copy! (-> dst vel-sxvel) (-> src vel-sxvel))
|
||||
@@ -143,67 +143,6 @@
|
||||
0
|
||||
(none))
|
||||
|
||||
(#unless PC_PORT
|
||||
(defun sp-get-particle ((sys sparticle-system) (group int) (binding sparticle-launch-state))
|
||||
"Allocate a free particle in group, beginning at binding's rotating block index when supplied.
|
||||
Clear its bitmap bit, mark its CPU record valid, and return it; return false when full."
|
||||
(let ((block-offset 0)
|
||||
(num-blocks (-> sys blocks 0))
|
||||
(block-index 0))
|
||||
(when (= group 1)
|
||||
(set! block-offset num-blocks)
|
||||
(set! num-blocks (-> sys blocks 1)))
|
||||
;; Rotate the first block examined so repeated launchers spread their allocations.
|
||||
(when binding
|
||||
(set! block-index (the int (-> binding randomize)))
|
||||
(+! (-> binding randomize) 1)
|
||||
(when (= (-> binding randomize) num-blocks)
|
||||
(set! (-> binding randomize) (the uint 0))))
|
||||
(dotimes (i num-blocks)
|
||||
(let ((free-bits (-> sys alloc-table (+ block-offset block-index))))
|
||||
(when (nonzero? free-bits)
|
||||
;; Find the least-significant set bit in six branchless halves. Shifting a candidate
|
||||
;; half to the sign end lets mov.nz keep it when that half contains a free slot;
|
||||
;; mov.z advances the bit index only when the candidate half is empty.
|
||||
(let ((free-bit (rlet ((bits :reg t1)
|
||||
(shifted :reg t2)
|
||||
(candidate-index :reg t3)
|
||||
(bit-index :reg a2))
|
||||
(m! bits free-bits)
|
||||
(m! bit-index 0)
|
||||
(sll32 shifted bits)
|
||||
(add.i candidate-index bit-index 32)
|
||||
(mov.nz bits shifted shifted)
|
||||
(mov.z bit-index candidate-index shifted)
|
||||
(sll shifted bits 16)
|
||||
(add.i candidate-index bit-index 16)
|
||||
(mov.nz bits shifted shifted)
|
||||
(mov.z bit-index candidate-index shifted)
|
||||
(sll shifted bits 8)
|
||||
(add.i candidate-index bit-index 8)
|
||||
(mov.nz bits shifted shifted)
|
||||
(mov.z bit-index candidate-index shifted)
|
||||
(sll shifted bits 4)
|
||||
(add.i candidate-index bit-index 4)
|
||||
(mov.nz bits shifted shifted)
|
||||
(mov.z bit-index candidate-index shifted)
|
||||
(sll shifted bits 2)
|
||||
(add.i candidate-index bit-index 2)
|
||||
(mov.nz bits shifted shifted)
|
||||
(mov.z bit-index candidate-index shifted)
|
||||
(sll shifted bits 1)
|
||||
(add.i candidate-index bit-index 1)
|
||||
(mov.nz bits shifted shifted)
|
||||
(mov.z bit-index candidate-index shifted)
|
||||
bit-index)))
|
||||
(let ((slot (+ free-bit (* (+ block-offset block-index) 64))))
|
||||
(logxor! (-> sys alloc-table (+ block-offset block-index)) (ash 1 free-bit))
|
||||
(+! (-> sys num-alloc group) 1)
|
||||
(let ((cpuinfo (-> sys cpuinfo-table slot))) (set! (-> cpuinfo valid) #t) (return cpuinfo))))))
|
||||
(+! block-index 1)
|
||||
(if (= block-index num-blocks) (set! block-index 0))))
|
||||
(the-as sparticle-cpuinfo #f)))
|
||||
|
||||
(#when PC_PORT
|
||||
(defun sp-get-particle ((sys sparticle-system) (group int) (binding sparticle-launch-state))
|
||||
"Allocate the first free particle in group, clear its bitmap bit, and return its CPU record.
|
||||
@@ -237,7 +176,7 @@
|
||||
timer so its block update can perform the release safely."
|
||||
(cond
|
||||
;; modified to check the end of the spad too.
|
||||
((in-scratchpad? cpuinfo) ;;(>= (the-as int cpuinfo) #x70000000) ;; TODO PC port
|
||||
((in-scratchpad? cpuinfo)
|
||||
;; If in the scratchpad, just set the timer to 0.
|
||||
(set! (-> cpuinfo timer) 0)
|
||||
0)
|
||||
@@ -295,588 +234,232 @@
|
||||
;; particle update
|
||||
;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(#unless PC_PORT
|
||||
(defun sp-process-block-2d ((sys sparticle-system)
|
||||
(defun sp-process-block-2d ((sys sparticle-system)
|
||||
(scratchpad-cpuinfo int)
|
||||
(scratchpad-sprite-data int)
|
||||
(start-slot int)
|
||||
(count int)
|
||||
(paused? symbol))
|
||||
"Update count 2D particle records in scratchpad: age, integrate, fade, invoke callbacks, handle
|
||||
death or relaunch, and write their billboard sprite data."
|
||||
(declare (asm-func none))
|
||||
;; Each iteration walks one 144-byte CPU record beside one 48-byte sprite record. The sprite
|
||||
;; record is three quadwords:
|
||||
;;
|
||||
;; +0 position.xyz, x-scale
|
||||
;; +16 sprite flags/matrix mode, rotation, y-scale
|
||||
;; +32 color.rgba
|
||||
;;
|
||||
;; The matching CPU record keeps velocity.xyz/x-scale velocity at +16, rotation/y-scale
|
||||
;; velocity at +32, color fade at +48, acceleration at +64, friction at +96, timer at +100,
|
||||
;; flags at +104, the per-particle update function at +112, relaunch state at +116/+120, cached
|
||||
;; launch alpha at +124, and validity at +128. Keeping these records separate lets the sprite
|
||||
;; record remain ready for the renderer while simulation-only state stays in the CPU record.
|
||||
;;
|
||||
;; *sp-frame-time*.x carries the integer 300 Hz tick count in its low byte. Its y lane scales
|
||||
;; position, rotation, scale, and color increments, while z and w express the same interval in
|
||||
;; 60 Hz frames for acceleration and friction. Invalid slots are skipped. Pausing freezes a
|
||||
;; particle unless run-while-paused is set. A timer of -1 never expires; zero frees the particle.
|
||||
;; On the first visible update, just-launched restores the intended alpha from cache-alpha.
|
||||
(rlet ((sys :reg gp)
|
||||
(cpuinfo :reg s5 :type sparticle-cpuinfo)
|
||||
(sprite-data :reg s4 :type sprite-vec-data-2d)
|
||||
(slot :reg s1)
|
||||
(remaining :reg s3)
|
||||
(paused? :reg s2)
|
||||
(ticks :reg s0)
|
||||
(frame-time :reg vf9)
|
||||
(sprite-pos-scale :reg vf1)
|
||||
(sprite-rot-scale :reg vf2)
|
||||
(sprite-color :reg vf3)
|
||||
(velocity-scale-rate :reg vf4)
|
||||
(rotation-scale-rate :reg vf5)
|
||||
(color-fade-rate :reg vf6)
|
||||
(acceleration :reg vf7)
|
||||
(friction-factor :reg vf8)
|
||||
(position-step :reg vf10)
|
||||
(rotation-scale-step :reg vf11)
|
||||
(color-step :reg vf12))
|
||||
;; Establish the scratchpad cursors and recover this frame's integer tick count.
|
||||
(asm-block setup-2d
|
||||
(label particle-update-2d-entry)
|
||||
(add.i sp sp -128)
|
||||
(s.d ra sp)
|
||||
(s.q s0 sp 16)
|
||||
(s.q s1 sp 32)
|
||||
(s.q s2 sp 48)
|
||||
(s.q s3 sp 64)
|
||||
(s.q s4 sp 80)
|
||||
(s.q s5 sp 96)
|
||||
(s.q gp sp 112)
|
||||
(m sys a0)
|
||||
(m cpuinfo a1)
|
||||
(m sprite-data a2)
|
||||
(m slot a3)
|
||||
(m remaining t0)
|
||||
(m paused? t1)
|
||||
(m! v1 *sp-frame-time*)
|
||||
(l.vf frame-time v1)
|
||||
(m v1 frame-time)
|
||||
(and.i ticks v1 255))
|
||||
;; Reject unused slots, then decide whether a paused particle is allowed to simulate.
|
||||
(asm-block particle-lifetime-2d
|
||||
(label particle-loop)
|
||||
(l.w.field v1 cpuinfo (valid))
|
||||
(b.eq v1 s7 next-particle :delay (nop!))
|
||||
(b.eq paused? s7 simulate-particle :delay (l.w.field v1 cpuinfo (flags)))
|
||||
(and.i v1 v1 #x2000)
|
||||
(b.nz v1 simulate-particle :delay (nop!))
|
||||
(l.w.field v1 cpuinfo (timer))
|
||||
(add.i a0 r0 -1)
|
||||
(b.eq v1 a0 reveal-new-particle :delay (nop!))
|
||||
(b.z v1 free-particle :delay (nop!))
|
||||
;; A newly launched particle is hidden with alpha zero. Reveal it without advancing time when
|
||||
;; the rest of its simulation is frozen.
|
||||
(label reveal-new-particle)
|
||||
(l.w.field a0 cpuinfo (flags))
|
||||
(and.i v1 a0 64)
|
||||
(xor a0 a0 v1)
|
||||
(b.z v1 next-particle :delay (s.w.field a0 cpuinfo (flags)))
|
||||
(l.w.field v1 cpuinfo (cache-alpha))
|
||||
(b next-particle :delay (s.w.field v1 sprite-data (a)))
|
||||
;; Age finite particles in 300 Hz ticks. The packed maximum clamps an overshoot to zero so the
|
||||
;; next iteration follows the ordinary zero-timer death path.
|
||||
(label simulate-particle)
|
||||
(l.w.field v1 cpuinfo (timer))
|
||||
(add.i a0 r0 -1)
|
||||
(b.eq v1 a0 run-callback :delay (sub a0 v1 ticks))
|
||||
(b.z v1 free-particle :delay (max.w v1 a0 r0))
|
||||
(s.w.field v1 cpuinfo (timer))
|
||||
;; Restore the launch alpha before callbacks so custom particle logic sees the visible state.
|
||||
(label run-callback)
|
||||
(l.w.field a0 cpuinfo (flags))
|
||||
(and.i v1 a0 64)
|
||||
(xor a0 a0 v1)
|
||||
(b.z v1 call-update-function :delay (s.w.field a0 cpuinfo (flags)))
|
||||
(l.w.field v1 cpuinfo (cache-alpha))
|
||||
(s.w.field v1 sprite-data (a)))
|
||||
;; The callback may edit either the CPU record or its renderer-facing sprite record.
|
||||
(asm-block callback-and-relaunch-2d
|
||||
(label call-update-function)
|
||||
(l.w.field t9 cpuinfo (func))
|
||||
(b.z t9 check-relaunch :delay (nop!))
|
||||
(add.i sp sp -80)
|
||||
(s.q sys sp)
|
||||
(s.q cpuinfo sp 16)
|
||||
(s.q sprite-data sp 32)
|
||||
(s.q slot sp 48)
|
||||
(m a0 sys)
|
||||
(m a1 cpuinfo)
|
||||
(m a2 sprite-data)
|
||||
(jalr ra t9 :delay (s.q remaining sp 64))
|
||||
(l.q sys sp)
|
||||
(l.q cpuinfo sp 16)
|
||||
(l.q sprite-data sp 32)
|
||||
(l.q slot sp 48)
|
||||
(l.q remaining sp 64)
|
||||
(add.i sp sp 80)
|
||||
;; A nonfalse next-launcher owns a countdown independent of the particle lifetime. Relaunch as
|
||||
;; soon as subtracting this frame's ticks carries it below zero.
|
||||
(label check-relaunch)
|
||||
(l.w.field a1 cpuinfo (next-launcher))
|
||||
(l.w.field v1 cpuinfo (next-time))
|
||||
(b.z a1 integrate :delay (sub a0 v1 ticks))
|
||||
(add.i v1 a0 -1)
|
||||
(b.ge v1 r0 integrate :delay (s.w.field a0 cpuinfo (next-time)))
|
||||
(add.i sp sp -96)
|
||||
(s.q sys sp)
|
||||
(s.q cpuinfo sp 16)
|
||||
(s.q sprite-data sp 32)
|
||||
(s.q slot sp 48)
|
||||
(s.q remaining sp 64)
|
||||
(s.q paused? sp 80)
|
||||
(m a0 sys)
|
||||
(m a3 sprite-data)
|
||||
(m a2 cpuinfo)
|
||||
(m! t9 sp-relaunch-particle-2d)
|
||||
(jalr ra t9 :delay (sll v0 ra 0))
|
||||
(l.q sys sp)
|
||||
(l.q cpuinfo sp 16)
|
||||
(l.q sprite-data sp 32)
|
||||
(l.q slot sp 48)
|
||||
(l.q remaining sp 64)
|
||||
(l.q paused? sp 80)
|
||||
(add.i sp sp 96))
|
||||
;; Integrate the four adjacent CPU vectors directly into the three sprite quadwords.
|
||||
(asm-block integrate-motion-2d
|
||||
(label integrate)
|
||||
(l.vf.field sprite-pos-scale sprite-data (x-y-z-sx))
|
||||
(l.vf.field sprite-rot-scale sprite-data (flag-rot-sy))
|
||||
(l.vf.field sprite-color sprite-data (r-g-b-a))
|
||||
(l.vf.field velocity-scale-rate cpuinfo (vel-sxvel))
|
||||
(l.vf.field rotation-scale-rate cpuinfo (rot-syvel))
|
||||
(l.vf.field color-fade-rate cpuinfo (fade))
|
||||
(l.vf.field acceleration cpuinfo (acc))
|
||||
(l.s.field f0 cpuinfo (friction))
|
||||
(m v1 f0)
|
||||
;; Acceleration uses 60 Hz frame units. Nonzero friction is converted to a retained-velocity
|
||||
;; factor for this interval, then applied to xyz without disturbing x-scale velocity in w.
|
||||
(mul.z.vf acceleration acceleration frame-time)
|
||||
(b.z v1 no-friction :delay (add.vf.xyz velocity-scale-rate velocity-scale-rate acceleration))
|
||||
(m friction-factor v1)
|
||||
(sub.x.vf.w friction-factor vf0 friction-factor)
|
||||
(mul.w.vf friction-factor friction-factor frame-time)
|
||||
(sub.w.vf.w friction-factor vf0 friction-factor)
|
||||
(mul.w.vf.xyz velocity-scale-rate velocity-scale-rate friction-factor)
|
||||
(label no-friction)
|
||||
;; Apply velocity, rotation/scale velocity, and fade with the full 300 Hz tick count. Clamp
|
||||
;; color to zero so the packed death tests below can compare its float bits as integers.
|
||||
(mul.y.vf position-step velocity-scale-rate frame-time)
|
||||
(mul.y.vf rotation-scale-step rotation-scale-rate frame-time)
|
||||
(mul.y.vf color-step color-fade-rate frame-time)
|
||||
(add.vf sprite-pos-scale sprite-pos-scale position-step)
|
||||
(add.vf.zw sprite-rot-scale sprite-rot-scale rotation-scale-step)
|
||||
(add.vf sprite-color sprite-color color-step)
|
||||
(max.x.vf sprite-color sprite-color vf0)
|
||||
(s.vf.field velocity-scale-rate cpuinfo (vel-sxvel))
|
||||
(s.vf.field sprite-pos-scale sprite-data (x-y-z-sx))
|
||||
(s.vf.field sprite-rot-scale sprite-data (flag-rot-sy))
|
||||
(s.vf.field sprite-color sprite-data (r-g-b-a)))
|
||||
;; 2D rotation is kept as an integer-valued float, matching the sprite packet format.
|
||||
(asm-block billboard-post-update
|
||||
(l.s.field f0 sprite-data (rot))
|
||||
(cvt.w.s f0 f0)
|
||||
(m v1 f0)
|
||||
(sll32 v1 v1 16)
|
||||
(sra32 v1 v1 16)
|
||||
(m f0 v1)
|
||||
(cvt.s.w f0 f0)
|
||||
(s.s.field f0 sprite-data (rot))
|
||||
;; Orbiting is a post-integration position adjustment around the particle's stored center.
|
||||
(l.w.field v1 cpuinfo (flags))
|
||||
(and.i v1 v1 128)
|
||||
(b.z v1 test-death-flags :delay (m! t9 sp-orbiter))
|
||||
(add.i sp sp -96)
|
||||
(s.q sys sp)
|
||||
(s.q cpuinfo sp 16)
|
||||
(s.q sprite-data sp 32)
|
||||
(s.q slot sp 48)
|
||||
(s.q remaining sp 64)
|
||||
(m a0 sys)
|
||||
(m a1 cpuinfo)
|
||||
(m a2 sprite-data)
|
||||
(jalr ra t9 :delay (s.q paused? sp 80))
|
||||
(l.q sys sp)
|
||||
(l.q cpuinfo sp 16)
|
||||
(l.q sprite-data sp 32)
|
||||
(l.q slot sp 48)
|
||||
(l.q remaining sp 64)
|
||||
(l.q paused? sp 80)
|
||||
(add.i sp sp 96))
|
||||
;; Packed extracts test RGB together, then alpha, then x- and y-scale. These correspond to
|
||||
;; die-when-color-0, die-when-faded, and die-when-scale-0.
|
||||
(asm-block death-tests-2d
|
||||
(label test-death-flags)
|
||||
(l.q.field v1 sprite-data (r-g-b-a))
|
||||
(l.w.field a0 cpuinfo (flags))
|
||||
(and.i a1 a0 2)
|
||||
(b.z a1 test-fade-death :delay (and.i a1 a0 4))
|
||||
(b.nz v1 test-fade-death :delay (pextuw t4 v1 r0))
|
||||
(b.z t4 free-particle :delay (nop!))
|
||||
(label test-fade-death)
|
||||
(b.z a1 test-scale-death :delay (and.i a0 a0 1))
|
||||
(pcpyud t4 v1 r0)
|
||||
(pexew t4 t4)
|
||||
(b.le t4 r0 free-particle :delay (nop!))
|
||||
(label test-scale-death)
|
||||
(b.z a0 next-particle :delay (nop!))
|
||||
(m v1 sprite-pos-scale)
|
||||
(pcpyud v1 v1 r0)
|
||||
(pexew v1 v1)
|
||||
(b.lt v1 r0 free-particle :delay (m v1 sprite-rot-scale))
|
||||
(pcpyud v1 v1 r0)
|
||||
(pexew v1 v1)
|
||||
(b.ge v1 r0 next-particle :delay (nop!))
|
||||
;; Return dead particles to the bitmap-managed pool.
|
||||
(label free-particle)
|
||||
(m! t9 sp-free-particle)
|
||||
(m a0 sys)
|
||||
(m a1 slot)
|
||||
(m a2 cpuinfo)
|
||||
(m a3 sprite-data)
|
||||
(jalr ra t9 :delay (sll v0 ra 0)))
|
||||
;; Advance every parallel cursor even when this slot was invalid.
|
||||
(asm-block advance-and-return-2d
|
||||
(label next-particle)
|
||||
(add.i remaining remaining -1)
|
||||
(add.i cpuinfo cpuinfo 144)
|
||||
(add.i sprite-data sprite-data 48)
|
||||
(b.nz remaining particle-loop :delay (add.i slot slot 1))
|
||||
;; Return the first slot after the processed block.
|
||||
(m v0 slot)
|
||||
(l.d ra sp)
|
||||
(l.q sys sp 112)
|
||||
(l.q cpuinfo sp 96)
|
||||
(l.q sprite-data sp 80)
|
||||
(l.q remaining sp 64)
|
||||
(l.q paused? sp 48)
|
||||
(l.q slot sp 32)
|
||||
(l.q ticks sp 16)
|
||||
(jr ra :delay (add.i sp sp 128))
|
||||
(nop! :count 3)))))
|
||||
"Update a scratchpad block of 2D particles using the original simulation rules."
|
||||
(let ((cpuinfos (the-as (inline-array sparticle-cpuinfo) scratchpad-cpuinfo))
|
||||
(sprites (the-as (inline-array sprite-vec-data-2d) scratchpad-sprite-data))
|
||||
(ticks (logand (the-as int (-> *sp-frame-time* x)) 255))
|
||||
(tick-scale (-> *sp-frame-time* y))
|
||||
(frame-scale (-> *sp-frame-time* z)))
|
||||
(dotimes (i count)
|
||||
(let ((cpuinfo (-> cpuinfos i))
|
||||
(sprite (-> sprites i))
|
||||
(slot (+ start-slot i)))
|
||||
(when (-> cpuinfo valid)
|
||||
(cond
|
||||
;; Paused particles do not age or simulate, but a zero timer still frees them and
|
||||
;; a newly launched particle still becomes visible.
|
||||
((and paused?
|
||||
(not (logtest? (-> cpuinfo flags) (sp-cpuinfo-flag run-while-paused))))
|
||||
(cond
|
||||
((= (-> cpuinfo timer) 0)
|
||||
(sp-free-particle sys slot cpuinfo sprite))
|
||||
((logtest? (-> cpuinfo flags) (sp-cpuinfo-flag just-launched))
|
||||
(logclear! (-> cpuinfo flags) (sp-cpuinfo-flag just-launched))
|
||||
(set! (-> sprite a) (-> cpuinfo cache-alpha)))))
|
||||
;; A timer which reached zero on the preceding update dies before doing any more work.
|
||||
((= (-> cpuinfo timer) 0)
|
||||
(sp-free-particle sys slot cpuinfo sprite))
|
||||
(else
|
||||
;; -1 is an infinite lifetime. Finite timers clamp at zero and are freed next update.
|
||||
(when (!= (-> cpuinfo timer) -1)
|
||||
(set! (-> cpuinfo timer) (max 0 (- (-> cpuinfo timer) ticks))))
|
||||
|
||||
(#when PC_PORT
|
||||
(def-mips2c sp-process-block-2d (function sparticle-system int int int int symbol none)))
|
||||
(when (logtest? (-> cpuinfo flags) (sp-cpuinfo-flag just-launched))
|
||||
(logclear! (-> cpuinfo flags) (sp-cpuinfo-flag just-launched))
|
||||
(set! (-> sprite a) (-> cpuinfo cache-alpha)))
|
||||
|
||||
(#unless PC_PORT
|
||||
(defun sp-process-block-3d ((sys sparticle-system)
|
||||
(when (nonzero? (the-as uint (-> cpuinfo func)))
|
||||
((the-as (function sparticle-system
|
||||
sparticle-cpuinfo
|
||||
sprite-vec-data-3d
|
||||
none)
|
||||
(-> cpuinfo func))
|
||||
sys
|
||||
cpuinfo
|
||||
(the-as sprite-vec-data-3d sprite)))
|
||||
|
||||
;; The 2D original relaunches when the decremented countdown is zero or negative.
|
||||
(when (nonzero? (the-as uint (-> cpuinfo next-launcher)))
|
||||
(let ((next-time (- (the-as int (-> cpuinfo next-time)) ticks)))
|
||||
(set! (-> cpuinfo next-time) (the-as uint next-time))
|
||||
(when (< next-time 1)
|
||||
(sp-relaunch-particle-2d sys
|
||||
(the-as sparticle-launcher (-> cpuinfo next-launcher))
|
||||
cpuinfo
|
||||
(the-as sprite-vec-data-3d sprite)))))
|
||||
|
||||
;; Acceleration is measured in 60 Hz frames. Friction retains
|
||||
;; 1 - (1 - friction) * frame-scale of the xyz velocity.
|
||||
(+! (-> cpuinfo vel-sxvel x) (* (-> cpuinfo acc x) frame-scale))
|
||||
(+! (-> cpuinfo vel-sxvel y) (* (-> cpuinfo acc y) frame-scale))
|
||||
(+! (-> cpuinfo vel-sxvel z) (* (-> cpuinfo acc z) frame-scale))
|
||||
(when (nonzero? (the-as int (-> cpuinfo friction)))
|
||||
(let ((retained (- 1.0 (* (- 1.0 (-> cpuinfo friction)) frame-scale))))
|
||||
(*! (-> cpuinfo vel-sxvel x) retained)
|
||||
(*! (-> cpuinfo vel-sxvel y) retained)
|
||||
(*! (-> cpuinfo vel-sxvel z) retained)))
|
||||
|
||||
;; Velocity, scale velocity, billboard rotation, and fade use 300 Hz ticks.
|
||||
(+! (-> sprite x) (* (-> cpuinfo vel-sxvel x) tick-scale))
|
||||
(+! (-> sprite y) (* (-> cpuinfo vel-sxvel y) tick-scale))
|
||||
(+! (-> sprite z) (* (-> cpuinfo vel-sxvel z) tick-scale))
|
||||
(+! (-> sprite sx) (* (-> cpuinfo vel-sxvel w) tick-scale))
|
||||
(+! (-> sprite rot) (* (-> cpuinfo rot-syvel z) tick-scale))
|
||||
(+! (-> sprite sy) (* (-> cpuinfo rot-syvel w) tick-scale))
|
||||
(+! (-> sprite r) (* (-> cpuinfo fade x) tick-scale))
|
||||
(+! (-> sprite g) (* (-> cpuinfo fade y) tick-scale))
|
||||
(+! (-> sprite b) (* (-> cpuinfo fade z) tick-scale))
|
||||
(+! (-> sprite a) (* (-> cpuinfo fade w) tick-scale))
|
||||
(set! (-> sprite r) (fmax (-> sprite r) 0.0))
|
||||
(set! (-> sprite g) (fmax (-> sprite g) 0.0))
|
||||
(set! (-> sprite b) (fmax (-> sprite b) 0.0))
|
||||
(set! (-> sprite a) (fmax (-> sprite a) 0.0))
|
||||
(set! (-> sprite rot) (unwrap-angle (-> sprite rot)))
|
||||
|
||||
(when (logtest? (-> cpuinfo flags) (sp-cpuinfo-flag orbit))
|
||||
(sp-orbiter sys cpuinfo (-> sprite x-y-z-sx)))
|
||||
|
||||
(let ((dies?
|
||||
(or
|
||||
(and (logtest? (-> cpuinfo flags) (sp-cpuinfo-flag die-when-color-0))
|
||||
(= (-> sprite r) 0.0)
|
||||
(= (-> sprite g) 0.0)
|
||||
(= (-> sprite b) 0.0))
|
||||
(and (logtest? (-> cpuinfo flags) (sp-cpuinfo-flag die-when-faded))
|
||||
(<= (the-as int (-> sprite a)) 0))
|
||||
(and (logtest? (-> cpuinfo flags) (sp-cpuinfo-flag die-when-scale-0))
|
||||
(or (< (the-as int (-> sprite sx)) 0)
|
||||
(< (the-as int (-> sprite sy)) 0))))))
|
||||
(when dies?
|
||||
(sp-free-particle sys slot cpuinfo sprite))))))))
|
||||
(none)))
|
||||
|
||||
|
||||
(defun sp-process-block-3d ((sys sparticle-system)
|
||||
(scratchpad-cpuinfo int)
|
||||
(scratchpad-sprite-data int)
|
||||
(start-slot int)
|
||||
(count int)
|
||||
(paused? symbol))
|
||||
"Update count 3D particle records in scratchpad: age, integrate, rotate, fade, invoke callbacks,
|
||||
handle death or relaunch, and write their oriented sprite data."
|
||||
(declare (asm-func none))
|
||||
;; The 3D path shares the 144-byte CPU record and 48-byte renderer record with the 2D path. The
|
||||
;; renderer record still stores position.xyz/x-scale at +0 and color.rgba at +32, but its middle
|
||||
;; quadword holds quaternion.xyz/y-scale rather than a billboard rotation. The complete
|
||||
;; rotational velocity quaternion is kept in the CPU record at +80.
|
||||
;;
|
||||
;; Quaternion w is omitted from the renderer record because a unit quaternion has only three
|
||||
;; independent components. Rebuild |w| from xyz, compose the rotational step, then choose the
|
||||
;; sign of the stored xyz so the omitted w can remain nonnegative. q and -q represent the same
|
||||
;; rotation, so the renderer can reconstruct an equivalent orientation without spending a sign
|
||||
;; bit.
|
||||
;;
|
||||
;; Lifetime, pause, callback, relaunch, integration, and packed death tests otherwise match the
|
||||
;; 2D update. A long frame of at least ten 300 Hz ticks composes the rotation step twice.
|
||||
(rlet ((sys :reg gp)
|
||||
(cpuinfo :reg s5 :type sparticle-cpuinfo)
|
||||
(sprite-data :reg s4 :type sprite-vec-data-3d)
|
||||
(slot :reg s0)
|
||||
(remaining :reg s3)
|
||||
(paused? :reg s2)
|
||||
(rotation :reg s1)
|
||||
(frame-time :reg vf16)
|
||||
(sprite-pos-scale :reg vf8)
|
||||
(sprite-quat-scale :reg vf9)
|
||||
(sprite-color :reg vf10)
|
||||
(velocity-scale-rate :reg vf11)
|
||||
(rotation-scale-rate :reg vf12)
|
||||
(color-fade-rate :reg vf13)
|
||||
(acceleration :reg vf14)
|
||||
(friction-factor :reg vf15)
|
||||
(position-step :reg vf17)
|
||||
(rotation-scale-step :reg vf18)
|
||||
(color-step :reg vf19)
|
||||
(stored-quat-scale :reg vf1)
|
||||
(full-quaternion :reg vf2))
|
||||
;; Establish the scratchpad cursors, reserve a complete quaternion, and cache the tick count.
|
||||
(asm-block setup-3d
|
||||
(label particle-update-3d-entry)
|
||||
(add.i sp sp -160)
|
||||
(s.d ra sp)
|
||||
(s.d fp sp 8)
|
||||
(m fp t9)
|
||||
(s.q s0 sp 48)
|
||||
(s.q s1 sp 64)
|
||||
(s.q s2 sp 80)
|
||||
(s.q s3 sp 96)
|
||||
(s.q s4 sp 112)
|
||||
(s.q s5 sp 128)
|
||||
(s.q gp sp 144)
|
||||
(m sys a0)
|
||||
(m cpuinfo a1)
|
||||
(m sprite-data a2)
|
||||
(m slot a3)
|
||||
(m remaining t0)
|
||||
(m paused? t1)
|
||||
(add.i rotation sp 16)
|
||||
(s.q r0 rotation)
|
||||
(m! v1 *sp-frame-time*)
|
||||
(l.vf frame-time v1)
|
||||
(m v1 frame-time)
|
||||
(and.i v1 v1 255)
|
||||
(s.q v1 sp 32)
|
||||
(nop!))
|
||||
;; Reject unused slots, then decide whether a paused particle is allowed to simulate.
|
||||
(asm-block particle-lifetime-3d
|
||||
(label particle-loop-3d)
|
||||
(l.w.field v1 cpuinfo (valid))
|
||||
(b.eq v1 s7 next-particle-3d :delay (nop!))
|
||||
(b.eq paused? s7 simulate-particle-3d :delay (l.w.field v1 cpuinfo (flags)))
|
||||
(and.i v1 v1 #x2000)
|
||||
(b.nz v1 simulate-particle-3d :delay (nop!))
|
||||
(l.w.field v1 cpuinfo (timer))
|
||||
(add.i a0 r0 -1)
|
||||
(b.eq v1 a0 reveal-new-particle-3d :delay (nop!))
|
||||
(b.z v1 free-particle-3d :delay (nop!))
|
||||
;; Reveal a newly launched particle without advancing it when simulation is frozen.
|
||||
(label reveal-new-particle-3d)
|
||||
(l.w.field a0 cpuinfo (flags))
|
||||
(and.i v1 a0 64)
|
||||
(xor a0 a0 v1)
|
||||
(b.z v1 next-particle-3d :delay (s.w.field a0 cpuinfo (flags)))
|
||||
(l.w.field v1 cpuinfo (cache-alpha))
|
||||
(b next-particle-3d :delay (s.w.field v1 sprite-data (a)))
|
||||
;; Age finite particles in 300 Hz ticks and clamp an overshoot to zero.
|
||||
(label simulate-particle-3d)
|
||||
(l.w.field v1 cpuinfo (timer))
|
||||
(add.i a0 r0 -1)
|
||||
(b.eq v1 a0 run-callback-3d :delay (l.q a0 sp 32))
|
||||
(sub a0 v1 a0)
|
||||
(b.z v1 free-particle-3d :delay (max.w v1 a0 r0))
|
||||
(s.w.field v1 cpuinfo (timer))
|
||||
;; Restore the launch alpha before custom particle logic runs.
|
||||
(label run-callback-3d)
|
||||
(l.w.field a0 cpuinfo (flags))
|
||||
(and.i v1 a0 64)
|
||||
(xor a0 a0 v1)
|
||||
(b.z v1 call-update-function-3d :delay (s.w.field a0 cpuinfo (flags)))
|
||||
(l.w.field v1 cpuinfo (cache-alpha))
|
||||
(s.w.field v1 sprite-data (a)))
|
||||
;; The callback may edit either the CPU record or its renderer-facing sprite record.
|
||||
(asm-block callback-3d
|
||||
(label call-update-function-3d)
|
||||
(l.w.field t9 cpuinfo (func))
|
||||
(b.z t9 check-relaunch-3d :delay (nop!))
|
||||
(add.i sp sp -96)
|
||||
(s.q sys sp)
|
||||
(s.q cpuinfo sp 16)
|
||||
(s.q sprite-data sp 32)
|
||||
(s.q slot sp 48)
|
||||
(s.q remaining sp 64)
|
||||
(m a0 sys)
|
||||
(m a1 cpuinfo)
|
||||
(m a2 sprite-data)
|
||||
(jalr ra t9 :delay (s.q paused? sp 80))
|
||||
(l.q sys sp)
|
||||
(l.q cpuinfo sp 16)
|
||||
(l.q sprite-data sp 32)
|
||||
(l.q slot sp 48)
|
||||
(l.q remaining sp 64)
|
||||
(l.q paused? sp 80)
|
||||
(add.i sp sp 96))
|
||||
;; Relaunch on the first frame that carries the independent next-time countdown below zero.
|
||||
(asm-block relaunch-3d
|
||||
(label check-relaunch-3d)
|
||||
(l.w.field a1 cpuinfo (next-launcher))
|
||||
(l.w.field v1 cpuinfo (next-time))
|
||||
(b.z a1 integrate-3d :delay (l.q a0 sp 32))
|
||||
(sub v1 v1 a0)
|
||||
(b.ge v1 r0 integrate-3d :delay (s.w.field v1 cpuinfo (next-time)))
|
||||
(add.i sp sp -96)
|
||||
(s.q sys sp)
|
||||
(s.q cpuinfo sp 16)
|
||||
(s.q sprite-data sp 32)
|
||||
(s.q slot sp 48)
|
||||
(s.q remaining sp 64)
|
||||
(s.q paused? sp 80)
|
||||
(m a0 sys)
|
||||
(m a3 sprite-data)
|
||||
(m a2 cpuinfo)
|
||||
(m! t9 sp-relaunch-particle-3d)
|
||||
(jalr ra t9 :delay (sll v0 ra 0))
|
||||
(l.q sys sp)
|
||||
(l.q cpuinfo sp 16)
|
||||
(l.q sprite-data sp 32)
|
||||
(l.q slot sp 48)
|
||||
(l.q remaining sp 64)
|
||||
(l.q paused? sp 80)
|
||||
(add.i sp sp 96))
|
||||
;; Integrate the four adjacent CPU vectors directly into the three sprite quadwords.
|
||||
(asm-block integrate-motion-3d
|
||||
(label integrate-3d)
|
||||
(l.vf.field sprite-pos-scale sprite-data (x-y-z-sx))
|
||||
(l.vf.field sprite-quat-scale sprite-data (qx-qy-qz-sy))
|
||||
(l.vf.field sprite-color sprite-data (r-g-b-a))
|
||||
(l.vf.field velocity-scale-rate cpuinfo (vel-sxvel))
|
||||
(l.vf.field rotation-scale-rate cpuinfo (rot-syvel))
|
||||
(l.vf.field color-fade-rate cpuinfo (fade))
|
||||
(l.vf.field acceleration cpuinfo (acc))
|
||||
(l.s.field f0 cpuinfo (friction))
|
||||
(m v1 f0)
|
||||
;; Acceleration uses 60 Hz frame units. Nonzero friction becomes a retained-velocity factor
|
||||
;; for this interval and applies to xyz without disturbing x-scale velocity in w.
|
||||
(mul.z.vf acceleration acceleration frame-time)
|
||||
(b.z v1 no-friction-3d :delay (add.vf.xyz velocity-scale-rate velocity-scale-rate acceleration))
|
||||
(m friction-factor v1)
|
||||
(sub.x.vf.w friction-factor vf0 friction-factor)
|
||||
(mul.w.vf friction-factor friction-factor frame-time)
|
||||
(sub.w.vf.w friction-factor vf0 friction-factor)
|
||||
(mul.w.vf.xyz velocity-scale-rate velocity-scale-rate friction-factor)
|
||||
(label no-friction-3d)
|
||||
;; Apply velocity, y-scale velocity, and fade with the full tick count. The xyz lanes of
|
||||
;; rotation-scale-rate are not used here; orientation advances from rotvel3d below.
|
||||
(mul.y.vf position-step velocity-scale-rate frame-time)
|
||||
(mul.y.vf rotation-scale-step rotation-scale-rate frame-time)
|
||||
(mul.y.vf color-step color-fade-rate frame-time)
|
||||
(add.vf sprite-pos-scale sprite-pos-scale position-step)
|
||||
(add.w.vf.w sprite-quat-scale sprite-quat-scale rotation-scale-step)
|
||||
(add.vf sprite-color sprite-color color-step)
|
||||
(max.x.vf sprite-color sprite-color vf0)
|
||||
(s.vf.field velocity-scale-rate cpuinfo (vel-sxvel))
|
||||
(s.vf.field sprite-pos-scale sprite-data (x-y-z-sx))
|
||||
(s.vf.field sprite-quat-scale sprite-data (qx-qy-qz-sy))
|
||||
(s.vf.field sprite-color sprite-data (r-g-b-a)))
|
||||
;; Rebuild w = sqrt(1-x^2-y^2-z^2) in the stack quaternion. The constant is loaded relative to
|
||||
;; this function because the hand-written function keeps 1.0 in its local literal pool.
|
||||
(asm-block update-quaternion
|
||||
(m v1 rotation)
|
||||
(m a0 sprite-data)
|
||||
(l.s f0 a0 16)
|
||||
(l.s f1 a0 20)
|
||||
(l.s f3 a0 24)
|
||||
(s.s f0 v1)
|
||||
(s.s f1 v1 4)
|
||||
(s.s f3 v1 8)
|
||||
(l.s f2 fp L157)
|
||||
(mul.s f3 f3 f3)
|
||||
(sub.s f2 f2 f3)
|
||||
(mul.s f1 f1 f1)
|
||||
(sub.s f1 f2 f1)
|
||||
(mul.s f0 f0 f0)
|
||||
(sub.s f0 f1 f0)
|
||||
(sqrt.s f0 f0)
|
||||
(s.s f0 v1 12)
|
||||
(m a0 f0)
|
||||
;; Compose the particle's rotational velocity once, or twice when a long frame crosses the
|
||||
;; ten-tick threshold.
|
||||
(m! v1 *sp-frame-time*)
|
||||
(l.s f0 v1)
|
||||
(m v1 f0)
|
||||
(and.i v1 v1 255)
|
||||
(add.i v1 v1 -10)
|
||||
(b.lt v1 r0 apply-rotation-step :delay (nop!))
|
||||
(m! t9 quaternion*!)
|
||||
(m a0 rotation)
|
||||
(m a1 rotation)
|
||||
(lea.field a2 cpuinfo (rotvel3d))
|
||||
(jalr ra t9 :delay (sll v0 ra 0))
|
||||
(label apply-rotation-step)
|
||||
(m! t9 quaternion*!)
|
||||
(m a0 rotation)
|
||||
(m a1 rotation)
|
||||
(lea.field a2 cpuinfo (rotvel3d))
|
||||
(jalr ra t9 :delay (sll v0 ra 0)))
|
||||
;; q and -q are equivalent. Store the sign whose omitted w is nonnegative so the renderer can
|
||||
;; reconstruct the same convention from xyz alone; preserve y-scale in the untouched w lane.
|
||||
(asm-block store-compact-quaternion
|
||||
(m a0 sprite-data)
|
||||
(m v1 rotation)
|
||||
(l.s f0 v1 12)
|
||||
(m f1 r0)
|
||||
(c.lt.s f0 f1)
|
||||
(b.fpf store-positive-quaternion :delay (nop!))
|
||||
(l.vf stored-quat-scale a0 16)
|
||||
(l.vf full-quaternion v1)
|
||||
(sub.vf.xyz stored-quat-scale vf0 full-quaternion)
|
||||
(s.vf stored-quat-scale a0 16)
|
||||
(m a0 stored-quat-scale)
|
||||
(b test-death-flags-3d :delay (nop!))
|
||||
(label store-positive-quaternion)
|
||||
(l.vf stored-quat-scale a0 16)
|
||||
(l.vf full-quaternion v1)
|
||||
(add.vf.xyz stored-quat-scale vf0 full-quaternion)
|
||||
(s.vf stored-quat-scale a0 16)
|
||||
(m a0 stored-quat-scale))
|
||||
;; Packed extracts test RGB together, then alpha, then x- and y-scale. These correspond to
|
||||
;; die-when-color-0, die-when-faded, and die-when-scale-0.
|
||||
(asm-block death-tests-3d
|
||||
(label test-death-flags-3d)
|
||||
(m v1 sprite-color)
|
||||
(l.w.field a0 cpuinfo (flags))
|
||||
(and.i a1 a0 2)
|
||||
(b.z a1 test-fade-death-3d :delay (and.i a1 a0 4))
|
||||
(b.nz v1 test-fade-death-3d :delay (pextuw a2 v1 r0))
|
||||
(b.z a2 free-particle-3d :delay (nop!))
|
||||
(label test-fade-death-3d)
|
||||
(b.z a1 test-scale-death-3d :delay (and.i a0 a0 1))
|
||||
(pcpyud v1 v1 r0)
|
||||
(pexew v1 v1)
|
||||
(b.le v1 r0 free-particle-3d :delay (nop!))
|
||||
(label test-scale-death-3d)
|
||||
(b.z a0 next-particle-3d :delay (nop!))
|
||||
(m v1 sprite-pos-scale)
|
||||
(pcpyud v1 v1 r0)
|
||||
(pexew v1 v1)
|
||||
(b.lt v1 r0 free-particle-3d :delay (m v1 sprite-quat-scale))
|
||||
(pcpyud v1 v1 r0)
|
||||
(pexew v1 v1)
|
||||
(b.ge v1 r0 next-particle-3d :delay (nop!))
|
||||
;; Return dead particles to the bitmap-managed pool.
|
||||
(label free-particle-3d)
|
||||
(m! t9 sp-free-particle)
|
||||
(m a0 sys)
|
||||
(m a1 slot)
|
||||
(m a2 cpuinfo)
|
||||
(m a3 sprite-data)
|
||||
(jalr ra t9 :delay (sll v0 ra 0)))
|
||||
;; Advance every parallel cursor even when this slot was invalid.
|
||||
(asm-block advance-and-return-3d
|
||||
(label next-particle-3d)
|
||||
(add.i remaining remaining -1)
|
||||
(add.i cpuinfo cpuinfo 144)
|
||||
(add.i sprite-data sprite-data 48)
|
||||
(b.nz remaining particle-loop-3d :delay (add.i slot slot 1))
|
||||
;; Return the first slot after the processed block.
|
||||
(m v0 slot)
|
||||
(l.d ra sp)
|
||||
(l.d fp sp 8)
|
||||
(l.q sys sp 144)
|
||||
(l.q cpuinfo sp 128)
|
||||
(l.q sprite-data sp 112)
|
||||
(l.q remaining sp 96)
|
||||
(l.q paused? sp 80)
|
||||
(l.q rotation sp 64)
|
||||
(l.q slot sp 48)
|
||||
(jr ra :delay (add.i sp sp 160))
|
||||
(nop! :count 3)))))
|
||||
"Update a scratchpad block of oriented 3D particles using the original simulation rules."
|
||||
(let ((cpuinfos (the-as (inline-array sparticle-cpuinfo) scratchpad-cpuinfo))
|
||||
(sprites (the-as (inline-array sprite-vec-data-3d) scratchpad-sprite-data))
|
||||
(ticks (logand (the-as int (-> *sp-frame-time* x)) 255))
|
||||
(tick-scale (-> *sp-frame-time* y))
|
||||
(frame-scale (-> *sp-frame-time* z)))
|
||||
(dotimes (i count)
|
||||
(let ((cpuinfo (-> cpuinfos i))
|
||||
(sprite (-> sprites i))
|
||||
(slot (+ start-slot i)))
|
||||
(when (-> cpuinfo valid)
|
||||
(cond
|
||||
((and paused?
|
||||
(not (logtest? (-> cpuinfo flags) (sp-cpuinfo-flag run-while-paused))))
|
||||
(cond
|
||||
((= (-> cpuinfo timer) 0)
|
||||
(sp-free-particle sys slot cpuinfo (the-as sprite-vec-data-2d sprite)))
|
||||
((logtest? (-> cpuinfo flags) (sp-cpuinfo-flag just-launched))
|
||||
(logclear! (-> cpuinfo flags) (sp-cpuinfo-flag just-launched))
|
||||
(set! (-> sprite a) (-> cpuinfo cache-alpha)))))
|
||||
((= (-> cpuinfo timer) 0)
|
||||
(sp-free-particle sys slot cpuinfo (the-as sprite-vec-data-2d sprite)))
|
||||
(else
|
||||
(when (!= (-> cpuinfo timer) -1)
|
||||
(set! (-> cpuinfo timer) (max 0 (- (-> cpuinfo timer) ticks))))
|
||||
|
||||
(#when PC_PORT
|
||||
(def-mips2c sp-process-block-3d (function sparticle-system int int int int symbol none)))
|
||||
(when (logtest? (-> cpuinfo flags) (sp-cpuinfo-flag just-launched))
|
||||
(logclear! (-> cpuinfo flags) (sp-cpuinfo-flag just-launched))
|
||||
(set! (-> sprite a) (-> cpuinfo cache-alpha)))
|
||||
|
||||
(when (nonzero? (the-as uint (-> cpuinfo func)))
|
||||
((the-as (function sparticle-system
|
||||
sparticle-cpuinfo
|
||||
sprite-vec-data-3d
|
||||
none)
|
||||
(-> cpuinfo func))
|
||||
sys
|
||||
cpuinfo
|
||||
sprite))
|
||||
|
||||
;; Unlike the 2D path, the 3D original waits until next-time is strictly negative.
|
||||
(when (nonzero? (the-as uint (-> cpuinfo next-launcher)))
|
||||
(let ((next-time (- (the-as int (-> cpuinfo next-time)) ticks)))
|
||||
(set! (-> cpuinfo next-time) (the-as uint next-time))
|
||||
(when (< next-time 0)
|
||||
(sp-relaunch-particle-3d sys
|
||||
(the-as sparticle-launcher (-> cpuinfo next-launcher))
|
||||
cpuinfo
|
||||
sprite))))
|
||||
|
||||
(+! (-> cpuinfo vel-sxvel x) (* (-> cpuinfo acc x) frame-scale))
|
||||
(+! (-> cpuinfo vel-sxvel y) (* (-> cpuinfo acc y) frame-scale))
|
||||
(+! (-> cpuinfo vel-sxvel z) (* (-> cpuinfo acc z) frame-scale))
|
||||
(when (nonzero? (the-as int (-> cpuinfo friction)))
|
||||
(let ((retained (- 1.0 (* (- 1.0 (-> cpuinfo friction)) frame-scale))))
|
||||
(*! (-> cpuinfo vel-sxvel x) retained)
|
||||
(*! (-> cpuinfo vel-sxvel y) retained)
|
||||
(*! (-> cpuinfo vel-sxvel z) retained)))
|
||||
|
||||
(+! (-> sprite x) (* (-> cpuinfo vel-sxvel x) tick-scale))
|
||||
(+! (-> sprite y) (* (-> cpuinfo vel-sxvel y) tick-scale))
|
||||
(+! (-> sprite z) (* (-> cpuinfo vel-sxvel z) tick-scale))
|
||||
(+! (-> sprite sx) (* (-> cpuinfo vel-sxvel w) tick-scale))
|
||||
(+! (-> sprite sy) (* (-> cpuinfo rot-syvel w) tick-scale))
|
||||
(+! (-> sprite r) (* (-> cpuinfo fade x) tick-scale))
|
||||
(+! (-> sprite g) (* (-> cpuinfo fade y) tick-scale))
|
||||
(+! (-> sprite b) (* (-> cpuinfo fade z) tick-scale))
|
||||
(+! (-> sprite a) (* (-> cpuinfo fade w) tick-scale))
|
||||
(set! (-> sprite r) (fmax (-> sprite r) 0.0))
|
||||
(set! (-> sprite g) (fmax (-> sprite g) 0.0))
|
||||
(set! (-> sprite b) (fmax (-> sprite b) 0.0))
|
||||
(set! (-> sprite a) (fmax (-> sprite a) 0.0))
|
||||
|
||||
;; Reconstruct the omitted quaternion w, apply one rotational step (two after a
|
||||
;; long frame), then store the equivalent sign with nonnegative omitted w.
|
||||
(let ((rotation (new 'stack-no-clear 'quaternion)))
|
||||
(set-vector! rotation
|
||||
(-> sprite qx)
|
||||
(-> sprite qy)
|
||||
(-> sprite qz)
|
||||
(sqrtf (- (- (- 1.0 (square (-> sprite qz)))
|
||||
(square (-> sprite qy)))
|
||||
(square (-> sprite qx)))))
|
||||
(when (>= ticks 10)
|
||||
(quaternion*! rotation rotation (-> cpuinfo rotvel3d)))
|
||||
(quaternion*! rotation rotation (-> cpuinfo rotvel3d))
|
||||
(cond
|
||||
((< (-> rotation w) 0.0)
|
||||
(set! (-> sprite qx) (- 0.0 (-> rotation x)))
|
||||
(set! (-> sprite qy) (- 0.0 (-> rotation y)))
|
||||
(set! (-> sprite qz) (- 0.0 (-> rotation z))))
|
||||
(else
|
||||
(set! (-> sprite qx) (-> rotation x))
|
||||
(set! (-> sprite qy) (-> rotation y))
|
||||
(set! (-> sprite qz) (-> rotation z)))))
|
||||
|
||||
(let ((dies?
|
||||
(or
|
||||
(and (logtest? (-> cpuinfo flags) (sp-cpuinfo-flag die-when-color-0))
|
||||
(= (-> sprite r) 0.0)
|
||||
(= (-> sprite g) 0.0)
|
||||
(= (-> sprite b) 0.0))
|
||||
(and (logtest? (-> cpuinfo flags) (sp-cpuinfo-flag die-when-faded))
|
||||
(<= (the-as int (-> sprite a)) 0))
|
||||
(and (logtest? (-> cpuinfo flags) (sp-cpuinfo-flag die-when-scale-0))
|
||||
(or (< (the-as int (-> sprite sx)) 0)
|
||||
(< (the-as int (-> sprite sy)) 0))))))
|
||||
(when dies?
|
||||
(sp-free-particle sys
|
||||
slot
|
||||
cpuinfo
|
||||
(the-as sprite-vec-data-2d sprite))))))))))
|
||||
(none))
|
||||
|
||||
(defun sp-copy-to-spr ((scratchpad-offset int) (memory pointer) (size int))
|
||||
"Copy size bytes from main memory to a scratchpad offset, rounded up to whole quadwords."
|
||||
@@ -900,59 +483,6 @@
|
||||
0
|
||||
(none))
|
||||
|
||||
(#unless PC_PORT
|
||||
(defun memcpy ((dst pointer) (src pointer) (size int))
|
||||
"Copy size rounded-up bytes from src to dst in 128-byte bursts followed by 16-byte quadwords."
|
||||
(declare (asm-func none))
|
||||
;; Particle scratchpad records are all quadword-padded, so this copier deliberately rounds size
|
||||
;; upward. Large transfers use eight quadword loads followed by eight stores per iteration.
|
||||
(rlet ((dst :reg a0)
|
||||
(src :reg a1)
|
||||
(size :reg a2)
|
||||
(quadwords :reg v1)
|
||||
(data0 :reg a2 :type i128)
|
||||
(data1 :reg a3 :type i128)
|
||||
(data2 :reg t0 :type i128)
|
||||
(data3 :reg t1 :type i128)
|
||||
(data4 :reg t2 :type i128)
|
||||
(data5 :reg t3 :type i128)
|
||||
(data6 :reg t4 :type i128)
|
||||
(data7 :reg t5 :type i128))
|
||||
(add.i quadwords size 15)
|
||||
(sra quadwords quadwords 4)
|
||||
(add.i quadwords quadwords -8)
|
||||
(b.lt quadwords r0 finish-bursts :delay (at-label copy-burst (l.q data0 src)))
|
||||
(l.q data1 src 16)
|
||||
(l.q data2 src 32)
|
||||
(l.q data3 src 48)
|
||||
(l.q data4 src 64)
|
||||
(l.q data5 src 80)
|
||||
(l.q data6 src 96)
|
||||
(l.q data7 src 112)
|
||||
(s.q data0 dst)
|
||||
(s.q data1 dst 16)
|
||||
(s.q data2 dst 32)
|
||||
(s.q data3 dst 48)
|
||||
(s.q data4 dst 64)
|
||||
(s.q data5 dst 80)
|
||||
(s.q data6 dst 96)
|
||||
(s.q data7 dst 112)
|
||||
(add.i src src 128)
|
||||
(add.i quadwords quadwords -8)
|
||||
(b.ge quadwords r0 copy-burst :delay (add.i dst dst 128))
|
||||
(label finish-bursts)
|
||||
(add.i quadwords quadwords 8)
|
||||
(b.z quadwords done-copying :delay (at-label copy-quadword (l.q data0 src)))
|
||||
(add.i src src 16)
|
||||
(s.q data0 dst)
|
||||
(add.i quadwords quadwords -1)
|
||||
(nop!)
|
||||
(b.nz quadwords copy-quadword :delay (add.i dst dst 16))
|
||||
(label done-copying)
|
||||
(m v0 r0)
|
||||
(jr ra :delay (add sp sp r0))
|
||||
(nop! :count 3))))
|
||||
|
||||
(defun sp-process-block ((sys sparticle-system) (start-slot int) (sprite-array-data sprite-array-2d) (count int))
|
||||
"Copy count parallel particle records beginning at start-slot into scratchpad, update them, and
|
||||
copy the mutable CPU and sprite records back. Shader records are input-only."
|
||||
@@ -976,8 +506,10 @@
|
||||
(set! scratchpad-cpuinfo (+ (the int (scratchpad-start)) cpu-offset))
|
||||
(set! scratchpad-sprite-data (+ (the int (scratchpad-start)) sprite-offset))
|
||||
(cond
|
||||
((-> sys is-3d) (sp-process-block-3d sys scratchpad-cpuinfo scratchpad-sprite-data start-slot count (paused?)))
|
||||
(else (sp-process-block-2d sys scratchpad-cpuinfo scratchpad-sprite-data start-slot count (paused?))))
|
||||
((-> sys is-3d)
|
||||
(sp-process-block-3d sys scratchpad-cpuinfo scratchpad-sprite-data start-slot count (paused?)))
|
||||
(else
|
||||
(sp-process-block-2d sys scratchpad-cpuinfo scratchpad-sprite-data start-slot count (paused?))))
|
||||
(sp-copy-from-spr cpu-offset (the-as pointer (-> sys cpuinfo-table start-slot)) cpu-size)
|
||||
(sp-copy-from-spr sprite-offset (&+ (-> sys vecdata-table) (* 48 start-slot)) sprite-size)))
|
||||
0
|
||||
@@ -1150,11 +682,11 @@
|
||||
(defun process-particles ()
|
||||
"Update particle timing, all 2D, HUD, and 3D particle groups, profiling bars, and deferred
|
||||
launches for one display frame."
|
||||
(local-vars (end-cycles int) (start-cycles int))
|
||||
(local-vars (start-ticks uint))
|
||||
(when *particles-flag*
|
||||
0
|
||||
0
|
||||
;;(.mfc0 start-cycles Count)
|
||||
(set! start-ticks (get-cpu-clock))
|
||||
(set! *sp-launcher-lock* #t)
|
||||
;; start a profile frame.
|
||||
(if *debug-segment*
|
||||
@@ -1181,15 +713,17 @@
|
||||
(set! *sp-launcher-lock* #f)
|
||||
;; launch queued particles
|
||||
(sp-clear-queue)
|
||||
;;(.mfc0 end-cycles Count)
|
||||
(let ((elapsed-cycles (- end-cycles start-cycles)))
|
||||
(let ((elapsed-usec
|
||||
(* 1000000.0 (cpu-ticks-to-seconds (- (get-cpu-clock) start-ticks)))))
|
||||
(if *display-sprite-info*
|
||||
(format *stdcon*
|
||||
"Particle time = ~D cycles for ~D 2D [~D warp] and ~D HUD and ~D 3D~%"
|
||||
elapsed-cycles
|
||||
"Particle time = ~F us for ~D 2D [~D warp] and ~D HUD and ~D 3D~%"
|
||||
elapsed-usec
|
||||
(-> *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)))))
|
||||
(#when PARTICLE_ADGIF_CACHE_STATS
|
||||
(particle-adgif-cache-stats-frame))
|
||||
0
|
||||
(none))
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
|
||||
(defmacro square (x)
|
||||
"Multiply x by itself."
|
||||
`(* ,x ,x))
|
||||
(with-gensyms (value)
|
||||
`(let ((,value ,x))
|
||||
(* ,value ,value))))
|
||||
|
||||
;; DECOMP BEGINS
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
;; DECOMP BEGINS
|
||||
|
||||
(defmethod eval-position! ((this trajectory) (elapsed-time float) (result vector))
|
||||
"Evaluate position after elapsed-time game-time ticks into result. Vertical motion includes
|
||||
one half gravity times time squared; evaluation is not clamped to the solved flight duration."
|
||||
"Evaluate position after elapsed-time game-time ticks into result."
|
||||
(vector-copy! result (-> this initial-position))
|
||||
(+! (-> result x) (* elapsed-time (-> this initial-velocity x)))
|
||||
(+! (-> result y) (* elapsed-time (-> this initial-velocity y)))
|
||||
@@ -18,16 +17,14 @@
|
||||
result)
|
||||
|
||||
(defmethod eval-velocity! ((this trajectory) (elapsed-time float) (result vector))
|
||||
"Evaluate velocity after elapsed-time game-time ticks into result. Only the vertical
|
||||
component changes under gravity."
|
||||
"Evaluate velocity after elapsed-time game-time ticks into result."
|
||||
(vector-copy! result (-> this initial-velocity))
|
||||
(+! (-> result y) (* elapsed-time (-> this gravity)))
|
||||
result)
|
||||
|
||||
(defmethod setup-from-to-duration! ((this trajectory) (start vector) (destination vector) (duration float) (gravity float))
|
||||
"Solve the initial velocity that moves from start to destination in duration game-time ticks
|
||||
under constant vertical gravity. Store the supplied duration; callers must provide a positive,
|
||||
nonzero duration."
|
||||
under constant vertical gravity."
|
||||
(vector-copy! (-> this initial-position) start)
|
||||
(set! (-> this gravity) gravity)
|
||||
(set! (-> this time) duration)
|
||||
@@ -41,7 +38,7 @@
|
||||
|
||||
(defmethod setup-from-to-xz-vel! ((this trajectory) (start vector) (destination vector) (xz-speed float) (gravity float))
|
||||
"Solve a trajectory from start to destination at the requested nonzero horizontal speed
|
||||
under constant vertical gravity. Horizontal distance divided by xz-speed determines the duration."
|
||||
under constant vertical gravity."
|
||||
(let ((duration (/ (vector-vector-xz-distance destination start) xz-speed)))
|
||||
(setup-from-to-duration! this start destination duration gravity))
|
||||
0
|
||||
@@ -74,8 +71,7 @@
|
||||
(none))
|
||||
|
||||
(defmethod debug-draw! ((this trajectory))
|
||||
"Draw the solved flight as ten translucent red line segments from time zero through the
|
||||
stored duration, without depth testing."
|
||||
"Draw the solved flight as ten translucent red line segments."
|
||||
(let ((previous-position (new 'stack-no-clear 'vector))
|
||||
(position (new 'stack-no-clear 'vector))
|
||||
(num-segments 10))
|
||||
|
||||
@@ -854,7 +854,7 @@
|
||||
(go flutflutegg-physics)))
|
||||
:trans
|
||||
(behavior ()
|
||||
(let* ((target-offset (ambient-control-method-10 (-> self ambient) (new 'stack-no-clear 'vector) (seconds 3) 368640.0 self))
|
||||
(let* ((target-offset (ambient-chatter-ready? (-> self ambient) (new 'stack-no-clear 'vector) (seconds 3) 368640.0 self))
|
||||
(random-mantissa (/ (the-as int (rand-uint31-gen *random-generator*)) 256))
|
||||
(random-bits (the-as number (logior #x3f800000 random-mantissa)))
|
||||
(random-choice (+ -1.0 (the-as float random-bits))))
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
(defmethod try-play-ambient-chatter ((this bird-lady))
|
||||
"When the target is within thirty metres and the thirty-second cooldown has elapsed, choose one
|
||||
of three Bird Lady idle lines with approximately equal probability."
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((choice (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.66 choice) (play-ambient (-> this ambient) "BIR-LO02" #f (-> this root trans)))
|
||||
|
||||
@@ -607,7 +607,7 @@
|
||||
"When the target is within thirty metres and the thirty-second cooldown has elapsed, choose
|
||||
one of nine Mayor idle lines with equal probability. After the Lurker-machine reminder is
|
||||
complete, six task-specific choices become silence while the three generic lines remain."
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((choice (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.8888889 choice)
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
(defmethod try-play-ambient-chatter ((this sculptor))
|
||||
"When the target is within thirty metres and the thirty-second cooldown has elapsed, choose
|
||||
one of seven Sculptor idle lines with approximately equal probability."
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((choice (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.85714287 choice) (play-ambient (-> this ambient) "SCU-LO01" #f (-> this root trans)))
|
||||
|
||||
@@ -445,7 +445,7 @@ Sages."
|
||||
"When the beam is off, try a thirty-second, thirty-metre ambient gate and choose one of three Red
|
||||
Sage lines with equal probability."
|
||||
(if (-> this beam-on) (return #f))
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((random-choice (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.6666667 random-choice) (play-ambient (-> this ambient) "RED-AM01" #f (-> this root trans)))
|
||||
@@ -538,7 +538,7 @@ Sages."
|
||||
"When the beam is off, try a thirty-second, thirty-metre ambient gate and choose one of three
|
||||
Blue Sage lines with equal probability."
|
||||
(if (-> this beam-on) (return #f))
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((random-choice (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.6666667 random-choice) (play-ambient (-> this ambient) "BLU-AM01" #f (-> this root trans)))
|
||||
@@ -622,7 +622,7 @@ Sages."
|
||||
"When the beam is off, try a thirty-second, thirty-metre ambient gate and choose one of three
|
||||
Yellow Sage lines with equal probability."
|
||||
(if (-> this beam-on) (return #f))
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((random-choice (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.6666667 random-choice) (play-ambient (-> this ambient) "YEL-AM01" #f (-> this root trans)))
|
||||
|
||||
@@ -1719,7 +1719,7 @@
|
||||
(when (not (or (ja-group? robotboss-blue-roar-ja) (ja-group? robotboss-blue-last-hit-ja))))
|
||||
(robotboss-blue-beam 8 #t)
|
||||
(robotboss-blue-beam 9 #f)
|
||||
(when (ambient-control-method-10 (-> self ambient) (new 'stack-no-clear 'vector) (seconds 10) (the-as float 327680.0) self)
|
||||
(when (ambient-chatter-ready? (-> self ambient) (new 'stack-no-clear 'vector) (seconds 10) (the-as float 327680.0) self)
|
||||
(let ((f0-2 (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.8 f0-2) (play-ambient (-> self ambient) "GOL-AM02" #t (the-as vector #f)))
|
||||
@@ -1977,7 +1977,7 @@
|
||||
(set! (-> this blue-smoke) #f)
|
||||
(set! (-> this red-smoke) #f)
|
||||
(set! (-> this yellow-smoke) #f)
|
||||
(ambient-control-method-9 (-> this ambient))
|
||||
(reset-ambient-cooldown! (-> this ambient))
|
||||
(set! (-> this yellow-gun) (new 'process 'joint-mod (joint-mod-handler-mode flex-blend) this 16))
|
||||
(set-vector! (-> this yellow-gun twist-max) 8192.0 8192.0 0.0 1.0)
|
||||
(set! (-> this yellow-gun up) (the-as uint 1))
|
||||
|
||||
@@ -1346,13 +1346,13 @@
|
||||
"Play fishing-specific reminder or ambient speech when its distance and cooldown permit."
|
||||
(cond
|
||||
((closed? (-> this tasks) (game-task jungle-fishgame) (task-status need-reminder))
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 1) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 1) 122880.0 this)
|
||||
(let ((f0-2 (rand-float-gen)))
|
||||
(if (< 0.5 f0-2)
|
||||
(play-ambient (-> this ambient) "FIS-LO03" #f (-> this root trans))
|
||||
(play-ambient (-> this ambient) "FIS-LO05" #f (-> this root trans))))))
|
||||
(else
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((f0-5 (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.875 f0-5) (play-ambient (-> this ambient) "FIS-LO01" #f (-> this root trans)))
|
||||
|
||||
@@ -678,7 +678,7 @@
|
||||
|
||||
(defmethod try-play-ambient-chatter ((this billy))
|
||||
"Occasionally play a random Billy ambient line while he is idle."
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((rng (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.9411765 rng) (play-ambient (-> this ambient) "BIL-AM01" #f (-> this root trans)))
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
(defmethod try-play-ambient-chatter ((this assistant))
|
||||
"When the target is within thirty metres, the thirty-second cooldown has elapsed, and the target
|
||||
is not more than four metres below her, choose one of five workshop lines."
|
||||
(let ((target-offset (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)))
|
||||
(let ((target-offset (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)))
|
||||
(when target-offset
|
||||
(let ((choice (rand-float-gen)))
|
||||
(cond
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
(defmethod try-play-ambient-chatter ((this explorer))
|
||||
"When the target is within thirty metres and the thirty-second cooldown has elapsed, choose
|
||||
one of seven Explorer lines; two task-specific lines are silent after their reminder closes."
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((choice (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.85714287 choice) (play-ambient (-> this ambient) "EXP-AM05" #f (-> this root trans)))
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
(defmethod try-play-ambient-chatter ((this farmer))
|
||||
"When the target is within thirty metres and the thirty-second cooldown has elapsed, choose
|
||||
one of four Farmer idle lines or remain silent."
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((choice (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.8333333 choice) (play-ambient (-> this ambient) "FAR-LO1A" #f (-> this root trans)))
|
||||
|
||||
@@ -343,7 +343,7 @@
|
||||
(defmethod try-play-ambient-chatter ((this sage))
|
||||
"When the target is within thirty metres, the thirty-second cooldown has elapsed, and the target
|
||||
is not more than three metres above him, choose one of five hut lines."
|
||||
(let ((target-offset (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)))
|
||||
(let ((target-offset (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)))
|
||||
(when target-offset
|
||||
(let ((choice (rand-float-gen)))
|
||||
(cond
|
||||
|
||||
@@ -351,7 +351,7 @@
|
||||
|
||||
(defmethod try-play-ambient-chatter ((this assistant-bluehut))
|
||||
"After the ambient cooldown and distance check pass, choose one of two welding lines."
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((roll (rand-float-gen)))
|
||||
(cond
|
||||
((= (get-task-status (game-task village2-levitator)) (task-status invalid)) #f)
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
(defmethod try-play-ambient-chatter ((this gambler))
|
||||
"After the ambient cooldown and distance checks pass, choose one of thirteen Gambler lines;
|
||||
suppress GAM-AM10 after the ogre-boss reminder is complete."
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 61440.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 61440.0 this)
|
||||
(let ((roll (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.9230769 roll) (play-ambient (-> this ambient) "GAM-AM01" #f (-> this root trans)))
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
(defmethod try-play-ambient-chatter ((this geologist))
|
||||
"After the ambient cooldown and distance check pass, choose a Geologist idle line, subject
|
||||
to the current mole and money task progress."
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((chatter-roll (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.8888889 chatter-roll) (play-ambient (-> this ambient) "GEO-AM01" #f (-> this root trans)))
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
|
||||
(defmethod try-play-ambient-chatter ((this sage-bluehut))
|
||||
"After the ambient cooldown and distance check pass, choose one of five Blue Sage idle lines."
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((roll (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.8 roll) (play-ambient (-> this ambient) "SAGELP20" #f (-> this root trans)))
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
(defmethod try-play-ambient-chatter ((this warrior))
|
||||
"After the two-second ambient cooldown and distance check pass, choose one of three Warrior
|
||||
idle lines."
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 2) 61440.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 2) 61440.0 this)
|
||||
(let ((roll (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.66 roll) (play-ambient (-> this ambient) "WAR-LO1A" #f (-> this root trans)))
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
(defmethod try-play-ambient-chatter ((this assistant-villagec))
|
||||
"Try to play one of the assistant's ambient voice lines after the chatter cooldown."
|
||||
(local-vars (lavatube-status task-status))
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((choice (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.85714287 choice) (play-ambient (-> this ambient) "ASSTLP31" #f (-> this root trans)))
|
||||
|
||||
@@ -353,7 +353,7 @@
|
||||
|
||||
(defmethod try-play-ambient-chatter ((this minershort))
|
||||
"Try to play one of the miners' ambient voice lines after the chatter cooldown."
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((choice (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.9655172 choice) (play-ambient (-> this ambient) "MIN-LO01" #f (-> this root trans)))
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
|
||||
(defmethod try-play-ambient-chatter ((this sage-villagec))
|
||||
"Try to play a task-appropriate Samos ambient voice line after the chatter cooldown."
|
||||
(when (ambient-control-method-10 (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(when (ambient-chatter-ready? (-> this ambient) (new 'stack-no-clear 'vector) (seconds 30) 122880.0 this)
|
||||
(let ((choice (rand-float-gen)))
|
||||
(cond
|
||||
((< 0.875 choice) (play-ambient (-> this ambient) "SAGELP31" #f (-> this root trans)))
|
||||
|
||||
Reference in New Issue
Block a user