mirror of
https://github.com/open-goal/jak-project
synced 2026-09-06 19:21:00 -04:00
[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:
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
```
|
||||
@@ -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`
|
||||
|
||||
Reference in New Issue
Block a user