[jak3] Decompile gcommon (#3321)

Decompile `gcommon`. I adjusted the spacing of docstring comments, and
removed some spammy decompiler warning prints.

I also added some random notes I had on VU programs from jak1/jak2. They
are not polished, but I think it's still worth including since we'll
have to go through them again for jak 3.
This commit is contained in:
water111
2024-01-20 12:33:39 -05:00
committed by GitHub
parent 9a4929ac0c
commit 1c0038294f
27 changed files with 4173 additions and 722 deletions
+44
View File
@@ -0,0 +1,44 @@
# BLERC
"blerc" = "merc blend shape" is the face animation program.
It works by updating the vertices used by merc.
It begine with a call to `(blerc-init)`, which resets the list of things to blerc.
Then, each process-drawable calls `merc-blend-shape` (if needed), which figures out the blend shape coefficients for the current frame, and passes it to `setup-blerc-chains`. This builds a DMA chain of all blercs.
However, this DMA chain doesn't get put in a VU1 bucket. Instead, after the full chain is build, `(blerc-execute)` will do a bunch of math and update the merc vertex data.
This was checked by running `(-> (the process-drawable (process-by-name "sidekick" *active-pool*)) draw mgeo effect 0 frag-geo)` and seeing that the `spad_from_dma_no_sadr_off` in `merc_blend_shape.cpp` was writing to this data.
There is a system to avoid doing blerc computations when not needed. Once blerc is disabled, it does a single final run with all 0's, which puts everything "back to normal", and then blerc no longer runs.
# Determining which fragments can be blerc'd
In the PC port, we will need to update the vertices. We want to update as few vertices as possible at runtime because this will likely be slow. Additionally, we need metadata to figure out how to go from modified merc data back to PC-port format. If we know which vertices can possibly be modified by blerc, we can skip including the metadata for the rest. Only a small number of merc vertices are faces, so this could be a big win for data size.
As far as I can tell, there's a `merc-blend-ctrl` per each fragment. It's size is `2 + merc_ctrl.header.blend_target_count`. If the `blend-vtx-count` field of a merc-blend-ctrl is 0, then this fragment has no blerc.
Checking some common models:
(showing number of effects, frags, lump4 bytes that have possible blerc)
```
BLERC: eichar-lod0, 3/4 e, 15/85 f, 1737/9984 v
BLERC: sidekick-lod0, 3/4 e, 9/35 f, 957/3933 v
```
# PC draw lists:
The plan is to make 3 sets of draws:
- Normal draw list
- Draw list that touches no blerc vertices
- Alternate draw list for blerc vertices
The first list is what is normally used. This may contain draws that mix together blerc and non-blerc vertices.
The second draw list can be used when blerc is active for this character for all the non-blerc vertices.
The third draw list will contain the blerc vertices, but will do indexing slightly differently. Instead of indexing into the giant array of all merc vertices, it will index into the yet-to-be-filled blerc index buffer for this character.
This approach gives us:
- the same performance as before if blerc is off
- easy way to do per-character blerc'd vertex uploads
- possibility to do per-frame blerc'd vertex uploads with some clever indexing.
- per-fragment granularity for blerc on/off
@@ -0,0 +1,137 @@
## Finding the Normals
We're going to assume that the generic tie math in Jak 1 is the same as ETIE as Jak 2. This could be wrong, but it should be easy to verify.
The tricky part is finding the normals. These are needed for ETIE, but not plain TIE.
For looking through the types, it seems like generic TIE uses the same normals as ETIE:
```lisp
(deftype generic-tie-normal (structure)
((x int8 :offset-assert 0)
(y int8 :offset-assert 1)
(z int8 :offset-assert 2)
(dummy int8 :offset-assert 3) ;; was 0 in ETIE normals.
)
:method-count-assert 9
:size-assert #x4
:flag-assert #x900000004
)
```
Searching around for normal
```lisp
(deftype generic-tie-header (structure)
((effect uint8 :offset-assert 0)
(interp-table-size uint8 :offset-assert 1)
(num-bps uint8 :offset-assert 2)
(num-ips uint8 :offset-assert 3)
(tint-color uint32 :offset-assert 4)
(index-table-offset uint16 :offset-assert 8)
(kick-table-offset uint16 :offset-assert 10)
(normal-table-offset uint16 :offset-assert 12) ;; here it is!
(interp-table-offset uint16 :offset-assert 14)
(gsf-header gsf-header :inline :offset-assert 16)
)
:method-count-assert 9
:size-assert #x20
:flag-assert #x900000020
)
```
My first guess is that the generic data for a TIE fragment is just a header, and these `uint16`s are byte-offsets for the normal data. I'd guess that there's a header per-fragment - they used `uint8`'s for `num-bps`/`num-ips` (base points, interpolated points). A single proto might have more than 255 points, but a fragment won't.
My guess is that `generic-ref` in a `tie-fragment` points to some data that starts with `generic-tie-header`.
```lisp
(deftype tie-fragment (drawable)
((gif-ref (inline-array adgif-shader) :offset 4)
(point-ref uint32 :offset 8)
(color-index uint16 :offset 12)
(base-colors uint8 :offset 14)
(tex-count uint16 :offset-assert 32)
(gif-count uint16 :offset-assert 34)
(vertex-count uint16 :offset-assert 36)
(color-count uint16 :offset-assert 38)
(num-tris uint16 :offset-assert 40)
(num-dverts uint16 :offset-assert 42)
(dp-ref uint32 :offset-assert 44)
(dp-qwc uint32 :offset-assert 48)
(generic-ref uint32 :offset-assert 52) ;; the data we want
(generic-count uint32 :offset-assert 56)
(debug-lines (array vector-array) :offset-assert 60)
)
:method-count-assert 18
:size-assert #x40
:flag-assert #x1200000040
)
```
Extract the data from the file
```cpp
u16 generic_qwc = read_plain_data_field<u32>(ref, "generic-count", dts);
if (generic_qwc) {
generic_data.resize(16 * generic_qwc);
auto generic_data_ref = deref_label(get_field_ref(ref, "generic-ref", dts));
memcpy_plain_data((u8*)generic_data.data(), generic_data_ref, generic_qwc * 16);
```
The data made sense. There were some cases where the `interp-table` and the `normal-table` appeared to be on top of each other, but this only occured when `num-ips` was 0 and `interp-table-size` was 0 too.
Treating `normal-table-offset` as a byte-offset from the start of the header seemed to work.
One fear I had is that the mesh for generic/non-generic is somehow different, so I sanity checked that `num-ips` + `num-bps` from the generic header matched the total unique vertex count from the TIE unpacker.
## Finding the Envmap shader
We need an additional shader for the environment map draw. Fortunately, this seems the same as Jak 2 - there's a field in `prototype-bucket-tie` for it. I turned on that code for Jak 1, and it found envmap shaders on reasonable things.
## Finding the tint color
This is a little trickier. In jak 2, the tint color was specified per bucket, but in Jak 1, it's specified per fragment. Luckily my data format for ETIE can support this, but `extract_tie.cpp` needs some refactoring to actually generate it.
## Normal Bugs
Some very small number of fragments appear totally wrong.
The most likely explanation is the normals are in the wrong order. The normals seems like valid normals, but looking at the mesh, they are clearly wrong.
I made the assumption that the order of normals would match the order of points, but could this be wrong?
There's this table called `index_table`:
```
0 2 32 33
1 3 16 17
34 35 4 5
6 8 36 37
7 9 18 20
38 39 19 21
10 11 40 41
22 23 12 13
42 43 24 25
14 15 44 45
26 27 28 29
46 47 30 31
```
but going through extremely carefully showed nothing interesting... It all matches up perfectly
Next step: maybe the normal matrix is wrong... let's try to actually find the mystery scaling factor:
```
vmulx.xyz vf16, vf10, vf14
```
the `vf14.x` here.
```
lui t6, 16256
mtc1 f1, t6 ;; 1.0
qmfc2.i s1, vf10
mtc1 f12, s1
dsra32 s2, s1, 0
mtc1 f13, s2
pextuw s2, r0, s2
mtc1 f14, s2
mula.s f12, f12
madda.s f13, f13
madd.s f15, f14, f14
rsqrt.s f15, f1, f15
mfc1 s1, f15
qmtc2.i vf14, s1
vmulx.xyz vf16, vf10, vf14
```
+513
View File
@@ -0,0 +1,513 @@
```
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; .function setup-blerc-chains-for-one-fragment
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(setup-blerc-chains-for-one-fragment
a0: num-targets
a1: blend-shape-coeffs (s16 array)
a2: dma-mem-ptr
a3: blend-data
t0: blend-ctrl
t1: geo-data)
B0:
L9:
daddiu sp, sp, -128
sd ra, 0(sp)
sq s0, 16(sp)
sq s1, 32(sp)
sq s2, 48(sp)
sq s3, 64(sp)
sq s4, 80(sp)
sq s5, 96(sp)
sq gp, 112(sp)
lb v1, 0(t0) ;; v1 = blend-vtx-count
addiu t2, r0, 0 ;; t2 = 0
por t7, r0, r0 ;; t7 = 0
lw t3, *blerc-globals*(s7) ;; t3 = *blerc-globals*
lw t3, 4(t3) ;; t3 = blec-globals.next
or t4, v1, r0 ;; t4 = blend-vtx-count.
;; link
B1:
L10:
lui t7, 4096
sll r0, r0, 0
daddiu t7, t7, 1
sll r0, r0, 0
beq t3, r0, L11
sq t7, 0(a2)
B2:
sw a2, 12(t3)
sll r0, r0, 0
B3:
L11:
or t3, a2, r0
daddu t6, t4, t4
daddu t5, v1, v1
daddu t6, t6, t4
daddu t7, t5, v1
daddu t5, t6, t6
daddu t6, t7, t7
daddu t7, t5, t5
daddiu t6, t6, 15
daddiu t5, t5, 15
andi t6, t6, 65520
dsrl t5, t5, 4
daddu t8, t2, t2
daddiu t7, t7, 15
daddu t9, t8, t2
dsrl t8, t7, 4
daddu ra, t9, t9
addiu t9, r0, 0
daddu t7, ra, ra
daddiu s3, a2, 32
daddu s2, ra, a3
daddu ra, t7, t1
lui t7, 12288
daddiu gp, a0, -1
daddu t7, t7, t5
or s5, a1, r0
sq t7, 0(s3)
daddiu s4, t0, 2
sw s2, 4(s3)
daddu s2, s2, t6
daddiu s3, s3, 16
sll r0, r0, 0
B4:
L12:
lb s1, 0(s4)
daddiu s4, s4, 1
lh s0, 0(s5)
daddiu s5, s5, 2
beq s1, r0, L13
sq t7, 0(s3)
B5:
sw s2, 4(s3)
daddu s2, s2, t6
beq s0, r0, L13
sw s0, 12(s3)
B6:
daddiu s3, s3, 16
daddiu t9, t9, 1
B7:
L13:
bne gp, r0, L12
daddiu gp, gp, -1
B8:
sq t7, 0(s3)
por t6, r0, r0
sw ra, 4(s3)
lui t6, 28672
sb t8, 0(s3)
sll r0, r0, 0
sq t6, 16(s3)
daddiu t6, s3, 32
sw t9, 20(a2)
sll r0, r0, 0
sw t4, 16(a2)
sll r0, r0, 0
sw ra, 24(a2)
sll r0, r0, 0
sw t8, 28(a2)
sll r0, r0, 0
bne t4, v1, L14
daddiu t5, t5, 1
B9:
daddiu t7, t9, 3
multu3 t5, t5, t7
daddiu t5, t5, -457
sll r0, r0, 0
blez t5, L14
sll r0, r0, 0
B10:
beq r0, r0, L10
addiu t4, r0, 24
B11:
L14:
or a2, t6, r0
daddu t2, t2, t4
beq t2, v1, L15
daddu t5, t2, t4
B12:
dsubu t5, t5, v1
sll r0, r0, 0
blez t5, L10
sll r0, r0, 0
B13:
beq r0, r0, L10
dsubu t4, v1, t2
B14:
L15:
lw v1, *blerc-globals*(s7)
sw t3, 4(v1)
or v0, a2, r0
ld ra, 0(sp)
lq gp, 112(sp)
lq s5, 96(sp)
lq s4, 80(sp)
lq s3, 64(sp)
lq s2, 48(sp)
lq s1, 32(sp)
lq s0, 16(sp)
jr ra
daddiu sp, sp, 128
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; .function blerc-execute
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;BAD PROLOGUE
;; Warnings:
;; INFO: Flagged as mips2c by config
;; INFO: Assembly Function
B0:
L37:
daddiu sp, sp, -96
sd ra, 0(sp)
sq s2, 16(sp)
sq s3, 32(sp)
sq s4, 48(sp)
sq s5, 64(sp)
sq gp, 80(sp)
lw v1, *blerc-globals*(s7)
lwu s5, 0(v1)
beq s5, r0, L56
or v1, s7, r0
B1:
addiu v1, r0, 0
addiu gp, r0, 0
addiu v1, r0, 0
lw v1, *gsf-buffer*(s7)
lw t9, flush-cache(s7)
addiu a0, r0, 0
jalr ra, t9
sll v0, ra, 0
addiu v1, r0, 848
lui a0, 28672
daddu a1, v1, a0
lw v1, *blerc-globals*(s7)
daddu v1, r0, v1
sll r0, r0, 0
lui a0, 4096
sll r0, r0, 0
ori a0, a0, 54272
andi a1, a1, 16383
B2:
L38:
lw a2, 0(a0)
sll r0, r0, 0
sll r0, r0, 0
sll r0, r0, 0
andi a2, a2, 256
sll r0, r0, 0
bne a2, r0, L38
lw a2, 0(v1)
B3:
beq a2, r0, L39
sw a1, 128(a0)
B4:
addiu v1, r0, 324
sw a2, 48(a0)
sw r0, 32(a0)
sync.l
sw v1, 0(a0)
sync.l
B5:
L39:
or v1, r0, r0
beq r0, r0, L55
sll r0, r0, 0
B6:
L40:
bne gp, r0, L41
sll r0, r0, 0
B7:
lui v1, 28672
daddu a0, r0, v1
beq r0, r0, L42
sll r0, r0, 0
B8:
L41:
addiu v1, r0, 8192
lui a0, 28672
daddu a0, v1, a0
B9:
L42:
bne gp, r0, L43
sll r0, r0, 0
B10:
addiu v1, r0, 8192
lui a1, 28672
daddu a1, v1, a1
beq r0, r0, L44
sll r0, r0, 0
B11:
L43:
lui v1, 28672
daddu a1, r0, v1
B12:
L44:
daddiu v1, a0, 848
daddu a2, r0, a0
daddiu a3, a1, 848
daddiu a1, v1, 12
sll r0, r0, 0
lui a2, 4096
sll r0, r0, 0
ori a2, a2, 54272
andi a3, a3, 16383
B13:
L45:
lw t0, 0(a2)
sll r0, r0, 0
sll r0, r0, 0
sll r0, r0, 0
andi t0, t0, 256
sll r0, r0, 0
bne t0, r0, L45
lw t0, 0(a1)
B14:
beq t0, r0, L46
sw a3, 128(a2)
B15:
addiu a1, r0, 324
sw t0, 48(a2)
sw r0, 32(a2)
sync.l
sw a1, 0(a2)
sync.l
B16:
L46:
;; blend ctrl
;; blend data
;; coeffs.
;;
or a1, r0, r0
or a2, a0, r0 ;; likely the blerc-block.
lw a3, *gsf-buffer*(s7)
daddiu t2, a2, 880 ;; t2 is whatever's after blerc-block
lb t1, 0(t2) ;; size of whatever's after
sll r0, r0, 0
or t0, a2, r0 ;; t0 is the blerc block
lw a1, 868(a2) ;; a1 = block.header.overlap
daddiu t3, t1, 1 ;; some counts??
or t1, a3, r0 ;; t1 = ee buffer.
sll t8, t3, 4 ;; t8 = some count (stride)
sll t3, a1, 4 ;; t3 = overlap * 16
daddu t9, t3, a3 ;; t9 = &buffer[overlap] (qws)
daddu t3, t2, t8 ;; t3 = &dummy[unk_count + 1] (qws)
beq a1, r0, L49
daddiu ra, t2, 16 ;; ra = after block ptr (+1 qw to skip a header I guess)
;; this block expands "overlap"s to
B17:
lh t5, 12(t3)
daddu t2, t3, t8
beq r0, r0, L48
sll r0, r0, 0
B18:
L47:
lh t5, 12(t2)
daddu t2, t2, t8
sq t6, 0(t1)
daddiu t1, t1, 16
B19:
L48:
pcpyh t5, t5
mfc1 r0, f31
bne t1, t9, L47
pcpyld t6, t5, t5
B20:
dsubu t3, t2, t8
sll r0, r0, 0
B21:
L49:
addiu t1, r0, 255 ;; cs
addiu t2, r0, 8192 ;; cs
lb s5, 0(t3)
daddiu s4, t3, 16
pcpyh t1, t1
mfc1 r0, f31
pcpyld t1, t1, t1
mfc1 r0, f31
pcpyh t2, t2
mfc1 r0, f31
pcpyld t2, t2, t2 ;; t2 = 8192's
mfc1 r0, f31
por t3, t1, r0 ;; t3 = 255's
mfc1 r0, f31
por t4, r0, r0
mfc1 r0, f31
B22:
L50:
ld t6, 0(ra)
daddu s2, ra, t8
daddiu ra, ra, 8
or s3, a3, r0
pextlb t6, r0, t6
mfc1 r0, f31
pmulth t7, t6, t2
ld t5, 0(s2)
daddiu s5, s5, -1
sll r0, r0, 0
beq r0, r0, L52
daddu s2, s2, t8
B23:
L51:
pmaddh t7, t5, t6
ld t5, 0(s2)
daddu s2, s2, t8
daddiu s3, s3, 16
B24:
L52:
lq t6, 0(s3)
pextlb t5, t5, r0
bne s3, t9, L51
psrah t5, t5, 8
B25:
pmfhl.uw t5
mfc1 r0, f31
psraw t7, t7, 13
mfc1 r0, f31
psraw t5, t5, 13
mfc1 r0, f31
pinteh t5, t5, t7
mfc1 r0, f31
pminh t3, t3, t5
mfc1 r0, f31
pmaxh t4, t4, t5
mfc1 r0, f31
pminh t5, t5, t1
mfc1 r0, f31
pmaxh t5, t5, r0
lq t7, 0(s4)
ppacb t5, r0, t5
mfc1 r0, f31
ppach t7, r0, t7
mfc1 r0, f31
pextlh t5, t5, t7
mfc1 r0, f31
sq t5, 0(t0)
daddiu t0, t0, 16
bne s5, r0, L50
daddiu s4, s4, 16
B26:
lw a3, *stats-blerc*(s7)
beq a3, s7, L53
lw a3, *blerc-globals*(s7)
B27:
lw t2, 12(a3)
lw t1, 16(a3)
lw t0, 20(a3)
lw a2, 864(a2)
multu3 a1, a1, a2
daddiu t2, t2, 1
daddu a2, t1, a2
daddu a1, t0, a1
sw t2, 12(a3)
sw a2, 16(a3)
sw a1, 20(a3)
pcpyud a1, t3, r0
pminh t3, t3, a1
dsrl32 a1, t3, 0
pminh t3, t3, a1
dsrl t3, t3, 16
lh a1, 8(a3)
pminh t3, t3, a1
sh t3, 8(a3)
pcpyud a1, t4, r0
pmaxh t4, t4, a1
dsrl32 a1, t4, 0
pmaxh t4, t4, a1
dsrl t4, t4, 16
lh a1, 10(a3)
pmaxh t4, t4, a1
sh t4, 10(a3)
B28:
L53:
or a1, r0, r0
lwu a1, 872(a0)
or a3, a0, r0
lwu a0, 876(a0)
lui a2, 4096
sll r0, r0, 0
ori a2, a2, 53248
andi a3, a3, 16383
B29:
L54:
lw t0, 0(a2)
sll r0, r0, 0
sll r0, r0, 0
sll r0, r0, 0
andi t0, t0, 256
sll r0, r0, 0
bne t0, r0, L54
sll r0, r0, 0
B30:
sw a3, 128(a2)
addiu a3, r0, 256
sw a1, 16(a2)
sll r0, r0, 0
sw a0, 32(a2)
sync.l
sw a3, 0(a2)
sync.l
or a0, r0, r0
addiu a0, r0, 1
dsubu gp, a0, gp
lwu s5, 12(v1)
or v1, s5, r0
B31:
L55:
bne s5, r0, L40
sll r0, r0, 0
B32:
or v1, s7, r0
B33:
L56:
or v0, r0, r0
ld ra, 0(sp)
lq gp, 80(sp)
lq s5, 64(sp)
lq s4, 48(sp)
lq s3, 32(sp)
lq s2, 16(sp)
jr ra
daddiu sp, sp, 96
sll r0, r0, 0
sll r0, r0, 0
sll r0, r0, 0
```
+261
View File
@@ -0,0 +1,261 @@
# Generic
What do we expect to fix by adding generic?
- "warp" effect (distortion effect that samples the framebuffer)
- "death" effect (get transformed vertices in world for spawning death effect particles)
- "ripple query" feature (get transformed vertices in world, kinda like depth)
- "hud drawing" for the orb/metal head gem
# Part 1: redirecting stuff to generic
Right now, we send everything to PC merc all the time, completely ignoring the original decision. I added code to redirect stuff back to generic when any of the above features are used. I noticed that warp stuff often contained two merc "effects" (really just chunks of frags drawn with the same renderer). One has `warp`, and the other has `force-mercneric`. So I redirected both of these to mercneric for now, though it's possible that the `force-mercneric` one can work with PC merc. (doesn't seem like a big deal either way...).
This seemed to work - lots of broken merc stuff is correclty redirected.
I also checked for the same sort of issue that we had with jak 1's "transparent sculptor visor". They have a trick to draw transparent stuff in a texture bucket that normally doesn't have transparency - they set `force-mercneric` and set bit `0b10` in the effect bits, which changes the blending mode for this effect. This same thing is possible in jak 2, and I added support for it as well. I couldn't find any cases where this actually mattered, but it doesn't hurt.
# Part 2: Generic Buffer Setup
Like with many renderers, the bucket setup is only done if the renderer is actually used.
This path is:
- `swap-display` is called in `main.gc`
- this calls `display-frame-finish`
- this calls `generic-vu1-init-buffers`
- this calls a function to initialize each generic bucket. For warp, it does
```
;; s5-0 is the usual gs-zbuf with depth writes enabled
(generic-vu1-init-buf-special (bucket-id gmerc-warp) s5-0)
```
```
(defun generic-vu1-init-buf-special ((arg0 bucket-id) (arg1 gs-zbuf))
"At the beginning of this bucket's chain, insert DMA data for fx-copy-buf and initializing generic."
(let ((s5-0 (-> *display* frames (-> *display* on-screen) bucket-group arg0)))
(when (!= s5-0 (-> s5-0 last))
(let* ((s4-0 (-> *display* frames (-> *display* on-screen) global-buf))
(s3-1 (-> s4-0 base))
)
;; generate DMA data for copying the framebuffer
(fx-copy-buf s4-0)
;; generate DMA data for initializing generic VU1.
(generic-init-buf s4-0 arg1)
(let ((v1-12 (the-as dma-packet (-> s4-0 base))))
(set! (-> v1-12 dma) (new 'static 'dma-tag :id (dma-tag-id next) :addr (-> s5-0 next)))
(set! (-> v1-12 vif0) (new 'static 'vif-tag))
(set! (-> v1-12 vif1) (new 'static 'vif-tag))
(set! (-> s4-0 base) (the-as pointer (&+ v1-12 16)))
)
(set! (-> s5-0 next) (the-as uint s3-1))
)
)
)
0
(none)
)
```
The import part is
```
;; generate DMA data for copying the framebuffer
(fx-copy-buf s4-0)
;; generate DMA data for initializing generic VU1.
(generic-init-buf s4-0 arg1)
```
The `fx-copy-buf` function needs to be decompiled. There's nothing too interesting, other than it copies the screen in chunks, rather than one giant sprite. The offsets and stuff are a bit confusing, but we might be able to guess our way through it.
For now, I'm going to leave out this function, and we'll just remember to do the copy on the C++ at the start of this bucket.
# Part 3: Submitting merc models to generic
In part 1, we set things up so the main `foreground-draw` functions sends models to `foreground-generic-merc`, which isn't decompiled yet. If this works like Jak 1, this doesn't actually do the EE side of generic yet - it just adds them to a list.
The next step was to mips2c/decompile `foreground-generic-merc` and the functions it calls. Unfortunately, the main function can't easily be decompiled. I suspect it's very similar to `draw-bones-generic-merc` where it fills out generic parameters based on settings. The death vertex and fragment adding loop are split into separate functions that we can decompile this time:
```
(defun foreground-generic-merc-death ((arg0 draw-control) (arg1 generic-merc-ctrl))
"Modify a generic-merc-ctrl to set up merc-death effect."
;; possibly disable drawing if requested
(when (and (>= (the-as int (- (-> arg0 death-timer-org) (-> arg0 death-timer)))
(the-as int (-> arg0 death-draw-overlap))
)
(!= (-> arg0 death-draw-overlap) 255)
)
(set! (-> arg1 header display-triangles) (the-as uint 0))
0
)
;; update the vertices that we're querying.
(when (not (paused?))
(let ((v1-6 (+ (-> arg0 death-vertex-skip) (rand-vu-int-count (the-as int (-> arg0 death-vertex-skip))))))
(set! (-> arg1 header death-vertex-skip) v1-6)
(set! (-> arg1 header death-effect) (-> arg0 death-effect))
(set! (-> arg1 header death-start-vertex)
(/ (* v1-6 (- (-> arg0 death-timer-org) (-> arg0 death-timer))) (-> arg0 death-timer-org))
)
)
)
(none)
)
(defun foreground-generic-merc-add-fragments ((arg0 merc-effect) (arg1 pointer) (arg2 mercneric-chain))
"Add fragments from a merc-effect to the generic chain."
(let ((v1-0 (-> arg0 frag-geo))
(a3-0 (the-as structure (-> arg0 frag-ctrl)))
(a0-1 (-> arg0 frag-count))
)
;; loop over fragments, adding matrix refs.
(dotimes (t0-0 (the-as int a0-1))
(let ((t1-2 (+ (* (-> (the-as merc-fragment-control a3-0) mat-xfer-count) 2) 4))
(t2-0 (-> v1-0 header mm-quadword-size))
)
(set! (-> (the-as dma-packet arg1) dma)
(new 'static 'dma-tag :id (dma-tag-id ref) :addr (the-as int v1-0) :qwc t2-0)
)
(set! (-> (the-as dma-packet arg1) vif0) (new 'static 'vif-tag))
(set! (-> (the-as dma-packet arg1) vif1) (new 'static 'vif-tag))
(when (nonzero? t0-0)
(set! (-> (the-as (pointer int32) (-> arg2 next)) 0) (the-as int arg1))
(set! (-> arg2 next) (the-as uint (&+ arg1 12)))
)
(let ((a1-1 (the-as object (&+ arg1 16))))
(dotimes (t3-6 (the-as int (-> (the-as merc-fragment-control a3-0) mat-xfer-count)))
(let ((t5-4
(-> (scratchpad-object foreground-work)
regs
mtxs
(-> (the-as merc-fragment-control a3-0) mat-dest-data t3-6 matrix-number)
)
)
)
(set! (-> (the-as dma-packet a1-1) dma)
(new 'static 'dma-tag :qwc #x7 :id (dma-tag-id ref) :addr (the-as int t5-4))
)
)
(set! (-> (the-as dma-packet a1-1) vif0) (new 'static 'vif-tag))
(set! (-> (the-as dma-packet a1-1) vif1) (new 'static 'vif-tag))
(set! a1-1 (&+ (the-as pointer a1-1) 16))
)
(set! (-> (the-as dma-packet a1-1) dma) (new 'static 'dma-tag :id (dma-tag-id end)))
(set! (-> (the-as dma-packet a1-1) vif0) (new 'static 'vif-tag))
(set! (-> (the-as dma-packet a1-1) vif1) (new 'static 'vif-tag))
(set! arg1 (&+ (the-as pointer a1-1) 16))
)
(set! a3-0 (&+ a3-0 t1-2))
(set! v1-0 (the-as merc-fragment (+ (the-as uint v1-0) (* t2-0 16))))
)
)
)
arg1
)
```
It's overall very similar, but the way of finding the matrices is little different, but it all makes sense. The generic chain will be processed later, and it will dma these matrices to the scratchpad for transforming generic merc data.
# Part 4: Running Generic Merc EE processing
Part 3 just adds fragments to a list. We actually have to process these fragments on the EE before drawing them with VU1. Assuming it's like Jak 1, the EE step transforms vertices to world frame and computes stuff like envmap coordinates. The VU1 part does the perspective tranformation and clipping.
This is run from `generic-merc-execute-all`, called from `drawable.gc`.
```
(defun generic-merc-execute-all ((arg0 dma-buffer))
"Run EE processing on all generic merc chains."
(let ((s4-0 (-> *foreground* foreground-grid))
(gp-0 (-> *display* frames (-> *display* on-screen) global-buf base))
)
(with-profiler 'generic-merc *profile-generic-merc-color*
;; reset profiling
(reset! (-> *perf-stats* data (perf-stat-bucket mercneric)))
(set! (-> (scratchpad-object generic-work) saves to-vu0-waits) (the-as uint 0))
(set! (-> (scratchpad-object generic-work) saves to-spr-waits) (the-as uint 0))
(set! (-> (scratchpad-object generic-work) saves from-spr-waits) (the-as uint 0))
(flush-cache 0)
;; init generic
(generic-initialize-without-sync (-> *math-camera* perspective) *default-lights*)
(generic-merc-init-asm)
(set! (-> (scratchpad-object generic-work) in-buf merc shadow write-limit) (the-as int (&+ (-> arg0 end) -262144)))
;; loop over grid of chains (levels x textures) and do the chain.
(dotimes (s3-1 7)
(dotimes (s2-1 7)
(generic-merc-do-chain (-> s4-0 level-buckets s3-1 data s2-1 mercneric) arg0)
)
)
;; do separate warp chain
(generic-merc-do-chain (-> s4-0 warp-chain) arg0)
;; finish profiling
(read! (-> *perf-stats* data (perf-stat-bucket mercneric)))
(update-wait-stats
(-> *perf-stats* data 38)
(-> (scratchpad-object generic-work) saves to-vu0-waits)
(-> (scratchpad-object generic-work) saves to-spr-waits)
(-> (scratchpad-object generic-work) saves from-spr-waits)
)
)
;; update dma memory usage stats.
(let ((v1-66 *dma-mem-usage*))
(when (nonzero? v1-66)
(set! (-> v1-66 length) (max 90 (-> v1-66 length)))
(set! (-> v1-66 data 89 name) "pris-generic")
(+! (-> v1-66 data 89 count) 1)
(+! (-> v1-66 data 89 used)
(&- (-> *display* frames (-> *display* on-screen) global-buf base) (the-as uint gp-0))
)
(set! (-> v1-66 data 89 total) (-> v1-66 data 89 used))
)
)
)
(none)
)
```
This function doesn't look too bad...
# Part 5: Decompiling `generic-merc` stuff
- `generic-merc-init-asm`
Nothing interesting, dma sync patched out.
- `mercneric-convert`
The worst function. Copied all the vu0 stuff from jak 1.
- `high-speed-reject`
Copied vu0 stuff from jak1
- `generic-translucent`
New for jak 2, easy.
- `generic-merc-query`
New for jak 2, easy. (maybe no mips2c?)
- `generic-merc-death`
New for jak 2, easy. (maybe no mips2c?)
- `generic-merc-execute-asm`
Did the same direct-call patch for the `mercneric-convert` call. Not sure why I did this originally, but it can only help.
- `generic-merc-do-chain`
New for jak 2, easy (patched out dma sync).
# Part 6: Decompiling `generic-effect` stuff
- `generic-work-init`
Not mips2c. Just copies stuff to generic-work.
- `generic-light-proc`
- `generic-warp-source`
- `generic-warp-envmap-dest`
- `generic-warp-dest`
- `generic-prepare-dma-single` (`generic-effect`)
- `generic-prepare-dma-double` (`generic-effect`)
- `generic-envmap-proc`