|
| 1 | +/* reflect.c - the sky, the car lights and more detail in the reflections. |
| 2 | + * HD engine only (NFS_ENHANCED): the original engine stays the 2004 picture, so |
| 3 | + * there the hooks do nothing and the two overrides call the game's own |
| 4 | + * functions. Every part is off unless asked for (launcher Settings > Picture, |
| 5 | + * Reflections). The HD engine already draws the reflection targets larger |
| 6 | + * (enhance.c "REFLECTION RESOLUTION"). |
| 7 | + * |
| 8 | + * Follows what Aero_'s "NFS HD Reflections" mod changes in Underground 2 (which |
| 9 | + * values and which calls); the code here is our own. |
| 10 | + * |
| 11 | + * The game draws three reflections into separate render targets: |
| 12 | + * road reflection view 4 320x240 (the wet road) |
| 13 | + * rear-view mirror view 3 256x256 (bumper and hood views) |
| 14 | + * car cube map views 16-21 128 per face (the car's paint) |
| 15 | + * |
| 16 | + * NFSU2_REFLECT_SKY=1: the sky in the road reflection and the mirror. |
| 17 | + * The sky (0x60f890: puts the sky model around the view's camera) is only |
| 18 | + * drawn for the main view and, as its own small sky, in the car cube map; the |
| 19 | + * road reflection and the mirror show black where the sky would be. Here the |
| 20 | + * road reflection (before its scenery, 0x5cab69) and the mirror (0x5cae71) |
| 21 | + * draw it too, the way the main view does (sky, then 0x5cf430 draws the |
| 22 | + * queued models). In 0x60f890 both take the main view's path (0x60f9f6, |
| 23 | + * edx = 1), the sky scaled to fit inside the view's far plane (10 km for the |
| 24 | + * road reflection, 192 m for the mirror). The road reflection draws the |
| 25 | + * scenery mirrored in z (0x5d67b9 negates each model's z axis), so its sky's |
| 26 | + * matrix (the newest one in the per-frame pool at 0x82da5c) is mirrored too. |
| 27 | + * |
| 28 | + * NFSU2_REFLECT_LIGHTS=1: the cars' lights in the road reflection. |
| 29 | + * The game calls 0x6154a0 (the cars' light glows for a reflection, from each |
| 30 | + * car's own light colours) for the road reflection inside the scenery |
| 31 | + * shader's pass (0x5cac17), where it draws nothing, and draws the glows with |
| 32 | + * the generic 0x494270 instead (0x5cacb5, a dimmer shader). Here 0x6154a0 is |
| 33 | + * skipped at the first place and drawn instead of 0x494270 for the road |
| 34 | + * reflection, in the light glows' own pass: the same glows, white and bright. |
| 35 | + * |
| 36 | + * NFSU2_REFLECT_DETAIL=1: more detail in the reflections. |
| 37 | + * The reflections draw the scenery's low-detail models (0x5d6666: flags |
| 38 | + * 0x1800 of the view = reflection, 0x20 = mirror) and the cars in the road |
| 39 | + * reflection at their lowest level of detail (0x63167d, 0x6311fc: the model |
| 40 | + * and wheel LOD); here they get the main view's. The cube map's far plane |
| 41 | + * (0x7fee8c, 64 m) moves to 10 km, so the car's paint reflects the city |
| 42 | + * rather than the street around it. It roughly doubles the draws into the |
| 43 | + * reflections. */ |
| 44 | +#include "cpu.h" |
| 45 | +#include <stdio.h> |
| 46 | +#include <stdlib.h> |
| 47 | +#include <string.h> |
| 48 | +#include <strings.h> |
| 49 | + |
| 50 | +void sub_006154a0_orig(CPU *c); |
| 51 | +void sub_00494270_orig(CPU *c); |
| 52 | + |
| 53 | +#if NFS_ENHANCED |
| 54 | + |
| 55 | +uint32_t call_guest(CPU *c, uint32_t fn, int nargs, const uint32_t *args); /* com.c */ |
| 56 | + |
| 57 | +/* hook sites (tools/gen_v2.sh HOOKS) */ |
| 58 | +#define H_ROAD_SCENERY GA(0x005cab69u) /* push 8: view 4's scenery list */ |
| 59 | +#define H_MIRROR_SCENERY GA(0x005cae71u) /* push 1: view 3's scenery list */ |
| 60 | +#define H_SKY_SCALE GA(0x0060f9f6u) /* cmp edx,1: the sky's scale is in st0 */ |
| 61 | +#define H_SCENERY_LOD GA(0x005d6666u) /* test bh,0x18: ebx = the view's flags */ |
| 62 | +#define H_CAR_LOD GA(0x0063167du) /* push ecx: ecx/edx = model/wheel LOD */ |
| 63 | +#define H_CAR_LOD2 GA(0x006311fcu) /* mov edx,[ebp+0xc]: eax/ecx = model/wheel LOD */ |
| 64 | +#define FN_SKY GA(0x0060f890u) |
| 65 | +#define FN_DRAW_QUEUED GA(0x005cf430u) |
| 66 | +#define CUBE_FAR GA(0x007fee8cu) /* float, 64.0 */ |
| 67 | +#define SKY_POOL GA(0x0082da5cu) /* next free 4x4 in the per-frame matrix pool */ |
| 68 | + |
| 69 | +static int g_init, g_sky, g_lights, g_detail; |
| 70 | +static unsigned long g_n_sky[2], g_n_lod, g_n_car, g_n_lights; |
| 71 | +static int g_lights_here; |
| 72 | + |
| 73 | +static int on(const char *name){ |
| 74 | + const char *e = getenv(name); |
| 75 | + return e && *e && *e != '0'; |
| 76 | +} |
| 77 | + |
| 78 | +static void refl_init(void){ |
| 79 | + if (g_init) return; |
| 80 | + g_init = 1; |
| 81 | + g_sky = on("NFSU2_REFLECT_SKY"); |
| 82 | + g_lights = on("NFSU2_REFLECT_LIGHTS"); |
| 83 | + g_detail = on("NFSU2_REFLECT_DETAIL"); |
| 84 | + if (g_sky || g_lights || g_detail) |
| 85 | + fprintf(stderr, "reflections:%s%s%s\n", |
| 86 | + g_sky ? " sky" : "", g_lights ? " car-lights" : "", g_detail ? " more-detail" : ""); |
| 87 | +} |
| 88 | + |
| 89 | +/* ---- hooks (debug.c guest_hook, before the instruction at ip) ------------- */ |
| 90 | +static int refl_view(uint32_t id){ return id == 3u || id == 4u || (id >= 16u && id <= 21u); } |
| 91 | + |
| 92 | +int refl_hook(CPU *c, uint32_t ip){ |
| 93 | + switch (ip){ |
| 94 | + case H_ROAD_SCENERY: case H_MIRROR_SCENERY: |
| 95 | + refl_init(); |
| 96 | + if (g_sky && c->esi){ |
| 97 | + uint32_t a[1] = { c->esi }, pool = MR32(SKY_POOL); |
| 98 | + call_guest(c, FN_SKY, 1, a); |
| 99 | + /* The road reflection draws the scenery mirrored in z (0x5d67b9 negates |
| 100 | + * each model's z axis); mirror the sky the same way, about the camera: |
| 101 | + * the sky is far enough for the plane not to matter. */ |
| 102 | + if (ip == H_ROAD_SCENERY && MR32(SKY_POOL) == pool + 0x40u) |
| 103 | + MRF32(pool + 0x28u) = -MRF32(pool + 0x28u); |
| 104 | + call_guest(c, FN_DRAW_QUEUED, 0, a); |
| 105 | + g_n_sky[ip == H_MIRROR_SCENERY]++; |
| 106 | + } |
| 107 | + return 1; |
| 108 | + case H_SKY_SCALE: |
| 109 | + /* The road reflection takes the main views' path (edx 1-3 draw the sky). |
| 110 | + * The main views' sky (scale 1) fits inside their 10 km far plane; the |
| 111 | + * mirror's far plane is 192 m, so its sky is scaled to fit inside its own. */ |
| 112 | + if (g_sky && (c->edx == 3u || c->edx == 4u)){ |
| 113 | + uint32_t v = MR32(c->ebp + 8u), cam = v ? MR32(v + 0x40u) : 0u; |
| 114 | + float far = cam ? MRF32(cam + 0xc0u) : 0.f, k = far / 10400.f; |
| 115 | + if (k > 0.f) ST(0) = (double)(k < 1.f ? k : 1.f); |
| 116 | + c->edx = 1u; |
| 117 | + } |
| 118 | + return 1; |
| 119 | + case H_SCENERY_LOD: |
| 120 | + refl_init(); |
| 121 | + if (g_detail && (c->ebx & 0x1820u)){ |
| 122 | + static int far_set; |
| 123 | + if (!far_set){ far_set = 1; MRF32(CUBE_FAR) = 10000.f; } |
| 124 | + c->ebx &= ~0x1820u; g_n_lod++; |
| 125 | + } |
| 126 | + return 1; |
| 127 | + case H_CAR_LOD: case H_CAR_LOD2: |
| 128 | + if (g_detail){ |
| 129 | + uint32_t v = MR32(c->ebp + 8u); |
| 130 | + if (v && refl_view(MR32(v + 4u))){ |
| 131 | + if (ip == H_CAR_LOD){ c->ecx = 0; c->edx = 0; } |
| 132 | + else { c->eax = 0; c->ecx = 0; } |
| 133 | + g_n_car++; |
| 134 | + } |
| 135 | + } |
| 136 | + return 1; |
| 137 | + } |
| 138 | + return 0; |
| 139 | +} |
| 140 | + |
| 141 | +/* ---- car lights in the road reflection (tools/gen_v2.sh OVR) -------------- */ |
| 142 | +void sub_006154a0(CPU *c){ /* cdecl (eView *view) */ |
| 143 | + refl_init(); |
| 144 | + if (g_lights && !g_lights_here){ POP32(c->eip); return; } /* drawn below instead */ |
| 145 | + sub_006154a0_orig(c); |
| 146 | +} |
| 147 | + |
| 148 | +void sub_00494270(CPU *c){ /* cdecl (eView *view, int, int pass) */ |
| 149 | + refl_init(); |
| 150 | + uint32_t v = MR32(c->esp + 4u); |
| 151 | + if (g_lights && v && MR32(v + 4u) == 4u && MR32(c->esp + 12u) == 1u){ |
| 152 | + g_lights_here = 1; |
| 153 | + PUSH32(v); PUSH32(MR32(c->esp + 4u)); |
| 154 | + sub_006154a0_orig(c); |
| 155 | + c->esp += 4u; |
| 156 | + g_lights_here = 0; |
| 157 | + g_n_lights++; |
| 158 | + POP32(c->eip); |
| 159 | + return; |
| 160 | + } |
| 161 | + sub_00494270_orig(c); |
| 162 | +} |
| 163 | + |
| 164 | +void refl_report(void){ |
| 165 | + if (!g_init || !(g_sky || g_lights || g_detail)) return; |
| 166 | + fprintf(stderr, " reflections : sky road %lu / mirror %lu, scenery LOD raised %lu, car LOD raised %lu, car lights drawn %lu\n", |
| 167 | + g_n_sky[0], g_n_sky[1], g_n_lod, g_n_car, g_n_lights); |
| 168 | +} |
| 169 | + |
| 170 | +#else /* original engine: the game's own reflections */ |
| 171 | +int refl_hook(CPU *c, uint32_t ip){ (void)c; (void)ip; return 0; } |
| 172 | +void sub_006154a0(CPU *c){ sub_006154a0_orig(c); } |
| 173 | +void sub_00494270(CPU *c){ sub_00494270_orig(c); } |
| 174 | +void refl_report(void){} |
| 175 | +#endif |
0 commit comments