diff --git a/decompiler/config/jak1/all-types.gc b/decompiler/config/jak1/all-types.gc index ee8230cfb0..abb89fe0c9 100644 --- a/decompiler/config/jak1/all-types.gc +++ b/decompiler/config/jak1/all-types.gc @@ -16771,7 +16771,7 @@ the function that applies its ambient effect." (deftype sprite-array-2d (basic) ((num-sprites int32 2 :offset-assert 4) (num-valid int32 2 :offset-assert 12) - (vec-data pointer :offset-assert 20) + (vec-data (inline-array sprite-vec-data-2d) :offset-assert 20) (adgif-data (inline-array adgif-shader) :offset-assert 24) (pad uint128 4 :offset-assert 32) (data uint128 1 :offset-assert 96) diff --git a/goal_src/jak1/engine/gfx/foreground/eye-h.gc b/goal_src/jak1/engine/gfx/foreground/eye-h.gc index be11285757..b775bce6fa 100644 --- a/goal_src/jak1/engine/gfx/foreground/eye-h.gc +++ b/goal_src/jak1/engine/gfx/foreground/eye-h.gc @@ -2,17 +2,18 @@ (in-package goal) (bundles "ENGINE.CGO" "GAME.CGO") (require "kernel/gkernel-h.gc") + (define-extern get-eye-block (function int int int)) - (define-extern merc-eye-anim (function process-drawable none)) - (define-extern update-eyes (function none)) +;; The eye renderer generates eye textures with an aniamted eyelid, iris, and pupil. +;; It consumes animation data and renders the eye to a texture later used by merc/generic. + ;; DECOMP BEGINS -;; One eye's animation state, packed into two vectors so consecutive keyframes can be interpolated -;; with two vector lerps. x and y steer the iris and pupil, lid controls eyelid closure, and the -;; second vector holds the three independent sprite scales. +;; Everything that can be animated/adjusted on a single eye as floats. +;; This can be driven from an animation or procedural blinking. (deftype eye (structure) ((data vector 2 :inline) (x float :overlay-at (-> data 0 x)) @@ -22,24 +23,24 @@ (pupil-scale float :offset 20) (lid-scale float :offset 24))) -;; One live eye-rendering slot. process identifies its drawable, level selects the level texture -;; bucket or the common eye bucket, shaders points at its iris material, and random-time and blink -;; drive the automatic blink envelope applied to both animated eyes. +;; Characters with animated eyes add an eye-control +;; note: background color of the eye is set to the 0,0 texel of the iris. (deftype eye-control (structure) - ((process handle) - (random-time uint16) - (level uint16) - (blink float) - (shaders (inline-array adgif-shader)) + ((process handle) ;; process owning the eye + (random-time uint16) ;; blink countdown + (level uint16) ;; level owning the character, controls when eye textures are uploaded + (blink float) ;; blink state (shared between the left and right eye) + (shaders (inline-array adgif-shader)) ;; shaders for the iris, pupil, lid. (left eye :inline) (right eye :inline))) ;; Fixed pool indexed by the eye-slot stored in merc geometry. +;; It's strange that eye slots weren't dynamically assigned. (deftype eye-control-array (basic) ((data eye-control 11 :inline))) -;; Reusable DMA/GIF packet headers for the eye sprites and adgif state, followed by the ten-sample -;; close-and-open blink envelope. +;; DMA templates, an optimization for generating the rendering DMA chain. +;; blink-table is the animation of closing, holding shut, and opening the eyelid. (deftype eye-work (structure) ((sprite-tmpl dma-gif-packet :inline) (sprite-tmpl2 dma-gif-packet :inline) diff --git a/goal_src/jak1/engine/gfx/shadow/shadow-h.gc b/goal_src/jak1/engine/gfx/shadow/shadow-h.gc index 98b2b02d36..14bfe3bf18 100644 --- a/goal_src/jak1/engine/gfx/shadow/shadow-h.gc +++ b/goal_src/jak1/engine/gfx/shadow/shadow-h.gc @@ -3,11 +3,13 @@ (bundles "ENGINE.CGO" "GAME.CGO") (require "kernel-defs.gc") +;; shadow-h is the "fake shadow" system. This draws a circular shadow on the ground. +;; It was later mostly replaced by shadow volumes based on simplified meshes. +;; A few things still use fake shadows, like projectiles. + ;; DECOMP BEGINS -;; One circular projected shadow in the sprite renderer's three-quadword input format. Position and -;; scale occupy the first quadword; qx/qy/qz encode the ground-normal rotation with quaternion w -;; omitted, and flags select the shadow blend state. +;; Fake shadow record: location, orientation, and size. (deftype fake-shadow (structure) ((px float) (py float) @@ -19,8 +21,9 @@ (flags int32)) :pack-me) -;; Double-buffered list of at most 32 shadows. Gameplay fills the active buffer while sprite drawing -;; consumes the previous one. +;; Double-buffered fake shadows. It doesn't seem like this is needed at all, +;; since sprite.gc ends up copying these into the DMA buffer directly, this may +;; be a leftover from an earlier rendering implementation. (deftype fake-shadow-buffer (basic) ((num-shadows int32) (data fake-shadow 32 :inline))) diff --git a/goal_src/jak1/engine/gfx/sprite/sparticle/sparticle-launcher-h.gc b/goal_src/jak1/engine/gfx/sprite/sparticle/sparticle-launcher-h.gc index ae4981621a..b61cc33ad0 100644 --- a/goal_src/jak1/engine/gfx/sprite/sparticle/sparticle-launcher-h.gc +++ b/goal_src/jak1/engine/gfx/sprite/sparticle/sparticle-launcher-h.gc @@ -3,31 +3,18 @@ (bundles "ENGINE.CGO" "GAME.CGO") (require "kernel/gcommon.gc") -;; The "sparticle" system is the particle system. -;; Features -;; - Support for 2D particles (autosave icon, progress menu graphics) -;; - Support for 3D particles (many of the effects) -;; - Uses the "sprite" renderer to draw particles +;; sparticle (likely sprite - particle) is the particle system. -;; The "sparticle-launcher" code is the framework for describing particle effects -;; The "sparticle" code is the system that runs particles -;; Note that neither of these link particles to the process system. See part-tracker for that. +;; the description of a single particle is a "sparticle-launcher", which is constant data. +;; The *part-id-table* stores references to each sparticle-launcher, indexed by ID -;; The highest-level class here is sparticle-launch-control. -;; Each instance of a particle effect must have one of these. -;; For example, there would be one of these per eco-vent. -;; These store some state (a sparticle-launch-state) and a reference to a sparticle-launch-group -;; Multiple launch-controls can refer to the same launch-group. +;; A "sparticle-launch-group" describes a group of sparticle-launchers, as a single effect. +;; For example, there might be a sparticle-launch-group for an explosion which has +;; both fireball and smoke sparticle-launchers. +;; The *part-group-id-table* stores references to all sparticle-launch-groups -;; A sparticle-launch-group is a description of a particle effect. -;; It can contain multiple types of particles. -;; The `*part-group-id-table*` array stores a reference to every launch-group, indexed by group id. -;; Each launch-group is just a list of sparticle-launchers, stored as an index -;; A launcher is a single particle effect. -;; The `*part-id-table*` has references to all particle effects. -;; It contains a list of "field-init-specs". When the particle effect starts, the system -;; iterates through this list and sets parameters about particles. +;; A particle effect is described in a group of field : value declarations. ;; There are five types of fields: ;; misc fields @@ -36,10 +23,9 @@ ;; launch fields ;; weird fields -;; The built-in parameters can be used for many simple effects, but sometimes it is not enough. -;; You can provide a callback function to update the particle's state if needed. +;; these are arranged based on how the engine consumes. -;; These are the user-settable state variables for each particle effect +;; List of all settable fields and grouping helpers (defenum sp-field-id :type uint16 (misc-fields-start 0) @@ -114,34 +100,48 @@ (spt-scalevel 66) (spt-end 67)) -;; How sp-init-fields! interprets an sp-field-init-spec when initializing a -;; field. Integer and floating-point fields store -;; initial + random-mult * (rand * random-range), where rand-vu returns [0, 1). +;; sp-flag controls how a field value is interpreted. +;; The int, float, float-int-rand options create a randomized number: +;; int: initial + truncate(rand * random-range) * random-mult +;; float: initial + rand * random-rangef * random-multf +;; float-int-rand: initial + truncate(rand * (random-range + 1)) * random-multf (defenum sp-flag :type uint16 - (int 0) ;; integer field; the random term is truncated to an integer - (float 1) ;; floating-point field - (float-int-rand 2) ;; float with an integer-quantized random term + (int 0) ;; end result is truncated to integer + (float 1) ;; everything is float + (float-int-rand 2) ;; float with an integer-quantized random offset (copy-from-other 3) ;; copy an earlier field at the negative offset in initial-value (object 4) ;; raw object or label pointer, such as a sound-spec or :data payload (symbol 5) ;; store the symbol's value - (launcher 6) ;; resolve initial-value as an index in *part-id-table* + (launcher 6) ;; treat initial-value as an index in *part-id-table* ) +;; options for an entire group +;; use-local-clock means the effect maintains its own clock. +;; this is used when effects have some explicit starting behavior, +;; for example an explosion might display fire at time 0 and smoke +;; at a later time. This is disabled for effects that need global +;; syncrhonization with other effects, like geysers. +;; always-draw disables view frustum culling for processing. +;; this is useful for particle effects that take a while to get +;; going, like a waterfall that drops particles from high up. +;; you woudln't want the waterfall to restart each time +;; the player looks away and back. +;; randomize-order "randomizes" the draw order of the particles +;; only used on the sentinel beach waterfall. (defenum sp-group-flag :bitfield #t :type uint16 (use-local-clock 0) (always-draw 1) (screen-space 2) - (unknown-bit-01 3) ;; beach-part + (randomize-order 3) ) ;; DECOMP BEGINS -;; This describes the initial value and some more info for a single field -;; Note that there are overlays here and some values only make sense in some -;; cases. +;; This describes the initial value and randomization parameter +;; for a field. (deftype sp-field-init-spec (structure) ((field sp-field-id) (flags sp-flag) @@ -157,6 +157,18 @@ (sym symbol :overlay-at initial-valuef) (sound sound-spec :overlay-at initial-valuef))) +;; Definition of a particle effect as a list of fields. +;; birthaccum and soundaccum are two accumulators for birth and sound triggers. +;; Each time (launch-particles is called, it adds `num` to `birthaccum`. If the value +;; is over 1.0, it triggers a launch. If the launch succeeds, it decrements by 1. +;; There is no automatic retry, the intent is that you call (launch-particles on +;; each frame, and the game launches `num` particles per 1/60 of a second on average +;; (note that on PAL it's still num particles per 1/60th of a second) +;; soundaccum accumulates `sound-spec.num` per launch, when it reaches 1 it triggers +;; a sound. +;; The soundaccum is always shared between all instances of a sparticle-launcher. +;; If a particles is launched in a group with a state, it uses a state-local birthaccum. +;; Otherwise, it uses this birthaccum. (deftype sparticle-launcher (basic) ((birthaccum float) (soundaccum float) @@ -179,7 +191,7 @@ ;; One launcher in a group, with its timing and visibility parameters. Timing ;; uses 300 Hz ticks. period is the cycle length, with zero meaning continuous ;; launch; length is the active window within a cycle; and offset changes the -;; instance's phase. fade-after stops emission beyond that camera distance, +;; instance's phase. fade-after decreases emission beyond that camera distance, ;; while falloff-to is the distance where the linearly scaled emission rate ;; reaches zero. hour-mask suppresses emission during selected in-game hours. (deftype sparticle-group-item (structure) @@ -217,6 +229,7 @@ (declare-type sparticle-cpuinfo structure) +;; state of a single launcher (deftype sparticle-launch-state (structure) ((group-item sparticle-group-item) (flags sp-launch-state-flags) @@ -233,6 +246,7 @@ (spec basic :overlay-at sprite) (id uint32 :overlay-at sprite3d))) +;; definition of a launch group (deftype sparticle-launch-group (basic) ((length int16) (duration uint16) diff --git a/goal_src/jak1/engine/gfx/sprite/sparticle/sparticle.gc b/goal_src/jak1/engine/gfx/sprite/sparticle/sparticle.gc index 0bb1868ab3..65d5e97f4f 100644 --- a/goal_src/jak1/engine/gfx/sprite/sparticle/sparticle.gc +++ b/goal_src/jak1/engine/gfx/sprite/sparticle/sparticle.gc @@ -91,10 +91,10 @@ ;; note: these constants must match the sizes of the sprite arrays. (define *sp-particle-system-2d* - (new 'global 'sparticle-system 1920 128 #f (-> *sprite-array-2d* vec-data) (-> *sprite-array-2d* adgif-data))) + (new 'global 'sparticle-system 1920 128 #f (the pointer (-> *sprite-array-2d* vec-data)) (-> *sprite-array-2d* adgif-data))) (define *sp-particle-system-3d* - (new 'global 'sparticle-system 256 0 #t (-> *sprite-array-3d* vec-data) (-> *sprite-array-3d* adgif-data))) + (new 'global 'sparticle-system 256 0 #t (the pointer (-> *sprite-array-3d* vec-data)) (-> *sprite-array-3d* adgif-data))) ;;;;;;;;;;;;;;;;;;;; ;; alloc and block diff --git a/goal_src/jak1/engine/gfx/sprite/sprite-h.gc b/goal_src/jak1/engine/gfx/sprite/sprite-h.gc index afc4c90f62..f42ca3fb8a 100644 --- a/goal_src/jak1/engine/gfx/sprite/sprite-h.gc +++ b/goal_src/jak1/engine/gfx/sprite/sprite-h.gc @@ -5,16 +5,24 @@ (#when PC_BIG_MEMORY (defconstant SPRITE_MAX_AMOUNT_MULT 12)) +;; sprite is the renderer for the particle system. +;; the vec-data types in this file are produced by the +;; particle system and then sent to the VU1 sprite renderer. + ;; DECOMP BEGINS +;; Rendering mode for sprites +;; - use camera for sprites that are placed in the world +;; - use screen for sprites that are placed in screen pixel coordinates (defenum sprite-matrix-mode :type int32 (camera 0) (screen 1)) -;; Three quadwords uploaded per 2D sprite. The first holds position and x scale, the second carries -;; mode-specific flags or warp turns together with matrix, rotation, and y scale, and the third is -;; floating-point color. +;; data uploaded to the VU1 sprite renderer per sprite. +;; contains position, scale, color, and flags +;; the "2d" sprites always face the camera and rotation +;; controls their rotation along the camera view direction. (deftype sprite-vec-data-2d (structure) ((x-y-z-sx vector :inline) (flag-rot-sy vector :inline) @@ -37,21 +45,24 @@ (data uint128 1 :overlay-at (-> x-y-z-sx quad)) (data64 uint64 6 :overlay-at (-> x-y-z-sx x)))) -;; Two fixed-capacity sprite groups. num-valid is the active prefix of each group. The allocation -;; stores three vector quadwords followed by five adgif quadwords for every sprite. +;; Array of data uploaded to VU1 for 2d sprites +;; vec-data holds one sprite-vec-data-2d per sprite +;; adgif-data holds one adgif-shader (texture/blending settings) per sprite. +;; There are two groups: camera and screen. (deftype sprite-array-2d (basic) ((num-sprites int32 2) (num-valid int32 2) - (vec-data pointer) + (vec-data (inline-array sprite-vec-data-2d)) (adgif-data (inline-array adgif-shader)) (pad uint128 4) (data uint128 1)) (:methods (new (symbol type int int) _type_))) -;; Three quadwords uploaded per 3D sprite. Quaternion xyz is negated when the input w is negative, -;; selecting the equivalent quaternion with positive w so the omitted component can be reconstructed -;; from sqrt(1 - x*x - y*y - z*z). The second quadword's w lane remains the y scale. +;; data uploaded to the VU1 sprite renderer per sprite +;; contains position, scale, color, flags, and orientation. +;; orientation is a quaternion. By convention, sprite quaternions +;; always have +w, so the w can be recovered from sqrt(1 - x^2 - y^2 - z^2) (deftype sprite-vec-data-3d (structure) ((x-y-z-sx vector :inline) (qx-qy-qz-sy vector :inline) @@ -73,11 +84,13 @@ (color rgbaf :inline :overlay-at (-> r-g-b-a x)) (data uint128 1 :overlay-at (-> x-y-z-sx quad)))) -;; 3D sprites use the same two-group storage contract; the normal global array leaves group 1 empty. +;; Array of data uploaded to VU1 for 3d sprites. +;; There are two groups, but only the first is used. +;; (doing screen-space 3d sprites was never needed) (deftype sprite-array-3d (basic) ((num-sprites int32 2) (num-valid int32 2) - (vec-data pointer) + (vec-data (inline-array sprite-vec-data-3d)) (adgif-data (inline-array adgif-shader)) (data uint128 1)) (:methods diff --git a/goal_src/jak1/engine/gfx/sprite/sprite.gc b/goal_src/jak1/engine/gfx/sprite/sprite.gc index 1b79e9ace9..0b5b6b7fdf 100644 --- a/goal_src/jak1/engine/gfx/sprite/sprite.gc +++ b/goal_src/jak1/engine/gfx/sprite/sprite.gc @@ -409,7 +409,7 @@ (set! (-> result num-valid 0) 0) (set! (-> result num-valid 1) 0) ;; internally, we put the vec-data and then the adgif-data - (set! (-> result vec-data) (-> result data)) + (set! (-> result vec-data) (the (inline-array sprite-vec-data-2d) (-> result data))) (set! (-> result adgif-data) (the-as (inline-array adgif-shader) (&-> result data vec-data-size))) result)) @@ -429,7 +429,7 @@ (set! (-> result num-sprites 1) group-1-size) (set! (-> result num-valid 0) 0) (set! (-> result num-valid 1) 0) - (set! (-> result vec-data) (-> result data)) + (set! (-> result vec-data) (the (inline-array sprite-vec-data-3d) (-> result data))) (set! (-> result adgif-data) (the-as (inline-array adgif-shader) (&-> result data vec-data-size))) result)) diff --git a/goal_src/jak1/levels/beach/beach-part.gc b/goal_src/jak1/levels/beach/beach-part.gc index 844dfe3d58..8bd7630e6f 100644 --- a/goal_src/jak1/levels/beach/beach-part.gc +++ b/goal_src/jak1/levels/beach/beach-part.gc @@ -250,7 +250,7 @@ (defpartgroup group-beach-waterfall :id 162 - :flags (always-draw unknown-bit-01) + :flags (always-draw randomize-order) :bounds (static-bspherem 0 55 0 55) :parts ((sp-item 677 :fade-after (meters 200) :falloff-to (meters 200))