From 08ce65fd9bafa56f0d0017a9670112acc1019bb7 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Mon, 20 Feb 2023 20:25:45 -0500 Subject: [PATCH] [jak2] add sprite glow renderer (#2232) Adds the "sprite glow" renderer, which is responsible for the glowing lights. --- common/math/Vector.h | 7 + decompiler/config/jak2/all-types.gc | 51 +- decompiler/config/jak2/hacks.jsonc | 2 +- decompiler/config/jak2/label_types.jsonc | 2 +- decompiler/config/jak2/stack_structures.jsonc | 2 +- decompiler/config/jak2/type_casts.jsonc | 16 + decompiler/util/data_decompile.cpp | 2 + docs/progress-notes/jak2/sprite_glow.md | 298 ++++++ game/CMakeLists.txt | 7 +- .../opengl_renderer/OpenGLRenderer.cpp | 4 +- game/graphics/opengl_renderer/Shader.cpp | 5 + game/graphics/opengl_renderer/Shader.h | 5 + .../opengl_renderer/shaders/glow_draw.frag | 16 + .../opengl_renderer/shaders/glow_draw.vert | 31 + .../opengl_renderer/shaders/glow_probe.frag | 10 + .../opengl_renderer/shaders/glow_probe.vert | 23 + .../shaders/glow_probe_downsample.frag | 13 + .../shaders/glow_probe_downsample.vert | 10 + .../shaders/glow_probe_read.frag | 14 + .../shaders/glow_probe_read.vert | 13 + .../shaders/glow_probe_read_debug.frag | 7 + .../shaders/glow_probe_read_debug.vert | 11 + .../opengl_renderer/sprite/GlowRenderer.cpp | 737 ++++++++++++++ .../opengl_renderer/sprite/GlowRenderer.h | 112 +++ .../opengl_renderer/{ => sprite}/Sprite3.cpp | 651 +------------ .../opengl_renderer/{ => sprite}/Sprite3.h | 8 +- .../sprite/Sprite3_Distort.cpp | 630 ++++++++++++ .../opengl_renderer/sprite/Sprite3_Glow.cpp | 228 +++++ .../{ => sprite}/SpriteRenderer.cpp | 0 .../{ => sprite}/SpriteRenderer.h | 2 +- .../{ => sprite}/sprite_common.h | 33 +- goal_src/jak2/engine/draw/drawable.gc | 2 +- goal_src/jak2/engine/gfx/hw/gs.gc | 1 + .../jak2/engine/gfx/sprite/simple-sprite-h.gc | 33 +- .../jak2/engine/gfx/sprite/sprite-glow.gc | 787 +++++++++++++++ goal_src/jak2/engine/gfx/sprite/sprite.gc | 7 +- .../levels/city/common/vehicle-effects.gc | 8 +- .../reference/jak2/engine/gfx/hw/gs_REF.gc | 12 +- .../jak2/engine/gfx/lightning_REF.gc | 4 +- .../engine/gfx/sprite/simple-sprite-h_REF.gc | 33 +- .../jak2/engine/gfx/sprite/sprite-glow_REF.gc | 917 ++++++++++++++++++ .../jak2/engine/gfx/sprite/sprite_REF.gc | 5 +- .../levels/city/common/vehicle-effects_REF.gc | 8 +- 43 files changed, 4043 insertions(+), 724 deletions(-) create mode 100644 docs/progress-notes/jak2/sprite_glow.md create mode 100644 game/graphics/opengl_renderer/shaders/glow_draw.frag create mode 100644 game/graphics/opengl_renderer/shaders/glow_draw.vert create mode 100644 game/graphics/opengl_renderer/shaders/glow_probe.frag create mode 100644 game/graphics/opengl_renderer/shaders/glow_probe.vert create mode 100644 game/graphics/opengl_renderer/shaders/glow_probe_downsample.frag create mode 100644 game/graphics/opengl_renderer/shaders/glow_probe_downsample.vert create mode 100644 game/graphics/opengl_renderer/shaders/glow_probe_read.frag create mode 100644 game/graphics/opengl_renderer/shaders/glow_probe_read.vert create mode 100644 game/graphics/opengl_renderer/shaders/glow_probe_read_debug.frag create mode 100644 game/graphics/opengl_renderer/shaders/glow_probe_read_debug.vert create mode 100644 game/graphics/opengl_renderer/sprite/GlowRenderer.cpp create mode 100644 game/graphics/opengl_renderer/sprite/GlowRenderer.h rename game/graphics/opengl_renderer/{ => sprite}/Sprite3.cpp (52%) rename game/graphics/opengl_renderer/{ => sprite}/Sprite3.h (95%) create mode 100644 game/graphics/opengl_renderer/sprite/Sprite3_Distort.cpp create mode 100644 game/graphics/opengl_renderer/sprite/Sprite3_Glow.cpp rename game/graphics/opengl_renderer/{ => sprite}/SpriteRenderer.cpp (100%) rename game/graphics/opengl_renderer/{ => sprite}/SpriteRenderer.h (98%) rename game/graphics/opengl_renderer/{ => sprite}/sprite_common.h (85%) create mode 100644 test/decompiler/reference/jak2/engine/gfx/sprite/sprite-glow_REF.gc diff --git a/common/math/Vector.h b/common/math/Vector.h index 203274f9b..81d767a0c 100644 --- a/common/math/Vector.h +++ b/common/math/Vector.h @@ -128,6 +128,13 @@ class Vector { return *this; } + Vector& operator+=(const T& other) { + for (int i = 0; i < Size; i++) { + m_data[i] += other; + } + return *this; + } + Vector elementwise_multiply(const Vector& other) const { Vector result; for (int i = 0; i < Size; i++) { diff --git a/decompiler/config/jak2/all-types.gc b/decompiler/config/jak2/all-types.gc index acff2be91..9b35cb3df 100644 --- a/decompiler/config/jak2/all-types.gc +++ b/decompiler/config/jak2/all-types.gc @@ -3662,6 +3662,7 @@ (signal 96) (finish 97) (label 98) + (hack 127) ) (defenum gs-reg64 @@ -4047,10 +4048,10 @@ ) (deftype gs-adcmd (structure) - ((word uint32 4 :offset-assert 0) - (quad uint128 :offset 0) - (data uint64 :offset 0) - (cmds gs-reg64 :offset 8) + ((word uint32 4 :offset-assert 0 :score -10) + (quad uint128 :offset 0 :score -10) + (data uint64 :offset 0 :score -10) + (cmds gs-reg64 :offset 8 :score 10) (cmd uint8 :offset 8) (x uint32 :offset 0) (y uint32 :offset 4) @@ -4356,10 +4357,10 @@ ) (deftype gs-packed-xyzw (vector) - ((ix int32 :offset 0) - (iy int32 :offset 4) - (iz int32 :offset 8) - (iw int32 :offset 12) + ((ix int32 :offset 0 :score 10) + (iy int32 :offset 4 :score 10) + (iz int32 :offset 8 :score 10) + (iw int32 :offset 12 :score 10) ) :method-count-assert 9 :size-assert #x10 @@ -20356,6 +20357,7 @@ (fade-b float :offset-assert 52) (tex-id texture-id :offset-assert 56) (dummy uint32 :offset-assert 60) + (quads vector 4 :inline :offset 0) ) :method-count-assert 10 :size-assert #x40 @@ -20368,14 +20370,14 @@ (deftype simple-sprite-system (structure) ((count int16 :offset-assert 0) (max-count int16 :offset-assert 2) - (data sprite-glow-data :offset-assert 4) + (data (inline-array sprite-glow-data) :offset-assert 4) ) :method-count-assert 12 :size-assert #x8 :flag-assert #xc00000008 (:methods - (add! (_type_ dma-buffer) none 9) - (simple-sprite-system-method-10 (_type_ dma-buffer) none 10) + (add! (_type_ sprite-glow-data) none 9) + (draw-all-sprites! (_type_ dma-buffer) none 10) (clear! (_type_) none 11) ) ) @@ -23852,14 +23854,14 @@ (deftype sprite-glow-template (structure) ((clear-init-giftag gs-gif-tag :inline :offset-assert 0) - (clear-init-adcmds vector4w 5 :inline :offset-assert 16) ;; gs-adcmd? + (clear-init-adcmds gs-adcmd 5 :inline :offset-assert 16) ;; gs-adcmd? (clear-draw-giftag gs-gif-tag :inline :offset-assert 96) (clear-draw-clr-0 gs-packed-rgba :inline :offset-assert 112) - (clear-draw-xyz-0 vector 2 :inline :offset-assert 128) + (clear-draw-xyz-0 gs-packed-xyzw 2 :inline :offset-assert 128) (clear-draw-clr-1 gs-packed-rgba :inline :offset-assert 160) (clear-draw-xyz-1 vector 2 :inline :offset-assert 176) (offscr-setup-giftag gs-gif-tag :inline :offset-assert 208) - (offscr-setup-adcmds vector4w 8 :inline :offset-assert 224) ;; gs-adcmd? + (offscr-setup-adcmds gs-adcmd 8 :inline :offset-assert 224) ;; gs-adcmd? (offscr-first-giftag gs-gif-tag :inline :offset-assert 352) (offscr-first-clr gs-packed-rgba :inline :offset-assert 368) (offscr-first-uv-0 gs-packed-uv :inline :offset-assert 384) @@ -23867,7 +23869,7 @@ (offscr-first-uv-1 gs-packed-uv :inline :offset-assert 416) (offscr-first-xyzw-1 gs-packed-xyzw :inline :offset-assert 432) (repeat-draw-giftag gs-gif-tag :inline :offset-assert 448) - (repeat-draw-adcmds vector4w 29 :inline :offset-assert 464) ;; gs-adcmd? + (repeat-draw-adcmds gs-adcmd 29 :inline :offset-assert 464) ;; gs-adcmd? (flare-alpha-giftag gs-gif-tag :inline :offset-assert 928) (flare-alpha-clr gs-packed-rgba :inline :offset-assert 944) (flare-alpha-uv gs-packed-uv :inline :offset-assert 960) @@ -23876,7 +23878,7 @@ (flare-alpha-xyzw-2 gs-packed-xyzw :inline :offset-assert 1008) (flare-alpha-xyzw-3 gs-packed-xyzw :inline :offset-assert 1024) (flare-init-giftag gs-gif-tag :inline :offset-assert 1040) - (flare-init-adcmds vector4w 8 :inline :offset-assert 1056) ;; gs-adcmd? + (flare-init-adcmds gs-adcmd 8 :inline :offset-assert 1056) ;; gs-adcmd? (flare-draw-giftag gs-gif-tag :inline :offset-assert 1184) (flare-draw-clr gs-packed-rgba :inline :offset-assert 1200) (flare-draw-stq-0 gs-packed-stq :inline :offset-assert 1216) @@ -23894,11 +23896,11 @@ ) (deftype sprite-glow-consts (structure) - ((camera matrix :inline :offset-assert 0) - (perspective matrix :inline :offset-assert 64) - (hvdf-offset vector :inline :offset-assert 128) - (hmge-scale vector :inline :offset-assert 144) - (consts vector :inline :offset-assert 160 :score -1) + ((camera matrix :inline :offset-assert 0) ;; 0 + (perspective matrix :inline :offset-assert 64) ;; 4 + (hvdf-offset vector :inline :offset-assert 128);; 8 + (hmge-scale vector :inline :offset-assert 144) ;; 9 + (consts vector :inline :offset-assert 160 :score -1) ;; 10 (pfog0 float :offset 160) (deg-to-rad float :offset 164) (min-scale float :offset 168) @@ -23935,6 +23937,7 @@ ((control-packet dma-packet :inline :offset-assert 0) (num-sprites uint32 :offset-assert 16) (dummys uint32 3 :offset-assert 20) + (num-sprites-quad uint128 :offset 16) (vecdata-packet dma-packet :inline :offset-assert 32) (vecdata sprite-glow-data :inline :offset-assert 48) (shader-packet dma-packet :inline :offset-assert 112) @@ -23950,9 +23953,11 @@ ((control-packet dma-packet :inline :offset-assert 0) (num-sprites uint32 :offset-assert 16) (dummys uint32 3 :offset-assert 20) + (num-sprites-quad uint128 :offset 16) (vecdata-packet dma-packet :inline :offset-assert 32) (vecdata sprite-glow-data :inline :offset-assert 48) (shader-packet dma-packet :inline :offset-assert 112) + (shader-packet-ptr pointer :offset 116) ;; pointer to adgif shader to upload. (mscal-packet dma-packet :inline :offset-assert 128) ) :method-count-assert 9 @@ -23966,9 +23971,9 @@ (define-extern sprite-glow-init-engine (function dma-buffer none)) (define-extern *sprite-glow-dma-packet-data* sprite-glow-dma-packet-data) (define-extern sprite-glow-add-sprite (function dma-buffer sprite-vec-data-2d float float float adgif-shader none)) -(define-extern sprite-glow-add-simple-sprite (function dma-buffer sprite-glow-dma-packet-data sprite-glow-data pointer)) +(define-extern sprite-glow-add-simple-sprite (function dma-buffer sprite-glow-dma-packet-data sprite-glow-data pointer none)) (define-extern sprite-glow-draw (function dma-buffer none)) -(define-extern add-shader-to-dma (function dma-buffer pointer)) +(define-extern add-shader-to-dma (function dma-buffer adgif-shader)) (define-extern *simple-sprite-system* simple-sprite-system) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/decompiler/config/jak2/hacks.jsonc b/decompiler/config/jak2/hacks.jsonc index 17fef9f0b..be7f599aa 100644 --- a/decompiler/config/jak2/hacks.jsonc +++ b/decompiler/config/jak2/hacks.jsonc @@ -495,7 +495,7 @@ "(method 181 gator)": [ 2, 3, 7, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 25, 26, 30 ], - "(method 10 simple-sprite-system)": [0, 1, 3, 4, 5, 6, 9, 11], + "(method 10 simple-sprite-system)": [0], "target-pilot-post": [0, 2, 4, 13, 16, 22, 27, 41], "(anon-function 0 ruins-obs)": [ 0, 5, 7, 12, 13, 15, 17, 23, 24, 33, 42, 45, 46, 53, 58 diff --git a/decompiler/config/jak2/label_types.jsonc b/decompiler/config/jak2/label_types.jsonc index b5e23f488..ee5bd3ae8 100644 --- a/decompiler/config/jak2/label_types.jsonc +++ b/decompiler/config/jak2/label_types.jsonc @@ -66,7 +66,7 @@ "sprite-distort": [["L27", "vu-function"]], "sprite-glow": [ ["L29", "vu-function"], - ["L27", "vector"] + ["L27", "dma-packet"] ], "debug": [ ["L250", "(inline-array vector)", 32], diff --git a/decompiler/config/jak2/stack_structures.jsonc b/decompiler/config/jak2/stack_structures.jsonc index 12727d09f..a869bcc17 100644 --- a/decompiler/config/jak2/stack_structures.jsonc +++ b/decompiler/config/jak2/stack_structures.jsonc @@ -1591,7 +1591,7 @@ [48, "vector4w"] ], "(method 22 traffic-tracker)": [[16, "mystery-traffic-object-spawn-params"]], - "(method 10 simple-sprite-system)": [[16, ["array", "texture-id", 4]]], + "(method 10 simple-sprite-system)": [[16, ["array", "texture-id", 128]]], "(anon-function 3 ruins-obs)": [[16, "event-message-block"]], "(event unbroken ruins-breakable-wall)": [[16, "attack-info"]], "(method 21 spider-eyes)": [[32, "vector"]], diff --git a/decompiler/config/jak2/type_casts.jsonc b/decompiler/config/jak2/type_casts.jsonc index 3ff340b59..59ce7c7ae 100644 --- a/decompiler/config/jak2/type_casts.jsonc +++ b/decompiler/config/jak2/type_casts.jsonc @@ -10525,5 +10525,21 @@ [8, "a3", "pal-electric-fan"], [15, "a3", "pal-electric-fan"], [22, "t0", "pal-electric-fan"] + ], + "sprite-glow-init-engine": [ + [[8, 22], "a0", "dma-packet"], + [[32, 50], "a1", "dma-packet"], + [[52, 74], "a1", "dma-packet"], + [[76, 84], "a0", "dma-packet"], + [[85, 92], "v1", "dma-packet"] + ], + "sprite-glow-add-sprite": [ + [[0, 33], "v1", "sprite-glow-cnt-template"] + ], + "sprite-glow-add-simple-sprite": [ + [[0, 33], "v1", "sprite-glow-ref-template"] + ], + "add-shader-to-dma": [ + [[6, 8], "v1", "(pointer uint32)"] ] } diff --git a/decompiler/util/data_decompile.cpp b/decompiler/util/data_decompile.cpp index 7a0820aea..d9a739252 100644 --- a/decompiler/util/data_decompile.cpp +++ b/decompiler/util/data_decompile.cpp @@ -749,6 +749,8 @@ const std::unordered_map< { {"ocean-near-indices", {{"data", ArrayFieldDecompMeta(TypeSpec("ocean-near-index"), 32)}}}, + {"simple-sprite-system", + {{"data", ArrayFieldDecompMeta(TypeSpec("sprite-glow-data"), 64)}}}, {"ocean-mid-masks", {{"data", ArrayFieldDecompMeta(TypeSpec("ocean-mid-mask"), 8)}}}, {"sparticle-launcher", {{"init-specs", ArrayFieldDecompMeta(TypeSpec("sp-field-init-spec"), 16)}}}, diff --git a/docs/progress-notes/jak2/sprite_glow.md b/docs/progress-notes/jak2/sprite_glow.md new file mode 100644 index 000000000..d13539353 --- /dev/null +++ b/docs/progress-notes/jak2/sprite_glow.md @@ -0,0 +1,298 @@ +# Sprite Glow: EE side +It's pretty simple, no asm. + +# VU Memory Map +Offset is 400 (0-400 -> 400-800 as input buffers) +``` + +400 - 800 (input second buffer) +800 - template copy 1 +884 - template copy 2 +980 - constants +``` +# DMA init +``` +@ 0x500930 tag: TAG: 0x00000000 cnt qwc 0x0018 + vif0: STCYCL cl: 4 wl: 4 + vif1: UNPACK-V4-32: 24 addr: 980 us: false tops: false + +@ 0x500ac0 tag: TAG: 0x00777650 ref qwc 0x0054 + vif0: STCYCL cl: 4 wl: 4 + vif1: UNPACK-V4-32: 84 addr: 800 us: false tops: false + +@ 0x500ad0 tag: TAG: 0x00777650 ref qwc 0x0054 + vif0: MSCAL 0x0 + vif1: UNPACK-V4-32: 84 addr: 884 us: false tops: false + +@ 0x500ae0 tag: TAG: 0x00000000 cnt qwc 0x0000 + vif0: BASE 0x0 + vif1: OFFSET 0x190 + +@ 0x500af0 tag: TAG: 0x00000000 cnt qwc 0x0000 + vif0: NOP + vif1: FLUSHE +``` + +First tag is uploading the constants. + +Next two are uploading two copies of the templates and run init. Output is double buffered, so this makes sense. + +Next tag sets up input double buffer. + +Final tag is just sync before starting the draws + +# Template system +The VU program is (as usual) double buffered. +There are two input buffers, containing data uploaded through VIF. +There are two output buffers, each containing the `sprite-glow-template`. +While the program runs, it transform vertices, putting the results in the template. +Once done, it `xgkick`s, which waits for the previous run's draw to finish if needed, +then begins the drawing process. + +# What is drawn? + +Annoyingly, there are 6 draws this time: +- Probe Clear alpha +- Probe Draw alpha +- Offscreen sample +- Offscreen repeat +- Draw Alpha +- Draw Final + +In summary: +- Probe Clear alpha: draw a `alpha = 0` square. Always drawn. +- Probe Draw alpha: draw an `alpha = 1` square. 1 px smaller than first draw. Uses normal z test. + +## Draw 1 and 2: "Probe Clear alpha" and "Probe Draw alpha" +First is the `clear`. The first tag is 5 regs of adgif: +- `texflush` +- `(new 'static 'gs-alpha :a 2 :b 2 :c 2 :d 1)` +- `(new 'static 'gs-test :ate 1 :afail 1 :zte 1 :ztst 2)` +- `(new 'static 'gs-zbuf :zbp 304 :psm 1 :zmsk 1)` +- `(new 'static 'gs-frame :fbp 408 :fbw 8 :fbmsk #xffffff)` + +The alpha is: +``` +Cv = (0 - 0) * 0 + Cd +``` +which means to not write any color. + +The test is: +- alpha test always fails, means only rgba is written always +- ztest is usual GEQUAL + +When combined with the alpha settings, this only writes alpha, no rgb or depth. + +The zbuf looks at the normal zbuf, but z writing is masked again. Just in case. + +The gs-frame yet again masks away rgb writes. Just in case. + +There are two draws. Both are sprites. The first draw sets alpha to 0 and uses z = #xffffff, so it always passes. + +The second draw is similar but: +- z comes from the transformed vertex. So it has normal depth test behavior +- alpha is 1 +- it's 1 pixel smaller than the first draw. + +So the end result is: +- a `alpha = 0` square always +- an `alpha = 1` square, offset in by 1 pixel on all sides, but only where depth passes. + +## Draw 3: Offscreen Sample +Switches to GS context 2. Samples from framebuffer and writes to a temporary texture (64 px width, uses only 32). +- only writes alpha +- mmag/mmin on +- tcc = 1, tfx = 1 (rgba, decal) - RGBA all come from texture, nothing fancy +- rgb writes masked (only write alpha) +- clamp clamps +- alpha test thing disables z buffer writing. + +This basically just copies the inner square from draw 2 to a separate texture. + +## Draw 4: Repeat draw +This appears to draw over itself again and again. I think it effectively blends using texture filtering. So the 0, 0 px will have the average value of alpha. + +## Draw 5: flare alpha +This is set up in the `repeat-draw-adcmds`. Drawing to the framebuffer again. + + + + +# VU1 program +``` +out memory map: + +68: adgif0 +69: adgif1 +70: adgif2 +71: adgif3 +72: adgif4 +``` +``` +;; math: + +first, input position is multiplied by camera matrix (including adding part). Only xyz is computed here. (p0, vf01) + +color: rgb *= a + +fade = clamp(0, 1, p0.z * fade_a + fade_b) + + + + +INIT program + iaddiu vi05, vi00, 0x320 | nop ;; vi05 = 800 (template) + lq.xyzw vf25, 988(vi00) | nop + lq.xyzw vf26, 989(vi00) | nop + lq.xyzw vf27, 990(vi00) | nop + lq.xyzw vf30, 996(vi00) | nop + lq.xyzw vf31, 997(vi00) | nop + lq.xyzw vf28, 1002(vi00) | nop + lq.xyzw vf29, 1003(vi00) | nop + nop | nop :e + nop | nop + +regs: + +vi02 = input ptr +vi03 = num_sprites +vi04 = adgif ptr +vi05 = output buffer (double buffered, so toggles) + +vf25 = hvdf +vf26 = hmge +vf27 = consts +vf30 = basis_x +vf31 = basis_y +vf28 = clamp_min +vf29 = clamp_max + +DRAW program + xtop vi02 | nop ;; vi02 = input buffer's control + nop | nop + ilwr.x vi03, vi02 | nop ;; vi03 = num_sprites (1 always?) + iaddi vi02, vi02, 0x1 | nop ;; vi02 = sprite data + iaddiu vi04, vi02, 0x90 | nop ;; vi04 = adgif data +L1: + lq.xyzw vf03, 2(vi02) | nop ;; vf03 = color + lq.xyzw vf02, 1(vi02) | nop ;; vf02 = [size_probe, z_offset, rot-angle, size-y] + lq.xyzw vf01, 0(vi02) | nop ;; vf01 = [position.xyz, size-x] + lq.xyzw vf24, 983(vi00) | nop ;; vf24 = [camera_mat[3]] + lq.xyzw vf21, 980(vi00) | nop ;; vf21 = [camera_mat[0]] + lq.xyzw vf22, 981(vi00) | nop ;; vf22 = [camera_mat[1]] + lq.xyzw vf23, 982(vi00) | nop ;; vf23 = [camera_mat[2]] + lq.xyzw vf04, 3(vi02) | mulaw.xyz ACC, vf24, vf00 ;; vf04 = [fade_a, fade_b, X, X] | multiply + lq.xyzw vf24, 987(vi00) | maddax.xyz ACC, vf21, vf01 ;; vf24 = [perspective[3]] | multiply + lq.xyzw vf21, 984(vi00) | madday.xyz ACC, vf22, vf01 ;; vf21 = [perspective[0]] | multiply + lq.xyzw vf22, 985(vi00) | maddz.xyz vf01, vf23, vf01 ;; vf22 = [perspective[1]] | multiply + lq.xyzw vf23, 986(vi00) | nop ;; vf23 = [perspective[2]] + lq.xyzw vf09, 0(vi04) | nop ;; vf09 = adgif[0] + lq.xyzw vf10, 1(vi04) | mulw.xyz vf03, vf03, vf03 ;; vf10 = adgif[1] | color multiply by alpha + div Q, vf02.y, vf01.z | mulz.x vf04, vf04, vf01 ;; Q = (z_offset / p0.z) | fade_a *= p0.z + lq.xyzw vf11, 2(vi04) | nop ;; vf11 = adgif[2] + 0.0078125 | nop :i ;; I = 0.0078125 (= 1/128) + lq.xyzw vf12, 3(vi04) | nop ;; vf12 = adgif[3] + lq.xyzw vf13, 4(vi04) | addy.x vf04, vf04, vf04 ;; vf13 = adgif[4] | fade_a += fade_b + sq.xyzw vf09, 68(vi05) | muly.z vf05, vf02, vf27 ;; adgif0 store | vf05.z = rot-angle * deg_to_rad + move.w vf05, vf00 | addw.z vf02, vf00, vf01 ;; vf05.w = 1 | vf02.z = size-x + sq.xyzw vf10, 69(vi05) | mul.w vf09, vf00, Q ;; agdif1 store | vf09.w = (z_offset / p0.z) + sq.xyzw vf11, 70(vi05) | nop ;; adgif2 store + sq.xyzw vf12, 71(vi05) | miniw.x vf04, vf04, vf00 ;; adgif3 store | clamp fade 1 + sq.xyzw vf13, 72(vi05) | nop ;; adgif4 store + nop | subw.w vf09, vf00, vf09 ;; vf09.w = 1 - (z_offset / p0.z); + nop | maxx.x vf04, vf04, vf00 ;; clamp fade 2 + nop | mulw.xyz vf01, vf01, vf09 ;; multiply by pscale + nop | mulx.xyz vf03, vf03, vf04 ;; multiply color by fade + nop | mulaw.xyzw ACC, vf24, vf00 ;; multiply by perspective matrix + nop | maddax.xyzw ACC, vf21, vf01 + nop | madday.xyzw ACC, vf22, vf01 + nop | muli.xyz vf03, vf03, I ;; color scaling. + nop | maddz.xyzw vf01, vf23, vf01 ;; perspective matrix. + nop | nop + iaddi vi03, vi03, -0x1 | mulz.z vf06, vf05, vf05 ;; dec sprite count | vf06 = rot^2 + lq.xyzw vf15, 991(vi00) | nop ;; vf15 = sincos01 + iaddi vi02, vi02, 0x3 | nop ;; inc input pointer... by the wrong amount lol + fcset 0x0 | mul.xyzw vf07, vf01, vf26 ;; hmge mult + nop | mulz.zw vf09, vf05, vf06 ;; vf09 = rot^3 + lq.xyzw vf15, 992(vi00) | mula.zw ACC, vf05, vf15 ;; vf15 = sincos23 | acc working on sincos. + nop | nop + div Q, vf00.w, vf07.w | clipw.xyz vf07, vf07 ;; Q = 1 / p_hmged.w | clip!!! + nop | mulz.zw vf10, vf09, vf06 ;; vf10 is rot thing + lq.xyzw vf15, 993(vi00) | madda.zw ACC, vf09, vf15 ;; vf15 is rot coeff, working on sincos + nop | nop + fcand vi01, 0x3f | nop ;; check clipping result + ibne vi00, vi01, L2 | mulz.zw vf09, vf10, vf06 ;; skip if clipped | working on rot + lq.xyzw vf15, 994(vi00) | madda.zw ACC, vf10, vf15 ;; rot | rot + nop | mul.xyz vf01, vf01, Q ;; perspective multiply + nop | mul.xyzw vf02, vf02, Q ;; vf02 *= q + nop | mulz.zw vf10, vf09, vf06 ;; rot + lq.xyzw vf15, 995(vi00) | madda.zw ACC, vf09, vf15 ;; rot89 | rot + nop | add.xyzw vf01, vf01, vf25 ;; hvdf offset + nop | maxw.x vf02, vf02, vf00 ;; clip size_probe to 1 + nop | miniw.x vf02, vf02, vf29 ;; min size_probe against clamp max.w + nop | miniz.zw vf02, vf02, vf29 ;; min zw against clamp max.z + nop | madd.zw vf05, vf10, vf15 ;; vf05 = sincos output + nop | ftoi0.xyzw vf03, vf03 ;; colors to ints or whatever + nop | addx.xy vf09, vf28, vf02 ;; vf09 = [cmin.x + probe_size, cmin.y + probe_size] + nop | subx.xy vf11, vf01, vf02 ;; vf11 = probe lower corner = [p0.xy - probe_size] + nop | addx.xy vf12, vf01, vf02 ;; vf12 = probe upper corner = [po.xy + probe_size] + nop | subx.xy vf10, vf29, vf02 ;; vf10 = [cmax.x - probe_size, cmax.y - probe_size] + nop | mulaz.xyzw ACC, vf30, vf05 + nop | msubw.xyzw vf15, vf31, vf05 ;; rotate the basis + + nop | max.xy vf20, vf01, vf09 ;; vf20 is some clamped position. + nop | addx.zw vf11, vf01, vf00 ;; vf11.zw = vf01.zw + nop | addx.zw vf12, vf01, vf00 ;; vf12.zw = vf01.zw + nop | subw.xy vf17, vf28, vf00 ;; vf17 = [clamp_min.x - 1, clamp_min.y - 1] + nop | mulz.xyzw vf15, vf15, vf02 ;; rot_basis *= vf02.z (x-scale) + nop | addw.xy vf18, vf28, vf00 ;; vf18 = [calm_min.x + 1, clamp_min.y + 1] + nop | ftoi4.xyzw vf11, vf11 ;; usual ftoi4 of clear positions + nop | ftoi4.xyzw vf12, vf12 + nop | mini.xy vf20, vf20, vf10 ;; vf20 clamp again. + nop | mulaw.xyzw ACC, vf30, vf05 + sq.xyzw vf03, 75(vi05) | maddz.xyzw vf16, vf31, vf05 ;; store color of flare | second row of rotation matrix + sq.xyz vf11, 11(vi05) | sub.xy vf17, vf20, vf17 ;; store clear pos | offset vf17 + sq.xyz vf12, 12(vi05) | sub.xy vf18, vf20, vf18 ;; store clear pos | offset vf18 + lq.xyzw vf11, 998(vi00) | subx.xy vf19, vf20, vf02 ;; compute first clear pos, + lq.xyzw vf12, 999(vi00) | mulw.xyzw vf16, vf16, vf02 ;; y scale + lq.xyzw vf13, 1000(vi00) | addx.xy vf20, vf20, vf02 ;; first clear pos, upper corner. + lq.xyzw vf14, 1001(vi00) | mulaw.xyzw ACC, vf01, vf00 + nop | maddax.xyzw ACC, vf15, vf11 + nop | maddy.xyzw vf11, vf16, vf11 + nop | mulaw.xyzw ACC, vf01, vf00 + nop | maddax.xyzw ACC, vf15, vf12 + nop | maddy.xyzw vf12, vf16, vf12 + nop | mulaw.xyzw ACC, vf01, vf00 + nop | maddax.xyzw ACC, vf15, vf13 + nop | maddy.xyzw vf13, vf16, vf13 + nop | mulaw.xyzw ACC, vf01, vf00 + nop | maddax.xyzw ACC, vf15, vf14 + nop | maddy.xyzw vf14, vf16, vf14 + nop | subx.xy vf17, vf17, vf02 + nop | addx.xy vf18, vf18, vf02 + iaddiu vi04, vi04, 0x50 | subw.xy vf19, vf19, vf00 ;; offset first clear pos lower + nop | addw.xy vf20, vf20, vf00 ;; offset first clear pos upper + nop | ftoi4.xyzw vf11, vf11 + nop | ftoi4.xyzw vf12, vf12 + nop | ftoi4.xyzw vf13, vf13 + nop | ftoi4.xyzw vf14, vf14 + sq.xy vf11, 61(vi05) | ftoi4.xyzw vf17, vf17 + sq.xy vf12, 62(vi05) | ftoi4.xyzw vf18, vf18 + sq.xy vf13, 63(vi05) | ftoi4.xyzw vf19, vf19 + sq.xy vf14, 64(vi05) | ftoi4.xyzw vf20, vf20 + sq.xy vf17, 24(vi05) | nop + sq.xy vf18, 26(vi05) | nop + sq.xy vf19, 8(vi05) | nop + sq.xy vf20, 9(vi05) | nop + sq.xy vf11, 77(vi05) | nop + sq.xy vf12, 79(vi05) | nop + sq.xy vf13, 81(vi05) | nop + sq.xy vf14, 83(vi05) | nop + xgkick vi05 | nop +L2: + iaddiu vi01, vi00, 0x694 | nop + ibne vi00, vi03, L1 | nop + isub vi05, vi01, vi05 | nop + nop | nop :e + nop | nop +``` \ No newline at end of file diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index cfb521399..35f58f3d2 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -132,6 +132,11 @@ set(RUNTIME_SOURCE graphics/opengl_renderer/foreground/Generic2_DMA.cpp graphics/opengl_renderer/foreground/Generic2_Build.cpp graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp + graphics/opengl_renderer/sprite/GlowRenderer.cpp + graphics/opengl_renderer/sprite/Sprite3.cpp + graphics/opengl_renderer/sprite/Sprite3_Distort.cpp + graphics/opengl_renderer/sprite/Sprite3_Glow.cpp + graphics/opengl_renderer/sprite/SpriteRenderer.cpp graphics/opengl_renderer/ocean/CommonOceanRenderer.cpp graphics/opengl_renderer/ocean/OceanMid.cpp graphics/opengl_renderer/ocean/OceanMid_PS2.cpp @@ -159,8 +164,6 @@ set(RUNTIME_SOURCE graphics/opengl_renderer/SkyBlendCPU.cpp graphics/opengl_renderer/SkyBlendGPU.cpp graphics/opengl_renderer/SkyRenderer.cpp - graphics/opengl_renderer/Sprite3.cpp - graphics/opengl_renderer/SpriteRenderer.cpp graphics/opengl_renderer/TextureUploadHandler.cpp graphics/opengl_renderer/VisDataHandler.cpp graphics/opengl_renderer/DepthCue.cpp diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.cpp b/game/graphics/opengl_renderer/OpenGLRenderer.cpp index 389a910e6..1bbbd9a4c 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.cpp +++ b/game/graphics/opengl_renderer/OpenGLRenderer.cpp @@ -9,8 +9,6 @@ #include "game/graphics/opengl_renderer/LightningRenderer.h" #include "game/graphics/opengl_renderer/ShadowRenderer.h" #include "game/graphics/opengl_renderer/SkyRenderer.h" -#include "game/graphics/opengl_renderer/Sprite3.h" -#include "game/graphics/opengl_renderer/SpriteRenderer.h" #include "game/graphics/opengl_renderer/TextureUploadHandler.h" #include "game/graphics/opengl_renderer/VisDataHandler.h" #include "game/graphics/opengl_renderer/background/Shrub.h" @@ -20,6 +18,8 @@ #include "game/graphics/opengl_renderer/foreground/Merc2.h" #include "game/graphics/opengl_renderer/ocean/OceanMidAndFar.h" #include "game/graphics/opengl_renderer/ocean/OceanNear.h" +#include "game/graphics/opengl_renderer/sprite/Sprite3.h" +#include "game/graphics/opengl_renderer/sprite/SpriteRenderer.h" #include "game/graphics/pipelines/opengl.h" #include "third-party/imgui/imgui.h" diff --git a/game/graphics/opengl_renderer/Shader.cpp b/game/graphics/opengl_renderer/Shader.cpp index 60b00dca2..81d672429 100644 --- a/game/graphics/opengl_renderer/Shader.cpp +++ b/game/graphics/opengl_renderer/Shader.cpp @@ -95,6 +95,11 @@ ShaderLibrary::ShaderLibrary(GameVersion version) { at(ShaderId::POST_PROCESSING) = {"post_processing", version}; at(ShaderId::DEPTH_CUE) = {"depth_cue", version}; at(ShaderId::EMERC) = {"emerc", version}; + at(ShaderId::GLOW_PROBE) = {"glow_probe", version}; + at(ShaderId::GLOW_PROBE_READ) = {"glow_probe_read", version}; + at(ShaderId::GLOW_PROBE_READ_DEBUG) = {"glow_probe_read_debug", version}; + at(ShaderId::GLOW_PROBE_DOWNSAMPLE) = {"glow_probe_downsample", version}; + at(ShaderId::GLOW_DRAW) = {"glow_draw", version}; for (auto& shader : m_shaders) { ASSERT_MSG(shader.okay(), "error compiling shader"); diff --git a/game/graphics/opengl_renderer/Shader.h b/game/graphics/opengl_renderer/Shader.h index 02a400116..e961a9a1c 100644 --- a/game/graphics/opengl_renderer/Shader.h +++ b/game/graphics/opengl_renderer/Shader.h @@ -48,6 +48,11 @@ enum class ShaderId { POST_PROCESSING = 22, DEPTH_CUE = 23, EMERC = 24, + GLOW_PROBE = 25, + GLOW_PROBE_READ = 26, + GLOW_PROBE_READ_DEBUG = 27, + GLOW_PROBE_DOWNSAMPLE = 28, + GLOW_DRAW = 29, MAX_SHADERS }; diff --git a/game/graphics/opengl_renderer/shaders/glow_draw.frag b/game/graphics/opengl_renderer/shaders/glow_draw.frag new file mode 100644 index 000000000..5f371fff3 --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/glow_draw.frag @@ -0,0 +1,16 @@ +#version 430 core + +out vec4 color; + +in vec4 fragment_color; +in float discard_flag; +in vec2 uv_texture; + +layout (binding = 0) uniform sampler2D tex; + + +void main() { + vec4 texture_color = texture(tex, uv_texture); + color.xyz = texture_color.xyz * fragment_color.xyz * 2.f * discard_flag / 128.f; + color.w = fragment_color.w * texture_color.w; +} diff --git a/game/graphics/opengl_renderer/shaders/glow_draw.vert b/game/graphics/opengl_renderer/shaders/glow_draw.vert new file mode 100644 index 000000000..753e2f04b --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/glow_draw.vert @@ -0,0 +1,31 @@ +#version 430 core + +layout (location = 0) in vec4 position_in; +layout (location = 1) in vec4 rgba_in; +layout (location = 2) in vec2 uv_texture_in; +layout (location = 3) in vec2 uv_probe_in; + +out vec4 fragment_color; +out vec2 uv_texture; +out float discard_flag; + +const float SCISSOR_ADJUST = HEIGHT_SCALE * 512.0/448.0; + +layout (binding = 1) uniform sampler2D probe_tex; + + +void main() { + vec4 transformed = position_in; + transformed.xy -= (2048.); + transformed.z /= (8388608); + transformed.z -= 1; + transformed.x /= (256); + transformed.y /= -(128); + transformed.xyz *= transformed.w; + // scissoring area adjust + transformed.y *= SCISSOR_ADJUST; + gl_Position = transformed; + fragment_color = rgba_in; + uv_texture = uv_texture_in; + discard_flag = texture(probe_tex, uv_probe_in).a; +} diff --git a/game/graphics/opengl_renderer/shaders/glow_probe.frag b/game/graphics/opengl_renderer/shaders/glow_probe.frag new file mode 100644 index 000000000..619d3ce21 --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/glow_probe.frag @@ -0,0 +1,10 @@ +#version 430 core + +out vec4 color; + +in vec4 fragment_color; + + +void main() { + color = fragment_color; +} diff --git a/game/graphics/opengl_renderer/shaders/glow_probe.vert b/game/graphics/opengl_renderer/shaders/glow_probe.vert new file mode 100644 index 000000000..c0d8c8d3d --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/glow_probe.vert @@ -0,0 +1,23 @@ +#version 430 core + +layout (location = 0) in vec4 position_in; +layout (location = 1) in vec4 rgba_in; + +out vec4 fragment_color; + +const float SCISSOR_ADJUST = HEIGHT_SCALE * 512.0/448.0; + +void main() { + vec4 transformed = position_in; + transformed.xy -= (2048.); + transformed.z /= (8388608); + transformed.z -= 1; + transformed.x /= (256); + transformed.y /= -(128); + transformed.xyz *= transformed.w; + // scissoring area adjust + transformed.y *= SCISSOR_ADJUST; + gl_Position = transformed; + + fragment_color = rgba_in; +} diff --git a/game/graphics/opengl_renderer/shaders/glow_probe_downsample.frag b/game/graphics/opengl_renderer/shaders/glow_probe_downsample.frag new file mode 100644 index 000000000..b50e89c45 --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/glow_probe_downsample.frag @@ -0,0 +1,13 @@ +#version 430 core + +out vec4 out_color; +uniform sampler2D tex; +in flat vec4 fragment_color; +in vec2 tex_coord; + +void main() { + vec2 texture_coords = vec2(tex_coord.x, tex_coord.y); + + // sample framebuffer texture + out_color = texture(tex, texture_coords); +} diff --git a/game/graphics/opengl_renderer/shaders/glow_probe_downsample.vert b/game/graphics/opengl_renderer/shaders/glow_probe_downsample.vert new file mode 100644 index 000000000..5e6626406 --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/glow_probe_downsample.vert @@ -0,0 +1,10 @@ +#version 430 core + +layout (location = 0) in vec4 position_in; + +out vec2 tex_coord; + +void main() { + gl_Position = vec4((position_in.xy * 2) - 1.f, 0.f, 1.f); + tex_coord = position_in.xy; +} diff --git a/game/graphics/opengl_renderer/shaders/glow_probe_read.frag b/game/graphics/opengl_renderer/shaders/glow_probe_read.frag new file mode 100644 index 000000000..16f189677 --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/glow_probe_read.frag @@ -0,0 +1,14 @@ +#version 430 core + +out vec4 out_color; + +uniform sampler2D tex; + +in vec2 tex_coord; + +void main() { + vec2 texture_coords = vec2(tex_coord.x, tex_coord.y); + + // sample framebuffer texture + out_color = texture(tex, texture_coords); +} diff --git a/game/graphics/opengl_renderer/shaders/glow_probe_read.vert b/game/graphics/opengl_renderer/shaders/glow_probe_read.vert new file mode 100644 index 000000000..6314f02d7 --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/glow_probe_read.vert @@ -0,0 +1,13 @@ +#version 430 core + +layout (location = 0) in vec4 position_in; +layout (location = 1) in vec4 rgba_in; +layout (location = 2) in vec2 uv; + +out vec2 tex_coord; + +void main() { + gl_Position = vec4((position_in.xy * 2) - 1.f, 0.f, 1.f); + tex_coord.x = uv.x / 512; + tex_coord.y = 1.f - ((uv.y + 16) / 448); +} diff --git a/game/graphics/opengl_renderer/shaders/glow_probe_read_debug.frag b/game/graphics/opengl_renderer/shaders/glow_probe_read_debug.frag new file mode 100644 index 000000000..d84f77698 --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/glow_probe_read_debug.frag @@ -0,0 +1,7 @@ +#version 430 core + +out vec4 out_color; + +void main() { + out_color = vec4(0, 0.5, 1, 1); +} diff --git a/game/graphics/opengl_renderer/shaders/glow_probe_read_debug.vert b/game/graphics/opengl_renderer/shaders/glow_probe_read_debug.vert new file mode 100644 index 000000000..9021c8c0d --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/glow_probe_read_debug.vert @@ -0,0 +1,11 @@ +#version 430 core + +layout (location = 0) in vec4 position_in; +layout (location = 1) in vec4 rgba_in; +layout (location = 2) in vec2 uv; + +void main() { + float x = (uv.x / 512) * 2 - 1; + float y = -(((uv.y + 16) / 448) * 2 - 1); + gl_Position = vec4(x, y, 0, 1.f); +} diff --git a/game/graphics/opengl_renderer/sprite/GlowRenderer.cpp b/game/graphics/opengl_renderer/sprite/GlowRenderer.cpp new file mode 100644 index 000000000..e0e9d4cae --- /dev/null +++ b/game/graphics/opengl_renderer/sprite/GlowRenderer.cpp @@ -0,0 +1,737 @@ +#include "GlowRenderer.h" + +#include "third-party/imgui/imgui.h" + +/* + * The glow renderer draws a sprite, but only if the center of the sprite is "visible". + * To determine visibility, we draw a test "probe" at the center of the sprite and see how many + * pixels of the probe were visible. + * + * The inputs to this renderer are the transformed vertices that would be computed on VU1. + * The convention is that float -> int conversions and scalings are dropped. + * + * To detect if this is visible, we do something a little different from the original game: + * - first copy the depth buffer to a separate texture. It seems like we could eliminate this copy + * eventually and always render to a texture. + * + * - For each sprite draw a "test probe" using this depth buffer. Write to a separate texture. + * This test probe is pretty small. First clear alpha to 0, then draw a test image with alpha = 1 + * and depth testing on. + * + * - Repeatedly sample the result of the probe as a texture and draw it to another texture with half + * the size. This effectively averages the alpha values with texture filtering. + * + * - Stop once we've reached 2x2. At this point we can sample the center of this "texture" and the + * alpha will indicate the fraction of pixels in the original probe that passed depth. Use this + * to scale the intensity of the actual sprite draw. + * + * There are a number of optimizations made at this point: + * - Probe clear/drawing are batched. + * - Instead of doing the "downsampling" one-at-a-time, all probes are copied to a big grid and + * the downsampling happens in batches. + * - The sampling of the final downsampled texture happens inside the vertex shader. On PS2, it's + * used as a texture, drawn as alpha-only over the entire region, then blended with the final + * draw. But the alpha of the entire first draw is constant, and we can figure it out in the + * vertex shader, so there's no need to do this approach. + * + * there are a few remaining improvements that could be made: + * - The final draws could be bucketed, this would reduce draw calls by a lot. + * - The depth buffer copy could likely be eliminated. + * - There's a possibility that overlapping probes do the "wrong" thing. This could be solved by + * copying from the depth buffer to the grid, then drawing probes on the grid. Currently the + * probes are drawn aligned with the framebuffer, then copied back to the grid. This approach + * would also lower the vram needed. + */ + +GlowRenderer::GlowRenderer() { + m_vertex_buffer.resize(kMaxVertices); + m_sprite_data_buffer.resize(kMaxSprites); + m_index_buffer.resize(kMaxIndices); + + // dynamic buffer: this will hold vertices that are generated by the game and updated each frame. + // the most important optimization here is to have as few uploads as possible. The size of the + // upload isn't too important - we only have at most 256 sprites so the overhead of uploading + // anything dominates. So we use a single vertex format for all of our draws. + { + glGenBuffers(1, &m_ogl.vertex_buffer); + glGenVertexArrays(1, &m_ogl.vao); + glBindVertexArray(m_ogl.vao); + glBindBuffer(GL_ARRAY_BUFFER, m_ogl.vertex_buffer); + auto bytes = kMaxVertices * sizeof(Vertex); + glBufferData(GL_ARRAY_BUFFER, bytes, nullptr, GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, // location 0 in the shader + 4, // 4 floats per vert + GL_FLOAT, // floats + GL_TRUE, // normalized, ignored, + sizeof(Vertex), // + (void*)offsetof(Vertex, x) // offset in array + ); + + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, // location 1 in the shader + 4, // 4 color components + GL_FLOAT, // floats + GL_TRUE, // normalized, ignored, + sizeof(Vertex), // + (void*)offsetof(Vertex, r) // offset in array + ); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, // location 2 in the shader + 2, // 2 uv + GL_FLOAT, // floats + GL_TRUE, // normalized, ignored, + sizeof(Vertex), // + (void*)offsetof(Vertex, u) // offset in array (why is this a pointer...) + ); + + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, // location 2 in the shader + 2, // 2 uv + GL_FLOAT, // floats + GL_TRUE, // normalized, ignored, + sizeof(Vertex), // + (void*)offsetof(Vertex, uu) // offset in array (why is this a pointer...) + ); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glGenBuffers(1, &m_ogl.index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ogl.index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, kMaxIndices * sizeof(u32), nullptr, GL_STREAM_DRAW); + glBindVertexArray(0); + } + + // static buffer: this will hold draws for downsampling. Because everything is normalized, we can + // reuse the same vertices for all downsampling! + // Note that we can't do a single giant square - otherwise it would "bleed" over the edges. + // the boundary between cells needs to be preserved. + { + glGenBuffers(1, &m_ogl_downsampler.vertex_buffer); + glGenVertexArrays(1, &m_ogl_downsampler.vao); + glBindVertexArray(m_ogl_downsampler.vao); + glBindBuffer(GL_ARRAY_BUFFER, m_ogl_downsampler.vertex_buffer); + std::vector vertices; + std::vector indices; + vertices.resize(kMaxSprites * 4); + indices.resize(kMaxSprites * 5); + for (int i = 0; i < kMaxSprites; i++) { + int x = i / kDownsampleBatchWidth; + int y = i % kDownsampleBatchWidth; + float step = 1.f / kDownsampleBatchWidth; + Vertex* vtx = &vertices.at(i * 4); + for (int j = 0; j < 4; j++) { + vtx[j].r = 0.f; // debug + vtx[j].g = 0.f; + vtx[j].b = 0.f; + vtx[j].a = 0.f; + vtx[j].x = x * step; + vtx[j].y = y * step; + vtx[j].z = 0; + vtx[j].w = 0; + } + vtx[1].x += step; + vtx[2].y += step; + vtx[3].x += step; + vtx[3].y += step; + indices.at(i * 5 + 0) = i * 4; + indices.at(i * 5 + 1) = i * 4 + 1; + indices.at(i * 5 + 2) = i * 4 + 2; + indices.at(i * 5 + 3) = i * 4 + 3; + indices.at(i * 5 + 4) = UINT32_MAX; + } + + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), vertices.data(), + GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, // location 0 in the shader + 4, // 4 floats per vert + GL_FLOAT, // floats + GL_TRUE, // normalized, ignored, + sizeof(Vertex), // + (void*)offsetof(Vertex, x) // offset in array + ); + + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, // location 1 in the shader + 4, // 4 color components + GL_FLOAT, // floats + GL_TRUE, // normalized, ignored, + sizeof(Vertex), // + (void*)offsetof(Vertex, r) // offset in array + ); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, // location 2 in the shader + 3, // 4 color components + GL_FLOAT, // floats + GL_TRUE, // normalized, ignored, + sizeof(Vertex), // + (void*)offsetof(Vertex, u) // offset in array (why is this a pointer...) + ); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glGenBuffers(1, &m_ogl_downsampler.index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ogl_downsampler.index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(u32), indices.data(), + GL_STATIC_DRAW); + glBindVertexArray(0); + } + + // probe fbo setup: this fbo will hold the result of drawing probes. + glGenFramebuffers(1, &m_ogl.probe_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, m_ogl.probe_fbo); + glGenTextures(1, &m_ogl.probe_fbo_rgba_tex); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_ogl.probe_fbo_rgba_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_ogl.probe_fbo_w, m_ogl.probe_fbo_h, 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); + glGenRenderbuffers(1, &m_ogl.probe_fbo_zbuf_rb); + glBindRenderbuffer(GL_RENDERBUFFER, m_ogl.probe_fbo_zbuf_rb); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_ogl.probe_fbo_w, + m_ogl.probe_fbo_h); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, + m_ogl.probe_fbo_zbuf_rb); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + m_ogl.probe_fbo_rgba_tex, 0); + GLenum render_targets[1] = {GL_COLOR_ATTACHMENT0}; + glDrawBuffers(1, render_targets); + auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + ASSERT(status == GL_FRAMEBUFFER_COMPLETE); + + // downsample fbo setup: each will hold a grid of probes. + // there's one fbo for each size. + int ds_size = kFirstDownsampleSize; + for (int i = 0; i < kDownsampleIterations; i++) { + m_ogl.downsample_fbos[i].size = ds_size * kDownsampleBatchWidth; + glGenFramebuffers(1, &m_ogl.downsample_fbos[i].fbo); + glBindFramebuffer(GL_FRAMEBUFFER, m_ogl.downsample_fbos[i].fbo); + glGenTextures(1, &m_ogl.downsample_fbos[i].tex); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_ogl.downsample_fbos[i].tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ds_size * kDownsampleBatchWidth, + ds_size * kDownsampleBatchWidth, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + m_ogl.downsample_fbos[i].tex, 0); + glDrawBuffers(1, render_targets); + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + ASSERT(status == GL_FRAMEBUFFER_COMPLETE); + ds_size /= 2; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // set up a default draw mode for sprites. If they don't set values, they will get this + // from the giftag + m_default_draw_mode.set_ab(true); + + // ;; (new 'static 'gs-test :ate 1 :afail 1 :zte 1 :ztst 2) + // (new 'static 'gs-adcmd :cmds (gs-reg64 test-1) :x #x51001) + m_default_draw_mode.set_at(true); + m_default_draw_mode.set_alpha_fail(GsTest::AlphaFail::FB_ONLY); + m_default_draw_mode.set_zt(true); + m_default_draw_mode.set_depth_test(GsTest::ZTest::GEQUAL); + m_default_draw_mode.set_alpha_test(DrawMode::AlphaTest::NEVER); + + // ;; (new 'static 'gs-zbuf :zbp 304 :psm 1 :zmsk 1) + // (new 'static 'gs-adcmd :cmds (gs-reg64 zbuf-1) :x #x1000130 :y #x1) + m_default_draw_mode.disable_depth_write(); +} + +namespace { +void copy_to_vertex(GlowRenderer::Vertex* vtx, const Vector4f& xyzw) { + vtx->x = xyzw.x(); + vtx->y = xyzw.y(); + vtx->z = xyzw.z(); + vtx->w = xyzw.w(); +} +} // namespace + +SpriteGlowOutput* GlowRenderer::alloc_sprite() { + ASSERT(m_next_sprite < m_sprite_data_buffer.size()); + return &m_sprite_data_buffer[m_next_sprite++]; +} + +void GlowRenderer::cancel_sprite() { + ASSERT(m_next_sprite); + m_next_sprite--; +} + +// vertex addition is done in passes, so the "pass1" for all sprites is before any "pass2" vertices. +// But, we still do a single big upload for all passes. +// this way pass1 can be a single giant draw. + +/*! + * Add pass 1 vertices, for drawing the probe. + */ +void GlowRenderer::add_sprite_pass_1(const SpriteGlowOutput& data) { + { // first draw is a GS sprite to clear the alpha. This is faster than glClear, and the game + // computes these for us and gives it a large z that always passes. + // We need to convert to triangle strip. + u32 idx_start = m_next_vertex; + Vertex* vtx = alloc_vtx(4); + for (int i = 0; i < 4; i++) { + vtx[i].r = 1.f; // red for debug + vtx[i].g = 0.f; + vtx[i].b = 0.f; + vtx[i].a = 0.f; // clearing alpha + } + copy_to_vertex(vtx, data.first_clear_pos[0]); + copy_to_vertex(vtx + 1, data.first_clear_pos[0]); + vtx[1].x = data.first_clear_pos[1].x(); + copy_to_vertex(vtx + 2, data.first_clear_pos[0]); + vtx[2].y = data.first_clear_pos[1].y(); + copy_to_vertex(vtx + 3, data.first_clear_pos[1]); + + u32* idx = alloc_index(5); + idx[0] = idx_start; + idx[1] = idx_start + 1; + idx[2] = idx_start + 2; + idx[3] = idx_start + 3; + idx[4] = UINT32_MAX; + } + + { // second draw is the actual probe, using the real Z, and setting alpha to 1. + u32 idx_start = m_next_vertex; + Vertex* vtx = alloc_vtx(4); + for (int i = 0; i < 4; i++) { + vtx[i].r = 0.f; + vtx[i].g = 1.f; // green for debug + vtx[i].b = 0.f; + vtx[i].a = 1.f; // setting alpha + } + copy_to_vertex(vtx, data.second_clear_pos[0]); + copy_to_vertex(vtx + 1, data.second_clear_pos[0]); + vtx[1].x = data.second_clear_pos[1].x(); + copy_to_vertex(vtx + 2, data.second_clear_pos[0]); + vtx[2].y = data.second_clear_pos[1].y(); + copy_to_vertex(vtx + 3, data.second_clear_pos[1]); + + u32* idx = alloc_index(5); + idx[0] = idx_start; + idx[1] = idx_start + 1; + idx[2] = idx_start + 2; + idx[3] = idx_start + 3; + idx[4] = UINT32_MAX; + } +} + +/*! + * Add pass 2 vertices, for copying from the probe fbo to the biggest grid. + */ +void GlowRenderer::add_sprite_pass_2(const SpriteGlowOutput& data, int sprite_idx) { + // output is a grid of kBatchWidth * kBatchWidth. + // for simplicity, we'll map to (0, 1) here, and the shader will convert to (-1, 1) for opengl. + int x = sprite_idx / kDownsampleBatchWidth; + int y = sprite_idx % kDownsampleBatchWidth; + float step = 1.f / kDownsampleBatchWidth; + + u32 idx_start = m_next_vertex; + Vertex* vtx = alloc_vtx(4); + for (int i = 0; i < 4; i++) { + vtx[i].r = 1.f; // debug + vtx[i].g = 0.f; + vtx[i].b = 0.f; + vtx[i].a = 0.f; + vtx[i].x = x * step; // start of our cell + vtx[i].y = y * step; + vtx[i].z = 0; + vtx[i].w = 0; + } + vtx[1].x += step; + vtx[2].y += step; + vtx[3].x += step; + vtx[3].y += step; + + // transformation code gives us these coordinates for where to sample probe fbo + vtx[0].u = data.offscreen_uv[0][0]; + vtx[0].v = data.offscreen_uv[0][1]; + vtx[1].u = data.offscreen_uv[1][0]; + vtx[1].v = data.offscreen_uv[0][1]; + vtx[2].u = data.offscreen_uv[0][0]; + vtx[2].v = data.offscreen_uv[1][1]; + vtx[3].u = data.offscreen_uv[1][0]; + vtx[3].v = data.offscreen_uv[1][1]; + + u32* idx = alloc_index(5); + idx[0] = idx_start; + idx[1] = idx_start + 1; + idx[2] = idx_start + 2; + idx[3] = idx_start + 3; + idx[4] = UINT32_MAX; +} + +/*! + * Add pass 3 vertices and update sprite records. This is the final draw. + */ +void GlowRenderer::add_sprite_pass_3(const SpriteGlowOutput& data, int sprite_idx) { + // figure out our cell, we'll need to read from this to see if we're visible or not. + int x = sprite_idx / kDownsampleBatchWidth; + int y = sprite_idx % kDownsampleBatchWidth; + float step = 1.f / kDownsampleBatchWidth; + + u32 idx_start = m_next_vertex; + Vertex* vtx = alloc_vtx(4); + for (int i = 0; i < 4; i++) { + // include the color, used by the shader + vtx[i].r = data.flare_draw_color[0]; + vtx[i].g = data.flare_draw_color[1]; + vtx[i].b = data.flare_draw_color[2]; + vtx[i].a = data.flare_draw_color[3]; + copy_to_vertex(&vtx[i], data.flare_xyzw[i]); + vtx[i].u = 0; + vtx[i].v = 0; + // where to sample from to see probe result + // offset by step/2 to sample the middle. + // we use 2x2 for the final resolution and sample the middle - should be the same as + // going to a 1x1, but saves a draw. + vtx[i].uu = x * step + step / 2; + vtx[i].vv = y * step + step / 2; + } + // texture uv's hardcoded to corners + vtx[1].u = 1; + vtx[3].v = 1; + vtx[2].u = 1; + vtx[2].v = 1; + + // get a record + auto& record = m_sprite_records[sprite_idx]; + record.draw_mode = m_default_draw_mode; + record.tbp = 0; + record.idx = m_next_index; + + u32* idx = alloc_index(5); + // flip first two - fan -> strip + idx[0] = idx_start + 1; + idx[1] = idx_start + 0; + idx[2] = idx_start + 2; + idx[3] = idx_start + 3; + idx[4] = UINT32_MAX; + + // handle adgif stuff + { + ASSERT(data.adgif.tex0_addr == (u32)GsRegisterAddress::TEX0_1); + GsTex0 reg(data.adgif.tex0_data); + record.tbp = reg.tbp0(); + record.draw_mode.set_tcc(reg.tcc()); + // shader is hardcoded for this right now. + ASSERT(reg.tcc() == 1); + ASSERT(reg.tfx() == GsTex0::TextureFunction::MODULATE); + } + + { + ASSERT((u8)data.adgif.tex1_addr == (u8)GsRegisterAddress::TEX1_1); + GsTex1 reg(data.adgif.tex1_data); + record.draw_mode.set_filt_enable(reg.mmag()); + } + + { + ASSERT(data.adgif.mip_addr == (u32)GsRegisterAddress::MIPTBP1_1); + // ignore + } + + // clamp or zbuf + if (GsRegisterAddress(data.adgif.clamp_addr) == GsRegisterAddress::ZBUF_1) { + GsZbuf x(data.adgif.clamp_data); + record.draw_mode.set_depth_write_enable(!x.zmsk()); + } else if (GsRegisterAddress(data.adgif.clamp_addr) == GsRegisterAddress::CLAMP_1) { + u32 val = data.adgif.clamp_data; + if (!(val == 0b101 || val == 0 || val == 1 || val == 0b100)) { + ASSERT_MSG(false, fmt::format("clamp: 0x{:x}", val)); + } + record.draw_mode.set_clamp_s_enable(val & 0b001); + record.draw_mode.set_clamp_t_enable(val & 0b100); + } else { + ASSERT(false); + } + + // alpha + ASSERT(data.adgif.alpha_addr == (u32)GsRegisterAddress::ALPHA_1); // ?? + + // ;; a = 0, b = 2, c = 1, d = 1 + // Cv = (Cs - 0) * Ad + D + // leaving out the multiply by Ad. + record.draw_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_FIX_DST); +} + +/*! + * Blit the depth buffer from the default rendering buffer to the depth buffer of the probe fbo. + */ +void GlowRenderer::blit_depth(SharedRenderState* render_state) { + if (m_ogl.probe_fbo_w != render_state->render_fb_w || + m_ogl.probe_fbo_h != render_state->render_fb_h) { + m_ogl.probe_fbo_w = render_state->render_fb_w; + m_ogl.probe_fbo_h = render_state->render_fb_h; + + glBindTexture(GL_TEXTURE_2D, m_ogl.probe_fbo_rgba_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_ogl.probe_fbo_w, m_ogl.probe_fbo_h, 0, GL_RGBA, + GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + + glBindRenderbuffer(GL_RENDERBUFFER, m_ogl.probe_fbo_zbuf_rb); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_ogl.probe_fbo_w, + m_ogl.probe_fbo_h); + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, render_state->render_fb); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_ogl.probe_fbo); + + glBlitFramebuffer(render_state->render_fb_x, // srcX0 + render_state->render_fb_y, // srcY0 + render_state->render_fb_x + render_state->render_fb_w, // srcX1 + render_state->render_fb_y + render_state->render_fb_h, // srcY1 + 0, // dstX0 + 0, // dstY0 + m_ogl.probe_fbo_w, // dstX1 + m_ogl.probe_fbo_h, // dstY1 + GL_DEPTH_BUFFER_BIT, // mask + GL_NEAREST // filter + ); +} + +void GlowRenderer::draw_debug_window() { + ImGui::Checkbox("Show Probes", &m_debug.show_probes); + ImGui::Checkbox("Show Copy", &m_debug.show_probe_copies); + ImGui::Text("Count: %d", m_debug.num_sprites); +} + +/*! + * Do all downsample draws to make 2x2 textures. + */ +void GlowRenderer::downsample_chain(SharedRenderState* render_state, + ScopedProfilerNode& prof, + u32 num_sprites) { + glBindVertexArray(m_ogl_downsampler.vao); + glEnable(GL_PRIMITIVE_RESTART); + glPrimitiveRestartIndex(UINT32_MAX); + render_state->shaders[ShaderId::GLOW_PROBE_DOWNSAMPLE].activate(); + for (int i = 0; i < kDownsampleIterations - 1; i++) { + auto* source = &m_ogl.downsample_fbos[i]; + auto* dest = &m_ogl.downsample_fbos[i + 1]; + glBindTexture(GL_TEXTURE_2D, source->tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindFramebuffer(GL_FRAMEBUFFER, dest->fbo); + glViewport(0, 0, dest->size, dest->size); + prof.add_draw_call(); + prof.add_tri(num_sprites * 4); + // the grid fill order is the same as the downsample order, so we don't need to do all cells + // if we aren't using all sprites. + glDrawElements(GL_TRIANGLE_STRIP, num_sprites * 5, GL_UNSIGNED_INT, nullptr); + } + glViewport(render_state->draw_offset_x, render_state->draw_offset_y, render_state->draw_region_w, + render_state->draw_region_h); +} + +/*! + * Draw probes (including the clear) to the probe fbo. Also copies vertex/index buffer. + */ +void GlowRenderer::draw_probes(SharedRenderState* render_state, + ScopedProfilerNode& prof, + u32 idx_start, + u32 idx_end) { + glBindFramebuffer(GL_FRAMEBUFFER, m_ogl.probe_fbo); + glBindVertexArray(m_ogl.vao); + glEnable(GL_PRIMITIVE_RESTART); + glPrimitiveRestartIndex(UINT32_MAX); + glBindBuffer(GL_ARRAY_BUFFER, m_ogl.vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, m_next_vertex * sizeof(Vertex), m_vertex_buffer.data(), + GL_STREAM_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ogl.index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_next_index * sizeof(u32), m_index_buffer.data(), + GL_STREAM_DRAW); + + // do probes + render_state->shaders[ShaderId::GLOW_PROBE].activate(); + prof.add_draw_call(); + prof.add_tri(m_next_sprite * 4); + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_GEQUAL); + glDrawElements(GL_TRIANGLE_STRIP, idx_end - idx_start, GL_UNSIGNED_INT, + (void*)(idx_start * sizeof(u32))); +} + +/*! + * Draw red/green probes to the framebuffer for debugging. + */ +void GlowRenderer::debug_draw_probes(SharedRenderState* render_state, + ScopedProfilerNode& prof, + u32 idx_start, + u32 idx_end) { + prof.add_draw_call(); + prof.add_tri(m_next_sprite * 4); + glBindFramebuffer(GL_FRAMEBUFFER, render_state->render_fb); + glDrawElements(GL_TRIANGLE_STRIP, idx_end - idx_start, GL_UNSIGNED_INT, + (void*)(idx_start * sizeof(u32))); +} + +/*! + * Copy probes from probe fbo to grid. + */ +void GlowRenderer::draw_probe_copies(SharedRenderState* render_state, + ScopedProfilerNode& prof, + u32 idx_start, + u32 idx_end) { + // read probe from probe fbo, write it to the first downsample fbo + render_state->shaders[ShaderId::GLOW_PROBE_READ].activate(); + glBindFramebuffer(GL_FRAMEBUFFER, m_ogl.downsample_fbos[0].fbo); + glBindTexture(GL_TEXTURE_2D, m_ogl.probe_fbo_rgba_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glViewport(0, 0, m_ogl.downsample_fbos[0].size, m_ogl.downsample_fbos[0].size); + prof.add_draw_call(); + prof.add_tri(m_next_sprite * 2); + glDrawElements(GL_TRIANGLE_STRIP, idx_end - idx_start, GL_UNSIGNED_INT, + (void*)(idx_start * sizeof(u32))); + glViewport(render_state->draw_offset_x, render_state->draw_offset_y, render_state->draw_region_w, + render_state->draw_region_h); +} + +/*! + * Draw blue squares to show where probes and being copied from, to help debug texture offsets. + */ +void GlowRenderer::debug_draw_probe_copies(SharedRenderState* render_state, + ScopedProfilerNode& prof, + u32 idx_start, + u32 idx_end) { + render_state->shaders[ShaderId::GLOW_PROBE_READ_DEBUG].activate(); + prof.add_draw_call(); + prof.add_tri(m_next_sprite * 2); + glBindFramebuffer(GL_FRAMEBUFFER, render_state->render_fb); + glDrawElements(GL_TRIANGLE_STRIP, idx_end - idx_start, GL_UNSIGNED_INT, + (void*)(idx_start * sizeof(u32))); +} + +/*! + * Draw all pending sprites. + */ +void GlowRenderer::flush(SharedRenderState* render_state, ScopedProfilerNode& prof) { + m_debug.num_sprites = m_next_sprite; + if (!m_next_sprite) { + // no sprites submitted. + return; + } + + // copy depth from framebuffer to a temporary buffer + // (this is a bit wasteful) + blit_depth(render_state); + + // generate vertex/index data for probes + u32 probe_idx_start = m_next_index; + for (u32 sidx = 0; sidx < m_next_sprite; sidx++) { + add_sprite_pass_1(m_sprite_data_buffer[sidx]); + } + + // generate vertex/index data for copy to downsample buffer + u32 copy_idx_start = m_next_index; + for (u32 sidx = 0; sidx < m_next_sprite; sidx++) { + add_sprite_pass_2(m_sprite_data_buffer[sidx], sidx); + } + u32 copy_idx_end = m_next_index; + + // generate vertex/index data for framebuffer draws + for (u32 sidx = 0; sidx < m_next_sprite; sidx++) { + add_sprite_pass_3(m_sprite_data_buffer[sidx], sidx); + } + + // draw probes + draw_probes(render_state, prof, probe_idx_start, copy_idx_start); + if (m_debug.show_probes) { + debug_draw_probes(render_state, prof, probe_idx_start, copy_idx_start); + } + + // copy probes + draw_probe_copies(render_state, prof, copy_idx_start, copy_idx_end); + if (m_debug.show_probe_copies) { + debug_draw_probe_copies(render_state, prof, copy_idx_start, copy_idx_end); + } + + // downsample probes. + downsample_chain(render_state, prof, m_next_sprite); + + draw_sprites(render_state, prof); + + m_next_vertex = 0; + m_next_index = 0; + m_next_sprite = 0; + glBindFramebuffer(GL_FRAMEBUFFER, render_state->render_fb); +} + +/*! + * Final drawing of sprites. + */ +void GlowRenderer::draw_sprites(SharedRenderState* render_state, ScopedProfilerNode& prof) { + glBindFramebuffer(GL_FRAMEBUFFER, render_state->render_fb); + glBindVertexArray(m_ogl.vao); + glEnable(GL_PRIMITIVE_RESTART); + glPrimitiveRestartIndex(UINT32_MAX); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, m_ogl.downsample_fbos[kDownsampleIterations - 1].tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + render_state->shaders[ShaderId::GLOW_DRAW].activate(); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_GEQUAL); + glEnable(GL_BLEND); + // Cv = (Cs - 0) * Ad + D + glBlendFunc(GL_ONE, GL_ONE); + glBlendEquation(GL_FUNC_ADD); + + glDepthMask(GL_FALSE); + + for (u32 i = 0; i < m_next_sprite; i++) { + const auto& record = m_sprite_records[i]; + auto tex = render_state->texture_pool->lookup(record.tbp); + if (!tex) { + fmt::print("Failed to find texture at {}, using random", record.tbp); + tex = render_state->texture_pool->get_placeholder_texture(); + } + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, *tex); + if (record.draw_mode.get_clamp_s_enable()) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + } + + if (record.draw_mode.get_clamp_t_enable()) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + if (record.draw_mode.get_filt_enable()) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + prof.add_draw_call(); + prof.add_tri(2); + glDrawElements(GL_TRIANGLE_STRIP, 5, GL_UNSIGNED_INT, (void*)(record.idx * sizeof(u32))); + } +} + +GlowRenderer::Vertex* GlowRenderer::alloc_vtx(int num) { + ASSERT(m_next_vertex + num <= m_vertex_buffer.size()); + auto* result = &m_vertex_buffer[m_next_vertex]; + m_next_vertex += num; + return result; +} + +u32* GlowRenderer::alloc_index(int num) { + ASSERT(m_next_index + num <= m_index_buffer.size()); + auto* result = &m_index_buffer[m_next_index]; + m_next_index += num; + return result; +} \ No newline at end of file diff --git a/game/graphics/opengl_renderer/sprite/GlowRenderer.h b/game/graphics/opengl_renderer/sprite/GlowRenderer.h new file mode 100644 index 000000000..c2ad440e7 --- /dev/null +++ b/game/graphics/opengl_renderer/sprite/GlowRenderer.h @@ -0,0 +1,112 @@ +#pragma once + +#include "game/graphics/opengl_renderer/sprite/sprite_common.h" + +class GlowRenderer { + public: + GlowRenderer(); + SpriteGlowOutput* alloc_sprite(); + void cancel_sprite(); + + void flush(SharedRenderState* render_state, ScopedProfilerNode& prof); + void draw_debug_window(); + + // Vertex can hold all possible values for all passes. The total number of vertices is very small + // so it ends up a lot faster to do a single upload, even if the size is like 50% larger than it + // could be. + struct Vertex { + float x, y, z, w; + float r, g, b, a; + float u, v; + float uu, vv; + }; + + private: + struct { + bool show_probes = false; + bool show_probe_copies = false; + int num_sprites = 0; + } m_debug; + void add_sprite_pass_1(const SpriteGlowOutput& data); + void add_sprite_pass_2(const SpriteGlowOutput& data, int sprite_idx); + void add_sprite_pass_3(const SpriteGlowOutput& data, int sprite_idx); + + void blit_depth(SharedRenderState* render_state); + + void draw_probes(SharedRenderState* render_state, + ScopedProfilerNode& prof, + u32 idx_start, + u32 idx_end); + + void debug_draw_probes(SharedRenderState* render_state, + ScopedProfilerNode& prof, + u32 idx_start, + u32 idx_end); + + void draw_probe_copies(SharedRenderState* render_state, + ScopedProfilerNode& prof, + u32 idx_start, + u32 idx_end); + + void debug_draw_probe_copies(SharedRenderState* render_state, + ScopedProfilerNode& prof, + u32 idx_start, + u32 idx_end); + void downsample_chain(SharedRenderState* render_state, ScopedProfilerNode& prof, u32 num_sprites); + + void draw_sprites(SharedRenderState* render_state, ScopedProfilerNode& prof); + + std::vector m_vertex_buffer; + std::vector m_sprite_data_buffer; + u32 m_next_sprite = 0; + + u32 m_next_vertex = 0; + Vertex* alloc_vtx(int num); + + std::vector m_index_buffer; + u32 m_next_index = 0; + u32* alloc_index(int num); + + struct DsFbo { + int size = -1; + GLuint fbo; + GLuint tex; + }; + + static constexpr int kDownsampleBatchWidth = 16; + static constexpr int kMaxSprites = kDownsampleBatchWidth * kDownsampleBatchWidth; + static constexpr int kMaxVertices = kMaxSprites * 32; // check. + static constexpr int kMaxIndices = kMaxSprites * 32; // check. + static constexpr int kDownsampleIterations = 5; + static constexpr int kFirstDownsampleSize = 32; // should be power of 2. + + struct { + GLuint vertex_buffer; + GLuint vao; + GLuint index_buffer; + + GLuint probe_fbo; + GLuint probe_fbo_rgba_tex; + GLuint probe_fbo_zbuf_rb; + int probe_fbo_w = 640; + int probe_fbo_h = 480; + + DsFbo downsample_fbos[kDownsampleIterations]; + } m_ogl; + + struct { + GLuint vao; + GLuint index_buffer; + GLuint vertex_buffer; + } m_ogl_downsampler; + + DrawMode m_default_draw_mode; + + struct SpriteRecord { + u32 tbp; + DrawMode draw_mode; + u32 idx; + }; + + std::array m_sprite_records; +}; diff --git a/game/graphics/opengl_renderer/Sprite3.cpp b/game/graphics/opengl_renderer/sprite/Sprite3.cpp similarity index 52% rename from game/graphics/opengl_renderer/Sprite3.cpp rename to game/graphics/opengl_renderer/sprite/Sprite3.cpp index 7f13ba507..c90ac3f2f 100644 --- a/game/graphics/opengl_renderer/Sprite3.cpp +++ b/game/graphics/opengl_renderer/sprite/Sprite3.cpp @@ -1,5 +1,3 @@ - - #include "Sprite3.h" #include "common/log/log.h" @@ -36,19 +34,8 @@ u32 process_sprite_chunk_header(DmaFollower& dma) { return header[0]; } -/*! - * Does the next DMA transfer look like the frame data for sprite distort? - */ -bool looks_like_distort_frame_data(const DmaFollower& dma) { - return dma.current_tag().kind == DmaTag::Kind::CNT && - dma.current_tag_vifcode0().kind == VifCode::Kind::NOP && - dma.current_tag_vifcode1().kind == VifCode::Kind::UNPACK_V4_32; -} -} // namespace - constexpr int SPRITE_RENDERER_MAX_SPRITES = 1920 * 10; -constexpr int SPRITE_RENDERER_MAX_DISTORT_SPRITES = - 256 * 10; // size of sprite-aux-list in GOAL code * SPRITE_MAX_AMOUNT_MULT +} // namespace Sprite3::Sprite3(const std::string& name, int my_id) : BucketRenderer(name, my_id), m_direct(name, my_id, 1024) { @@ -143,620 +130,6 @@ void Sprite3::opengl_setup_normal() { m_current_mode = m_default_mode; } -void Sprite3::opengl_setup_distort() { - // Create framebuffer to snapshot current render to a texture that can be bound for the distort - // shader This will represent tex0 from the original GS data - glGenFramebuffers(1, &m_distort_ogl.fbo); - glBindFramebuffer(GL_FRAMEBUFFER, m_distort_ogl.fbo); - - glGenTextures(1, &m_distort_ogl.fbo_texture); - glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_distort_ogl.fbo_width, m_distort_ogl.fbo_height, 0, - GL_RGB, GL_UNSIGNED_BYTE, NULL); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - // Texture clamping here matches the GS init data for distort - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - m_distort_ogl.fbo_texture, 0); - - ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); - - glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - // Non-instancing - // ---------------------- - glGenBuffers(1, &m_distort_ogl.vertex_buffer); - glGenVertexArrays(1, &m_distort_ogl.vao); - glBindVertexArray(m_distort_ogl.vao); - glBindBuffer(GL_ARRAY_BUFFER, m_distort_ogl.vertex_buffer); - // note: each sprite shares a single vertex per slice, account for that here - int distort_vert_buffer_len = - SPRITE_RENDERER_MAX_DISTORT_SPRITES * - ((5 - 1) * 11 + 1); // max * ((verts_per_slice - 1) * max_slices + 1) - glBufferData(GL_ARRAY_BUFFER, distort_vert_buffer_len * sizeof(SpriteDistortVertex), nullptr, - GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, // location 0 in the shader - 3, // 3 floats per vert - GL_FLOAT, // floats - GL_FALSE, // don't normalize, ignored - sizeof(SpriteDistortVertex), // - (void*)offsetof(SpriteDistortVertex, xyz) // offset in array - ); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, // location 1 in the shader - 2, // 2 floats per vert - GL_FLOAT, // floats - GL_FALSE, // don't normalize, ignored - sizeof(SpriteDistortVertex), // - (void*)offsetof(SpriteDistortVertex, st) // offset in array - ); - - // note: add one extra element per sprite that marks the end of a triangle strip - int distort_idx_buffer_len = SPRITE_RENDERER_MAX_DISTORT_SPRITES * - ((5 * 11) + 1); // max * ((verts_per_slice * max_slices) + 1) - glGenBuffers(1, &m_distort_ogl.index_buffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_distort_ogl.index_buffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, distort_idx_buffer_len * sizeof(u32), nullptr, - GL_DYNAMIC_DRAW); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindVertexArray(0); - - m_sprite_distorter_vertices.resize(distort_vert_buffer_len); - m_sprite_distorter_indices.resize(distort_idx_buffer_len); - m_sprite_distorter_frame_data.resize(SPRITE_RENDERER_MAX_DISTORT_SPRITES); - - // Instancing - // ---------------------- - glGenVertexArrays(1, &m_distort_instanced_ogl.vao); - glBindVertexArray(m_distort_instanced_ogl.vao); - - int distort_max_sprite_slices = 0; - for (int i = 3; i < 12; i++) { - // For each 'resolution', there can be that many slices - distort_max_sprite_slices += i; - } - - glGenBuffers(1, &m_distort_instanced_ogl.vertex_buffer); - glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.vertex_buffer); - - int distort_instanced_vert_buffer_len = distort_max_sprite_slices * 5; // 5 vertices per slice - glBufferData(GL_ARRAY_BUFFER, distort_instanced_vert_buffer_len * sizeof(SpriteDistortVertex), - nullptr, GL_STREAM_DRAW); - - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, // location 0 in the shader - 3, // 3 floats per vert - GL_FLOAT, // floats - GL_FALSE, // don't normalize, ignored - sizeof(SpriteDistortVertex), // - (void*)offsetof(SpriteDistortVertex, xyz) // offset in array - ); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, // location 1 in the shader - 2, // 2 floats per vert - GL_FLOAT, // floats - GL_FALSE, // don't normalize, ignored - sizeof(SpriteDistortVertex), // - (void*)offsetof(SpriteDistortVertex, st) // offset in array - ); - - glGenBuffers(1, &m_distort_instanced_ogl.instance_buffer); - glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.instance_buffer); - - int distort_instance_buffer_len = SPRITE_RENDERER_MAX_DISTORT_SPRITES; - glBufferData(GL_ARRAY_BUFFER, distort_instance_buffer_len * sizeof(SpriteDistortInstanceData), - nullptr, GL_DYNAMIC_DRAW); - - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, // location 2 in the shader - 4, // 4 floats per vert - GL_FLOAT, // floats - GL_FALSE, // normalized, ignored, - sizeof(SpriteDistortInstanceData), // - (void*)offsetof(SpriteDistortInstanceData, x_y_z_s) // offset in array - ); - glEnableVertexAttribArray(3); - glVertexAttribPointer(3, // location 3 in the shader - 4, // 4 floats per vert - GL_FLOAT, // floats - GL_FALSE, // normalized, ignored, - sizeof(SpriteDistortInstanceData), // - (void*)offsetof(SpriteDistortInstanceData, sx_sy_sz_t) // offset in array - ); - - glVertexAttribDivisor(2, 1); - glVertexAttribDivisor(3, 1); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindVertexArray(0); - - m_sprite_distorter_vertices_instanced.resize(distort_instanced_vert_buffer_len); - - for (int i = 3; i < 12; i++) { - auto vec = std::vector(); - vec.resize(distort_instance_buffer_len); - - m_sprite_distorter_instances_by_res[i] = vec; - } -} - -/*! - * Run the sprite distorter. - */ -void Sprite3::render_distorter(DmaFollower& dma, - SharedRenderState* render_state, - ScopedProfilerNode& prof) { - // Skip to distorter DMA - m_direct.reset_state(); - while (dma.current_tag().qwc != 7) { - auto direct_data = dma.read_and_advance(); - m_direct.render_vif(direct_data.vif0(), direct_data.vif1(), direct_data.data, - direct_data.size_bytes, render_state, prof); - } - m_direct.flush_pending(render_state, prof); - - // Read DMA - { - auto prof_node = prof.make_scoped_child("dma"); - distort_dma(dma, prof_node); - } - - if (!m_enabled || !m_distort_enable) { - // Distort disabled, we can stop here since all the DMA has been read - return; - } - - // Set up vertex data - { - auto prof_node = prof.make_scoped_child("setup"); - if (m_enable_distort_instancing) { - distort_setup_instanced(prof_node); - } else { - distort_setup(prof_node); - } - } - - // Draw - { - auto prof_node = prof.make_scoped_child("drawing"); - if (m_enable_distort_instancing) { - distort_draw_instanced(render_state, prof_node); - } else { - distort_draw(render_state, prof_node); - } - } -} - -/*! - * Reads all sprite distort related DMA packets. - */ -void Sprite3::distort_dma(DmaFollower& dma, ScopedProfilerNode& /*prof*/) { - // First should be the GS setup - auto sprite_distorter_direct_setup = dma.read_and_advance(); - ASSERT(sprite_distorter_direct_setup.vifcode0().kind == VifCode::Kind::NOP); - ASSERT(sprite_distorter_direct_setup.vifcode1().kind == VifCode::Kind::DIRECT); - ASSERT(sprite_distorter_direct_setup.vifcode1().immediate == 7); - memcpy(&m_sprite_distorter_setup, sprite_distorter_direct_setup.data, 7 * 16); - - auto gif_tag = m_sprite_distorter_setup.gif_tag; - ASSERT(gif_tag.nloop() == 1); - ASSERT(gif_tag.eop() == 1); - ASSERT(gif_tag.nreg() == 6); - ASSERT(gif_tag.reg(0) == GifTag::RegisterDescriptor::AD); - - auto zbuf1 = m_sprite_distorter_setup.zbuf; - ASSERT(zbuf1.zbp() == 0x1c0); - ASSERT(zbuf1.zmsk() == true); - ASSERT(zbuf1.psm() == TextureFormat::PSMZ24); - - auto tex0 = m_sprite_distorter_setup.tex0; - ASSERT(tex0.tbw() == 8); - ASSERT(tex0.tw() == 9); - ASSERT(tex0.th() == 8); - - auto tex1 = m_sprite_distorter_setup.tex1; - ASSERT(tex1.mmag() == true); - ASSERT(tex1.mmin() == 1); - - auto alpha = m_sprite_distorter_setup.alpha; - ASSERT(alpha.a_mode() == GsAlpha::BlendMode::SOURCE); - ASSERT(alpha.b_mode() == GsAlpha::BlendMode::DEST); - ASSERT(alpha.c_mode() == GsAlpha::BlendMode::SOURCE); - ASSERT(alpha.d_mode() == GsAlpha::BlendMode::DEST); - - // Next is the aspect used by the sine tables (PC only) - // - // This was added to let the renderer reliably detect when the sine tables changed, - // which is whenever the aspect ratio changed. However, the tables aren't always - // updated on the same frame that the aspect changed, so this just lets the game - // easily notify the renderer when it finally does get updated. - auto sprite_distort_tables_aspect = dma.read_and_advance(); - ASSERT(sprite_distort_tables_aspect.size_bytes == 16); - ASSERT(sprite_distort_tables_aspect.vifcode1().kind == VifCode::Kind::PC_PORT); - memcpy(&m_sprite_distorter_sine_tables_aspect, sprite_distort_tables_aspect.data, - sizeof(math::Vector4f)); - - // Next thing should be the sine tables - auto sprite_distorter_tables = dma.read_and_advance(); - unpack_to_stcycl(&m_sprite_distorter_sine_tables, sprite_distorter_tables, - VifCode::Kind::UNPACK_V4_32, 4, 4, 0x8b * 16, 0x160, false, false); - - ASSERT(GsPrim(m_sprite_distorter_sine_tables.gs_gif_tag.prim()).kind() == - GsPrim::Kind::TRI_STRIP); - - // Finally, should be frame data packets (containing sprites) - // Up to 170 sprites will be DMA'd at a time followed by a mscalf, - // and this process can happen twice up to a maximum of 256 sprites DMA'd - // (256 is the size of sprite-aux-list which drives this). - int sprite_idx = 0; - m_distort_stats.total_sprites = 0; - - while (looks_like_distort_frame_data(dma)) { - math::Vector num_sprites_vec; - - // Read sprite packets - do { - int qwc = dma.current_tag().qwc; - int dest = dma.current_tag_vifcode1().immediate; - auto distort_data = dma.read_and_advance(); - - if (dest == 511) { - // VU address 511 specifies the number of sprites - unpack_to_no_stcycl(&num_sprites_vec, distort_data, VifCode::Kind::UNPACK_V4_32, 16, dest, - false, false); - } else { - // VU address >= 512 is the actual vertex data - ASSERT(dest >= 512); - ASSERT(sprite_idx + (qwc / 3) <= (int)m_sprite_distorter_frame_data.capacity()); - - unpack_to_no_stcycl(&m_sprite_distorter_frame_data.at(sprite_idx), distort_data, - VifCode::Kind::UNPACK_V4_32, qwc * 16, dest, false, false); - - sprite_idx += qwc / 3; - } - } while (looks_like_distort_frame_data(dma)); - - // Sprite packets should always end with a mscalf flush - ASSERT(dma.current_tag().kind == DmaTag::Kind::CNT); - ASSERT(dma.current_tag_vifcode0().kind == VifCode::Kind::MSCALF); - ASSERT(dma.current_tag_vifcode1().kind == VifCode::Kind::FLUSH); - dma.read_and_advance(); - - m_distort_stats.total_sprites += num_sprites_vec.x(); - } - - // Done - ASSERT(m_distort_stats.total_sprites <= SPRITE_RENDERER_MAX_DISTORT_SPRITES); -} - -/*! - * Sets up OpenGL data for each distort sprite. - */ -void Sprite3::distort_setup(ScopedProfilerNode& /*prof*/) { - m_distort_stats.total_tris = 0; - - m_sprite_distorter_vertices.clear(); - m_sprite_distorter_indices.clear(); - - int sprite_idx = 0; - int sprites_left = m_distort_stats.total_sprites; - - // This part is mostly ripped from the VU program - while (sprites_left != 0) { - // flag seems to represent the 'resolution' of the circle sprite used to create the distortion - // effect For example, a flag value of 3 will create a circle using 3 "pie-slice" shapes - u32 flag = m_sprite_distorter_frame_data.at(sprite_idx).flag; - u32 slices_left = flag; - - // flag has a minimum value of 3 which represents the first ientry - // Additionally, the ientry index has 352 added to it (which is the start of the entry array - // in VU memory), so we need to subtract that as well - int entry_index = m_sprite_distorter_sine_tables.ientry[flag - 3].x() - 352; - - // Here would be adding the giftag, but we don't need that - - // Get the frame data for the next distort sprite - SpriteDistortFrameData frame_data = m_sprite_distorter_frame_data.at(sprite_idx); - sprite_idx++; - - // Build the OpenGL data for the sprite - math::Vector2f vf03 = frame_data.st; - math::Vector3f vf14 = frame_data.xyz; - - // Each slice shares a center vertex, we can use this fact and cut out duplicate vertices - u32 center_vert_idx = m_sprite_distorter_vertices.size(); - m_sprite_distorter_vertices.push_back({vf14, vf03}); - - do { - math::Vector3f vf06 = m_sprite_distorter_sine_tables.entry[entry_index++].xyz(); - math::Vector2f vf07 = m_sprite_distorter_sine_tables.entry[entry_index++].xy(); - math::Vector3f vf08 = m_sprite_distorter_sine_tables.entry[entry_index + 0].xyz(); - math::Vector2f vf09 = m_sprite_distorter_sine_tables.entry[entry_index + 1].xy(); - - slices_left--; - - math::Vector2f vf11 = (vf07 * frame_data.rgba.z()) + frame_data.st; - math::Vector2f vf13 = (vf09 * frame_data.rgba.z()) + frame_data.st; - math::Vector3f vf06_2 = (vf06 * frame_data.rgba.x()) + frame_data.xyz; - math::Vector2f vf07_2 = (vf07 * frame_data.rgba.x()) + frame_data.st; - math::Vector3f vf08_2 = (vf08 * frame_data.rgba.x()) + frame_data.xyz; - math::Vector2f vf09_2 = (vf09 * frame_data.rgba.x()) + frame_data.st; - math::Vector3f vf10 = (vf06 * frame_data.rgba.y()) + frame_data.xyz; - math::Vector3f vf12 = (vf08 * frame_data.rgba.y()) + frame_data.xyz; - math::Vector3f vf06_3 = vf06_2; - math::Vector3f vf08_3 = vf08_2; - - m_sprite_distorter_indices.push_back(m_sprite_distorter_vertices.size()); - m_sprite_distorter_vertices.push_back({vf06_3, vf07_2}); - - m_sprite_distorter_indices.push_back(m_sprite_distorter_vertices.size()); - m_sprite_distorter_vertices.push_back({vf08_3, vf09_2}); - - m_sprite_distorter_indices.push_back(m_sprite_distorter_vertices.size()); - m_sprite_distorter_vertices.push_back({vf10, vf11}); - - m_sprite_distorter_indices.push_back(m_sprite_distorter_vertices.size()); - m_sprite_distorter_vertices.push_back({vf12, vf13}); - - // Originally, would add the shared center vertex, but in our case we can just add the index - m_sprite_distorter_indices.push_back(center_vert_idx); - // m_sprite_distorter_vertices.push_back({vf14, vf03}); - - m_distort_stats.total_tris += 2; - } while (slices_left != 0); - - // Mark the end of the triangle strip - m_sprite_distorter_indices.push_back(UINT32_MAX); - - sprites_left--; - } -} - -/*! - * Sets up OpenGL data for rendering distort sprites using instanced rendering. - * - * A mesh is built once for each possible sprite resolution and is only re-built - * when the dimensions of the window are changed. These meshes are built just like - * the triangle strips in the VU program, but with the sprite-specific data removed. - * - * Required sprite-specific frame data is kept as is and is grouped by resolution. - */ -void Sprite3::distort_setup_instanced(ScopedProfilerNode& /*prof*/) { - if (m_distort_instanced_ogl.last_aspect_x != m_sprite_distorter_sine_tables_aspect.x() || - m_distort_instanced_ogl.last_aspect_y != m_sprite_distorter_sine_tables_aspect.y()) { - m_distort_instanced_ogl.last_aspect_x = m_sprite_distorter_sine_tables_aspect.x(); - m_distort_instanced_ogl.last_aspect_y = m_sprite_distorter_sine_tables_aspect.y(); - // Aspect ratio changed, which means we have a new sine table - m_sprite_distorter_vertices_instanced.clear(); - - // Build a mesh for every possible distort sprite resolution - auto vf03 = math::Vector2f(0, 0); - auto vf14 = math::Vector3f(0, 0, 0); - - for (int res = 3; res < 12; res++) { - int entry_index = m_sprite_distorter_sine_tables.ientry[res - 3].x() - 352; - - for (int i = 0; i < res; i++) { - math::Vector3f vf06 = m_sprite_distorter_sine_tables.entry[entry_index++].xyz(); - math::Vector2f vf07 = m_sprite_distorter_sine_tables.entry[entry_index++].xy(); - math::Vector3f vf08 = m_sprite_distorter_sine_tables.entry[entry_index + 0].xyz(); - math::Vector2f vf09 = m_sprite_distorter_sine_tables.entry[entry_index + 1].xy(); - - // Normally, there would be a bunch of transformations here against the sprite data. - // Instead, we'll let the shader do it and just store the sine table specific parts here. - - m_sprite_distorter_vertices_instanced.push_back({vf06, vf07}); - m_sprite_distorter_vertices_instanced.push_back({vf08, vf09}); - m_sprite_distorter_vertices_instanced.push_back({vf06, vf07}); - m_sprite_distorter_vertices_instanced.push_back({vf08, vf09}); - m_sprite_distorter_vertices_instanced.push_back({vf14, vf03}); - } - } - - m_distort_instanced_ogl.vertex_data_changed = true; - } - - // Set up instance data for each sprite - m_distort_stats.total_tris = 0; - - for (auto& [res, vec] : m_sprite_distorter_instances_by_res) { - vec.clear(); - } - - for (int i = 0; i < m_distort_stats.total_sprites; i++) { - SpriteDistortFrameData frame_data = m_sprite_distorter_frame_data.at(i); - - // Shader just needs the position, tex coords, and scale - auto x_y_z_s = math::Vector4f(frame_data.xyz.x(), frame_data.xyz.y(), frame_data.xyz.z(), - frame_data.st.x()); - auto sx_sy_sz_t = math::Vector4f(frame_data.rgba.x(), frame_data.rgba.y(), frame_data.rgba.z(), - frame_data.st.y()); - - int res = frame_data.flag; - - m_sprite_distorter_instances_by_res[res].push_back({x_y_z_s, sx_sy_sz_t}); - - m_distort_stats.total_tris += res * 2; - } -} - -/*! - * Draws each distort sprite. - */ -void Sprite3::distort_draw(SharedRenderState* render_state, ScopedProfilerNode& prof) { - // First, make sure the distort framebuffer is the correct size - distort_setup_framebuffer_dims(render_state); - - if (m_distort_stats.total_tris == 0) { - // No distort sprites to draw, we can end early - return; - } - - // Do common distort drawing logic - distort_draw_common(render_state, prof); - - // Set up shader - auto shader = &render_state->shaders[ShaderId::SPRITE_DISTORT]; - shader->activate(); - - Vector4f colorf = Vector4f(m_sprite_distorter_sine_tables.color.x() / 255.0f, - m_sprite_distorter_sine_tables.color.y() / 255.0f, - m_sprite_distorter_sine_tables.color.z() / 255.0f, - m_sprite_distorter_sine_tables.color.w() / 255.0f); - glUniform4fv(glGetUniformLocation(shader->id(), "u_color"), 1, colorf.data()); - - // Bind vertex array - glBindVertexArray(m_distort_ogl.vao); - - // Enable prim restart, we need this to break up the triangle strips - glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex(UINT32_MAX); - - // Upload vertex data - glBindBuffer(GL_ARRAY_BUFFER, m_distort_ogl.vertex_buffer); - glBufferData(GL_ARRAY_BUFFER, m_sprite_distorter_vertices.size() * sizeof(SpriteDistortVertex), - m_sprite_distorter_vertices.data(), GL_DYNAMIC_DRAW); - - // Upload element data - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_distort_ogl.index_buffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_sprite_distorter_indices.size() * sizeof(u32), - m_sprite_distorter_indices.data(), GL_DYNAMIC_DRAW); - - // Draw - prof.add_draw_call(); - prof.add_tri(m_distort_stats.total_tris); - - glDrawElements(GL_TRIANGLE_STRIP, m_sprite_distorter_indices.size(), GL_UNSIGNED_INT, (void*)0); - - // Done - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindVertexArray(0); -} - -/*! - * Draws each distort sprite using instanced rendering. - */ -void Sprite3::distort_draw_instanced(SharedRenderState* render_state, ScopedProfilerNode& prof) { - // First, make sure the distort framebuffer is the correct size - distort_setup_framebuffer_dims(render_state); - - if (m_distort_stats.total_tris == 0) { - // No distort sprites to draw, we can end early - return; - } - - // Do common distort drawing logic - distort_draw_common(render_state, prof); - - // Set up shader - auto shader = &render_state->shaders[ShaderId::SPRITE_DISTORT_INSTANCED]; - shader->activate(); - - Vector4f colorf = Vector4f(m_sprite_distorter_sine_tables.color.x() / 255.0f, - m_sprite_distorter_sine_tables.color.y() / 255.0f, - m_sprite_distorter_sine_tables.color.z() / 255.0f, - m_sprite_distorter_sine_tables.color.w() / 255.0f); - glUniform4fv(glGetUniformLocation(shader->id(), "u_color"), 1, colorf.data()); - - // Bind vertex array - glBindVertexArray(m_distort_instanced_ogl.vao); - - // Upload vertex data (if it changed) - if (m_distort_instanced_ogl.vertex_data_changed) { - m_distort_instanced_ogl.vertex_data_changed = false; - - glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.vertex_buffer); - glBufferData(GL_ARRAY_BUFFER, - m_sprite_distorter_vertices_instanced.size() * sizeof(SpriteDistortVertex), - m_sprite_distorter_vertices_instanced.data(), GL_STREAM_DRAW); - } - - // Draw each resolution group - glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.instance_buffer); - prof.add_tri(m_distort_stats.total_tris); - - int vert_offset = 0; - for (int res = 3; res < 12; res++) { - auto& instances = m_sprite_distorter_instances_by_res[res]; - int num_verts = res * 5; - - if (instances.size() > 0) { - // Upload instance data - glBufferData(GL_ARRAY_BUFFER, instances.size() * sizeof(SpriteDistortInstanceData), - instances.data(), GL_DYNAMIC_DRAW); - - // Draw - prof.add_draw_call(); - - glDrawArraysInstanced(GL_TRIANGLE_STRIP, vert_offset, num_verts, instances.size()); - } - - vert_offset += num_verts; - } - - // Done - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); -} - -void Sprite3::distort_draw_common(SharedRenderState* render_state, ScopedProfilerNode& /*prof*/) { - // The distort effect needs to read the current framebuffer, so copy what's been rendered so far - // to a texture that we can then pass to the shader - glBindFramebuffer(GL_READ_FRAMEBUFFER, render_state->render_fb); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_distort_ogl.fbo); - - glBlitFramebuffer(render_state->render_fb_x, // srcX0 - render_state->render_fb_y, // srcY0 - render_state->render_fb_x + render_state->render_fb_w, // srcX1 - render_state->render_fb_y + render_state->render_fb_h, // srcY1 - 0, // dstX0 - 0, // dstY0 - m_distort_ogl.fbo_width, // dstX1 - m_distort_ogl.fbo_height, // dstY1 - GL_COLOR_BUFFER_BIT, // mask - GL_NEAREST // filter - ); - - glBindFramebuffer(GL_FRAMEBUFFER, render_state->render_fb); - - // Set up OpenGL state - m_current_mode.set_depth_write_enable(!m_sprite_distorter_setup.zbuf.zmsk()); // zbuf - glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture); // tex0 - m_current_mode.set_filt_enable(m_sprite_distorter_setup.tex1.mmag()); // tex1 - update_mode_from_alpha1(m_sprite_distorter_setup.alpha.data, m_current_mode); // alpha1 - // note: clamp and miptbp are skipped since that is set up ahead of time with the distort - // framebuffer texture - - setup_opengl_from_draw_mode(m_current_mode, GL_TEXTURE0, false); -} - -void Sprite3::distort_setup_framebuffer_dims(SharedRenderState* render_state) { - // Distort framebuffer must be the same dimensions as the default window framebuffer - if (m_distort_ogl.fbo_width != render_state->render_fb_w || - m_distort_ogl.fbo_height != render_state->render_fb_h) { - m_distort_ogl.fbo_width = render_state->render_fb_w; - m_distort_ogl.fbo_height = render_state->render_fb_h; - - glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_distort_ogl.fbo_width, m_distort_ogl.fbo_height, 0, - GL_RGB, GL_UNSIGNED_BYTE, NULL); - - glBindTexture(GL_TEXTURE_2D, 0); - } -} - /*! * Handle DMA data that does the per-frame setup. * This should get the dma chain immediately after the call to sprite-draw-distorters. @@ -1026,23 +399,29 @@ void Sprite3::render_jak2(DmaFollower& dma, auto child = prof.make_scoped_child("2d-group1"); render_2d_group1(dma, render_state, child); flush_sprites(render_state, prof, true); + auto nop_flushe = dma.read_and_advance(); + ASSERT(nop_flushe.vifcode0().kind == VifCode::Kind::NOP); + ASSERT(nop_flushe.vifcode1().kind == VifCode::Kind::FLUSHE); } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); - // TODO finish this up. + { + auto p = prof.make_scoped_child("glow"); + glow_dma_and_draw(dma, render_state, p); + } + // fmt::print("next bucket is 0x{}\n", render_state->next_bucket); while (dma.current_tag_offset() != render_state->next_bucket) { - // auto tag = dma.current_tag(); - // fmt::print("@ 0x{:x} tag: {}", dma.current_tag_offset(), tag.print()); + // auto tag = dma.current_tag(); auto data = dma.read_and_advance(); - VifCode code(data.vif0()); + (void)data; + // VifCode code(data.vif0()); + // fmt::print("@ 0x{:x} tag: {}", dma.current_tag_offset(), tag.print()); // fmt::print(" vif0: {}\n", code.print()); - if (code.kind == VifCode::Kind::NOP) { - // fmt::print(" vif1: {}\n", VifCode(data.vif1()).print()); - } + // fmt::print(" vif1: {}\n", VifCode(data.vif1()).print()); } } @@ -1128,6 +507,8 @@ void Sprite3::draw_debug_window() { ImGui::Checkbox("3d", &m_3d_enable); ImGui::Checkbox("Distort", &m_distort_enable); ImGui::Checkbox("Distort instancing", &m_enable_distort_instancing); + ImGui::Separator(); + m_glow_renderer.draw_debug_window(); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/game/graphics/opengl_renderer/Sprite3.h b/game/graphics/opengl_renderer/sprite/Sprite3.h similarity index 95% rename from game/graphics/opengl_renderer/Sprite3.h rename to game/graphics/opengl_renderer/sprite/Sprite3.h index 5b7777cff..0555d7c35 100644 --- a/game/graphics/opengl_renderer/Sprite3.h +++ b/game/graphics/opengl_renderer/sprite/Sprite3.h @@ -9,7 +9,8 @@ #include "game/graphics/opengl_renderer/BucketRenderer.h" #include "game/graphics/opengl_renderer/DirectRenderer.h" #include "game/graphics/opengl_renderer/background/background_common.h" -#include "game/graphics/opengl_renderer/sprite_common.h" +#include "game/graphics/opengl_renderer/sprite/GlowRenderer.h" +#include "game/graphics/opengl_renderer/sprite/sprite_common.h" class Sprite3 : public BucketRenderer { public: @@ -61,6 +62,11 @@ class Sprite3 : public BucketRenderer { void flush_sprites(SharedRenderState* render_state, ScopedProfilerNode& prof, bool double_draw); + GlowRenderer m_glow_renderer; + void glow_dma_and_draw(DmaFollower& dma, + SharedRenderState* render_state, + ScopedProfilerNode& prof); + struct SpriteDistorterSetup { GifTag gif_tag; GsZbuf zbuf; diff --git a/game/graphics/opengl_renderer/sprite/Sprite3_Distort.cpp b/game/graphics/opengl_renderer/sprite/Sprite3_Distort.cpp new file mode 100644 index 000000000..8e293e70f --- /dev/null +++ b/game/graphics/opengl_renderer/sprite/Sprite3_Distort.cpp @@ -0,0 +1,630 @@ +#include "Sprite3.h" +#include "game/graphics/opengl_renderer/dma_helpers.h" + +namespace { +/*! + * Does the next DMA transfer look like the frame data for sprite distort? + */ +bool looks_like_distort_frame_data(const DmaFollower& dma) { + return dma.current_tag().kind == DmaTag::Kind::CNT && + dma.current_tag_vifcode0().kind == VifCode::Kind::NOP && + dma.current_tag_vifcode1().kind == VifCode::Kind::UNPACK_V4_32; +} + +constexpr int SPRITE_RENDERER_MAX_DISTORT_SPRITES = + 256 * 10; // size of sprite-aux-list in GOAL code * SPRITE_MAX_AMOUNT_MULT +} // namespace + +void Sprite3::opengl_setup_distort() { + // Create framebuffer to snapshot current render to a texture that can be bound for the distort + // shader This will represent tex0 from the original GS data + glGenFramebuffers(1, &m_distort_ogl.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, m_distort_ogl.fbo); + + glGenTextures(1, &m_distort_ogl.fbo_texture); + glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_distort_ogl.fbo_width, m_distort_ogl.fbo_height, 0, + GL_RGB, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + // Texture clamping here matches the GS init data for distort + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + m_distort_ogl.fbo_texture, 0); + + ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Non-instancing + // ---------------------- + glGenBuffers(1, &m_distort_ogl.vertex_buffer); + glGenVertexArrays(1, &m_distort_ogl.vao); + glBindVertexArray(m_distort_ogl.vao); + glBindBuffer(GL_ARRAY_BUFFER, m_distort_ogl.vertex_buffer); + // note: each sprite shares a single vertex per slice, account for that here + int distort_vert_buffer_len = + SPRITE_RENDERER_MAX_DISTORT_SPRITES * + ((5 - 1) * 11 + 1); // max * ((verts_per_slice - 1) * max_slices + 1) + glBufferData(GL_ARRAY_BUFFER, distort_vert_buffer_len * sizeof(SpriteDistortVertex), nullptr, + GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, // location 0 in the shader + 3, // 3 floats per vert + GL_FLOAT, // floats + GL_FALSE, // don't normalize, ignored + sizeof(SpriteDistortVertex), // + (void*)offsetof(SpriteDistortVertex, xyz) // offset in array + ); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, // location 1 in the shader + 2, // 2 floats per vert + GL_FLOAT, // floats + GL_FALSE, // don't normalize, ignored + sizeof(SpriteDistortVertex), // + (void*)offsetof(SpriteDistortVertex, st) // offset in array + ); + + // note: add one extra element per sprite that marks the end of a triangle strip + int distort_idx_buffer_len = SPRITE_RENDERER_MAX_DISTORT_SPRITES * + ((5 * 11) + 1); // max * ((verts_per_slice * max_slices) + 1) + glGenBuffers(1, &m_distort_ogl.index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_distort_ogl.index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, distort_idx_buffer_len * sizeof(u32), nullptr, + GL_DYNAMIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + m_sprite_distorter_vertices.resize(distort_vert_buffer_len); + m_sprite_distorter_indices.resize(distort_idx_buffer_len); + m_sprite_distorter_frame_data.resize(SPRITE_RENDERER_MAX_DISTORT_SPRITES); + + // Instancing + // ---------------------- + glGenVertexArrays(1, &m_distort_instanced_ogl.vao); + glBindVertexArray(m_distort_instanced_ogl.vao); + + int distort_max_sprite_slices = 0; + for (int i = 3; i < 12; i++) { + // For each 'resolution', there can be that many slices + distort_max_sprite_slices += i; + } + + glGenBuffers(1, &m_distort_instanced_ogl.vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.vertex_buffer); + + int distort_instanced_vert_buffer_len = distort_max_sprite_slices * 5; // 5 vertices per slice + glBufferData(GL_ARRAY_BUFFER, distort_instanced_vert_buffer_len * sizeof(SpriteDistortVertex), + nullptr, GL_STREAM_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, // location 0 in the shader + 3, // 3 floats per vert + GL_FLOAT, // floats + GL_FALSE, // don't normalize, ignored + sizeof(SpriteDistortVertex), // + (void*)offsetof(SpriteDistortVertex, xyz) // offset in array + ); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, // location 1 in the shader + 2, // 2 floats per vert + GL_FLOAT, // floats + GL_FALSE, // don't normalize, ignored + sizeof(SpriteDistortVertex), // + (void*)offsetof(SpriteDistortVertex, st) // offset in array + ); + + glGenBuffers(1, &m_distort_instanced_ogl.instance_buffer); + glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.instance_buffer); + + int distort_instance_buffer_len = SPRITE_RENDERER_MAX_DISTORT_SPRITES; + glBufferData(GL_ARRAY_BUFFER, distort_instance_buffer_len * sizeof(SpriteDistortInstanceData), + nullptr, GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, // location 2 in the shader + 4, // 4 floats per vert + GL_FLOAT, // floats + GL_FALSE, // normalized, ignored, + sizeof(SpriteDistortInstanceData), // + (void*)offsetof(SpriteDistortInstanceData, x_y_z_s) // offset in array + ); + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, // location 3 in the shader + 4, // 4 floats per vert + GL_FLOAT, // floats + GL_FALSE, // normalized, ignored, + sizeof(SpriteDistortInstanceData), // + (void*)offsetof(SpriteDistortInstanceData, sx_sy_sz_t) // offset in array + ); + + glVertexAttribDivisor(2, 1); + glVertexAttribDivisor(3, 1); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + m_sprite_distorter_vertices_instanced.resize(distort_instanced_vert_buffer_len); + + for (int i = 3; i < 12; i++) { + auto vec = std::vector(); + vec.resize(distort_instance_buffer_len); + + m_sprite_distorter_instances_by_res[i] = vec; + } +} + +/*! + * Run the sprite distorter. + */ +void Sprite3::render_distorter(DmaFollower& dma, + SharedRenderState* render_state, + ScopedProfilerNode& prof) { + // Skip to distorter DMA + m_direct.reset_state(); + while (dma.current_tag().qwc != 7) { + auto direct_data = dma.read_and_advance(); + m_direct.render_vif(direct_data.vif0(), direct_data.vif1(), direct_data.data, + direct_data.size_bytes, render_state, prof); + } + m_direct.flush_pending(render_state, prof); + + // Read DMA + { + auto prof_node = prof.make_scoped_child("dma"); + distort_dma(dma, prof_node); + } + + if (!m_enabled || !m_distort_enable) { + // Distort disabled, we can stop here since all the DMA has been read + return; + } + + // Set up vertex data + { + auto prof_node = prof.make_scoped_child("setup"); + if (m_enable_distort_instancing) { + distort_setup_instanced(prof_node); + } else { + distort_setup(prof_node); + } + } + + // Draw + { + auto prof_node = prof.make_scoped_child("drawing"); + if (m_enable_distort_instancing) { + distort_draw_instanced(render_state, prof_node); + } else { + distort_draw(render_state, prof_node); + } + } +} + +/*! + * Reads all sprite distort related DMA packets. + */ +void Sprite3::distort_dma(DmaFollower& dma, ScopedProfilerNode& /*prof*/) { + // First should be the GS setup + auto sprite_distorter_direct_setup = dma.read_and_advance(); + ASSERT(sprite_distorter_direct_setup.vifcode0().kind == VifCode::Kind::NOP); + ASSERT(sprite_distorter_direct_setup.vifcode1().kind == VifCode::Kind::DIRECT); + ASSERT(sprite_distorter_direct_setup.vifcode1().immediate == 7); + memcpy(&m_sprite_distorter_setup, sprite_distorter_direct_setup.data, 7 * 16); + + auto gif_tag = m_sprite_distorter_setup.gif_tag; + ASSERT(gif_tag.nloop() == 1); + ASSERT(gif_tag.eop() == 1); + ASSERT(gif_tag.nreg() == 6); + ASSERT(gif_tag.reg(0) == GifTag::RegisterDescriptor::AD); + + auto zbuf1 = m_sprite_distorter_setup.zbuf; + ASSERT(zbuf1.zbp() == 0x1c0); + ASSERT(zbuf1.zmsk() == true); + ASSERT(zbuf1.psm() == TextureFormat::PSMZ24); + + auto tex0 = m_sprite_distorter_setup.tex0; + ASSERT(tex0.tbw() == 8); + ASSERT(tex0.tw() == 9); + ASSERT(tex0.th() == 8); + + auto tex1 = m_sprite_distorter_setup.tex1; + ASSERT(tex1.mmag() == true); + ASSERT(tex1.mmin() == 1); + + auto alpha = m_sprite_distorter_setup.alpha; + ASSERT(alpha.a_mode() == GsAlpha::BlendMode::SOURCE); + ASSERT(alpha.b_mode() == GsAlpha::BlendMode::DEST); + ASSERT(alpha.c_mode() == GsAlpha::BlendMode::SOURCE); + ASSERT(alpha.d_mode() == GsAlpha::BlendMode::DEST); + + // Next is the aspect used by the sine tables (PC only) + // + // This was added to let the renderer reliably detect when the sine tables changed, + // which is whenever the aspect ratio changed. However, the tables aren't always + // updated on the same frame that the aspect changed, so this just lets the game + // easily notify the renderer when it finally does get updated. + auto sprite_distort_tables_aspect = dma.read_and_advance(); + ASSERT(sprite_distort_tables_aspect.size_bytes == 16); + ASSERT(sprite_distort_tables_aspect.vifcode1().kind == VifCode::Kind::PC_PORT); + memcpy(&m_sprite_distorter_sine_tables_aspect, sprite_distort_tables_aspect.data, + sizeof(math::Vector4f)); + + // Next thing should be the sine tables + auto sprite_distorter_tables = dma.read_and_advance(); + unpack_to_stcycl(&m_sprite_distorter_sine_tables, sprite_distorter_tables, + VifCode::Kind::UNPACK_V4_32, 4, 4, 0x8b * 16, 0x160, false, false); + + ASSERT(GsPrim(m_sprite_distorter_sine_tables.gs_gif_tag.prim()).kind() == + GsPrim::Kind::TRI_STRIP); + + // Finally, should be frame data packets (containing sprites) + // Up to 170 sprites will be DMA'd at a time followed by a mscalf, + // and this process can happen twice up to a maximum of 256 sprites DMA'd + // (256 is the size of sprite-aux-list which drives this). + int sprite_idx = 0; + m_distort_stats.total_sprites = 0; + + while (looks_like_distort_frame_data(dma)) { + math::Vector num_sprites_vec; + + // Read sprite packets + do { + int qwc = dma.current_tag().qwc; + int dest = dma.current_tag_vifcode1().immediate; + auto distort_data = dma.read_and_advance(); + + if (dest == 511) { + // VU address 511 specifies the number of sprites + unpack_to_no_stcycl(&num_sprites_vec, distort_data, VifCode::Kind::UNPACK_V4_32, 16, dest, + false, false); + } else { + // VU address >= 512 is the actual vertex data + ASSERT(dest >= 512); + ASSERT(sprite_idx + (qwc / 3) <= (int)m_sprite_distorter_frame_data.capacity()); + + unpack_to_no_stcycl(&m_sprite_distorter_frame_data.at(sprite_idx), distort_data, + VifCode::Kind::UNPACK_V4_32, qwc * 16, dest, false, false); + + sprite_idx += qwc / 3; + } + } while (looks_like_distort_frame_data(dma)); + + // Sprite packets should always end with a mscalf flush + ASSERT(dma.current_tag().kind == DmaTag::Kind::CNT); + ASSERT(dma.current_tag_vifcode0().kind == VifCode::Kind::MSCALF); + ASSERT(dma.current_tag_vifcode1().kind == VifCode::Kind::FLUSH); + dma.read_and_advance(); + + m_distort_stats.total_sprites += num_sprites_vec.x(); + } + + // Done + ASSERT(m_distort_stats.total_sprites <= SPRITE_RENDERER_MAX_DISTORT_SPRITES); +} + +/*! + * Sets up OpenGL data for each distort sprite. + */ +void Sprite3::distort_setup(ScopedProfilerNode& /*prof*/) { + m_distort_stats.total_tris = 0; + + m_sprite_distorter_vertices.clear(); + m_sprite_distorter_indices.clear(); + + int sprite_idx = 0; + int sprites_left = m_distort_stats.total_sprites; + + // This part is mostly ripped from the VU program + while (sprites_left != 0) { + // flag seems to represent the 'resolution' of the circle sprite used to create the distortion + // effect For example, a flag value of 3 will create a circle using 3 "pie-slice" shapes + u32 flag = m_sprite_distorter_frame_data.at(sprite_idx).flag; + u32 slices_left = flag; + + // flag has a minimum value of 3 which represents the first ientry + // Additionally, the ientry index has 352 added to it (which is the start of the entry array + // in VU memory), so we need to subtract that as well + int entry_index = m_sprite_distorter_sine_tables.ientry[flag - 3].x() - 352; + + // Here would be adding the giftag, but we don't need that + + // Get the frame data for the next distort sprite + SpriteDistortFrameData frame_data = m_sprite_distorter_frame_data.at(sprite_idx); + sprite_idx++; + + // Build the OpenGL data for the sprite + math::Vector2f vf03 = frame_data.st; + math::Vector3f vf14 = frame_data.xyz; + + // Each slice shares a center vertex, we can use this fact and cut out duplicate vertices + u32 center_vert_idx = m_sprite_distorter_vertices.size(); + m_sprite_distorter_vertices.push_back({vf14, vf03}); + + do { + math::Vector3f vf06 = m_sprite_distorter_sine_tables.entry[entry_index++].xyz(); + math::Vector2f vf07 = m_sprite_distorter_sine_tables.entry[entry_index++].xy(); + math::Vector3f vf08 = m_sprite_distorter_sine_tables.entry[entry_index + 0].xyz(); + math::Vector2f vf09 = m_sprite_distorter_sine_tables.entry[entry_index + 1].xy(); + + slices_left--; + + math::Vector2f vf11 = (vf07 * frame_data.rgba.z()) + frame_data.st; + math::Vector2f vf13 = (vf09 * frame_data.rgba.z()) + frame_data.st; + math::Vector3f vf06_2 = (vf06 * frame_data.rgba.x()) + frame_data.xyz; + math::Vector2f vf07_2 = (vf07 * frame_data.rgba.x()) + frame_data.st; + math::Vector3f vf08_2 = (vf08 * frame_data.rgba.x()) + frame_data.xyz; + math::Vector2f vf09_2 = (vf09 * frame_data.rgba.x()) + frame_data.st; + math::Vector3f vf10 = (vf06 * frame_data.rgba.y()) + frame_data.xyz; + math::Vector3f vf12 = (vf08 * frame_data.rgba.y()) + frame_data.xyz; + math::Vector3f vf06_3 = vf06_2; + math::Vector3f vf08_3 = vf08_2; + + m_sprite_distorter_indices.push_back(m_sprite_distorter_vertices.size()); + m_sprite_distorter_vertices.push_back({vf06_3, vf07_2}); + + m_sprite_distorter_indices.push_back(m_sprite_distorter_vertices.size()); + m_sprite_distorter_vertices.push_back({vf08_3, vf09_2}); + + m_sprite_distorter_indices.push_back(m_sprite_distorter_vertices.size()); + m_sprite_distorter_vertices.push_back({vf10, vf11}); + + m_sprite_distorter_indices.push_back(m_sprite_distorter_vertices.size()); + m_sprite_distorter_vertices.push_back({vf12, vf13}); + + // Originally, would add the shared center vertex, but in our case we can just add the index + m_sprite_distorter_indices.push_back(center_vert_idx); + // m_sprite_distorter_vertices.push_back({vf14, vf03}); + + m_distort_stats.total_tris += 2; + } while (slices_left != 0); + + // Mark the end of the triangle strip + m_sprite_distorter_indices.push_back(UINT32_MAX); + + sprites_left--; + } +} + +/*! + * Sets up OpenGL data for rendering distort sprites using instanced rendering. + * + * A mesh is built once for each possible sprite resolution and is only re-built + * when the dimensions of the window are changed. These meshes are built just like + * the triangle strips in the VU program, but with the sprite-specific data removed. + * + * Required sprite-specific frame data is kept as is and is grouped by resolution. + */ +void Sprite3::distort_setup_instanced(ScopedProfilerNode& /*prof*/) { + if (m_distort_instanced_ogl.last_aspect_x != m_sprite_distorter_sine_tables_aspect.x() || + m_distort_instanced_ogl.last_aspect_y != m_sprite_distorter_sine_tables_aspect.y()) { + m_distort_instanced_ogl.last_aspect_x = m_sprite_distorter_sine_tables_aspect.x(); + m_distort_instanced_ogl.last_aspect_y = m_sprite_distorter_sine_tables_aspect.y(); + // Aspect ratio changed, which means we have a new sine table + m_sprite_distorter_vertices_instanced.clear(); + + // Build a mesh for every possible distort sprite resolution + auto vf03 = math::Vector2f(0, 0); + auto vf14 = math::Vector3f(0, 0, 0); + + for (int res = 3; res < 12; res++) { + int entry_index = m_sprite_distorter_sine_tables.ientry[res - 3].x() - 352; + + for (int i = 0; i < res; i++) { + math::Vector3f vf06 = m_sprite_distorter_sine_tables.entry[entry_index++].xyz(); + math::Vector2f vf07 = m_sprite_distorter_sine_tables.entry[entry_index++].xy(); + math::Vector3f vf08 = m_sprite_distorter_sine_tables.entry[entry_index + 0].xyz(); + math::Vector2f vf09 = m_sprite_distorter_sine_tables.entry[entry_index + 1].xy(); + + // Normally, there would be a bunch of transformations here against the sprite data. + // Instead, we'll let the shader do it and just store the sine table specific parts here. + + m_sprite_distorter_vertices_instanced.push_back({vf06, vf07}); + m_sprite_distorter_vertices_instanced.push_back({vf08, vf09}); + m_sprite_distorter_vertices_instanced.push_back({vf06, vf07}); + m_sprite_distorter_vertices_instanced.push_back({vf08, vf09}); + m_sprite_distorter_vertices_instanced.push_back({vf14, vf03}); + } + } + + m_distort_instanced_ogl.vertex_data_changed = true; + } + + // Set up instance data for each sprite + m_distort_stats.total_tris = 0; + + for (auto& [res, vec] : m_sprite_distorter_instances_by_res) { + vec.clear(); + } + + for (int i = 0; i < m_distort_stats.total_sprites; i++) { + SpriteDistortFrameData frame_data = m_sprite_distorter_frame_data.at(i); + + // Shader just needs the position, tex coords, and scale + auto x_y_z_s = math::Vector4f(frame_data.xyz.x(), frame_data.xyz.y(), frame_data.xyz.z(), + frame_data.st.x()); + auto sx_sy_sz_t = math::Vector4f(frame_data.rgba.x(), frame_data.rgba.y(), frame_data.rgba.z(), + frame_data.st.y()); + + int res = frame_data.flag; + + m_sprite_distorter_instances_by_res[res].push_back({x_y_z_s, sx_sy_sz_t}); + + m_distort_stats.total_tris += res * 2; + } +} + +/*! + * Draws each distort sprite. + */ +void Sprite3::distort_draw(SharedRenderState* render_state, ScopedProfilerNode& prof) { + // First, make sure the distort framebuffer is the correct size + distort_setup_framebuffer_dims(render_state); + + if (m_distort_stats.total_tris == 0) { + // No distort sprites to draw, we can end early + return; + } + + // Do common distort drawing logic + distort_draw_common(render_state, prof); + + // Set up shader + auto shader = &render_state->shaders[ShaderId::SPRITE_DISTORT]; + shader->activate(); + + Vector4f colorf = Vector4f(m_sprite_distorter_sine_tables.color.x() / 255.0f, + m_sprite_distorter_sine_tables.color.y() / 255.0f, + m_sprite_distorter_sine_tables.color.z() / 255.0f, + m_sprite_distorter_sine_tables.color.w() / 255.0f); + glUniform4fv(glGetUniformLocation(shader->id(), "u_color"), 1, colorf.data()); + + // Bind vertex array + glBindVertexArray(m_distort_ogl.vao); + + // Enable prim restart, we need this to break up the triangle strips + glEnable(GL_PRIMITIVE_RESTART); + glPrimitiveRestartIndex(UINT32_MAX); + + // Upload vertex data + glBindBuffer(GL_ARRAY_BUFFER, m_distort_ogl.vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, m_sprite_distorter_vertices.size() * sizeof(SpriteDistortVertex), + m_sprite_distorter_vertices.data(), GL_DYNAMIC_DRAW); + + // Upload element data + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_distort_ogl.index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_sprite_distorter_indices.size() * sizeof(u32), + m_sprite_distorter_indices.data(), GL_DYNAMIC_DRAW); + + // Draw + prof.add_draw_call(); + prof.add_tri(m_distort_stats.total_tris); + + glDrawElements(GL_TRIANGLE_STRIP, m_sprite_distorter_indices.size(), GL_UNSIGNED_INT, (void*)0); + + // Done + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +/*! + * Draws each distort sprite using instanced rendering. + */ +void Sprite3::distort_draw_instanced(SharedRenderState* render_state, ScopedProfilerNode& prof) { + // First, make sure the distort framebuffer is the correct size + distort_setup_framebuffer_dims(render_state); + + if (m_distort_stats.total_tris == 0) { + // No distort sprites to draw, we can end early + return; + } + + // Do common distort drawing logic + distort_draw_common(render_state, prof); + + // Set up shader + auto shader = &render_state->shaders[ShaderId::SPRITE_DISTORT_INSTANCED]; + shader->activate(); + + Vector4f colorf = Vector4f(m_sprite_distorter_sine_tables.color.x() / 255.0f, + m_sprite_distorter_sine_tables.color.y() / 255.0f, + m_sprite_distorter_sine_tables.color.z() / 255.0f, + m_sprite_distorter_sine_tables.color.w() / 255.0f); + glUniform4fv(glGetUniformLocation(shader->id(), "u_color"), 1, colorf.data()); + + // Bind vertex array + glBindVertexArray(m_distort_instanced_ogl.vao); + + // Upload vertex data (if it changed) + if (m_distort_instanced_ogl.vertex_data_changed) { + m_distort_instanced_ogl.vertex_data_changed = false; + + glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, + m_sprite_distorter_vertices_instanced.size() * sizeof(SpriteDistortVertex), + m_sprite_distorter_vertices_instanced.data(), GL_STREAM_DRAW); + } + + // Draw each resolution group + glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.instance_buffer); + prof.add_tri(m_distort_stats.total_tris); + + int vert_offset = 0; + for (int res = 3; res < 12; res++) { + auto& instances = m_sprite_distorter_instances_by_res[res]; + int num_verts = res * 5; + + if (instances.size() > 0) { + // Upload instance data + glBufferData(GL_ARRAY_BUFFER, instances.size() * sizeof(SpriteDistortInstanceData), + instances.data(), GL_DYNAMIC_DRAW); + + // Draw + prof.add_draw_call(); + + glDrawArraysInstanced(GL_TRIANGLE_STRIP, vert_offset, num_verts, instances.size()); + } + + vert_offset += num_verts; + } + + // Done + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void Sprite3::distort_draw_common(SharedRenderState* render_state, ScopedProfilerNode& /*prof*/) { + // The distort effect needs to read the current framebuffer, so copy what's been rendered so far + // to a texture that we can then pass to the shader + glBindFramebuffer(GL_READ_FRAMEBUFFER, render_state->render_fb); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_distort_ogl.fbo); + + glBlitFramebuffer(render_state->render_fb_x, // srcX0 + render_state->render_fb_y, // srcY0 + render_state->render_fb_x + render_state->render_fb_w, // srcX1 + render_state->render_fb_y + render_state->render_fb_h, // srcY1 + 0, // dstX0 + 0, // dstY0 + m_distort_ogl.fbo_width, // dstX1 + m_distort_ogl.fbo_height, // dstY1 + GL_COLOR_BUFFER_BIT, // mask + GL_NEAREST // filter + ); + + glBindFramebuffer(GL_FRAMEBUFFER, render_state->render_fb); + + // Set up OpenGL state + m_current_mode.set_depth_write_enable(!m_sprite_distorter_setup.zbuf.zmsk()); // zbuf + glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture); // tex0 + m_current_mode.set_filt_enable(m_sprite_distorter_setup.tex1.mmag()); // tex1 + update_mode_from_alpha1(m_sprite_distorter_setup.alpha.data, m_current_mode); // alpha1 + // note: clamp and miptbp are skipped since that is set up ahead of time with the distort + // framebuffer texture + + setup_opengl_from_draw_mode(m_current_mode, GL_TEXTURE0, false); +} + +void Sprite3::distort_setup_framebuffer_dims(SharedRenderState* render_state) { + // Distort framebuffer must be the same dimensions as the default window framebuffer + if (m_distort_ogl.fbo_width != render_state->render_fb_w || + m_distort_ogl.fbo_height != render_state->render_fb_h) { + m_distort_ogl.fbo_width = render_state->render_fb_w; + m_distort_ogl.fbo_height = render_state->render_fb_h; + + glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_distort_ogl.fbo_width, m_distort_ogl.fbo_height, 0, + GL_RGB, GL_UNSIGNED_BYTE, NULL); + + glBindTexture(GL_TEXTURE_2D, 0); + } +} diff --git a/game/graphics/opengl_renderer/sprite/Sprite3_Glow.cpp b/game/graphics/opengl_renderer/sprite/Sprite3_Glow.cpp new file mode 100644 index 000000000..dd0de9bea --- /dev/null +++ b/game/graphics/opengl_renderer/sprite/Sprite3_Glow.cpp @@ -0,0 +1,228 @@ +#include "game/graphics/opengl_renderer/sprite/Sprite3.h" + +struct SpriteGlowData { + float pos[3]; + float size_x; + + float size_probe; + float z_offset; + float rot_angle; + float size_y; + + float color[4]; + + float fade_a; + float fade_b; + u32 tex_id; + u32 dummy; +}; +static_assert(sizeof(SpriteGlowData) == 16 * 4); + +/*! + * Transformation math from the sprite-glow vu1 program. + * Populates the SpriteGlowOutput struct with the same data that would get filled into the + * output template on VU1. Excludes float to int conversions. + * + * Not a particularly efficient implementation, but I think the total number of glow sprites is + * small, so not a big deal. + */ +bool glow_math(const SpriteGlowConsts* consts, + const void* vec_data, + const void* adgif_data, + SpriteGlowOutput* out) { + const auto* in = (const SpriteGlowData*)vec_data; + static_assert(sizeof(out->adgif) == 5 * 16); + memcpy(&out->adgif, adgif_data, 5 * 16); + + // the transformation here is a bit strange - there's two matrix multiplies. + // one for camera, and one for perspective. Usually they do one, or when they really need both + // for stuff like emerc, they optimize knowing which entires of perspective are always 0. + // But not this time. My guess is that the VU program time is very small compared to actual + // drawing, so they don't really care. + + // Transform point to camera frame. + Vector4f p0 = consts->camera[3] + consts->camera[0] * in->pos[0] + + consts->camera[1] * in->pos[1] + consts->camera[2] * in->pos[2]; + + // Compute fade. Interestingly, the fade is computed based on depth, not distance from the camera. + // I think this is kind of wrong, and it leads to some weird fadeout behavior. + float fade = in->fade_a * p0.z() + in->fade_b; // fade_a is negative + if (fade < 0) + fade = 0; + if (fade > 1) + fade = 1; + + // Adjust color based on fade. + Vector4f rgba(in->color[0], in->color[1], in->color[2], in->color[3]); + rgba.x() *= rgba.w() * fade / 128.f; + rgba.y() *= rgba.w() * fade / 128.f; + rgba.z() *= rgba.w() * fade / 128.f; + out->flare_draw_color = rgba; + + // Apply an offset. This moves the point along a line between its original position, and the + // camera (so this offset doesn't make the thing move up/down/left/right on screen, just "toward" + // the camera). + float pscale = 1.f - (in->z_offset / p0.z()); + p0.x() *= pscale; + p0.y() *= pscale; + p0.z() *= pscale; + + // Apply perspective transformation (no divide yet) + p0 = consts->perspective[3] + consts->perspective[0] * p0.x() + consts->perspective[1] * p0.y() + + consts->perspective[2] * p0.z(); + + // HMGE's meaning is unknown, but it's scaling factors for clipping. Apply those, and reject if + // the origin is off-screen. + Vector4f pos_hmged = p0.elementwise_multiply(consts->hmge); + float clip_plus = std::abs(pos_hmged.w()); + float clip_minus = -clip_plus; + if (pos_hmged.x() > clip_plus || pos_hmged.x() < clip_minus) + return false; + if (pos_hmged.y() > clip_plus || pos_hmged.y() < clip_minus) + return false; + if (pos_hmged.z() > clip_plus || pos_hmged.z() < clip_minus) + return false; + + // apply perspective divide. Interestingly using hmge's w here... + float perspective_q = 1.f / pos_hmged.w(); + p0.x() *= perspective_q; + p0.y() *= perspective_q; + p0.z() *= perspective_q; + out->perspective_q = perspective_q; + + // apply offset to final point. These offsets are applied after perspective divide, and are + // required for the PS2 screen coordinates (centered at 2048, 2048). + p0 += consts->hvdf; + + // from this point on, things are in screen coordinates. So our sizes (not screen coordinates) + // should be scaled by q to become sizes in screen coordinates. + Vector4f vf02(in->size_probe, in->z_offset, in->size_x, in->size_y); + vf02 *= perspective_q; + + // clamp the probe size to be in (1, clamp_max.w) + if (vf02.x() < 1) + vf02.x() = 1; // size_probe + if (vf02.x() > consts->clamp_max.w()) + vf02.x() = consts->clamp_max.w(); // size probe + + // clamp the maximum size_x/size_y to clamp_max.z + if (vf02.z() > consts->clamp_max.z()) + vf02.z() = consts->clamp_max.z(); // size x + if (vf02.w() > consts->clamp_max.z()) + vf02.w() = consts->clamp_max.z(); // size y + + // compute the minimum safe position for the center of the probe, so corner ends up at min/max + math::Vector2f vf09_min_probe_center(consts->clamp_min.x() + vf02.x(), + consts->clamp_min.y() + vf02.x()); + math::Vector2f vf10_max_probe_center(consts->clamp_max.x() - vf02.x(), + consts->clamp_max.y() - vf02.x()); + + // clear corners. these don't have rotation applied, I guess (vf11, vf12) + out->second_clear_pos[0] = Vector4f(p0.x() - vf02.x(), p0.y() - vf02.x(), p0.z(), p0.w()); + out->second_clear_pos[1] = Vector4f(p0.x() + vf02.x(), p0.y() + vf02.x(), p0.z(), p0.w()); + + // compute offset from center of sprite to corner. This includes the rotation + math::Vector2f basis_x(consts->basis_x[0], 0); // x scaling factor + math::Vector2f basis_y(0, consts->basis_y[1]); // y scarling factor + // rotate them + float rot_rad = in->rot_angle * consts->deg_to_rad; + float rot_sin = std::sin(rot_rad); + float rot_cos = std::cos(rot_rad); + math::Vector2f vf15_rotated_basis_x = basis_x * rot_sin - basis_y * rot_cos; + math::Vector2f vf16_rotated_basis_y = basis_x * rot_cos + basis_y * rot_sin; + vf15_rotated_basis_x *= vf02.z(); // scale x + vf16_rotated_basis_y *= vf02.w(); // scale y + + // limit position so the clear doesn't go out of bounds + // max.xy vf20, vf01, vf09 -> is this bugged? I think the x broadcast here is wrong + // this breaks fadeout as the sprite moves off the top of the screen. I've fixed it here because + // I'm pretty sure this is just a mistake. + math::Vector2f vf20_pos(std::max(p0.x(), vf09_min_probe_center.x()), + std::max(p0.y(), vf09_min_probe_center.y())); + vf20_pos.min_in_place(vf10_max_probe_center); + + // vf17 thing, vf18 thing + math::Vector2f vf17(consts->clamp_min.x() - 1, consts->clamp_min.y() - 1); + math::Vector2f vf18(consts->clamp_min.x() + 1, consts->clamp_min.y() + 1); + vf17 = vf20_pos - vf17; + vf17 -= vf02.x(); + vf18 = vf20_pos - vf18; + vf18 += vf02.x(); + out->offscreen_uv[0] = vf17; + out->offscreen_uv[1] = vf18; + + out->first_clear_pos[0] = + Vector4f(vf20_pos.x() - vf02.x() - 1, vf20_pos.y() - vf02.x() - 1, 0xffffff, p0.w()); + out->first_clear_pos[1] = + Vector4f(vf20_pos.x() + vf02.x() + 1, vf20_pos.y() + vf02.x() + 1, 0xffffff, p0.w()); + + // mulaw.xyzw ACC, vf01, vf00 + // maddax.xyzw ACC, vf15, vf11 + // maddy.xyzw vf11, vf16, vf11 + for (int i = 0; i < 4; i++) { + out->flare_xyzw[i] = p0; + math::Vector2f off = (vf15_rotated_basis_x * consts->xy_array[i].x()) + + (vf16_rotated_basis_y * consts->xy_array[i].y()); + out->flare_xyzw[i].x() += off.x(); + out->flare_xyzw[i].y() += off.y(); + } + return true; +} + +/*! + * Handle glow dma and draw glow sprites using GlowRenderer + */ +void Sprite3::glow_dma_and_draw(DmaFollower& dma, + SharedRenderState* render_state, + ScopedProfilerNode& prof) { + auto maybe_consts_setup = dma.read_and_advance(); + if (maybe_consts_setup.size_bytes != sizeof(SpriteGlowConsts)) { + fmt::print("no consts...\n"); + return; + } + SpriteGlowConsts consts; + memcpy(&consts, maybe_consts_setup.data, sizeof(SpriteGlowConsts)); + + auto templ_1 = dma.read_and_advance(); + ASSERT(templ_1.size_bytes == 16 * 0x54); + + auto templ_2 = dma.read_and_advance(); + ASSERT(templ_2.size_bytes == 16 * 0x54); + + auto bo = dma.read_and_advance(); + ASSERT(bo.size_bytes == 0); + + auto flushe = dma.read_and_advance(); + ASSERT(flushe.size_bytes == 0); + + auto control_xfer = dma.read_and_advance(); + while (control_xfer.size_bytes == 0 && control_xfer.vifcode0().kind == VifCode::Kind::NOP && + control_xfer.vifcode1().kind == VifCode::Kind::NOP) { + control_xfer = dma.read_and_advance(); + } + while (control_xfer.size_bytes == 16) { + auto vecdata_xfer = dma.read_and_advance(); + auto shader_xfer = dma.read_and_advance(); + auto call = dma.read_and_advance(); + (void)call; + + u32 num_sprites; + memcpy(&num_sprites, control_xfer.data, 4); + ASSERT(num_sprites == 1); // always, for whatever reason. + + ASSERT(vecdata_xfer.size_bytes == 4 * 16); + ASSERT(shader_xfer.size_bytes == 5 * 16); + + auto* out = m_glow_renderer.alloc_sprite(); + if (!glow_math(&consts, vecdata_xfer.data, shader_xfer.data, out)) { + m_glow_renderer.cancel_sprite(); + } + control_xfer = dma.read_and_advance(); + while (control_xfer.size_bytes == 0 && control_xfer.vifcode0().kind == VifCode::Kind::NOP && + control_xfer.vifcode1().kind == VifCode::Kind::NOP) { + control_xfer = dma.read_and_advance(); + } + } + + m_glow_renderer.flush(render_state, prof); +} diff --git a/game/graphics/opengl_renderer/SpriteRenderer.cpp b/game/graphics/opengl_renderer/sprite/SpriteRenderer.cpp similarity index 100% rename from game/graphics/opengl_renderer/SpriteRenderer.cpp rename to game/graphics/opengl_renderer/sprite/SpriteRenderer.cpp diff --git a/game/graphics/opengl_renderer/SpriteRenderer.h b/game/graphics/opengl_renderer/sprite/SpriteRenderer.h similarity index 98% rename from game/graphics/opengl_renderer/SpriteRenderer.h rename to game/graphics/opengl_renderer/sprite/SpriteRenderer.h index 74b20c4d8..1fd12724b 100644 --- a/game/graphics/opengl_renderer/SpriteRenderer.h +++ b/game/graphics/opengl_renderer/sprite/SpriteRenderer.h @@ -5,7 +5,7 @@ #include "game/graphics/opengl_renderer/BucketRenderer.h" #include "game/graphics/opengl_renderer/DirectRenderer.h" -#include "game/graphics/opengl_renderer/sprite_common.h" +#include "game/graphics/opengl_renderer/sprite/sprite_common.h" class SpriteRenderer : public BucketRenderer { public: diff --git a/game/graphics/opengl_renderer/sprite_common.h b/game/graphics/opengl_renderer/sprite/sprite_common.h similarity index 85% rename from game/graphics/opengl_renderer/sprite_common.h rename to game/graphics/opengl_renderer/sprite/sprite_common.h index 619ee08d1..5cd0d2d94 100644 --- a/game/graphics/opengl_renderer/sprite_common.h +++ b/game/graphics/opengl_renderer/sprite/sprite_common.h @@ -209,4 +209,35 @@ enum SpriteProgMem { static_assert(offsetof(SpriteFrameData, hmge_scale) == 256); static_assert(sizeof(SpriteFrameDataJak1) == 0x290, "SpriteFrameData size"); -static_assert(sizeof(SpriteFrameData) == 0x2a0, "SpriteFrameData size"); \ No newline at end of file +static_assert(sizeof(SpriteFrameData) == 0x2a0, "SpriteFrameData size"); + +/*! + * Post-transformation description of a sprite glow - passed to the GlowRenderer. + */ +struct SpriteGlowOutput { + math::Vector4f first_clear_pos[2]; // 8, 9 + math::Vector4f second_clear_pos[2]; // 11, 12 corners for the second clear draw + math::Vector2f offscreen_uv[2]; // 24, 26 + math::Vector4f flare_xyzw[4]; + AdGifData adgif; // 68, 69, 70, 71, 72 + math::Vector4f flare_draw_color; // 75 + float perspective_q; +}; + +struct SpriteGlowConsts { + math::Vector4f camera[4]; + math::Vector4f perspective[4]; + math::Vector4f hvdf; + math::Vector4f hmge; + float pfog0; + float deg_to_rad; + float min_scale; + float inv_area; + math::Vector4f sincos[5]; + math::Vector4f basis_x; + math::Vector4f basis_y; + math::Vector4f xy_array[4]; + math::Vector4f clamp_min; + math::Vector4f clamp_max; +}; +static_assert(sizeof(SpriteGlowConsts) == 0x180); \ No newline at end of file diff --git a/goal_src/jak2/engine/draw/drawable.gc b/goal_src/jak2/engine/draw/drawable.gc index 10e9a289e..10dd092f9 100644 --- a/goal_src/jak2/engine/draw/drawable.gc +++ b/goal_src/jak2/engine/draw/drawable.gc @@ -1805,7 +1805,7 @@ (when (not (paused?)) (clear *stdcon1*) (debug-reset-buffers) - ;(clear! *simple-sprite-system*) + (clear! *simple-sprite-system*) ) (set! (-> s5-1 bucket-group) (dma-buffer-add-buckets (-> s5-1 calc-buf) 327)) ) diff --git a/goal_src/jak2/engine/gfx/hw/gs.gc b/goal_src/jak2/engine/gfx/hw/gs.gc index 7783f38d0..fb855dd22 100644 --- a/goal_src/jak2/engine/gfx/hw/gs.gc +++ b/goal_src/jak2/engine/gfx/hw/gs.gc @@ -79,6 +79,7 @@ (signal 96) (finish 97) (label 98) + (hack 127) ) (defenum gs-reg64 diff --git a/goal_src/jak2/engine/gfx/sprite/simple-sprite-h.gc b/goal_src/jak2/engine/gfx/sprite/simple-sprite-h.gc index e21080e6a..361aadf53 100644 --- a/goal_src/jak2/engine/gfx/sprite/simple-sprite-h.gc +++ b/goal_src/jak2/engine/gfx/sprite/simple-sprite-h.gc @@ -8,17 +8,18 @@ ;; DECOMP BEGINS (deftype sprite-glow-data (structure) - ((position vector :inline :offset-assert 0) - (size-x float :offset 12) - (size-probe float :offset-assert 16) - (z-offset float :offset-assert 20) - (rot-angle float :offset-assert 24) - (size-y float :offset-assert 28) - (color rgbaf :inline :offset-assert 32) - (fade-a float :offset-assert 48) - (fade-b float :offset-assert 52) - (tex-id texture-id :offset-assert 56) - (dummy uint32 :offset-assert 60) + ((position vector :inline :offset-assert 0) + (size-x float :offset 12) + (size-probe float :offset-assert 16) + (z-offset float :offset-assert 20) + (rot-angle float :offset-assert 24) + (size-y float :offset-assert 28) + (color rgbaf :inline :offset-assert 32) + (fade-a float :offset-assert 48) + (fade-b float :offset-assert 52) + (tex-id texture-id :offset-assert 56) + (dummy uint32 :offset-assert 60) + (quads vector 4 :inline :offset 0) ) :method-count-assert 10 :size-assert #x40 @@ -39,16 +40,16 @@ ) (deftype simple-sprite-system (structure) - ((count int16 :offset-assert 0) - (max-count int16 :offset-assert 2) - (data sprite-glow-data :offset-assert 4) + ((count int16 :offset-assert 0) + (max-count int16 :offset-assert 2) + (data (inline-array sprite-glow-data) :offset-assert 4) ) :method-count-assert 12 :size-assert #x8 :flag-assert #xc00000008 (:methods - (add! (_type_ dma-buffer) none 9) - (simple-sprite-system-method-10 (_type_ dma-buffer) none 10) + (add! (_type_ sprite-glow-data) none 9) + (draw-all-sprites! (_type_ dma-buffer) none 10) (clear! (_type_) none 11) ) ) diff --git a/goal_src/jak2/engine/gfx/sprite/sprite-glow.gc b/goal_src/jak2/engine/gfx/sprite/sprite-glow.gc index e04e3816e..ed0b3d267 100644 --- a/goal_src/jak2/engine/gfx/sprite/sprite-glow.gc +++ b/goal_src/jak2/engine/gfx/sprite/sprite-glow.gc @@ -7,3 +7,790 @@ ;; DECOMP BEGINS +(deftype sprite-glow-template (structure) + ((clear-init-giftag gs-gif-tag :inline :offset-assert 0) + (clear-init-adcmds gs-adcmd 5 :inline :offset-assert 16) + (clear-draw-giftag gs-gif-tag :inline :offset-assert 96) + (clear-draw-clr-0 gs-packed-rgba :inline :offset-assert 112) + (clear-draw-xyz-0 gs-packed-xyzw 2 :inline :offset-assert 128) ;; 8 + (clear-draw-clr-1 gs-packed-rgba :inline :offset-assert 160) + (clear-draw-xyz-1 vector 2 :inline :offset-assert 176) ;; 11 + + (offscr-setup-giftag gs-gif-tag :inline :offset-assert 208) ;; 13 + (offscr-setup-adcmds gs-adcmd 8 :inline :offset-assert 224) + (offscr-first-giftag gs-gif-tag :inline :offset-assert 352) + (offscr-first-clr gs-packed-rgba :inline :offset-assert 368) + (offscr-first-uv-0 gs-packed-uv :inline :offset-assert 384) ;; 24 + (offscr-first-xyzw-0 gs-packed-xyzw :inline :offset-assert 400) + (offscr-first-uv-1 gs-packed-uv :inline :offset-assert 416) ;; 26 + (offscr-first-xyzw-1 gs-packed-xyzw :inline :offset-assert 432) + + (repeat-draw-giftag gs-gif-tag :inline :offset-assert 448) ;; 28 + (repeat-draw-adcmds gs-adcmd 29 :inline :offset-assert 464) + + (flare-alpha-giftag gs-gif-tag :inline :offset-assert 928) ;; 58 + (flare-alpha-clr gs-packed-rgba :inline :offset-assert 944) + (flare-alpha-uv gs-packed-uv :inline :offset-assert 960) + (flare-alpha-xyzw-0 gs-packed-xyzw :inline :offset-assert 976) ;; 61 + (flare-alpha-xyzw-1 gs-packed-xyzw :inline :offset-assert 992) + (flare-alpha-xyzw-2 gs-packed-xyzw :inline :offset-assert 1008) + (flare-alpha-xyzw-3 gs-packed-xyzw :inline :offset-assert 1024) + + (flare-init-giftag gs-gif-tag :inline :offset-assert 1040) ;; 65 + (flare-init-adcmds gs-adcmd 8 :inline :offset-assert 1056) + + (flare-draw-giftag gs-gif-tag :inline :offset-assert 1184) ;; 74 + (flare-draw-clr gs-packed-rgba :inline :offset-assert 1200) + (flare-draw-stq-0 gs-packed-stq :inline :offset-assert 1216) + (flare-draw-xyzw-0 gs-packed-xyzw :inline :offset-assert 1232) ;; 77 + (flare-draw-stq-1 gs-packed-stq :inline :offset-assert 1248) + (flare-draw-xyzw-1 gs-packed-xyzw :inline :offset-assert 1264) + (flare-draw-stq-2 gs-packed-stq :inline :offset-assert 1280) + (flare-draw-xyzw-2 gs-packed-xyzw :inline :offset-assert 1296) + (flare-draw-stq-3 gs-packed-stq :inline :offset-assert 1312) + (flare-draw-xyzw-3 gs-packed-xyzw :inline :offset-assert 1328) + ) + :method-count-assert 9 + :size-assert #x540 + :flag-assert #x900000540 + ) + + +(deftype sprite-glow-consts (structure) + ((camera matrix :inline :offset-assert 0) + (perspective matrix :inline :offset-assert 64) + (hvdf-offset vector :inline :offset-assert 128) + (hmge-scale vector :inline :offset-assert 144) + (consts vector :inline :offset-assert 160) + (pfog0 float :offset 160) + (deg-to-rad float :offset 164) + (min-scale float :offset 168) + (inv-area float :offset 172) + (sincos-01 vector :inline :offset-assert 176) + (sincos-23 vector :inline :offset-assert 192) + (sincos-45 vector :inline :offset-assert 208) + (sincos-67 vector :inline :offset-assert 224) + (sincos-89 vector :inline :offset-assert 240) + (basis-x vector :inline :offset-assert 256) + (basis-y vector :inline :offset-assert 272) + (xy-array vector 4 :inline :offset-assert 288) + (clamp-min vector :inline :offset-assert 352) + (clamp-max vector :inline :offset-assert 368) + ) + :method-count-assert 9 + :size-assert #x180 + :flag-assert #x900000180 + ) + + +(define *sprite-glow-template* + (new 'static 'sprite-glow-template + :clear-init-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 :nloop #x5 :nreg #x1) + :regs (new 'static 'gif-tag-regs :regs0 (gif-reg-id a+d)) + ) + :clear-init-adcmds (new 'static 'inline-array gs-adcmd 5 + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + ;; (new 'static 'gs-alpha :a 2 :b 2 :c 2 :d 1) + (new 'static 'gs-adcmd :cmds (gs-reg64 alpha-1) :x #x6a) + ;; (new 'static 'gs-test :ate 1 :afail 1 :zte 1 :ztst 2) + (new 'static 'gs-adcmd :cmds (gs-reg64 test-1) :x #x51001) + ;; (new 'static 'gs-zbuf :zbp 304 :psm 1 :zmsk 1) + (new 'static 'gs-adcmd :cmds (gs-reg64 zbuf-1) :x #x1000130 :y #x1) + ;; (new 'static 'gs-frame :fbp 408 :fbw 8 :fbmsk #xffffff) + (new 'static 'gs-adcmd :cmds (gs-reg64 frame-1) :x #x80198 :y #xffffff) + ) + :clear-draw-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 + :nloop #x2 + :pre #x1 + :prim (new 'static 'gs-prim :prim (gs-prim-type sprite)) + :nreg #x3 + ) + :regs (new 'static 'gif-tag-regs :regs0 (gif-reg-id rgbaq) :regs1 (gif-reg-id xyzf2) :regs2 (gif-reg-id xyzf2)) + ) + :clear-draw-xyz-0 (new 'static 'inline-array gs-packed-xyzw 2 + (new 'static 'gs-packed-xyzw :iz -1) + (new 'static 'gs-packed-xyzw :iz -1) + ) + :clear-draw-clr-1 (new 'static 'gs-packed-rgba :x #xff :w #xff) + :offscr-setup-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 :nloop #x8 :nreg #x1) + :regs (new 'static 'gif-tag-regs :regs0 (gif-reg-id a+d)) + ) + :offscr-setup-adcmds (new 'static 'inline-array gs-adcmd 8 + ;; (new 'static 'gs-frame :fbp 144 :fbw 1 :fbmsk #xffffff) + (new 'static 'gs-adcmd :cmds (gs-reg64 frame-2) :x #x10090 :y #xffffff) + ;; (new 'static 'gs-zbuf :zbp 304 :psm 1) + (new 'static 'gs-adcmd :cmds (gs-reg64 zbuf-2) :x #x1000130) + ;; mmag/mmin on + (new 'static 'gs-adcmd :cmds (gs-reg64 tex1-2) :x #x60) + ;; (new 'static 'gs-tex0 :tbp0 #x3300 :tbw 8 :tw 9 :th 9 :tcc 1 :tfx 1) + (new 'static 'gs-adcmd :cmds (gs-reg64 tex0-2) :x #x64023300 :y #xe) + ;; wms, wmt both 1 + (new 'static 'gs-adcmd :cmds (gs-reg64 clamp-2) :x #x5) + ;; ate 1, afail 1, zte 1, ztst 1 + (new 'static 'gs-adcmd :cmds (gs-reg64 test-2) :x #x31001) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyoffset-2)) + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + ) + :offscr-first-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 + :nloop #x1 + :pre #x1 + :prim (new 'static 'gs-prim :prim (gs-prim-type sprite) :tme #x1 :fst #x1 :ctxt #x1) + :nreg #x5 + ) + :regs (new 'static 'gif-tag-regs + :regs0 (gif-reg-id rgbaq) + :regs1 (gif-reg-id uv) + :regs2 (gif-reg-id xyz2) + :regs3 (gif-reg-id uv) + :regs4 (gif-reg-id xyz2) + ) + ) + :offscr-first-clr (new 'static 'gs-packed-rgba :x #x80 :y #x80 :z #x80 :w #x80) + :offscr-first-xyzw-1 (new 'static 'gs-packed-xyzw :ix #x200 :iy #x200) + :repeat-draw-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 :nloop #x1d :nreg #x1) + :regs (new 'static 'gif-tag-regs :regs0 (gif-reg-id a+d)) + ) + :repeat-draw-adcmds (new 'static 'inline-array gs-adcmd 29 + ;; tbp: 0x1200, tbw 1, tw = 5, th = 5, tcc = 1, tfx = 1 + (new 'static 'gs-adcmd :cmds (gs-reg64 tex0-2) :x #x54005200 :y #xd) + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x100010) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x2100210) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2) :x #x1000100) + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x100010) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x1100110) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2) :x #x800080) + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x100010) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x900090) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2) :x #x400040) + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x100010) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x500050) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2) :x #x200020) + ;; fbp = 408, fbw = 8 (normal screen drawing) + (new 'static 'gs-adcmd :cmds (gs-reg64 frame-2) :x #x80198 :y #xffffff) + ;; (new 'static 'gs-test :ate 1 :afail 1 :zte 1 :ztst 2) + (new 'static 'gs-adcmd :cmds (gs-reg64 test-2) :x #x51001) + ;; (new 'static 'gs-zbuf :zbp 304 :psm 1 :zmsk 1) + (new 'static 'gs-adcmd :cmds (gs-reg64 zbuf-2) :x #x1000130 :y #x1) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyoffset-2) :x #x7000 :y #x7300) + (new 'static 'gs-adcmd :cmds (gs-reg64 scissor-2) :x #x1ff0000 :y #x19f0000) + ;; zte = 1, ztst = 2 + (new 'static 'gs-adcmd :cmds (gs-reg64 test-2) :x #x50000) + ;; (new 'static 'gs-alpha :a 2 :b 2 :c 2 :d 1) + (new 'static 'gs-adcmd :cmds (gs-reg64 alpha-2) :x #x6a) + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + ) + :flare-alpha-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 + :nloop #x1 + :pre #x1 + :prim (new 'static 'gs-prim :prim (gs-prim-type tri-fan) :tme #x1 :fst #x1 :ctxt #x1) + :nreg #x6 + ) + :regs (new 'static 'gif-tag-regs + :regs0 (gif-reg-id rgbaq) + :regs1 (gif-reg-id uv) + :regs2 (gif-reg-id xyzf2) + :regs3 (gif-reg-id xyzf2) + :regs4 (gif-reg-id xyzf2) + :regs5 (gif-reg-id xyzf2) + ) + ) + :flare-alpha-clr (new 'static 'gs-packed-rgba :x #x80 :y #x80 :z #x80 :w #x80) + :flare-alpha-uv (new 'static 'gs-packed-uv :x (the-as float #x10) :y (the-as float #x10)) + :flare-alpha-xyzw-0 (new 'static 'gs-packed-xyzw :iz -1) + :flare-alpha-xyzw-1 (new 'static 'gs-packed-xyzw :iz -1) + :flare-alpha-xyzw-2 (new 'static 'gs-packed-xyzw :iz -1) + :flare-alpha-xyzw-3 (new 'static 'gs-packed-xyzw :iz -1) + :flare-init-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 :nloop #x8 :nreg #x1) + :regs (new 'static 'gif-tag-regs :regs0 (gif-reg-id a+d)) + ) + :flare-init-adcmds (new 'static 'inline-array gs-adcmd 8 + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + ;; normal frame, no mask + (new 'static 'gs-adcmd :cmds (gs-reg64 frame-1) :x #x80198) + (new 'static 'gs-adcmd :cmds (gs-reg64 hack)) + (new 'static 'gs-adcmd :cmds (gs-reg64 hack)) + (new 'static 'gs-adcmd :cmds (gs-reg64 hack)) + (new 'static 'gs-adcmd :cmds (gs-reg64 hack)) + (new 'static 'gs-adcmd :cmds (gs-reg64 hack)) + ;; a = 0, b = 2, c = 1, d = 1 + (new 'static 'gs-adcmd :cmds (gs-reg64 alpha-1) :x #x58) + ) + :flare-draw-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 + :nloop #x1 + :eop #x1 + :pre #x1 + :prim (new 'static 'gs-prim :prim (gs-prim-type tri-fan) :tme #x1 :abe #x1) + :nreg #x9 + ) + :regs (new 'static 'gif-tag-regs + :regs0 (gif-reg-id rgbaq) + :regs1 (gif-reg-id st) + :regs2 (gif-reg-id xyzf2) + :regs3 (gif-reg-id st) + :regs4 (gif-reg-id xyzf2) + :regs5 (gif-reg-id st) + :regs6 (gif-reg-id xyzf2) + :regs7 (gif-reg-id st) + :regs8 (gif-reg-id xyzf2) + ) + ) + :flare-draw-clr (new 'static 'gs-packed-rgba :y 64 :w #x80) + :flare-draw-stq-0 (new 'static 'gs-packed-stq :z 1.0) + :flare-draw-xyzw-0 (new 'static 'gs-packed-xyzw :iz -1) + :flare-draw-stq-1 (new 'static 'gs-packed-stq :x 1.0 :z 1.0) + :flare-draw-xyzw-1 (new 'static 'gs-packed-xyzw :iz -1) + :flare-draw-stq-2 (new 'static 'gs-packed-stq :x 1.0 :y 1.0 :z 1.0) + :flare-draw-xyzw-2 (new 'static 'gs-packed-xyzw :iz -1) + :flare-draw-stq-3 (new 'static 'gs-packed-stq :y 1.0 :z 1.0) + :flare-draw-xyzw-3 (new 'static 'gs-packed-xyzw :iz -1) + ) + ) + +(define sprite-glow-vu1-block (new 'static 'vu-function #|:length #x86 :qlength 67|#)) + +;; WARN: Return type mismatch vector vs none. +(defun sprite-glow-init-consts ((arg0 sprite-glow-consts)) + (let ((v1-0 *math-camera*)) + (let* ((t0-0 (-> arg0 camera)) + (t1-0 (-> v1-0 camera-rot)) + (a1-0 (-> t1-0 quad 0)) + (a2-0 (-> t1-0 quad 1)) + (a3-0 (-> t1-0 quad 2)) + (t1-1 (-> t1-0 trans quad)) + ) + (set! (-> t0-0 quad 0) a1-0) + (set! (-> t0-0 quad 1) a2-0) + (set! (-> t0-0 quad 2) a3-0) + (set! (-> t0-0 trans quad) t1-1) + ) + (let* ((t0-1 (-> arg0 perspective)) + (t1-2 (-> v1-0 perspective)) + (a1-1 (-> t1-2 quad 0)) + (a2-1 (-> t1-2 quad 1)) + (a3-1 (-> t1-2 quad 2)) + (t1-3 (-> t1-2 trans quad)) + ) + (set! (-> t0-1 quad 0) a1-1) + (set! (-> t0-1 quad 1) a2-1) + (set! (-> t0-1 quad 2) a3-1) + (set! (-> t0-1 trans quad) t1-3) + ) + (set! (-> arg0 hvdf-offset quad) (-> v1-0 hvdf-off quad)) + (set! (-> arg0 hmge-scale quad) (-> v1-0 hmge-scale quad)) + (set! (-> arg0 basis-x quad) (the-as uint128 0)) + (set! (-> arg0 basis-x x) (- (-> *math-camera* perspective vector 0 x))) + (set! (-> arg0 basis-y quad) (the-as uint128 0)) + (set! (-> arg0 basis-y y) (- (-> *math-camera* perspective vector 1 y))) + (set! (-> arg0 pfog0) (-> v1-0 pfog0)) + ) + (set! (-> arg0 deg-to-rad) 0.000095873795) + (set! (-> arg0 min-scale) (sqrtf (* (/ 1.0 (-> arg0 basis-x x)) (/ 1.0 (-> arg0 basis-y y))))) + (set! (-> arg0 inv-area) (/ 1.0 (* (-> arg0 min-scale) (-> arg0 min-scale)))) + (let ((v1-5 (-> arg0 sincos-01))) + (set! (-> v1-5 z) 0.999998) + (set! (-> v1-5 w) 1.0) + ) + (let ((v1-6 (-> arg0 sincos-23))) + (set! (-> v1-6 z) -0.16666014) + (set! (-> v1-6 w) -0.49998003) + ) + (let ((v1-7 (-> arg0 sincos-45))) + (set! (-> v1-7 z) 0.008326521) + (set! (-> v1-7 w) 0.041620404) + ) + (let ((v1-8 (-> arg0 sincos-67))) + (set! (-> v1-8 z) -0.0001956241) + (set! (-> v1-8 w) -0.0013636408) + ) + (let ((v1-9 (-> arg0 sincos-89))) + (set! (-> v1-9 z) 0.0000023042373) + (set! (-> v1-9 w) 0.000020170546) + ) + (set-vector! (-> arg0 xy-array 0) -0.5 -0.5 0.0 0.0) + (set-vector! (-> arg0 xy-array 1) 0.5 -0.5 0.0 0.0) + (set-vector! (-> arg0 xy-array 2) 0.5 0.5 0.0 0.0) + (set-vector! (-> arg0 xy-array 3) -0.5 0.5 0.0 0.0) + (set-vector! (-> arg0 clamp-min) 1792.0 1840.0 0.0 0.0) + (set-vector! (-> arg0 clamp-max) 2304.0 2256.0 2048.0 50.0) + (none) + ) + +(defun sprite-glow-init-engine ((arg0 dma-buffer)) + (dma-buffer-add-vu-function arg0 sprite-glow-vu1-block 1) + (let ((s5-0 24)) + (let* ((v1-0 arg0) + (a0-2 (the-as dma-packet (-> v1-0 base))) + ) + (set! (-> a0-2 dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc s5-0)) + (set! (-> a0-2 vif0) (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl))) + (set! (-> a0-2 vif1) (new 'static 'vif-tag :imm #x3d4 :cmd (vif-cmd unpack-v4-32) :num s5-0)) + (set! (-> v1-0 base) (the-as pointer (&+ a0-2 16))) + ) + (sprite-glow-init-consts (the-as sprite-glow-consts (-> arg0 base))) + (&+! (-> arg0 base) (* s5-0 16)) + ) + (let ((v1-3 84)) + (let* ((a0-6 arg0) + (a1-6 (the-as dma-packet (-> a0-6 base))) + ) + (set! (-> a1-6 dma) + (new 'static 'dma-tag :id (dma-tag-id ref) :addr (the-as int *sprite-glow-template*) :qwc v1-3) + ) + (set! (-> a1-6 vif0) (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl))) + (set! (-> a1-6 vif1) (new 'static 'vif-tag :imm #x320 :cmd (vif-cmd unpack-v4-32) :num v1-3)) + (set! (-> a0-6 base) (the-as pointer (&+ a1-6 16))) + ) + (let* ((a0-7 arg0) + (a1-8 (the-as dma-packet (-> a0-7 base))) + ) + (set! (-> a1-8 dma) + (new 'static 'dma-tag :id (dma-tag-id ref) :addr (the-as int *sprite-glow-template*) :qwc v1-3) + ) + (set! (-> a1-8 vif0) (new 'static 'vif-tag :cmd (vif-cmd mscal) :msk #x1 :imm #x0)) + (set! (-> a1-8 vif1) (new 'static 'vif-tag :imm #x374 :cmd (vif-cmd unpack-v4-32) :num v1-3)) + (set! (-> a0-7 base) (the-as pointer (&+ a1-8 16))) + ) + ) + (let* ((v1-8 arg0) + (a0-8 (the-as dma-packet (-> v1-8 base))) + ) + (set! (-> a0-8 dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) + (set! (-> a0-8 vif0) (new 'static 'vif-tag :cmd (vif-cmd base))) + (set! (-> a0-8 vif1) (new 'static 'vif-tag :imm #x190 :cmd (vif-cmd offset))) + (set! (-> v1-8 base) (the-as pointer (&+ a0-8 16))) + ) + (let ((v1-9 (the-as dma-packet (-> arg0 base)))) + (set! (-> v1-9 dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) + (set! (-> v1-9 vif0) (new 'static 'vif-tag)) + (set! (-> v1-9 vif1) (new 'static 'vif-tag :cmd (vif-cmd flushe) :msk #x1)) + (set! (-> arg0 base) (the-as pointer (&+ v1-9 16))) + ) + 0 + (none) + ) + +(deftype sprite-glow-dma-packet-data (structure) + ((control-packet dma-packet :inline :offset-assert 0) + (vecdata-packet dma-packet :inline :offset-assert 16) + (shader-cnt-packet dma-packet :inline :offset-assert 32) + (shader-ref-packet dma-packet :inline :offset-assert 48) + (mscal-packet dma-packet :inline :offset-assert 64) + ) + :method-count-assert 9 + :size-assert #x50 + :flag-assert #x900000050 + ) + + +(deftype sprite-glow-cnt-template (structure) + ((control-packet dma-packet :inline :offset-assert 0) + (num-sprites uint32 :offset-assert 16) + (dummys uint32 3 :offset-assert 20) + (num-sprites-quad uint128 :offset 16) + (vecdata-packet dma-packet :inline :offset-assert 32) + (vecdata sprite-glow-data :inline :offset-assert 48) + (shader-packet dma-packet :inline :offset-assert 112) + (shader adgif-shader :inline :offset-assert 128) + (mscal-packet dma-packet :inline :offset-assert 208) + ) + :method-count-assert 9 + :size-assert #xe0 + :flag-assert #x9000000e0 + ) + + +(deftype sprite-glow-ref-template (structure) + ((control-packet dma-packet :inline :offset-assert 0) + (num-sprites uint32 :offset-assert 16) + (dummys uint32 3 :offset-assert 20) + (num-sprites-quad uint128 :offset 16) + (vecdata-packet dma-packet :inline :offset-assert 32) + (vecdata sprite-glow-data :inline :offset-assert 48) + (shader-packet dma-packet :inline :offset-assert 112) + (shader-packet-ptr pointer :offset 116) + (mscal-packet dma-packet :inline :offset-assert 128) + ) + :method-count-assert 9 + :size-assert #x90 + :flag-assert #x900000090 + ) + + +(define *sprite-glow-dma-packet-data* + (new 'static 'sprite-glow-dma-packet-data + :control-packet (new 'static 'dma-packet + :dma (new 'static 'dma-tag :qwc #x1 :id (dma-tag-id cnt)) + :vif0 (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl)) + :vif1 (new 'static 'vif-tag :imm #x8000 :num #x1 :cmd (vif-cmd unpack-v4-32)) + ) + :vecdata-packet (new 'static 'dma-packet + :dma (new 'static 'dma-tag :qwc #x4 :id (dma-tag-id cnt)) + :vif0 (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl)) + :vif1 (new 'static 'vif-tag :imm #x8001 :num #x4 :cmd (vif-cmd unpack-v4-32)) + ) + :shader-cnt-packet (new 'static 'dma-packet + :dma (new 'static 'dma-tag :qwc #x5 :id (dma-tag-id cnt)) + :vif0 (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl)) + :vif1 (new 'static 'vif-tag :imm #x8091 :num #x5 :cmd (vif-cmd unpack-v4-32)) + ) + :shader-ref-packet (new 'static 'dma-packet + :dma (new 'static 'dma-tag :qwc #x5 :id (dma-tag-id ref)) + :vif0 (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl)) + :vif1 (new 'static 'vif-tag :imm #x8091 :num #x5 :cmd (vif-cmd unpack-v4-32)) + ) + :mscal-packet (new 'static 'dma-packet + :dma (new 'static 'dma-tag :id (dma-tag-id cnt)) + :vif1 (new 'static 'vif-tag :cmd (vif-cmd flushe) :msk #x1) + ) + ) + ) + +(set! (-> *sprite-glow-dma-packet-data* mscal-packet vif0) + (new 'static 'vif-tag :cmd (vif-cmd mscalf) :msk #x1 :imm #xa) + ) + +;; WARN: Return type mismatch sprite-glow-cnt-template vs none. +(defun sprite-glow-add-sprite ((arg0 dma-buffer) (arg1 sprite-vec-data-2d) (arg2 float) (arg3 float) (arg4 float) (arg5 adgif-shader)) + (let ((v1-0 (the-as sprite-glow-cnt-template (-> arg0 base)))) + (let* ((t5-0 *sprite-glow-dma-packet-data*) + (t2-0 (-> t5-0 control-packet quad)) + (t3-0 (-> t5-0 vecdata-packet quad)) + (t4-0 (-> t5-0 shader-cnt-packet quad)) + (t5-1 (-> t5-0 mscal-packet quad)) + ) + (set! (-> v1-0 control-packet quad) t2-0) + (set! (-> v1-0 num-sprites-quad) (the-as uint128 0)) + (set! (-> v1-0 vecdata-packet quad) t3-0) + (set! (-> v1-0 shader-packet quad) t4-0) + (set! (-> v1-0 mscal-packet quad) t5-1) + ) + (let ((t2-1 (-> arg1 x-y-z-sx quad)) + (t3-1 (-> arg1 flag-rot-sy quad)) + (a1-1 (-> arg1 r-g-b-a quad)) + ) + (set! (-> v1-0 vecdata position quad) t2-1) + (set! (-> v1-0 vecdata quads 1 quad) t3-1) + (set! (-> v1-0 vecdata color quad) a1-1) + ) + (let ((a1-2 1)) + (set! (-> v1-0 vecdata z-offset) arg4) + (set! (-> v1-0 vecdata fade-a) arg2) + (set! (-> v1-0 vecdata fade-b) arg3) + (set! (-> v1-0 num-sprites) (the-as uint a1-2)) + ) + (let ((a1-3 (-> arg5 quad 0 quad)) + (a2-1 (-> arg5 quad 1 quad)) + (a3-1 (-> arg5 quad 2 quad)) + (t0-1 (-> arg5 quad 3 quad)) + (t1-1 (-> arg5 quad 4 quad)) + ) + (set! (-> v1-0 shader quad 0 quad) a1-3) + (set! (-> v1-0 shader quad 1 quad) a2-1) + (set! (-> v1-0 shader quad 2 quad) a3-1) + (set! (-> v1-0 shader quad 3 quad) t0-1) + (set! (-> v1-0 shader quad 4 quad) t1-1) + ) + (set! (-> arg0 base) (the-as pointer (&+ v1-0 224))) + ) + (none) + ) + +;; WARN: Return type mismatch sprite-glow-ref-template vs none. +(defun sprite-glow-add-simple-sprite ((arg0 dma-buffer) (arg1 sprite-glow-dma-packet-data) (arg2 sprite-glow-data) (arg3 pointer)) + (let ((v1-0 (the-as sprite-glow-ref-template (-> arg0 base)))) + (let ((t0-0 (-> arg1 control-packet quad)) + (t1-0 (-> arg1 vecdata-packet quad)) + (t2-0 (-> arg1 shader-ref-packet quad)) + (a1-1 (-> arg1 mscal-packet quad)) + ) + (set! (-> v1-0 control-packet quad) t0-0) + (set! (-> v1-0 num-sprites-quad) (the-as uint128 0)) + (set! (-> v1-0 vecdata-packet quad) t1-0) + (set! (-> v1-0 shader-packet quad) t2-0) + (set! (-> v1-0 mscal-packet quad) a1-1) + ) + (let ((a1-2 (-> arg2 position quad)) + (t0-1 (-> arg2 quads 1 quad)) + (t1-1 (-> arg2 color quad)) + (a2-1 (-> arg2 quads 3 quad)) + (t2-1 1) + ) + (set! (-> v1-0 vecdata position quad) a1-2) + (set! (-> v1-0 vecdata quads 1 quad) t0-1) + (set! (-> v1-0 vecdata color quad) t1-1) + (set! (-> v1-0 vecdata quads 3 quad) a2-1) + (set! (-> v1-0 num-sprites) (the-as uint t2-1)) + ) + (set! (-> v1-0 shader-packet-ptr) arg3) + (set! (-> arg0 base) (the-as pointer (&+ v1-0 144))) + ) + (none) + ) + +(defun sprite-glow-draw ((arg0 dma-buffer)) + (local-vars (a2-0 float)) + (let* ((s5-0 *sprite-aux-list*) + (s4-0 (-> s5-0 entry)) + (s3-0 #f) + ) + (dotimes (s2-0 s4-0) + (let ((v1-2 (-> s5-0 data s2-0))) + (when (and (not s3-0) (= (-> v1-2 aux-type) (sprite-aux-type glow))) + (let* ((a0-5 (-> v1-2 aux-data omega)) + (a3-0 (cond + ((zero? a0-5) + (set! a2-0 0.0) + 1.0 + ) + (else + (let* ((f1-1 (* 0.00024414062 a0-5)) + (f0-4 (- f1-1 (the float (the int f1-1)))) + ) + (let ((f1-3 (* 4096.0 (- f1-1 f0-4)))) + (set! a2-0 (/ -1.0 (* (- 1.0 f0-4) f1-3))) + ) + (/ f0-4 (- 1.0 f0-4)) + ) + ) + ) + ) + ) + (sprite-glow-add-sprite arg0 (-> v1-2 vec-data) a2-0 a3-0 (-> v1-2 aux-data user-float) (-> v1-2 gif-data)) + ) + ) + ) + ) + ) + 0 + (none) + ) + +(defmethod add! simple-sprite-system ((obj simple-sprite-system) (arg0 sprite-glow-data)) + (let ((v1-0 (-> obj count))) + (when (< v1-0 (-> obj max-count)) + (let* ((a2-3 (-> obj data v1-0)) + (v1-2 arg0) + (a1-1 a2-3) + (a2-4 (-> v1-2 position quad)) + (a3-0 (-> v1-2 quads 1 quad)) + (t0-0 (-> v1-2 color quad)) + (v1-3 (-> v1-2 quads 3 quad)) + ) + (set! (-> a1-1 position quad) a2-4) + (set! (-> a1-1 quads 1 quad) a3-0) + (set! (-> a1-1 color quad) t0-0) + (set! (-> a1-1 quads 3 quad) v1-3) + ) + (+! (-> obj count) 1) + ) + ) + 0 + (none) + ) + +;; WARN: Return type mismatch pointer vs adgif-shader. +(defun add-shader-to-dma ((arg0 dma-buffer)) + (let* ((a1-0 (new 'static 'dma-packet :dma (new 'static 'dma-tag :id (dma-tag-id next)))) + (v1-0 (-> arg0 base)) + (a1-1 (-> a1-0 quad)) + (v0-0 (&+ v1-0 16)) + ) + (let ((a2-0 (&+ v1-0 96))) + (set! (-> (the-as (pointer uint128) v1-0)) a1-1) + (set! (-> (the-as (pointer uint32) v1-0) 1) (the-as uint a2-0)) + (set! (-> arg0 base) a2-0) + ) + (the-as adgif-shader v0-0) + ) + ) + +(defmethod draw-all-sprites! simple-sprite-system ((obj simple-sprite-system) (arg0 dma-buffer)) + (local-vars (sv-528 sprite-glow-dma-packet-data) (sv-532 (pointer texture-id)) (sv-536 pointer)) + (b! (zero? (-> obj count)) cfg-13 :delay (nop!)) + (set! sv-528 *sprite-glow-dma-packet-data*) + (set! sv-532 (new 'stack-no-clear 'array 'texture-id 128)) + (set! sv-536 (-> arg0 base)) + (when (> (-> obj count) 128) + (format 0 "TOO MANY SPRITES (~D)~%" (-> obj count)) + (break!) + ) + (dotimes (v1-5 (-> obj count)) + (set! (-> sv-532 v1-5) (-> obj data v1-5 tex-id)) + ) + (countdown (s4-0 (-> obj count)) + (let ((s3-0 (-> sv-532 s4-0))) + (when (nonzero? s3-0) + (let ((s2-0 (add-shader-to-dma arg0))) + (adgif-shader<-texture-simple! s2-0 (lookup-texture-by-id s3-0)) + (countdown (s1-1 (+ s4-0 1)) + (when (= s3-0 (-> sv-532 s1-1)) + (set! (-> sv-532 s1-1) (new 'static 'texture-id)) + (let ((a2-1 (-> obj data s1-1))) + (sprite-glow-add-simple-sprite arg0 sv-528 a2-1 (the-as pointer s2-0)) + ) + ) + ) + ) + ) + ) + ) + (label cfg-13) + 0 + (none) + ) + +(defmethod clear! simple-sprite-system ((obj simple-sprite-system)) + (set! (-> obj count) 0) + 0 + (none) + ) + +(define *simple-sprite-system* + (new 'static 'simple-sprite-system :max-count #x80 :data (new 'static 'inline-array sprite-glow-data 128 + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + ) + ) + ) diff --git a/goal_src/jak2/engine/gfx/sprite/sprite.gc b/goal_src/jak2/engine/gfx/sprite/sprite.gc index 63746c113..cb850067b 100644 --- a/goal_src/jak2/engine/gfx/sprite/sprite.gc +++ b/goal_src/jak2/engine/gfx/sprite/sprite.gc @@ -929,10 +929,9 @@ ;; sprite glow ;;;;;;;;;;;;;;;; - ;; TODO: figure out what this is doing - ; (sprite-glow-init-engine dma-buff) - ; (sprite-glow-draw dma-buff) - ; (simple-sprite-system-method-10 *simple-sprite-system* dma-buff) + (sprite-glow-init-engine dma-buff) + (sprite-glow-draw dma-buff) + (draw-all-sprites! *simple-sprite-system* dma-buff) (let* ((v1-26 dma-buff) (pkt6 (the-as dma-packet (-> v1-26 base))) ) diff --git a/goal_src/jak2/levels/city/common/vehicle-effects.gc b/goal_src/jak2/levels/city/common/vehicle-effects.gc index 2fc747365..274ab391a 100644 --- a/goal_src/jak2/levels/city/common/vehicle-effects.gc +++ b/goal_src/jak2/levels/city/common/vehicle-effects.gc @@ -271,7 +271,7 @@ ) (when (nonzero? *simple-sprite-system*) ;; added nonzero check - (add! *simple-sprite-system* (the-as dma-buffer (-> s3-1 1))) + (add! *simple-sprite-system* (the sprite-glow-data (-> s3-1 1))) ) (forward-up->quaternion (the-as quaternion (-> s3-1 0)) arg1 (the-as vector (-> obj rbody state matrix))) (quaternion-rotate-local-x! (the-as quaternion (-> s3-1 0)) (the-as quaternion (-> s3-1 0)) 32768.0) @@ -386,7 +386,7 @@ (set! (-> s4-0 color x) 255.0) (set! (-> s4-0 color y) (rand-vu-float-range 192.0 255.0)) (set! (-> s4-0 color w) (* f30-1 (rand-vu-float-range 16.0 18.0))) - (add! *simple-sprite-system* (the-as dma-buffer s4-0)) + (add! *simple-sprite-system* s4-0) (let ((f0-21 (-> obj camera-dist2)) (f1-6 245760.0) ) @@ -399,7 +399,7 @@ (set! (-> s4-0 fade-b) 2.3332994) (set! (-> s4-0 color z) (rand-vu-float-range 128.0 160.0)) (set! (-> s4-0 color w) (* f30-1 (rand-vu-float-range 32.0 36.0))) - (add! *simple-sprite-system* (the-as dma-buffer s4-0)) + (add! *simple-sprite-system* s4-0) ) ) ) @@ -419,7 +419,7 @@ (set! (-> s4-1 rot-angle) (* 182.04445 (rand-vu-float-range -4.0 4.0))) (set! (-> s4-1 color y) (* 64.0 (rand-vu))) (set! (-> s4-1 color w) (* f30-1 (rand-vu-float-range 16.0 21.0))) - (add! *simple-sprite-system* (the-as dma-buffer s4-1)) + (add! *simple-sprite-system* s4-1) ) ) ) diff --git a/test/decompiler/reference/jak2/engine/gfx/hw/gs_REF.gc b/test/decompiler/reference/jak2/engine/gfx/hw/gs_REF.gc index 46a7a5353..89129b09e 100644 --- a/test/decompiler/reference/jak2/engine/gfx/hw/gs_REF.gc +++ b/test/decompiler/reference/jak2/engine/gfx/hw/gs_REF.gc @@ -410,7 +410,7 @@ bits 5 and 6 (0x20 and 0x40) should be zero" (goto cfg-4) ) (format #t "[~8x] ~A~%" obj 'gs-adcmd) - (format #t "~1Tword[4] @ #x~X~%" (-> obj word)) + (format #t "~1Tword[4] @ #x~X~%" (&-> obj x)) (format #t "~1Tquad: ~D~%" (-> obj quad)) (format #t "~1Tdata: ~D~%" (-> obj data)) (format #t "~1Tcmds: ~D~%" (-> obj cmds)) @@ -1039,16 +1039,16 @@ bits 5 and 6 (0x20 and 0x40) should be zero" (goto cfg-4) ) (format #t "[~8x] ~A~%" obj 'gs-packed-xyzw) - (format #t "~1Tdata[4] @ #x~X~%" (&-> obj x)) + (format #t "~1Tdata[4] @ #x~X~%" (&-> obj ix)) (format #t "~1Tx: ~f~%" (-> obj x)) (format #t "~1Ty: ~f~%" (-> obj y)) (format #t "~1Tz: ~f~%" (-> obj z)) (format #t "~1Tw: ~f~%" (-> obj w)) (format #t "~1Tquad: ~D~%" (-> obj quad)) - (format #t "~1Tix: ~D~%" (-> obj x)) - (format #t "~1Tiy: ~D~%" (-> obj y)) - (format #t "~1Tiz: ~D~%" (-> obj z)) - (format #t "~1Tiw: ~D~%" (-> obj w)) + (format #t "~1Tix: ~D~%" (-> obj ix)) + (format #t "~1Tiy: ~D~%" (-> obj iy)) + (format #t "~1Tiz: ~D~%" (-> obj iz)) + (format #t "~1Tiw: ~D~%" (-> obj iw)) (label cfg-4) obj ) diff --git a/test/decompiler/reference/jak2/engine/gfx/lightning_REF.gc b/test/decompiler/reference/jak2/engine/gfx/lightning_REF.gc index 8bafaa319..65acde31d 100644 --- a/test/decompiler/reference/jak2/engine/gfx/lightning_REF.gc +++ b/test/decompiler/reference/jak2/engine/gfx/lightning_REF.gc @@ -27,8 +27,8 @@ :regs (new 'static 'gif-tag-regs-32 :regs0 (gif-reg-id st) :regs1 (gif-reg-id rgbaq) :regs2 (gif-reg-id xyzf2)) ) :adnops (new 'static 'inline-array gs-adcmd 2 - (new 'static 'gs-adcmd :word (new 'static 'array uint32 4 #x0 #x0 #x7f #x0)) - (new 'static 'gs-adcmd :word (new 'static 'array uint32 4 #x0 #x0 #x7f #x0)) + (new 'static 'gs-adcmd :cmds (gs-reg64 hack)) + (new 'static 'gs-adcmd :cmds (gs-reg64 hack)) ) ) ) diff --git a/test/decompiler/reference/jak2/engine/gfx/sprite/simple-sprite-h_REF.gc b/test/decompiler/reference/jak2/engine/gfx/sprite/simple-sprite-h_REF.gc index 4281482b2..e249b1a6d 100644 --- a/test/decompiler/reference/jak2/engine/gfx/sprite/simple-sprite-h_REF.gc +++ b/test/decompiler/reference/jak2/engine/gfx/sprite/simple-sprite-h_REF.gc @@ -3,17 +3,18 @@ ;; definition of type sprite-glow-data (deftype sprite-glow-data (structure) - ((position vector :inline :offset-assert 0) - (size-x float :offset 12) - (size-probe float :offset-assert 16) - (z-offset float :offset-assert 20) - (rot-angle float :offset-assert 24) - (size-y float :offset-assert 28) - (color rgbaf :inline :offset-assert 32) - (fade-a float :offset-assert 48) - (fade-b float :offset-assert 52) - (tex-id texture-id :offset-assert 56) - (dummy uint32 :offset-assert 60) + ((position vector :inline :offset-assert 0) + (size-x float :offset 12) + (size-probe float :offset-assert 16) + (z-offset float :offset-assert 20) + (rot-angle float :offset-assert 24) + (size-y float :offset-assert 28) + (color rgbaf :inline :offset-assert 32) + (fade-a float :offset-assert 48) + (fade-b float :offset-assert 52) + (tex-id texture-id :offset-assert 56) + (dummy uint32 :offset-assert 60) + (quads vector 4 :inline :offset 0) ) :method-count-assert 10 :size-assert #x40 @@ -59,16 +60,16 @@ ;; definition of type simple-sprite-system (deftype simple-sprite-system (structure) - ((count int16 :offset-assert 0) - (max-count int16 :offset-assert 2) - (data sprite-glow-data :offset-assert 4) + ((count int16 :offset-assert 0) + (max-count int16 :offset-assert 2) + (data (inline-array sprite-glow-data) :offset-assert 4) ) :method-count-assert 12 :size-assert #x8 :flag-assert #xc00000008 (:methods - (add! (_type_ dma-buffer) none 9) - (simple-sprite-system-method-10 (_type_ dma-buffer) none 10) + (add! (_type_ sprite-glow-data) none 9) + (draw-all-sprites! (_type_ dma-buffer) none 10) (clear! (_type_) none 11) ) ) diff --git a/test/decompiler/reference/jak2/engine/gfx/sprite/sprite-glow_REF.gc b/test/decompiler/reference/jak2/engine/gfx/sprite/sprite-glow_REF.gc new file mode 100644 index 000000000..a29b5c2e7 --- /dev/null +++ b/test/decompiler/reference/jak2/engine/gfx/sprite/sprite-glow_REF.gc @@ -0,0 +1,917 @@ +;;-*-Lisp-*- +(in-package goal) + +;; definition of type sprite-glow-template +(deftype sprite-glow-template (structure) + ((clear-init-giftag gs-gif-tag :inline :offset-assert 0) + (clear-init-adcmds gs-adcmd 5 :inline :offset-assert 16) + (clear-draw-giftag gs-gif-tag :inline :offset-assert 96) + (clear-draw-clr-0 gs-packed-rgba :inline :offset-assert 112) + (clear-draw-xyz-0 gs-packed-xyzw 2 :inline :offset-assert 128) + (clear-draw-clr-1 gs-packed-rgba :inline :offset-assert 160) + (clear-draw-xyz-1 vector 2 :inline :offset-assert 176) + (offscr-setup-giftag gs-gif-tag :inline :offset-assert 208) + (offscr-setup-adcmds gs-adcmd 8 :inline :offset-assert 224) + (offscr-first-giftag gs-gif-tag :inline :offset-assert 352) + (offscr-first-clr gs-packed-rgba :inline :offset-assert 368) + (offscr-first-uv-0 gs-packed-uv :inline :offset-assert 384) + (offscr-first-xyzw-0 gs-packed-xyzw :inline :offset-assert 400) + (offscr-first-uv-1 gs-packed-uv :inline :offset-assert 416) + (offscr-first-xyzw-1 gs-packed-xyzw :inline :offset-assert 432) + (repeat-draw-giftag gs-gif-tag :inline :offset-assert 448) + (repeat-draw-adcmds gs-adcmd 29 :inline :offset-assert 464) + (flare-alpha-giftag gs-gif-tag :inline :offset-assert 928) + (flare-alpha-clr gs-packed-rgba :inline :offset-assert 944) + (flare-alpha-uv gs-packed-uv :inline :offset-assert 960) + (flare-alpha-xyzw-0 gs-packed-xyzw :inline :offset-assert 976) + (flare-alpha-xyzw-1 gs-packed-xyzw :inline :offset-assert 992) + (flare-alpha-xyzw-2 gs-packed-xyzw :inline :offset-assert 1008) + (flare-alpha-xyzw-3 gs-packed-xyzw :inline :offset-assert 1024) + (flare-init-giftag gs-gif-tag :inline :offset-assert 1040) + (flare-init-adcmds gs-adcmd 8 :inline :offset-assert 1056) + (flare-draw-giftag gs-gif-tag :inline :offset-assert 1184) + (flare-draw-clr gs-packed-rgba :inline :offset-assert 1200) + (flare-draw-stq-0 gs-packed-stq :inline :offset-assert 1216) + (flare-draw-xyzw-0 gs-packed-xyzw :inline :offset-assert 1232) + (flare-draw-stq-1 gs-packed-stq :inline :offset-assert 1248) + (flare-draw-xyzw-1 gs-packed-xyzw :inline :offset-assert 1264) + (flare-draw-stq-2 gs-packed-stq :inline :offset-assert 1280) + (flare-draw-xyzw-2 gs-packed-xyzw :inline :offset-assert 1296) + (flare-draw-stq-3 gs-packed-stq :inline :offset-assert 1312) + (flare-draw-xyzw-3 gs-packed-xyzw :inline :offset-assert 1328) + ) + :method-count-assert 9 + :size-assert #x540 + :flag-assert #x900000540 + ) + +;; definition for method 3 of type sprite-glow-template +(defmethod inspect sprite-glow-template ((obj sprite-glow-template)) + (when (not obj) + (set! obj obj) + (goto cfg-4) + ) + (format #t "[~8x] ~A~%" obj 'sprite-glow-template) + (format #t "~1Tclear-init-giftag: #~%" (-> obj clear-init-giftag)) + (format #t "~1Tclear-init-adcmds[5] @ #x~X~%" (-> obj clear-init-adcmds)) + (format #t "~1Tclear-draw-giftag: #~%" (-> obj clear-draw-giftag)) + (format #t "~1Tclear-draw-clr-0: #~%" (-> obj clear-draw-clr-0)) + (format #t "~1Tclear-draw-xyz-0[2] @ #x~X~%" (-> obj clear-draw-xyz-0)) + (format #t "~1Tclear-draw-clr-1: #~%" (-> obj clear-draw-clr-1)) + (format #t "~1Tclear-draw-xyz-1[2] @ #x~X~%" (-> obj clear-draw-xyz-1)) + (format #t "~1Toffscr-setup-giftag: #~%" (-> obj offscr-setup-giftag)) + (format #t "~1Toffscr-setup-adcmds[8] @ #x~X~%" (-> obj offscr-setup-adcmds)) + (format #t "~1Toffscr-first-giftag: #~%" (-> obj offscr-first-giftag)) + (format #t "~1Toffscr-first-clr: #~%" (-> obj offscr-first-clr)) + (format #t "~1Toffscr-first-uv-0: #~%" (-> obj offscr-first-uv-0)) + (format #t "~1Toffscr-first-xyzw-0: #~%" (-> obj offscr-first-xyzw-0)) + (format #t "~1Toffscr-first-uv-1: #~%" (-> obj offscr-first-uv-1)) + (format #t "~1Toffscr-first-xyzw-1: #~%" (-> obj offscr-first-xyzw-1)) + (format #t "~1Trepeat-draw-giftag: #~%" (-> obj repeat-draw-giftag)) + (format #t "~1Trepeat-draw-adcmds[29] @ #x~X~%" (-> obj repeat-draw-adcmds)) + (format #t "~1Tflare-alpha-giftag: #~%" (-> obj flare-alpha-giftag)) + (format #t "~1Tflare-alpha-clr: #~%" (-> obj flare-alpha-clr)) + (format #t "~1Tflare-alpha-uv: #~%" (-> obj flare-alpha-uv)) + (format #t "~1Tflare-alpha-xyzw-0: #~%" (-> obj flare-alpha-xyzw-0)) + (format #t "~1Tflare-alpha-xyzw-1: #~%" (-> obj flare-alpha-xyzw-1)) + (format #t "~1Tflare-alpha-xyzw-2: #~%" (-> obj flare-alpha-xyzw-2)) + (format #t "~1Tflare-alpha-xyzw-3: #~%" (-> obj flare-alpha-xyzw-3)) + (format #t "~1Tflare-init-giftag: #~%" (-> obj flare-init-giftag)) + (format #t "~1Tflare-init-adcmds[8] @ #x~X~%" (-> obj flare-init-adcmds)) + (format #t "~1Tflare-draw-giftag: #~%" (-> obj flare-draw-giftag)) + (format #t "~1Tflare-draw-clr: #~%" (-> obj flare-draw-clr)) + (format #t "~1Tflare-draw-stq-0: #~%" (-> obj flare-draw-stq-0)) + (format #t "~1Tflare-draw-xyzw-0: #~%" (-> obj flare-draw-xyzw-0)) + (format #t "~1Tflare-draw-stq-1: #~%" (-> obj flare-draw-stq-1)) + (format #t "~1Tflare-draw-xyzw-1: #~%" (-> obj flare-draw-xyzw-1)) + (format #t "~1Tflare-draw-stq-2: #~%" (-> obj flare-draw-stq-2)) + (format #t "~1Tflare-draw-xyzw-2: #~%" (-> obj flare-draw-xyzw-2)) + (format #t "~1Tflare-draw-stq-3: #~%" (-> obj flare-draw-stq-3)) + (format #t "~1Tflare-draw-xyzw-3: #~%" (-> obj flare-draw-xyzw-3)) + (label cfg-4) + obj + ) + +;; definition of type sprite-glow-consts +(deftype sprite-glow-consts (structure) + ((camera matrix :inline :offset-assert 0) + (perspective matrix :inline :offset-assert 64) + (hvdf-offset vector :inline :offset-assert 128) + (hmge-scale vector :inline :offset-assert 144) + (consts vector :inline :offset-assert 160) + (pfog0 float :offset 160) + (deg-to-rad float :offset 164) + (min-scale float :offset 168) + (inv-area float :offset 172) + (sincos-01 vector :inline :offset-assert 176) + (sincos-23 vector :inline :offset-assert 192) + (sincos-45 vector :inline :offset-assert 208) + (sincos-67 vector :inline :offset-assert 224) + (sincos-89 vector :inline :offset-assert 240) + (basis-x vector :inline :offset-assert 256) + (basis-y vector :inline :offset-assert 272) + (xy-array vector 4 :inline :offset-assert 288) + (clamp-min vector :inline :offset-assert 352) + (clamp-max vector :inline :offset-assert 368) + ) + :method-count-assert 9 + :size-assert #x180 + :flag-assert #x900000180 + ) + +;; definition for method 3 of type sprite-glow-consts +(defmethod inspect sprite-glow-consts ((obj sprite-glow-consts)) + (when (not obj) + (set! obj obj) + (goto cfg-4) + ) + (format #t "[~8x] ~A~%" obj 'sprite-glow-consts) + (format #t "~1Tcamera: #~%" (-> obj camera)) + (format #t "~1Tperspective: #~%" (-> obj perspective)) + (format #t "~1Thvdf-offset: #~%" (-> obj hvdf-offset)) + (format #t "~1Thmge-scale: #~%" (-> obj hmge-scale)) + (format #t "~1Tconsts: #~%" (&-> obj pfog0)) + (format #t "~1Tpfog0: ~f~%" (-> obj pfog0)) + (format #t "~1Tdeg-to-rad: ~f~%" (-> obj deg-to-rad)) + (format #t "~1Tmin-scale: ~f~%" (-> obj min-scale)) + (format #t "~1Tinv-area: ~f~%" (-> obj inv-area)) + (format #t "~1Tsincos-01: #~%" (-> obj sincos-01)) + (format #t "~1Tsincos-23: #~%" (-> obj sincos-23)) + (format #t "~1Tsincos-45: #~%" (-> obj sincos-45)) + (format #t "~1Tsincos-67: #~%" (-> obj sincos-67)) + (format #t "~1Tsincos-89: #~%" (-> obj sincos-89)) + (format #t "~1Tbasis-x: #~%" (-> obj basis-x)) + (format #t "~1Tbasis-y: #~%" (-> obj basis-y)) + (format #t "~1Txy-array[4] @ #x~X~%" (-> obj xy-array)) + (format #t "~1Tclamp-min: #~%" (-> obj clamp-min)) + (format #t "~1Tclamp-max: #~%" (-> obj clamp-max)) + (label cfg-4) + obj + ) + +;; definition for symbol *sprite-glow-template*, type sprite-glow-template +(define *sprite-glow-template* + (new 'static 'sprite-glow-template + :clear-init-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 :nloop #x5 :nreg #x1) + :regs (new 'static 'gif-tag-regs :regs0 (gif-reg-id a+d)) + ) + :clear-init-adcmds (new 'static 'inline-array gs-adcmd 5 + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + (new 'static 'gs-adcmd :cmds (gs-reg64 alpha-1) :x #x6a) + (new 'static 'gs-adcmd :cmds (gs-reg64 test-1) :x #x51001) + (new 'static 'gs-adcmd :cmds (gs-reg64 zbuf-1) :x #x1000130 :y #x1) + (new 'static 'gs-adcmd :cmds (gs-reg64 frame-1) :x #x80198 :y #xffffff) + ) + :clear-draw-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 + :nloop #x2 + :pre #x1 + :prim (new 'static 'gs-prim :prim (gs-prim-type sprite)) + :nreg #x3 + ) + :regs (new 'static 'gif-tag-regs :regs0 (gif-reg-id rgbaq) :regs1 (gif-reg-id xyzf2) :regs2 (gif-reg-id xyzf2)) + ) + :clear-draw-xyz-0 (new 'static 'inline-array gs-packed-xyzw 2 + (new 'static 'gs-packed-xyzw :iz -1) + (new 'static 'gs-packed-xyzw :iz -1) + ) + :clear-draw-clr-1 (new 'static 'gs-packed-rgba :x #xff :w #xff) + :offscr-setup-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 :nloop #x8 :nreg #x1) + :regs (new 'static 'gif-tag-regs :regs0 (gif-reg-id a+d)) + ) + :offscr-setup-adcmds (new 'static 'inline-array gs-adcmd 8 + (new 'static 'gs-adcmd :cmds (gs-reg64 frame-2) :x #x10090 :y #xffffff) + (new 'static 'gs-adcmd :cmds (gs-reg64 zbuf-2) :x #x1000130) + (new 'static 'gs-adcmd :cmds (gs-reg64 tex1-2) :x #x60) + (new 'static 'gs-adcmd :cmds (gs-reg64 tex0-2) :x #x64023300 :y #xe) + (new 'static 'gs-adcmd :cmds (gs-reg64 clamp-2) :x #x5) + (new 'static 'gs-adcmd :cmds (gs-reg64 test-2) :x #x31001) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyoffset-2)) + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + ) + :offscr-first-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 + :nloop #x1 + :pre #x1 + :prim (new 'static 'gs-prim :prim (gs-prim-type sprite) :tme #x1 :fst #x1 :ctxt #x1) + :nreg #x5 + ) + :regs (new 'static 'gif-tag-regs + :regs0 (gif-reg-id rgbaq) + :regs1 (gif-reg-id uv) + :regs2 (gif-reg-id xyz2) + :regs3 (gif-reg-id uv) + :regs4 (gif-reg-id xyz2) + ) + ) + :offscr-first-clr (new 'static 'gs-packed-rgba :x #x80 :y #x80 :z #x80 :w #x80) + :offscr-first-xyzw-1 (new 'static 'gs-packed-xyzw :ix #x200 :iy #x200) + :repeat-draw-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 :nloop #x1d :nreg #x1) + :regs (new 'static 'gif-tag-regs :regs0 (gif-reg-id a+d)) + ) + :repeat-draw-adcmds (new 'static 'inline-array gs-adcmd 29 + (new 'static 'gs-adcmd :cmds (gs-reg64 tex0-2) :x #x54005200 :y #xd) + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x100010) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x2100210) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2) :x #x1000100) + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x100010) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x1100110) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2) :x #x800080) + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x100010) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x900090) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2) :x #x400040) + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x100010) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2)) + (new 'static 'gs-adcmd :cmds (gs-reg64 uv) :x #x500050) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyz2) :x #x200020) + (new 'static 'gs-adcmd :cmds (gs-reg64 frame-2) :x #x80198 :y #xffffff) + (new 'static 'gs-adcmd :cmds (gs-reg64 test-2) :x #x51001) + (new 'static 'gs-adcmd :cmds (gs-reg64 zbuf-2) :x #x1000130 :y #x1) + (new 'static 'gs-adcmd :cmds (gs-reg64 xyoffset-2) :x #x7000 :y #x7300) + (new 'static 'gs-adcmd :cmds (gs-reg64 scissor-2) :x #x1ff0000 :y #x19f0000) + (new 'static 'gs-adcmd :cmds (gs-reg64 test-2) :x #x50000) + (new 'static 'gs-adcmd :cmds (gs-reg64 alpha-2) :x #x6a) + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + ) + :flare-alpha-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 + :nloop #x1 + :pre #x1 + :prim (new 'static 'gs-prim :prim (gs-prim-type tri-fan) :tme #x1 :fst #x1 :ctxt #x1) + :nreg #x6 + ) + :regs (new 'static 'gif-tag-regs + :regs0 (gif-reg-id rgbaq) + :regs1 (gif-reg-id uv) + :regs2 (gif-reg-id xyzf2) + :regs3 (gif-reg-id xyzf2) + :regs4 (gif-reg-id xyzf2) + :regs5 (gif-reg-id xyzf2) + ) + ) + :flare-alpha-clr (new 'static 'gs-packed-rgba :x #x80 :y #x80 :z #x80 :w #x80) + :flare-alpha-uv (new 'static 'gs-packed-uv :x (the-as float #x10) :y (the-as float #x10)) + :flare-alpha-xyzw-0 (new 'static 'gs-packed-xyzw :iz -1) + :flare-alpha-xyzw-1 (new 'static 'gs-packed-xyzw :iz -1) + :flare-alpha-xyzw-2 (new 'static 'gs-packed-xyzw :iz -1) + :flare-alpha-xyzw-3 (new 'static 'gs-packed-xyzw :iz -1) + :flare-init-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 :nloop #x8 :nreg #x1) + :regs (new 'static 'gif-tag-regs :regs0 (gif-reg-id a+d)) + ) + :flare-init-adcmds (new 'static 'inline-array gs-adcmd 8 + (new 'static 'gs-adcmd :cmds (gs-reg64 texflush)) + (new 'static 'gs-adcmd :cmds (gs-reg64 frame-1) :x #x80198) + (new 'static 'gs-adcmd :cmds (gs-reg64 hack)) + (new 'static 'gs-adcmd :cmds (gs-reg64 hack)) + (new 'static 'gs-adcmd :cmds (gs-reg64 hack)) + (new 'static 'gs-adcmd :cmds (gs-reg64 hack)) + (new 'static 'gs-adcmd :cmds (gs-reg64 hack)) + (new 'static 'gs-adcmd :cmds (gs-reg64 alpha-1) :x #x58) + ) + :flare-draw-giftag (new 'static 'gs-gif-tag + :tag (new 'static 'gif-tag64 + :nloop #x1 + :eop #x1 + :pre #x1 + :prim (new 'static 'gs-prim :prim (gs-prim-type tri-fan) :tme #x1 :abe #x1) + :nreg #x9 + ) + :regs (new 'static 'gif-tag-regs + :regs0 (gif-reg-id rgbaq) + :regs1 (gif-reg-id st) + :regs2 (gif-reg-id xyzf2) + :regs3 (gif-reg-id st) + :regs4 (gif-reg-id xyzf2) + :regs5 (gif-reg-id st) + :regs6 (gif-reg-id xyzf2) + :regs7 (gif-reg-id st) + :regs8 (gif-reg-id xyzf2) + ) + ) + :flare-draw-clr (new 'static 'gs-packed-rgba :y 64 :w #x80) + :flare-draw-stq-0 (new 'static 'gs-packed-stq :z 1.0) + :flare-draw-xyzw-0 (new 'static 'gs-packed-xyzw :iz -1) + :flare-draw-stq-1 (new 'static 'gs-packed-stq :x 1.0 :z 1.0) + :flare-draw-xyzw-1 (new 'static 'gs-packed-xyzw :iz -1) + :flare-draw-stq-2 (new 'static 'gs-packed-stq :x 1.0 :y 1.0 :z 1.0) + :flare-draw-xyzw-2 (new 'static 'gs-packed-xyzw :iz -1) + :flare-draw-stq-3 (new 'static 'gs-packed-stq :y 1.0 :z 1.0) + :flare-draw-xyzw-3 (new 'static 'gs-packed-xyzw :iz -1) + ) + ) + +;; definition for symbol sprite-glow-vu1-block, type vu-function +(define sprite-glow-vu1-block (new 'static 'vu-function :length #x86 :qlength 67)) + +;; definition for function sprite-glow-init-consts +;; INFO: Used lq/sq +;; WARN: Return type mismatch vector vs none. +(defun sprite-glow-init-consts ((arg0 sprite-glow-consts)) + (let ((v1-0 *math-camera*)) + (let* ((t0-0 (-> arg0 camera)) + (t1-0 (-> v1-0 camera-rot)) + (a1-0 (-> t1-0 quad 0)) + (a2-0 (-> t1-0 quad 1)) + (a3-0 (-> t1-0 quad 2)) + (t1-1 (-> t1-0 trans quad)) + ) + (set! (-> t0-0 quad 0) a1-0) + (set! (-> t0-0 quad 1) a2-0) + (set! (-> t0-0 quad 2) a3-0) + (set! (-> t0-0 trans quad) t1-1) + ) + (let* ((t0-1 (-> arg0 perspective)) + (t1-2 (-> v1-0 perspective)) + (a1-1 (-> t1-2 quad 0)) + (a2-1 (-> t1-2 quad 1)) + (a3-1 (-> t1-2 quad 2)) + (t1-3 (-> t1-2 trans quad)) + ) + (set! (-> t0-1 quad 0) a1-1) + (set! (-> t0-1 quad 1) a2-1) + (set! (-> t0-1 quad 2) a3-1) + (set! (-> t0-1 trans quad) t1-3) + ) + (set! (-> arg0 hvdf-offset quad) (-> v1-0 hvdf-off quad)) + (set! (-> arg0 hmge-scale quad) (-> v1-0 hmge-scale quad)) + (set! (-> arg0 basis-x quad) (the-as uint128 0)) + (set! (-> arg0 basis-x x) (- (-> *math-camera* perspective vector 0 x))) + (set! (-> arg0 basis-y quad) (the-as uint128 0)) + (set! (-> arg0 basis-y y) (- (-> *math-camera* perspective vector 1 y))) + (set! (-> arg0 pfog0) (-> v1-0 pfog0)) + ) + (set! (-> arg0 deg-to-rad) 0.000095873795) + (set! (-> arg0 min-scale) (sqrtf (* (/ 1.0 (-> arg0 basis-x x)) (/ 1.0 (-> arg0 basis-y y))))) + (set! (-> arg0 inv-area) (/ 1.0 (* (-> arg0 min-scale) (-> arg0 min-scale)))) + (let ((v1-5 (-> arg0 sincos-01))) + (set! (-> v1-5 z) 0.999998) + (set! (-> v1-5 w) 1.0) + ) + (let ((v1-6 (-> arg0 sincos-23))) + (set! (-> v1-6 z) -0.16666014) + (set! (-> v1-6 w) -0.49998003) + ) + (let ((v1-7 (-> arg0 sincos-45))) + (set! (-> v1-7 z) 0.008326521) + (set! (-> v1-7 w) 0.041620404) + ) + (let ((v1-8 (-> arg0 sincos-67))) + (set! (-> v1-8 z) -0.0001956241) + (set! (-> v1-8 w) -0.0013636408) + ) + (let ((v1-9 (-> arg0 sincos-89))) + (set! (-> v1-9 z) 0.0000023042373) + (set! (-> v1-9 w) 0.000020170546) + ) + (set-vector! (-> arg0 xy-array 0) -0.5 -0.5 0.0 0.0) + (set-vector! (-> arg0 xy-array 1) 0.5 -0.5 0.0 0.0) + (set-vector! (-> arg0 xy-array 2) 0.5 0.5 0.0 0.0) + (set-vector! (-> arg0 xy-array 3) -0.5 0.5 0.0 0.0) + (set-vector! (-> arg0 clamp-min) 1792.0 1840.0 0.0 0.0) + (set-vector! (-> arg0 clamp-max) 2304.0 2256.0 2048.0 50.0) + (none) + ) + +;; definition for function sprite-glow-init-engine +;; WARN: Return type mismatch int vs none. +(defun sprite-glow-init-engine ((arg0 dma-buffer)) + (dma-buffer-add-vu-function arg0 sprite-glow-vu1-block 1) + (let ((s5-0 24)) + (let* ((v1-0 arg0) + (a0-2 (the-as dma-packet (-> v1-0 base))) + ) + (set! (-> a0-2 dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc s5-0)) + (set! (-> a0-2 vif0) (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl))) + (set! (-> a0-2 vif1) (new 'static 'vif-tag :imm #x3d4 :cmd (vif-cmd unpack-v4-32) :num s5-0)) + (set! (-> v1-0 base) (the-as pointer (&+ a0-2 16))) + ) + (sprite-glow-init-consts (the-as sprite-glow-consts (-> arg0 base))) + (&+! (-> arg0 base) (* s5-0 16)) + ) + (let ((v1-3 84)) + (let* ((a0-6 arg0) + (a1-6 (the-as dma-packet (-> a0-6 base))) + ) + (set! (-> a1-6 dma) + (new 'static 'dma-tag :id (dma-tag-id ref) :addr (the-as int *sprite-glow-template*) :qwc v1-3) + ) + (set! (-> a1-6 vif0) (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl))) + (set! (-> a1-6 vif1) (new 'static 'vif-tag :imm #x320 :cmd (vif-cmd unpack-v4-32) :num v1-3)) + (set! (-> a0-6 base) (the-as pointer (&+ a1-6 16))) + ) + (let* ((a0-7 arg0) + (a1-8 (the-as dma-packet (-> a0-7 base))) + ) + (set! (-> a1-8 dma) + (new 'static 'dma-tag :id (dma-tag-id ref) :addr (the-as int *sprite-glow-template*) :qwc v1-3) + ) + (set! (-> a1-8 vif0) (new 'static 'vif-tag :cmd (vif-cmd mscal) :msk #x1 :imm #x0)) + (set! (-> a1-8 vif1) (new 'static 'vif-tag :imm #x374 :cmd (vif-cmd unpack-v4-32) :num v1-3)) + (set! (-> a0-7 base) (the-as pointer (&+ a1-8 16))) + ) + ) + (let* ((v1-8 arg0) + (a0-8 (the-as dma-packet (-> v1-8 base))) + ) + (set! (-> a0-8 dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) + (set! (-> a0-8 vif0) (new 'static 'vif-tag :cmd (vif-cmd base))) + (set! (-> a0-8 vif1) (new 'static 'vif-tag :imm #x190 :cmd (vif-cmd offset))) + (set! (-> v1-8 base) (the-as pointer (&+ a0-8 16))) + ) + (let ((v1-9 (the-as dma-packet (-> arg0 base)))) + (set! (-> v1-9 dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) + (set! (-> v1-9 vif0) (new 'static 'vif-tag)) + (set! (-> v1-9 vif1) (new 'static 'vif-tag :cmd (vif-cmd flushe) :msk #x1)) + (set! (-> arg0 base) (the-as pointer (&+ v1-9 16))) + ) + 0 + (none) + ) + +;; definition of type sprite-glow-dma-packet-data +(deftype sprite-glow-dma-packet-data (structure) + ((control-packet dma-packet :inline :offset-assert 0) + (vecdata-packet dma-packet :inline :offset-assert 16) + (shader-cnt-packet dma-packet :inline :offset-assert 32) + (shader-ref-packet dma-packet :inline :offset-assert 48) + (mscal-packet dma-packet :inline :offset-assert 64) + ) + :method-count-assert 9 + :size-assert #x50 + :flag-assert #x900000050 + ) + +;; definition for method 3 of type sprite-glow-dma-packet-data +(defmethod inspect sprite-glow-dma-packet-data ((obj sprite-glow-dma-packet-data)) + (when (not obj) + (set! obj obj) + (goto cfg-4) + ) + (format #t "[~8x] ~A~%" obj 'sprite-glow-dma-packet-data) + (format #t "~1Tcontrol-packet: #~%" (-> obj control-packet)) + (format #t "~1Tvecdata-packet: #~%" (-> obj vecdata-packet)) + (format #t "~1Tshader-cnt-packet: #~%" (-> obj shader-cnt-packet)) + (format #t "~1Tshader-ref-packet: #~%" (-> obj shader-ref-packet)) + (format #t "~1Tmscal-packet: #~%" (-> obj mscal-packet)) + (label cfg-4) + obj + ) + +;; definition of type sprite-glow-cnt-template +(deftype sprite-glow-cnt-template (structure) + ((control-packet dma-packet :inline :offset-assert 0) + (num-sprites uint32 :offset-assert 16) + (dummys uint32 3 :offset-assert 20) + (num-sprites-quad uint128 :offset 16) + (vecdata-packet dma-packet :inline :offset-assert 32) + (vecdata sprite-glow-data :inline :offset-assert 48) + (shader-packet dma-packet :inline :offset-assert 112) + (shader adgif-shader :inline :offset-assert 128) + (mscal-packet dma-packet :inline :offset-assert 208) + ) + :method-count-assert 9 + :size-assert #xe0 + :flag-assert #x9000000e0 + ) + +;; definition for method 3 of type sprite-glow-cnt-template +(defmethod inspect sprite-glow-cnt-template ((obj sprite-glow-cnt-template)) + (when (not obj) + (set! obj obj) + (goto cfg-4) + ) + (format #t "[~8x] ~A~%" obj 'sprite-glow-cnt-template) + (format #t "~1Tcontrol-packet: #~%" (-> obj control-packet)) + (format #t "~1Tnum-sprites: ~D~%" (-> obj num-sprites)) + (format #t "~1Tdummys[3] @ #x~X~%" (-> obj dummys)) + (format #t "~1Tvecdata-packet: #~%" (-> obj vecdata-packet)) + (format #t "~1Tvecdata: #~%" (-> obj vecdata)) + (format #t "~1Tshader-packet: #~%" (-> obj shader-packet)) + (format #t "~1Tshader: #~%" (-> obj shader)) + (format #t "~1Tmscal-packet: #~%" (-> obj mscal-packet)) + (label cfg-4) + obj + ) + +;; definition of type sprite-glow-ref-template +(deftype sprite-glow-ref-template (structure) + ((control-packet dma-packet :inline :offset-assert 0) + (num-sprites uint32 :offset-assert 16) + (dummys uint32 3 :offset-assert 20) + (num-sprites-quad uint128 :offset 16) + (vecdata-packet dma-packet :inline :offset-assert 32) + (vecdata sprite-glow-data :inline :offset-assert 48) + (shader-packet dma-packet :inline :offset-assert 112) + (shader-packet-ptr pointer :offset 116) + (mscal-packet dma-packet :inline :offset-assert 128) + ) + :method-count-assert 9 + :size-assert #x90 + :flag-assert #x900000090 + ) + +;; definition for method 3 of type sprite-glow-ref-template +(defmethod inspect sprite-glow-ref-template ((obj sprite-glow-ref-template)) + (when (not obj) + (set! obj obj) + (goto cfg-4) + ) + (format #t "[~8x] ~A~%" obj 'sprite-glow-ref-template) + (format #t "~1Tcontrol-packet: #~%" (-> obj control-packet)) + (format #t "~1Tnum-sprites: ~D~%" (-> obj num-sprites)) + (format #t "~1Tdummys[3] @ #x~X~%" (-> obj dummys)) + (format #t "~1Tvecdata-packet: #~%" (-> obj vecdata-packet)) + (format #t "~1Tvecdata: #~%" (-> obj vecdata)) + (format #t "~1Tshader-packet: #~%" (-> obj shader-packet)) + (format #t "~1Tmscal-packet: #~%" (-> obj mscal-packet)) + (label cfg-4) + obj + ) + +;; definition for symbol *sprite-glow-dma-packet-data*, type sprite-glow-dma-packet-data +(define *sprite-glow-dma-packet-data* + (new 'static 'sprite-glow-dma-packet-data + :control-packet (new 'static 'dma-packet + :dma (new 'static 'dma-tag :qwc #x1 :id (dma-tag-id cnt)) + :vif0 (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl)) + :vif1 (new 'static 'vif-tag :imm #x8000 :num #x1 :cmd (vif-cmd unpack-v4-32)) + ) + :vecdata-packet (new 'static 'dma-packet + :dma (new 'static 'dma-tag :qwc #x4 :id (dma-tag-id cnt)) + :vif0 (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl)) + :vif1 (new 'static 'vif-tag :imm #x8001 :num #x4 :cmd (vif-cmd unpack-v4-32)) + ) + :shader-cnt-packet (new 'static 'dma-packet + :dma (new 'static 'dma-tag :qwc #x5 :id (dma-tag-id cnt)) + :vif0 (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl)) + :vif1 (new 'static 'vif-tag :imm #x8091 :num #x5 :cmd (vif-cmd unpack-v4-32)) + ) + :shader-ref-packet (new 'static 'dma-packet + :dma (new 'static 'dma-tag :qwc #x5 :id (dma-tag-id ref)) + :vif0 (new 'static 'vif-tag :imm #x404 :cmd (vif-cmd stcycl)) + :vif1 (new 'static 'vif-tag :imm #x8091 :num #x5 :cmd (vif-cmd unpack-v4-32)) + ) + :mscal-packet (new 'static 'dma-packet + :dma (new 'static 'dma-tag :id (dma-tag-id cnt)) + :vif1 (new 'static 'vif-tag :cmd (vif-cmd flushe) :msk #x1) + ) + ) + ) + +;; failed to figure out what this is: +(set! (-> *sprite-glow-dma-packet-data* mscal-packet vif0) + (new 'static 'vif-tag :cmd (vif-cmd mscalf) :msk #x1 :imm #xa) + ) + +;; definition for function sprite-glow-add-sprite +;; INFO: Used lq/sq +;; WARN: Return type mismatch sprite-glow-cnt-template vs none. +(defun sprite-glow-add-sprite ((arg0 dma-buffer) (arg1 sprite-vec-data-2d) (arg2 float) (arg3 float) (arg4 float) (arg5 adgif-shader)) + (let ((v1-0 (the-as sprite-glow-cnt-template (-> arg0 base)))) + (let* ((t5-0 *sprite-glow-dma-packet-data*) + (t2-0 (-> t5-0 control-packet quad)) + (t3-0 (-> t5-0 vecdata-packet quad)) + (t4-0 (-> t5-0 shader-cnt-packet quad)) + (t5-1 (-> t5-0 mscal-packet quad)) + ) + (set! (-> v1-0 control-packet quad) t2-0) + (set! (-> v1-0 num-sprites-quad) (the-as uint128 0)) + (set! (-> v1-0 vecdata-packet quad) t3-0) + (set! (-> v1-0 shader-packet quad) t4-0) + (set! (-> v1-0 mscal-packet quad) t5-1) + ) + (let ((t2-1 (-> arg1 x-y-z-sx quad)) + (t3-1 (-> arg1 flag-rot-sy quad)) + (a1-1 (-> arg1 r-g-b-a quad)) + ) + (set! (-> v1-0 vecdata position quad) t2-1) + (set! (-> v1-0 vecdata quads 1 quad) t3-1) + (set! (-> v1-0 vecdata color quad) a1-1) + ) + (let ((a1-2 1)) + (set! (-> v1-0 vecdata z-offset) arg4) + (set! (-> v1-0 vecdata fade-a) arg2) + (set! (-> v1-0 vecdata fade-b) arg3) + (set! (-> v1-0 num-sprites) (the-as uint a1-2)) + ) + (let ((a1-3 (-> arg5 quad 0 quad)) + (a2-1 (-> arg5 quad 1 quad)) + (a3-1 (-> arg5 quad 2 quad)) + (t0-1 (-> arg5 quad 3 quad)) + (t1-1 (-> arg5 quad 4 quad)) + ) + (set! (-> v1-0 shader quad 0 quad) a1-3) + (set! (-> v1-0 shader quad 1 quad) a2-1) + (set! (-> v1-0 shader quad 2 quad) a3-1) + (set! (-> v1-0 shader quad 3 quad) t0-1) + (set! (-> v1-0 shader quad 4 quad) t1-1) + ) + (set! (-> arg0 base) (the-as pointer (&+ v1-0 224))) + ) + (none) + ) + +;; definition for function sprite-glow-add-simple-sprite +;; INFO: Used lq/sq +;; WARN: Return type mismatch sprite-glow-ref-template vs none. +(defun sprite-glow-add-simple-sprite ((arg0 dma-buffer) (arg1 sprite-glow-dma-packet-data) (arg2 sprite-glow-data) (arg3 pointer)) + (let ((v1-0 (the-as sprite-glow-ref-template (-> arg0 base)))) + (let ((t0-0 (-> arg1 control-packet quad)) + (t1-0 (-> arg1 vecdata-packet quad)) + (t2-0 (-> arg1 shader-ref-packet quad)) + (a1-1 (-> arg1 mscal-packet quad)) + ) + (set! (-> v1-0 control-packet quad) t0-0) + (set! (-> v1-0 num-sprites-quad) (the-as uint128 0)) + (set! (-> v1-0 vecdata-packet quad) t1-0) + (set! (-> v1-0 shader-packet quad) t2-0) + (set! (-> v1-0 mscal-packet quad) a1-1) + ) + (let ((a1-2 (-> arg2 position quad)) + (t0-1 (-> arg2 quads 1 quad)) + (t1-1 (-> arg2 color quad)) + (a2-1 (-> arg2 quads 3 quad)) + (t2-1 1) + ) + (set! (-> v1-0 vecdata position quad) a1-2) + (set! (-> v1-0 vecdata quads 1 quad) t0-1) + (set! (-> v1-0 vecdata color quad) t1-1) + (set! (-> v1-0 vecdata quads 3 quad) a2-1) + (set! (-> v1-0 num-sprites) (the-as uint t2-1)) + ) + (set! (-> v1-0 shader-packet-ptr) arg3) + (set! (-> arg0 base) (the-as pointer (&+ v1-0 144))) + ) + (none) + ) + +;; definition for function sprite-glow-draw +;; WARN: Return type mismatch int vs none. +(defun sprite-glow-draw ((arg0 dma-buffer)) + (local-vars (a2-0 float)) + (let* ((s5-0 *sprite-aux-list*) + (s4-0 (-> s5-0 entry)) + (s3-0 #f) + ) + (dotimes (s2-0 s4-0) + (let ((v1-2 (-> s5-0 data s2-0))) + (when (and (not s3-0) (= (-> v1-2 aux-type) (sprite-aux-type glow))) + (let* ((a0-5 (-> v1-2 aux-data omega)) + (a3-0 (cond + ((zero? a0-5) + (set! a2-0 0.0) + 1.0 + ) + (else + (let* ((f1-1 (* 0.00024414062 a0-5)) + (f0-4 (- f1-1 (the float (the int f1-1)))) + ) + (let ((f1-3 (* 4096.0 (- f1-1 f0-4)))) + (set! a2-0 (/ -1.0 (* (- 1.0 f0-4) f1-3))) + ) + (/ f0-4 (- 1.0 f0-4)) + ) + ) + ) + ) + ) + (sprite-glow-add-sprite arg0 (-> v1-2 vec-data) a2-0 a3-0 (-> v1-2 aux-data user-float) (-> v1-2 gif-data)) + ) + ) + ) + ) + ) + 0 + (none) + ) + +;; definition for method 9 of type simple-sprite-system +;; INFO: Used lq/sq +;; WARN: Return type mismatch int vs none. +(defmethod add! simple-sprite-system ((obj simple-sprite-system) (arg0 sprite-glow-data)) + (let ((v1-0 (-> obj count))) + (when (< v1-0 (-> obj max-count)) + (let* ((a2-3 (-> obj data v1-0)) + (v1-2 arg0) + (a1-1 a2-3) + (a2-4 (-> v1-2 position quad)) + (a3-0 (-> v1-2 quads 1 quad)) + (t0-0 (-> v1-2 color quad)) + (v1-3 (-> v1-2 quads 3 quad)) + ) + (set! (-> a1-1 position quad) a2-4) + (set! (-> a1-1 quads 1 quad) a3-0) + (set! (-> a1-1 color quad) t0-0) + (set! (-> a1-1 quads 3 quad) v1-3) + ) + (+! (-> obj count) 1) + ) + ) + 0 + (none) + ) + +;; definition for function add-shader-to-dma +;; INFO: Used lq/sq +;; WARN: Return type mismatch pointer vs adgif-shader. +(defun add-shader-to-dma ((arg0 dma-buffer)) + (let* ((a1-0 (new 'static 'dma-packet :dma (new 'static 'dma-tag :id (dma-tag-id next)))) + (v1-0 (-> arg0 base)) + (a1-1 (-> a1-0 quad)) + (v0-0 (&+ v1-0 16)) + ) + (let ((a2-0 (&+ v1-0 96))) + (set! (-> (the-as (pointer uint128) v1-0)) a1-1) + (set! (-> (the-as (pointer uint32) v1-0) 1) (the-as uint a2-0)) + (set! (-> arg0 base) a2-0) + ) + (the-as adgif-shader v0-0) + ) + ) + +;; definition for method 10 of type simple-sprite-system +;; WARN: Return type mismatch int vs none. +(defmethod draw-all-sprites! simple-sprite-system ((obj simple-sprite-system) (arg0 dma-buffer)) + (local-vars (sv-528 sprite-glow-dma-packet-data) (sv-532 (pointer texture-id)) (sv-536 pointer)) + (b! (zero? (-> obj count)) cfg-13 :delay (nop!)) + (set! sv-528 *sprite-glow-dma-packet-data*) + (set! sv-532 (new 'stack-no-clear 'array 'texture-id 128)) + (set! sv-536 (-> arg0 base)) + (dotimes (v1-5 (-> obj count)) + (set! (-> sv-532 v1-5) (-> obj data v1-5 tex-id)) + ) + (countdown (s4-0 (-> obj count)) + (let ((s3-0 (-> sv-532 s4-0))) + (when (nonzero? s3-0) + (let ((s2-0 (add-shader-to-dma arg0))) + (adgif-shader<-texture-simple! s2-0 (lookup-texture-by-id s3-0)) + (countdown (s1-1 (+ s4-0 1)) + (when (= s3-0 (-> sv-532 s1-1)) + (set! (-> sv-532 s1-1) (new 'static 'texture-id)) + (let ((a2-1 (-> obj data s1-1))) + (sprite-glow-add-simple-sprite arg0 sv-528 a2-1 (the-as pointer s2-0)) + ) + ) + ) + ) + ) + ) + ) + (label cfg-13) + 0 + (none) + ) + +;; definition for method 11 of type simple-sprite-system +;; WARN: Return type mismatch int vs none. +(defmethod clear! simple-sprite-system ((obj simple-sprite-system)) + (set! (-> obj count) 0) + 0 + (none) + ) + +;; definition for symbol *simple-sprite-system*, type simple-sprite-system +(define *simple-sprite-system* + (new 'static 'simple-sprite-system :max-count #x80 :data (new 'static 'inline-array sprite-glow-data 128 + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + (new 'static 'sprite-glow-data) + ) + ) + ) diff --git a/test/decompiler/reference/jak2/engine/gfx/sprite/sprite_REF.gc b/test/decompiler/reference/jak2/engine/gfx/sprite/sprite_REF.gc index 878f09ee8..3ebc0ef43 100644 --- a/test/decompiler/reference/jak2/engine/gfx/sprite/sprite_REF.gc +++ b/test/decompiler/reference/jak2/engine/gfx/sprite/sprite_REF.gc @@ -791,8 +791,7 @@ ;; definition for function sprite-draw ;; WARN: Return type mismatch int vs none. -;; WARN: Failed store: (s.w! (+ v1-27 8) 0) at op 160 -;; WARN: Failed store: (s.w! (+ v1-27 12) 0) at op 161 +;; ERROR: Failed store: (s.w! (+ v1-27 8) 0) at op 160 (defun sprite-draw ((disp display)) (let ((dma-mem-begin (-> *display* frames (-> *display* on-screen) global-buf base))) (with-dma-buffer-add-bucket ((dma-buff (-> *display* frames (-> *display* on-screen) global-buf)) @@ -878,7 +877,7 @@ ) (sprite-glow-init-engine dma-buff) (sprite-glow-draw dma-buff) - (simple-sprite-system-method-10 *simple-sprite-system* dma-buff) + (draw-all-sprites! *simple-sprite-system* dma-buff) (let ((v1-26 dma-buff)) (let ((pkt6 (the-as dma-packet (-> v1-26 base)))) (set! (-> pkt6 dma) (new 'static 'dma-tag :id (dma-tag-id cnt))) diff --git a/test/decompiler/reference/jak2/levels/city/common/vehicle-effects_REF.gc b/test/decompiler/reference/jak2/levels/city/common/vehicle-effects_REF.gc index 5d4e85a1b..7ffe59890 100644 --- a/test/decompiler/reference/jak2/levels/city/common/vehicle-effects_REF.gc +++ b/test/decompiler/reference/jak2/levels/city/common/vehicle-effects_REF.gc @@ -273,7 +273,7 @@ (set! (-> s3-1 1 normal w) f0-14) (set! (-> s3-1 1 normal x) (* 0.025 f0-14)) ) - (add! *simple-sprite-system* (the-as dma-buffer (-> s3-1 1))) + (add! *simple-sprite-system* (the-as sprite-glow-data (-> s3-1 1))) (forward-up->quaternion (the-as quaternion (-> s3-1 0)) arg1 (the-as vector (-> obj rbody state matrix))) (quaternion-rotate-local-x! (the-as quaternion (-> s3-1 0)) (the-as quaternion (-> s3-1 0)) 32768.0) (let ((v1-16 (-> s3-1 0 normal))) @@ -390,7 +390,7 @@ (set! (-> s4-0 color x) 255.0) (set! (-> s4-0 color y) (rand-vu-float-range 192.0 255.0)) (set! (-> s4-0 color w) (* f30-1 (rand-vu-float-range 16.0 18.0))) - (add! *simple-sprite-system* (the-as dma-buffer s4-0)) + (add! *simple-sprite-system* s4-0) (let ((f0-21 (-> obj camera-dist2)) (f1-6 245760.0) ) @@ -403,7 +403,7 @@ (set! (-> s4-0 fade-b) 2.3332994) (set! (-> s4-0 color z) (rand-vu-float-range 128.0 160.0)) (set! (-> s4-0 color w) (* f30-1 (rand-vu-float-range 32.0 36.0))) - (add! *simple-sprite-system* (the-as dma-buffer s4-0)) + (add! *simple-sprite-system* s4-0) ) ) ) @@ -423,7 +423,7 @@ (set! (-> s4-1 rot-angle) (* 182.04445 (rand-vu-float-range -4.0 4.0))) (set! (-> s4-1 color y) (* 64.0 (rand-vu))) (set! (-> s4-1 color w) (* f30-1 (rand-vu-float-range 16.0 21.0))) - (add! *simple-sprite-system* (the-as dma-buffer s4-1)) + (add! *simple-sprite-system* s4-1) ) ) )