Subversion Repositories Games.Carmageddon

Rev

Rev 18 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
#include "spark.h"
20 pmbaty 2
#include "brender/brender.h"
1 pmbaty 3
#include "car.h"
4
#include "depth.h"
5
#include "errors.h"
6
#include "globvars.h"
7
#include "globvrkm.h"
8
#include "graphics.h"
9
#include "harness/hooks.h"
10
#include "harness/trace.h"
11
#include "loading.h"
12
#include "opponent.h"
13
#include "piping.h"
14
#include "replay.h"
15
#include "trig.h"
16
#include "utility.h"
17
#include "world.h"
20 pmbaty 18
 
1 pmbaty 19
#include <stdlib.h>
20
 
21
int gNext_spark;
22
int gSpark_flags;
23
int gNext_shrapnel;
24
int gShrapnel_flags;
25
br_model* gShrapnel_model[2];
26
int gSmoke_flags;
27
int gSmoke_num;
28
int gOffset = 0;
29
int gColumn_flags;
30
int gNext_column;
31
br_pixelmap* gBlack_smoke_shade_table;
32
br_pixelmap* gDark_smoke_shade_table;
33
br_pixelmap* gGrey_smoke_shade_table;
34
int gSmoke_on = 1;
35
int gNum_splash_types;
36
int gIt_type;
37
br_pixelmap* gIt_shade_table;
38
br_pixelmap** gDust_table = &gShade_list[8];
39
br_pixelmap* gFlame_map[20];
40
tBRender_smoke* gBR_smoke_pointers[30];
41
tSplash gSplash[32];
42
br_material* gSplash_material[20];
43
tBRender_smoke gBR_smoke_structs[30];
44
tSmoke_column gSmoke_column[25];
45
br_matrix4 gCameraToScreen;
46
tSpark gSparks[32];
47
br_pixelmap* gShade_list[16];
48
int gN_BR_smoke_structs;
49
tSmoke gSmoke[25];
50
tU32 gSplash_flags;
51
tU32 gNext_splash;
52
br_model* gLollipop_model;
53
int gNum_dust_tables;
54
br_model* gSplash_model;
55
int gDust_rotate;
56
br_camera* gSpark_cam;
57
br_material* gBlack_material;
58
tShrapnel gShrapnel[15];
59
 
60
// gSmoke_column has 25 elements but all the code just checks the first 5 elements
61
#define MAX_SMOKE_COLUMNS 5
62
 
63
// Bugfix: At higher FPS, flame animation runs too quickly, so introduce a new frame timer
64
#define FLAME_ANIMATION_FRAME_INTERVAL 40
65
 
66
// Bugfix: At higher FPS, `CreatePuffOfSmoke` is called too often and causes smoke cirlces to be recycled too quickly so assume around 25fps
67
#define SMOKE_COLUMN_NEW_PUFF_INTERVAL 30
68
 
69
// IDA: void __cdecl DrawDot(br_scalar z, tU8 *scr_ptr, tU16 *depth_ptr, tU8 *shade_ptr)
70
void DrawDot(br_scalar z, tU8* scr_ptr, tU16* depth_ptr, tU8* shade_ptr) {
71
    LOG_TRACE("(%f, %p, %p, %p)", z, scr_ptr, depth_ptr, shade_ptr);
72
 
73
    if (*depth_ptr > (1.0 - z) * 32768.0f) {
74
        *depth_ptr = (1.0 - z) * 32768.0f;
75
        *scr_ptr = shade_ptr[*scr_ptr];
76
    }
77
}
78
 
79
// IDA: void __usercall SetWorldToScreen(br_pixelmap *pScreen@<EAX>)
80
void SetWorldToScreen(br_pixelmap* pScreen) {
81
    br_matrix4 mat;
82
    br_matrix4 mat2;
83
    LOG_TRACE("(%p)", pScreen);
84
 
85
    BrMatrix4Perspective(&mat, gSpark_cam->field_of_view, gSpark_cam->aspect, -gSpark_cam->hither_z, -gSpark_cam->yon_z);
86
    BrMatrix4Scale(&mat2, pScreen->width / 2, pScreen->height / 2, 1.0f);
87
    BrMatrix4Mul(&gCameraToScreen, &mat, &mat2);
88
}
89
 
90
// IDA: void __usercall DrawLine3DThroughBRender(br_vector3 *pStart@<EAX>, br_vector3 *pEnd@<EDX>)
91
void DrawLine3DThroughBRender(br_vector3* pStart, br_vector3* pEnd) {
92
    LOG_TRACE("(%p, %p)", pStart, pEnd);
93
    NOT_IMPLEMENTED();
94
}
95
 
96
// IDA: int __usercall DrawLine3D@<EAX>(br_vector3 *start@<EAX>, br_vector3 *end@<EDX>, br_pixelmap *pScreen@<EBX>, br_pixelmap *pDepth_buffer@<ECX>, br_pixelmap *shade_table)
97
int DrawLine3D(br_vector3* start, br_vector3* end, br_pixelmap* pScreen, br_pixelmap* pDepth_buffer, br_pixelmap* shade_table) {
98
    br_vector3 o;
99
    br_vector3 p;
100
    //br_vector3 tv; // Pierre-Marie Baty -- unused variable
101
    br_vector4 o2;
102
    br_vector4 p2;
103
    br_scalar ts;
104
    LOG_TRACE("(%p, %p, %p, %p, %p)", start, end, pScreen, pDepth_buffer, shade_table);
105
 
106
    o = *start;
107
    p = *end;
108
    if (-gSpark_cam->hither_z < o.v[2] || -gSpark_cam->hither_z < p.v[2]) {
109
        if (-gSpark_cam->hither_z < o.v[2] && -gSpark_cam->hither_z < p.v[2]) {
110
            return 0;
111
        }
112
        ts = (p.v[2] + gSpark_cam->hither_z) / (p.v[2] - o.v[2]);
113
        if (-gSpark_cam->hither_z < o.v[2]) {
114
            o.v[0] = p.v[0] - (p.v[0] - o.v[0]) * ts;
115
            o.v[1] = p.v[1] - (p.v[1] - o.v[1]) * ts;
116
            o.v[2] = -gSpark_cam->hither_z;
117
        }
118
        if (-gSpark_cam->hither_z < p.v[2]) {
119
            p.v[0] = p.v[0] - (p.v[0] - o.v[0]) * ts;
120
            p.v[1] = p.v[1] - (p.v[1] - o.v[1]) * ts;
121
            p.v[2] = -gSpark_cam->hither_z;
122
        }
123
    }
124
    BrMatrix4ApplyP(&o2, &o, &gCameraToScreen);
125
    BrMatrix4ApplyP(&p2, &p, &gCameraToScreen);
126
    o.v[0] = o2.v[0] / o2.v[3];
127
    o.v[1] = o2.v[1] / o2.v[3];
128
    o.v[2] = o2.v[2] / o2.v[3];
129
    p.v[0] = p2.v[0] / p2.v[3];
130
    p.v[1] = p2.v[1] / p2.v[3];
131
    p.v[2] = p2.v[2] / p2.v[3];
132
    return DrawLine2D(&o, &p, pScreen, pDepth_buffer, 1.0, shade_table);
133
}
134
 
135
// IDA: int __usercall DrawLine2D@<EAX>(br_vector3 *o@<EAX>, br_vector3 *p@<EDX>, br_pixelmap *pScreen@<EBX>, br_pixelmap *pDepth_buffer@<ECX>, br_scalar brightness, br_pixelmap *shade_table)
136
int DrawLine2D(br_vector3* o, br_vector3* p, br_pixelmap* pScreen, br_pixelmap* pDepth_buffer, br_scalar brightness, br_pixelmap* shade_table) {
137
    tU8* scr_ptr;
138
    tU16* depth_ptr;
139
    tU8* shade_ptr;
140
    int x1;
141
    int x2;
142
    int y1;
143
    int y2;
144
    int d;
145
    int dx;
146
    int dy;
147
    int ax;
148
    int sx;
149
    int ay;
150
    int sy;
151
    int x;
152
    int y;
153
    br_scalar zbuff;
154
    br_scalar zbuff_inc;
155
    int darken_count;
156
    int darken_init;
157
    LOG_TRACE("(%p, %p, %p, %p, %f, %p)", o, p, pScreen, pDepth_buffer, brightness, shade_table);
158
 
159
    scr_ptr = (tU8*)pScreen->pixels + pScreen->base_x + pScreen->base_y * pScreen->row_bytes;
160
    depth_ptr = (tU16*)pDepth_buffer->pixels;
161
    shade_ptr = (tU8*)shade_table->pixels + shade_table->base_y * shade_table->row_bytes;
162
    x1 = pScreen->width / 2 + o->v[0];
163
    x2 = pScreen->width / 2 + p->v[0];
164
    y1 = pScreen->height / 2 - o->v[1];
165
    y2 = pScreen->height / 2 - p->v[1];
166
    if (brightness < 0.001 || brightness > 1.0) {
167
        return 0;
168
    }
169
    if (x1 < 0 || x2 < 0) {
170
        if (x1 < 0 && x2 < 0) {
171
            return 0;
172
        }
173
        if (x1 >= 0) {
174
            y2 = y1 - x1 * (y1 - y2) / (x1 - x2);
175
            p->v[2] = o->v[2] - (o->v[2] - p->v[2]) * (float)x1 / (float)(x1 - x2);
176
            x2 = 0;
177
        } else {
178
            y1 = y2 - x2 * (y2 - y1) / (x2 - x1);
179
            o->v[2] = p->v[2] - (p->v[2] - o->v[2]) * (float)x2 / (float)(x2 - x1);
180
            x1 = 0;
181
        }
182
    }
183
    if (pScreen->width <= x1 || pScreen->width <= x2) {
184
        if (pScreen->width <= x1 && pScreen->width <= x2) {
185
            return 0;
186
        }
187
        if (pScreen->width > x1) {
188
            y2 = y1 - (y1 - y2) * (x1 - (pScreen->width - 1)) / (x1 - x2);
189
            p->v[2] = o->v[2] - (o->v[2] - p->v[2]) * (x1 - (float)(pScreen->width - 1)) / (float)(x1 - x2);
190
            x2 = pScreen->width - 1;
191
        } else {
192
            y1 = y2 - (y2 - y1) * (x2 - (pScreen->width - 1)) / (x2 - x1);
193
            o->v[2] = p->v[2] - (p->v[2] - o->v[2]) * (x2 - (float)(pScreen->width - 1)) / (float)(x2 - x1);
194
            x1 = pScreen->width - 1;
195
        }
196
    }
197
    if (y1 < 0 || y2 < 0) {
198
        if (y1 < 0 && y2 < 0) {
199
            return 0;
200
        }
201
        if (y1 >= 0) {
202
            x2 = x1 - y1 * (x1 - x2) / (y1 - y2);
203
            p->v[2] = o->v[2] - (o->v[2] - p->v[2]) * (float)y1 / (float)(y1 - y2);
204
            y2 = 0;
205
        } else {
206
            x1 = x2 - y2 * (x2 - x1) / (y2 - y1);
207
            o->v[2] = p->v[2] - (p->v[2] - o->v[2]) * (float)y2 / (float)(y2 - y1);
208
            y1 = 0;
209
        }
210
    }
211
    if (pScreen->height <= y1 || pScreen->height <= y2) {
212
        if (pScreen->height <= y1 && pScreen->height <= y2) {
213
            return 0;
214
        }
215
        if (pScreen->height > y1) {
216
            x2 = x1 - (x1 - x2) * (y1 - (pScreen->height - 1)) / (y1 - y2);
217
            p->v[2] = o->v[2] - (o->v[2] - p->v[2]) * (float)(y1 - (pScreen->height - 1)) / (float)(y1 - y2);
218
            y2 = pScreen->height - 1;
219
        } else {
220
            x1 = x2 - (x2 - x1) * (y2 - (pScreen->height - 1)) / (y2 - y1);
221
            o->v[2] = p->v[2] - (p->v[2] - o->v[2]) * (float)(y2 - (pScreen->height - 1)) / (float)(y2 - y1);
222
            y1 = pScreen->height - 1;
223
        }
224
    }
225
    zbuff = o->v[2];
226
    dx = x2 - x1;
227
    dy = y2 - y1;
228
    ax = 2 * abs(dx);
229
    if (x2 - x1 < 0) {
230
        sx = -1;
231
    } else {
232
        sx = 1;
233
    }
234
    ay = 2 * abs(dy);
235
    if (dy < 0) {
236
        sy = -1;
237
    } else {
238
        sy = 1;
239
    }
240
    x = x1;
241
    y = y1;
242
    scr_ptr += x1 + y1 * pScreen->row_bytes;
243
    depth_ptr += x1 + y1 * (pDepth_buffer->row_bytes / 2);
244
    darken_init = (brightness - 0.001) * (float)shade_table->height;
245
    if (ay >= ax) {
246
        d = ax - ay / 2;
247
        darken_init = 500 * ay / darken_init;
248
        darken_count = darken_init;
249
        zbuff_inc = (p->v[2] - o->v[2]) * 2.0 / (float)ay;
250
        while (1) {
251
            DrawDot(zbuff, scr_ptr, depth_ptr, shade_ptr);
252
            if (y == y2) {
253
                break;
254
            }
255
            if (d >= 0) {
256
                scr_ptr += sx;
257
                depth_ptr += sx;
258
                d -= ay;
259
            }
260
            y += sy;
261
            d += ax;
262
            scr_ptr += sy * pScreen->row_bytes;
263
            depth_ptr += sy * (pDepth_buffer->row_bytes / 2);
264
            zbuff = zbuff_inc + zbuff;
265
            darken_count -= 1000;
266
            while (darken_count <= 0) {
267
                darken_count += darken_init;
268
                shade_ptr += shade_table->row_bytes;
269
            }
270
        }
271
    } else {
272
        d = ay - ax / 2;
273
        darken_init = 500 * ax / darken_init;
274
        darken_count = darken_init;
275
        zbuff_inc = (p->v[2] - o->v[2]) * 2.0 / (float)ax;
276
        while (1) {
277
            DrawDot(zbuff, scr_ptr, depth_ptr, shade_ptr);
278
            if (x == x2) {
279
                break;
280
            }
281
            if (d >= 0) {
282
                scr_ptr += sy * pScreen->row_bytes;
283
                depth_ptr += sy * (pDepth_buffer->row_bytes / 2);
284
                d -= ax;
285
            }
286
            x += sx;
287
            scr_ptr += sx;
288
            depth_ptr += sx;
289
            d += ay;
290
            zbuff = zbuff_inc + zbuff;
291
            darken_count -= 1000;
292
            while (darken_count <= 0) {
293
                darken_count += darken_init;
294
                shade_ptr += shade_table->row_bytes;
295
            }
296
        }
297
    }
298
    return 1;
299
}
300
 
301
// IDA: void __usercall SetLineModelCols(tU8 pCol@<EAX>)
302
void SetLineModelCols(tU8 pCol) {
303
    LOG_TRACE("(%d)", pCol);
304
    NOT_IMPLEMENTED();
305
}
306
 
307
// IDA: void __usercall ReplaySparks(br_pixelmap *pRender_screen@<EAX>, br_pixelmap *pDepth_buffer@<EDX>, br_actor *pCamera@<EBX>, tU32 pTime@<ECX>)
308
void ReplaySparks(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_actor* pCamera, tU32 pTime) {
309
    int i;
310
    br_vector3 pos;
311
    br_vector3 o;
312
    br_vector3 p;
313
    br_vector3 tv;
314
    br_vector3 new_pos;
315
    LOG_TRACE("(%p, %p, %p, %d)", pRender_screen, pDepth_buffer, pCamera, pTime);
316
 
317
    for (i = 0; i < COUNT_OF(gSparks); i++) {
318
        if (gSpark_flags & (1 << i)) {
319
            if (gSparks[i].car == NULL) {
320
                BrVector3Copy(&pos, &gSparks[i].pos);
321
            } else {
322
                BrMatrix34ApplyP(&tv, &o, &gSparks[i].car->car_master_actor->t.t.mat);
323
                BrVector3Copy(&o, &tv);
324
                BrMatrix34ApplyP(&pos, &gSparks[i].pos, &gSparks[i].car->car_master_actor->t.t.mat);
325
            }
326
            BrVector3Add(&o, &pos, &gSparks[i].length);
327
            BrVector3Sub(&tv, &pos, (br_vector3*)gCamera_to_world.m[3]);
328
            BrMatrix34TApplyV(&new_pos, &tv, &gCamera_to_world);
329
            BrVector3Sub(&tv, &o, (br_vector3*)gCamera_to_world.m[3]);
330
            BrMatrix34TApplyV(&p, &tv, &gCamera_to_world);
331
            if (gSparks[i].colour) {
332
                DrawLine3D(&p, &new_pos, pRender_screen, pDepth_buffer, gFog_shade_table);
333
            } else {
334
                DrawLine3D(&p, &new_pos, pRender_screen, pDepth_buffer, gAcid_shade_table);
335
            }
336
        }
337
    }
338
}
339
 
340
// IDA: void __usercall RenderSparks(br_pixelmap *pRender_screen@<EAX>, br_pixelmap *pDepth_buffer@<EDX>, br_actor *pCamera@<EBX>, br_matrix34 *pCamera_to_world@<ECX>, tU32 pTime)
341
void RenderSparks(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_actor* pCamera, br_matrix34* pCamera_to_world, tU32 pTime) {
342
    int i;
343
    int time;
344
    br_vector3 tv;
345
    br_vector3 o;
346
    br_vector3 p;
347
    br_vector3 pos;
348
    br_vector3 new_pos;
349
    br_scalar ts;
350
    LOG_TRACE("(%p, %p, %p, %p, %d)", pRender_screen, pDepth_buffer, pCamera, pCamera_to_world, pTime);
351
 
352
    gSpark_cam = pCamera->type_data;
353
    SetWorldToScreen(pRender_screen);
354
 
355
    if (!gSpark_flags) {
356
        return;
357
    }
358
 
359
    if (gAction_replay_mode) {
360
        ReplaySparks(pRender_screen, pDepth_buffer, pCamera, pTime);
361
        return;
362
    }
363
    StartPipingSession(ePipe_chunk_spark);
364
    for (i = 0; i < COUNT_OF(gSparks); i++) {
365
        if (((1u << i) & gSpark_flags) == 0) {
366
            continue;
367
        }
368
        if (gSparks[i].count <= 0) {
369
            gSparks[i].count = 0;
370
            gSpark_flags &= ~(1u << i);
371
        }
372
        ts = BrVector3Dot(&gSparks[i].normal, &gSparks[i].v);
373
        BrVector3Scale(&tv, &gSparks[i].normal, ts);
374
        BrVector3Sub(&gSparks[i].v, &gSparks[i].v, &tv);
375
        if (gSparks[i].time_sync) {
376
            BrVector3Scale(&o, &gSparks[i].v, gSparks[i].time_sync / 1000.0);
377
            gSparks[i].count = gSparks[i].time_sync + gSparks[i].count - pTime;
378
            gSparks[i].time_sync = 0;
379
        } else {
380
            BrVector3Scale(&o, &gSparks[i].v, pTime / 1000.0);
381
            gSparks[i].count -= pTime;
382
        }
383
        BrVector3Accumulate(&gSparks[i].pos, &o);
384
        time = 1000 - gSparks[i].count;
385
        if (time > 150) {
386
            time = 150;
387
        }
388
        ts = -time / 1000.0;
389
        if (gSparks[i].colour) {
390
            ts = ts / 2.0;
391
        }
392
        BrVector3Scale(&gSparks[i].length, &gSparks[i].v, ts);
393
        ts = pTime * 10.0 / 6900.0;
394
        if (gSparks[i].car) {
395
            BrMatrix34ApplyV(&tv, &gSparks[i].length, &gSparks[i].car->car_master_actor->t.t.mat);
396
            BrVector3Copy(&gSparks[i].length, &tv);
397
            BrMatrix34ApplyP(&pos, &gSparks[i].pos, &gSparks[i].car->car_master_actor->t.t.mat);
398
            o = tv;
399
            gSparks[i].v.v[0] = gSparks[i].v.v[0] - gSparks[i].car->car_master_actor->t.t.mat.m[0][1] * ts;
400
            gSparks[i].v.v[1] = gSparks[i].v.v[1] - gSparks[i].car->car_master_actor->t.t.mat.m[1][1] * ts;
401
            gSparks[i].v.v[2] = gSparks[i].v.v[2] - gSparks[i].car->car_master_actor->t.t.mat.m[2][1] * ts;
402
        } else {
403
            BrVector3Copy(&pos, &gSparks[i].pos);
404
            gSparks[i].v.v[1] = gSparks[i].v.v[1] - ts;
405
        }
406
        AddSparkToPipingSession(i + (gSparks[i].colour << 8), &pos, &gSparks[i].length);
407
        BrVector3Add(&o, &gSparks[i].length, &pos);
408
        BrVector3Sub(&tv, &pos, (br_vector3*)gCamera_to_world.m[3]);
409
        BrMatrix34TApplyV(&new_pos, &tv, &gCamera_to_world);
410
        BrVector3Sub(&tv, &o, (br_vector3*)gCamera_to_world.m[3]);
411
        BrMatrix34TApplyV(&p, &tv, &gCamera_to_world);
412
        BrVector3SetFloat(&tv, FRandomBetween(-0.1f, 0.1f), FRandomBetween(-0.1f, 0.1f), FRandomBetween(-0.1f, 0.1f));
413
        BrVector3Accumulate(&gSparks[i].v, &tv);
414
        ts = 1.0f - BrVector3Length(&gSparks[i].v) / 1.4f * pTime / 1000.0f;
415
        if (ts < 0.1f) {
416
            ts = 0.1f;
417
        }
418
        BrVector3Scale(&gSparks[i].v, &gSparks[i].v, ts);
419
        if (gSparks[i].colour) {
420
            DrawLine3D(&p, &new_pos, pRender_screen, pDepth_buffer, gFog_shade_table);
421
        } else {
422
            DrawLine3D(&p, &new_pos, pRender_screen, pDepth_buffer, gAcid_shade_table);
423
        }
424
    }
425
    EndPipingSession();
426
}
427
 
428
// IDA: void __usercall CreateSingleSpark(tCar_spec *pCar@<EAX>, br_vector3 *pPos@<EDX>, br_vector3 *pVel@<EBX>)
429
void CreateSingleSpark(tCar_spec* pCar, br_vector3* pPos, br_vector3* pVel) {
430
    LOG_TRACE("(%p, %p, %p)", pCar, pPos, pVel);
431
 
432
    BrVector3Copy(&gSparks[gNext_spark].pos, pPos);
433
    BrVector3SetFloat(&gSparks[gNext_spark].normal, 0.0f, 0.0f, 0.0f);
434
    BrVector3Copy(&gSparks[gNext_spark].v, pVel);
435
    gSparks[gNext_spark].count = 500;
436
    gSparks[gNext_spark].car = pCar;
437
    gSpark_flags |= 1u << gNext_spark;
438
    gSparks[gNext_spark].time_sync = 1;
439
    gSparks[gNext_spark].colour = 1;
440
    gNext_spark++;
441
    if (gNext_spark >= COUNT_OF(gSparks)) {
442
        gNext_spark = 0;
443
    }
444
}
445
 
446
// IDA: void __usercall CreateSparks(br_vector3 *pos@<EAX>, br_vector3 *v@<EDX>, br_vector3 *pForce@<EBX>, br_scalar sparkiness, tCar_spec *pCar)
447
void CreateSparks(br_vector3* pos, br_vector3* v, br_vector3* pForce, br_scalar sparkiness, tCar_spec* pCar) {
448
    br_vector3 norm;
449
    br_vector3 normal;
450
    br_vector3 tv;
451
    br_vector3 tv2;
452
    br_vector3 pos2;
453
    br_scalar ts;
454
    br_scalar ts2;
455
    int num;
456
    int i;
457
    LOG_TRACE("(%p, %p, %p, %f, %p)", pos, v, pForce, sparkiness, pCar);
458
 
459
    ts = BrVector3Length(pForce);
460
    BrVector3InvScale(&normal, pForce, ts);
461
    ts2 = BrVector3Dot(pForce, v);
462
    if (ts2 >= 0) {
463
        ts2 = 1.f / (10.f * ts);
464
    } else {
465
        ts2 = 1.f / (10.f * ts) - ts2 / (ts * ts);
466
    }
467
 
468
    BrVector3Scale(&norm, pForce, ts2);
469
    BrVector3Accumulate(v, &norm);
470
    num = FRandomBetween(0.f, BrVector3Length(v) / 2.f + 0.7f) * sparkiness;
471
 
472
    if (num > 10) {
473
        num = 10;
474
    }
475
    for (i = 0; i < num; i++) {
476
        BrVector3Copy(&gSparks[gNext_spark].pos, pos);
477
        BrVector3Copy(&gSparks[gNext_spark].normal, &normal);
478
        BrVector3Copy(&gSparks[gNext_spark].v, v);
479
        gSparks[gNext_spark].v.v[0] *= FRandomBetween(.5f, .9f);
480
        gSparks[gNext_spark].v.v[1] *= FRandomBetween(.5f, .9f);
481
        gSparks[gNext_spark].v.v[2] *= FRandomBetween(.5f, .9f);
482
        gSparks[gNext_spark].count = 1000;
483
        gSparks[gNext_spark].car = NULL;
484
        gSpark_flags |= 1u << gNext_spark;
485
        gSparks[gNext_spark].time_sync = gMechanics_time_sync;
486
        gSparks[gNext_spark].colour = 0;
487
        gNext_spark++;
488
        if (gNext_spark >= COUNT_OF(gSparks)) {
489
            gNext_spark = 0;
490
        }
491
    }
492
    if ((ts * sparkiness) >= 10.f) {
493
        tv.v[0] = pos->v[0] - pCar->car_master_actor->t.t.translate.t.v[0] / WORLD_SCALE;
494
        tv.v[1] = pos->v[1] - pCar->car_master_actor->t.t.translate.t.v[1] / WORLD_SCALE;
495
        tv.v[2] = pos->v[2] - pCar->car_master_actor->t.t.translate.t.v[2] / WORLD_SCALE;
496
        BrMatrix34TApplyV(&pos2, &tv, &pCar->car_master_actor->t.t.mat);
497
        BrMatrix34TApplyV(&norm, &normal, &pCar->car_master_actor->t.t.mat);
498
        BrVector3Scale(&tv, &norm, .1f);
499
        BrVector3Accumulate(&pos2, &tv);
500
        num = (ts * sparkiness / 10.f) + 3;
501
        if (num > 10) {
502
            num = 10;
503
        }
504
        for (i = 0; i < num; i++) {
505
            BrVector3Copy(&gSparks[gNext_spark].pos, &pos2);
506
            BrVector3Copy(&gSparks[gNext_spark].normal, &norm);
507
            BrVector3SetFloat(&tv, FRandomBetween(-1.f, 1.f), FRandomBetween(-.2f, 1.f), FRandomBetween(-1.f, 1.f));
508
            ts2 = BrVector3Dot(&norm, &tv);
509
            BrVector3Scale(&tv2, &norm, ts2);
510
            BrVector3Sub(&gSparks[gNext_spark].v, &tv, &tv2);
511
            gSparks[gNext_spark].count = 1000;
512
            gSparks[gNext_spark].car = pCar;
513
            gSpark_flags |= 1u << gNext_spark;
514
            gSparks[gNext_spark].time_sync = gMechanics_time_sync;
515
            gSparks[gNext_spark].colour = 0;
516
            gNext_spark++;
517
            if (gNext_spark >= COUNT_OF(gSparks)) {
518
                gNext_spark = 0;
519
            }
520
        }
521
        CreateShrapnelShower(pos, v, &normal, ts, pCar, pCar);
522
    }
523
}
524
 
525
// IDA: void __usercall CreateSparkShower(br_vector3 *pos@<EAX>, br_vector3 *v@<EDX>, br_vector3 *pForce@<EBX>, tCar_spec *pCar1@<ECX>, tCar_spec *pCar2)
526
void CreateSparkShower(br_vector3* pos, br_vector3* v, br_vector3* pForce, tCar_spec* pCar1, tCar_spec* pCar2) {
527
    br_scalar ts;
528
    br_scalar ts2;
529
    int num;
530
    int i;
531
    tCar_spec* c;
532
    br_vector3 tv;
533
    //br_vector3 tv2; // Pierre-Marie Baty -- unused variable
534
    br_vector3 normal;
535
    LOG_TRACE("(%p, %p, %p, %p, %p)", pos, v, pForce, pCar1, pCar2);
536
 
537
    ts = BrVector3Length(pForce);
538
    if (pCar1->driver == eDriver_local_human) {
539
        c = pCar1;
540
    } else {
541
        c = pCar2;
542
    }
543
    BrVector3InvScale(&tv, pForce, ts);
544
    if (ts < 10.f) {
545
        return;
546
    }
547
    CreateShrapnelShower(pos, v, &tv, ts, pCar1, pCar2);
548
    ts2 = BrVector3Dot(pForce, v) / (ts * ts);
549
    BrVector3Scale(v, pForce, ts2);
550
    normal.v[0] = pos->v[0] - c->car_master_actor->t.t.translate.t.v[0] / WORLD_SCALE;
551
    normal.v[1] = pos->v[1] - c->car_master_actor->t.t.translate.t.v[1] / WORLD_SCALE;
552
    normal.v[2] = pos->v[2] - c->car_master_actor->t.t.translate.t.v[2] / WORLD_SCALE;
553
    BrMatrix34TApplyV(pos, &normal, &c->car_master_actor->t.t.mat);
554
    BrMatrix34TApplyV(&normal, pForce, &c->car_master_actor->t.t.mat);
555
    num = (ts / 10.f) + 3;
556
    for (i = 0; i < num; i++) {
557
        BrVector3Copy(&gSparks[gNext_spark].pos, pos);
558
        BrVector3SetFloat(&gSparks[gNext_spark].normal, 0.f, 0.f, 0.f);
559
        BrVector3SetFloat(&normal, FRandomBetween(-1.f, 1.f), FRandomBetween(-.2f, 1.f), FRandomBetween(-1.f, 1.f));
560
        ts2 = BrVector3LengthSquared(&normal) / (ts * ts);
561
        BrVector3Scale(&tv, &normal, ts2);
562
        BrVector3Sub(&gSparks[gNext_spark].v, &normal, &tv);
563
        BrVector3Accumulate(&gSparks[gNext_spark].v, v);
564
        gSparks[gNext_spark].count = 1000;
565
        gSparks[gNext_spark].car = c;
566
        gSpark_flags |= 1u << gNext_spark;
567
        gSparks[gNext_spark].time_sync = gMechanics_time_sync;
568
        gSparks[gNext_spark].colour = 0;
569
        gNext_spark++;
570
        if (gNext_spark >= COUNT_OF(gSparks)) {
571
            gNext_spark = 0;
572
        }
573
    }
574
}
575
 
576
// IDA: void __usercall AdjustSpark(int pSpark_num@<EAX>, br_vector3 *pos@<EDX>, br_vector3 *length@<EBX>)
577
void AdjustSpark(int pSpark_num, br_vector3* pos, br_vector3* length) {
578
    br_vector3 tv;
579
    br_matrix34* mat;
580
    int i;
581
    LOG_TRACE("(%d, %p, %p)", pSpark_num, pos, length);
582
 
583
    i = pSpark_num & 0xff;
584
    gSpark_flags |= 1u << pSpark_num;
585
    if (gSparks[i].car != NULL) {
586
        mat = &gSparks[i].car->car_master_actor->t.t.mat;
587
        tv.v[0] = pos->v[0] - mat->m[3][0];
588
        tv.v[1] = pos->v[0] - mat->m[3][1];
589
        tv.v[2] = pos->v[0] - mat->m[3][2];
590
        BrMatrix34TApplyV(&gSparks[i].pos, &tv, mat);
591
    } else {
592
        gSparks[i].pos.v[0] = pos->v[0];
593
        gSparks[i].pos.v[1] = pos->v[1];
594
        gSparks[i].pos.v[2] = pos->v[2];
595
    }
596
    gSparks[i].length.v[0] = length->v[0];
597
    gSparks[i].length.v[1] = length->v[1];
598
    gSparks[i].length.v[2] = length->v[2];
599
    gSparks[i].colour = pSpark_num >> 8;
600
}
601
 
602
// IDA: void __usercall AdjustShrapnel(int pShrapnel_num@<EAX>, br_vector3 *pos@<EDX>, tU16 pAge@<EBX>, br_material *pMaterial@<ECX>)
603
void AdjustShrapnel(int pShrapnel_num, br_vector3* pos, tU16 pAge, br_material* pMaterial) {
604
    int i;
605
    LOG_TRACE("(%d, %p, %d, %p)", pShrapnel_num, pos, pAge, pMaterial);
606
 
607
    i = pShrapnel_num & 0x7fff;
608
    if (!(gShrapnel_flags & (1u << i))) {
609
        BrActorAdd(gNon_track_actor, gShrapnel[i].actor);
610
    }
611
    gShrapnel_flags |= 1u << i;
612
    gShrapnel[i].actor->t.t.translate.t.v[0] = pos->v[0];
613
    gShrapnel[i].actor->t.t.translate.t.v[1] = pos->v[1];
614
    gShrapnel[i].actor->t.t.translate.t.v[2] = pos->v[2];
615
    if (pShrapnel_num & 0x8000) {
616
        gShrapnel[i].age = pAge;
617
        gShrapnel[i].actor->material = pMaterial;
618
    }
619
}
620
 
621
// IDA: void __cdecl ResetSparks()
622
void ResetSparks(void) {
623
    LOG_TRACE("()");
624
 
625
    gSpark_flags = 0;
626
}
627
 
628
// IDA: void __cdecl ResetShrapnel()
629
void ResetShrapnel(void) {
630
    int i;
631
    LOG_TRACE("()");
632
 
633
    if (gShrapnel_flags == 0) {
634
        return;
635
    }
636
    for (i = 0; i < COUNT_OF(gShrapnel); i++) {
637
        if (gShrapnel_flags & (1u << i)) {
638
            BrActorRemove(gShrapnel[i].actor);
639
        }
640
    }
641
    gShrapnel_flags = 0;
642
}
643
 
644
// IDA: void __usercall CreateShrapnelShower(br_vector3 *pos@<EAX>, br_vector3 *v@<EDX>, br_vector3 *pNormal@<EBX>, br_scalar pForce, tCar_spec *c1, tCar_spec *c2)
645
void CreateShrapnelShower(br_vector3* pos, br_vector3* v, br_vector3* pNormal, br_scalar pForce, tCar_spec* c1, tCar_spec* c2) {
646
    br_scalar ts;
647
    br_scalar ts2;
648
    br_scalar rnd;
649
    int num;
650
    int i;
651
    tCar_spec* c;
652
    br_vector3 tv;
653
    br_vector3 tv2;
654
    br_vector3 vel;
655
    LOG_TRACE("(%p, %p, %p, %f, %p, %p)", pos, v, pNormal, pForce, c1, c2);
656
 
657
    if (pForce < 10.f) {
658
        return;
659
    }
660
    ts = .3f;
661
    if (v->v[1] < 0.f) {
662
        ts = .3f - v->v[1];
663
    }
664
    ts2 = pNormal->v[1] * ts;
665
 
666
    tv.v[0] = v->v[0] - ts2 * pNormal->v[0];
667
    tv.v[1] = v->v[1] + ts - pNormal->v[1] * ts2;
668
    tv.v[2] = v->v[2] - pNormal->v[2] * ts2;
669
 
670
    num = (pForce / 10.f) * 3;
671
    rnd = ((pForce + 20.f) * 3.f) / 200.f;
672
    for (i = 0; i < num; i++) {
673
        if ((gShrapnel_flags & (1u << gNext_shrapnel)) == 0) {
674
            BrActorAdd(gNon_track_actor, gShrapnel[gNext_shrapnel].actor);
675
        }
676
        gShrapnel_flags |= 1u << gNext_shrapnel;
677
        BrVector3Copy(&gShrapnel[gNext_shrapnel].actor->t.t.translate.t, pos);
678
        BrVector3SetFloat(&vel, FRandomBetween(-rnd, rnd), FRandomBetween(0.3f - tv.v[1], rnd), FRandomBetween(-rnd, rnd));
679
        ts2 = BrVector3Dot(pNormal, &vel);
680
        BrVector3Scale(&tv2, pNormal, ts2);
681
        BrVector3Sub(&gShrapnel[gNext_shrapnel].v, &vel, &tv2);
682
        BrVector3Accumulate(&gShrapnel[gNext_shrapnel].v, &tv);
683
        gShrapnel[gNext_shrapnel].time_sync = gMechanics_time_sync;
684
        gShrapnel[gNext_shrapnel].age = 0;
685
        if (IRandomBetween(0, 2) != 0) {
686
            c = (IRandomBetween(0, 1) != 0) ? c1 : c2;
687
            gShrapnel[gNext_shrapnel].actor->material = c->shrapnel_material[IRandomBetween(0, c->max_shrapnel_material - 1)];
688
        } else {
689
            gShrapnel[gNext_shrapnel].actor->material = gBlack_material;
690
        }
691
        gNext_shrapnel++;
692
        if (gNext_shrapnel >= COUNT_OF(gShrapnel)) {
693
            gNext_shrapnel = 0;
694
        }
695
    }
696
}
697
 
698
// IDA: void __cdecl InitShrapnel()
699
void InitShrapnel(void) {
700
    int i;
701
    //int j; // Pierre-Marie Baty -- unused variable
702
    LOG_TRACE("()");
703
 
704
    for (i = 0; i < COUNT_OF(gShrapnel); i++) {
705
        gShrapnel[i].actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
706
        gShrapnel[i].actor->parent = NULL;
707
        gShrapnel[i].actor->model = gShrapnel_model[1];
708
        gShrapnel[i].actor->render_style = BR_RSTYLE_DEFAULT;
709
        gShrapnel[i].actor->t.type = BR_TRANSFORM_MATRIX34;
710
        gShrapnel[i].actor->material = BrMaterialFind("DEBRIS.MAT");
711
        gShrapnel[i].age = 0;
712
        gShrapnel[i].shear1 = FRandomBetween(-2.f, 2.f);
713
        gShrapnel[i].shear2 = FRandomBetween(-2.f, 2.f);
714
        BrVector3SetFloat(&gShrapnel[i].axis,
715
            FRandomBetween(-1.f, 1.f), FRandomBetween(-1.f, 1.f), FRandomBetween(-1.f, 1.f));
716
        BrVector3Normalise(&gShrapnel[i].axis, &gShrapnel[i].axis);
717
    }
718
}
719
 
720
// IDA: void __cdecl LoadInShrapnel()
721
void LoadInShrapnel(void) {
722
    LOG_TRACE("()");
723
 
724
    gShrapnel_model[0] = LoadModel("FRAG4.DAT");
725
    gShrapnel_model[1] = LoadModel("FRAG5.DAT");
726
    BrModelAdd(gShrapnel_model[0]);
727
    BrModelAdd(gShrapnel_model[1]);
728
    gBlack_material = BrMaterialFind("M14.MAT");
729
}
730
 
731
// IDA: void __usercall KillShrapnel(int i@<EAX>)
732
void KillShrapnel(int i) {
733
    LOG_TRACE("(%d)", i);
734
 
735
    BrActorRemove(gShrapnel[i].actor);
736
    gShrapnel_flags &= ~(1u << i);
737
}
738
 
739
// IDA: void __cdecl DisposeShrapnel()
740
void DisposeShrapnel(void) {
741
    int i;
742
    LOG_TRACE("()");
743
 
744
    for (i = 0; i < COUNT_OF(gShrapnel); i++) {
745
        if (gShrapnel_flags & (1u << i)) {
746
            BrActorRemove(gShrapnel[i].actor);
747
        }
748
        BrActorFree(gShrapnel[i].actor);
749
    }
750
    gShrapnel_flags = 0;
751
    for (i = 0; i < COUNT_OF(gShrapnel_model); i++) {
752
        BrModelRemove(gShrapnel_model[i]);
753
        BrModelFree(gShrapnel_model[i]);
754
    }
755
}
756
 
757
// IDA: void __usercall ReplayShrapnel(tU32 pTime@<EAX>)
758
void ReplayShrapnel(tU32 pTime) {
759
    int i;
760
    br_matrix34* mat;
761
    LOG_TRACE("(%d)", pTime);
762
 
763
    for (i = 0; i < COUNT_OF(gShrapnel); i++) {
764
        mat = &gShrapnel[i].actor->t.t.mat;
765
        if (gShrapnel_flags & (1 << i)) {
766
            gShrapnel[i].age += GetReplayRate() * pTime;
767
            DrMatrix34Rotate(mat, gShrapnel[i].age * BrDegreeToAngle(1), &gShrapnel[i].axis);
768
            BrMatrix34PreShearX(mat, gShrapnel[i].shear1, gShrapnel[i].shear2);
769
        }
770
    }
771
}
772
 
773
// IDA: void __usercall MungeShrapnel(tU32 pTime@<EAX>)
774
void MungeShrapnel(tU32 pTime) {
775
    br_vector3 disp;
776
    int i;
777
    br_matrix34* mat;
778
    br_scalar ts;
779
    LOG_TRACE("(%d)", pTime);
780
 
781
    MungeSmokeColumn(pTime);
782
    MungeSplash(pTime);
783
 
784
    if (gAction_replay_mode) {
785
        ReplayShrapnel(pTime);
786
        return;
787
    }
788
 
789
    StartPipingSession(ePipe_chunk_shrapnel);
790
    for (i = 0; i < COUNT_OF(gShrapnel); i++) {
791
        mat = &gShrapnel[i].actor->t.t.mat;
792
        if (((1u << i) & gShrapnel_flags) == 0) {
793
            continue;
794
        }
795
        if (gShrapnel[i].age == -1) {
796
            KillShrapnel(i);
797
        } else {
798
            if (gShrapnel[i].time_sync) {
799
                BrVector3Scale(&disp, &gShrapnel[i].v, gShrapnel[i].time_sync / 1000.0f);
800
                gShrapnel[i].time_sync = 0;
801
            } else {
802
                BrVector3Scale(&disp, &gShrapnel[i].v, pTime / 1000.0f);
803
                gShrapnel[i].age += pTime;
804
            }
805
            mat->m[3][0] = mat->m[3][0] + disp.v[0];
806
            mat->m[3][1] = mat->m[3][1] + disp.v[1];
807
            mat->m[3][2] = mat->m[3][2] + disp.v[2];
808
            gShrapnel[i].v.v[1] -= (10 * pTime) * 0.00014492753f;
809
            DrMatrix34Rotate(mat, 182 * gShrapnel[i].age, &gShrapnel[i].axis);
810
            BrMatrix34PreShearX(mat, gShrapnel[i].shear1, gShrapnel[i].shear2);
811
            // bug: should this be using "&gShrapnel[i].v"??
812
            ts = 1.0 - BrVector3Length(&gSparks[i].v) / 1.4 * pTime / 1000.0;
813
            if (ts < 0.1) {
814
                ts = (br_scalar) 0.1; // Pierre-Marie Baty -- added type cast
815
            }
816
            BrVector3Scale(&gShrapnel[i].v, &gShrapnel[i].v, ts);
817
            AddShrapnelToPipingSession(i + ((gShrapnel[i].age > 1000 || gShrapnel[i].age < pTime) << 15), (br_vector3*)mat->m[3], gShrapnel[i].age - pTime, gShrapnel[i].actor->material);
818
            if (gShrapnel[i].age > 1000) {
819
                gShrapnel[i].age = -1;
820
            }
821
        }
822
    }
823
    EndPipingSession();
824
}
825
 
826
// IDA: void __usercall DrMatrix34Rotate(br_matrix34 *mat@<EAX>, br_angle r@<EDX>, br_vector3 *a@<EBX>)
827
void DrMatrix34Rotate(br_matrix34* mat, br_angle r, br_vector3* a) {
828
    br_scalar t;
829
    br_scalar s;
830
    br_scalar c;
831
    br_scalar txy;
832
    br_scalar txz;
833
    br_scalar tyz;
834
    br_scalar sx;
835
    br_scalar sy;
836
    br_scalar sz;
837
    LOG_TRACE("(%p, %d, %p)", mat, r, a);
838
 
839
    s = FastScalarSinAngle(r);
840
    c = FastScalarCosAngle(r);
841
    t = 1.0f - c;
842
    txy = t * a->v[0] * a->v[1];
843
    txz = t * a->v[0] * a->v[2];
844
    tyz = t * a->v[1] * a->v[2];
845
    sx = a->v[0] * s;
846
    sy = a->v[1] * s;
847
    sz = a->v[2] * s;
848
    mat->m[0][0] = a->v[0] * a->v[0] * t + c;
849
    mat->m[0][1] = sz + txy;
850
    mat->m[0][2] = txz - sy;
851
    mat->m[1][0] = txy - sz;
852
    mat->m[1][1] = a->v[1] * a->v[1] * t + c;
853
    mat->m[1][2] = sx + tyz;
854
    mat->m[2][0] = sy + txz;
855
    mat->m[2][1] = tyz - sx;
856
    mat->m[2][2] = a->v[2] * a->v[2] * t + c;
857
}
858
 
859
// IDA: void __usercall SmokeLine(int l@<EAX>, int x@<EDX>, br_scalar zbuff, int r_squared, tU8 *scr_ptr, tU16 *depth_ptr, tU8 *shade_ptr, br_scalar r_multiplier, br_scalar z_multiplier, br_scalar shade_offset)
860
void SmokeLine(int l, int x, br_scalar zbuff, int r_squared, tU8* scr_ptr, tU16* depth_ptr, tU8* shade_ptr, br_scalar r_multiplier, br_scalar z_multiplier, br_scalar shade_offset) {
861
    int i;
862
    int offset; /* Added by dethrace. */
863
    int r_multiplier_int;
864
    int shade_offset_int;
865
    tU16 z;
866
    LOG_TRACE("(%d, %d, %f, %d, %p, %p, %p, %f, %f, %f)", l, x, zbuff, r_squared, scr_ptr, depth_ptr, shade_ptr, r_multiplier, z_multiplier, shade_offset);
867
 
868
    scr_ptr += gOffset;
869
    if (gProgram_state.cockpit_on) {
870
        depth_ptr += gOffset;
871
    }
872
    z = (1.f - zbuff) * 32768.0f;
873
    r_multiplier_int = r_multiplier * 65536.0f;
874
    shade_offset_int = shade_offset * 65536.0f;
875
 
876
    for (i = 0; i < l; i++) {
877
        if (*depth_ptr > z) {
878
            offset = ((shade_offset_int - r_squared * r_multiplier_int) >> 8) & 0xffffff00;
879
#if defined(DETHRACE_FIX_BUGS)
880
            /* Prevent buffer underflows by capping negative offsets. */
881
            offset = MAX(0, offset);
882
#endif
883
            *scr_ptr = shade_ptr[*scr_ptr + offset];
884
        }
885
        r_multiplier = x + r_squared;
886
        scr_ptr++;
887
        x++;
888
        depth_ptr++;
889
        r_squared = x + r_multiplier;
890
    }
891
}
892
 
893
// IDA: void __usercall SmokeCircle(br_vector3 *o@<EAX>, br_scalar r, br_scalar extra_z, br_scalar strength, br_scalar pAspect, br_pixelmap *pRender_screen, br_pixelmap *pDepth_buffer, br_pixelmap *pShade_table)
894
void SmokeCircle(br_vector3* o, br_scalar r, br_scalar extra_z, br_scalar strength, br_scalar pAspect, br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_pixelmap* pShade_table) {
895
    tU8* scr_ptr;
896
    tU16* depth_ptr;
897
    tU8* shade_ptr;
898
    tU8* osp;
899
    tU16* odp;
900
    int ox;
901
    int oy;
902
    //int i; // Pierre-Marie Baty -- unused variable
903
    int r_squared;
904
    int max_r_squared;
905
    int l;
906
    int l2;
907
    int x;
908
    int x2;
909
    //int sx; // Pierre-Marie Baty -- unused variable
910
    int y;
911
    int inc;
912
    int y_limit;
913
    int max_x;
914
    int min_x;
915
    br_scalar shade_offset;
916
    br_scalar r_multiplier;
917
    br_scalar ry;
918
    br_scalar z_multiplier;
919
    br_scalar zbuff;
920
    br_scalar aspect_squared;
921
    void (*line)(int, int, br_scalar, int, tU8*, tU16*, tU8*, br_scalar, br_scalar, br_scalar);
922
    LOG_TRACE("(%p, %f, %f, %f, %f, %p, %p, %p)", o, r, extra_z, strength, pAspect, pRender_screen, pDepth_buffer, pShade_table);
923
 
924
    line = SmokeLine;
925
    ox = pRender_screen->width / 2 + o->v[0];
926
    oy = pRender_screen->height / 2 + o->v[1];
927
    max_r_squared = r * r;
928
    zbuff = o->v[2];
929
    aspect_squared = pAspect * pAspect;
930
    if (pRender_screen->width / 4 <= r
931
        || pRender_screen->width <= ox - r
932
        || ox + r < 0.0f) {
933
        return;
934
    }
935
    shade_ptr = (tU8*)pShade_table->pixels + pShade_table->row_bytes * (pShade_table->base_y + 1);
936
    shade_offset = strength * 14.99f;
937
    r_multiplier = shade_offset / (double)max_r_squared;
938
    z_multiplier = extra_z / (double)max_r_squared;
939
    max_x = pRender_screen->width - ox - 1;
940
    min_x = -ox;
941
    ry = r / pAspect;
942
    l = pRender_screen->height - oy - 1;
943
    scr_ptr = pRender_screen->pixels;
944
    scr_ptr += pRender_screen->base_x + pRender_screen->base_y * pRender_screen->row_bytes + ox + l * pRender_screen->row_bytes;
945
    depth_ptr = (tU16*)pDepth_buffer->pixels + ox + l * (pDepth_buffer->row_bytes / 2);
946
    osp = scr_ptr;
947
    odp = depth_ptr;
948
    if (pRender_screen->height > oy && oy + ry >= 0.0) {
949
        r_squared = r * r;
950
        inc = -r;
951
        y = 0;
952
        y_limit = ry;
953
        if (oy < 0) {
954
            y = -oy;
955
            r_squared = (y * y) * aspect_squared;
956
            scr_ptr += oy * pRender_screen->row_bytes;
957
            depth_ptr += oy * (pDepth_buffer->row_bytes / 2);
958
            inc = -sqrtf(max_r_squared - r_squared);
959
            r_squared += inc * inc;
960
        }
961
        if (pRender_screen->height < oy + ry) {
962
            y_limit = pRender_screen->height - oy - 1;
963
        }
964
        l = -2 * inc;
965
        scr_ptr += inc;
966
        depth_ptr += inc;
967
        gOffset = 0;
968
        while (1) {
969
 
970
            x = inc + gOffset;
971
            if (min_x <= inc + gOffset && l + x - 1 <= max_x) {
972
                line(l, inc, zbuff, r_squared, scr_ptr, depth_ptr, shade_ptr, r_multiplier, z_multiplier, shade_offset);
973
            } else {
974
                if (max_x < x || l + x < min_x) {
975
                    break;
976
                }
977
                x2 = l;
978
                if (l + x - 1 - max_x > 0) {
979
                    x2 = max_x - (x - 1);
980
                }
981
                if (min_x - x <= 0) {
982
                    line(x2, inc, zbuff, r_squared, scr_ptr, depth_ptr, shade_ptr, r_multiplier, z_multiplier, shade_offset);
983
                } else {
984
                    line(x2 - (min_x - x), min_x - x + inc, zbuff, y * y + (min_x - x + inc) * (min_x - x + inc), &scr_ptr[min_x - x], &depth_ptr[min_x - x], shade_ptr, r_multiplier, z_multiplier, shade_offset);
985
                }
986
            }
987
            if (y_limit <= y) {
988
                break;
989
            }
990
            y++;
991
            scr_ptr -= pRender_screen->row_bytes;
992
            depth_ptr -= pDepth_buffer->row_bytes / 2;
993
            for (r_squared += (2 * y - 1) * aspect_squared; max_r_squared < r_squared && inc < 0; r_squared += 2 * inc - 1) {
994
                inc++;
995
                scr_ptr++;
996
                depth_ptr++;
997
                l -= 2;
998
            }
999
            gOffset += IRandomBetween(-1, 1);
1000
            if (gOffset > r / 5.f) {
1001
                gOffset = r / 5.f;
1002
            }
1003
            if (gOffset < -(r / 5.f)) {
1004
                gOffset = -(r / 5.f);
1005
            }
1006
        }
1007
    }
1008
    if (pAspect < 1.0) {
1009
        aspect_squared = 9.f;
1010
        ry = r / 3.f;
1011
    }
1012
    if (oy > 0 && oy <= pRender_screen->height + ry - 2.f) {
1013
        r_squared = (r * r);
1014
        inc = -r;
1015
        y = 0;
1016
        scr_ptr = osp;
1017
        depth_ptr = odp;
1018
        y_limit = ry;
1019
        if (pRender_screen->height < oy) {
1020
            l2 = oy - pRender_screen->height;
1021
            y = pRender_screen->height - oy;
1022
            r_squared = y * y * aspect_squared;
1023
            scr_ptr = &osp[l2 * pRender_screen->row_bytes];
1024
            depth_ptr = &odp[l2 * (pDepth_buffer->row_bytes / 2)];
1025
            inc = -sqrtf(max_r_squared - r_squared);
1026
            r_squared += inc * inc;
1027
        }
1028
        if (oy - ry < 0.f) {
1029
            y_limit = oy;
1030
        }
1031
        l = -2 * inc;
1032
        scr_ptr += inc;
1033
        depth_ptr += inc;
1034
        gOffset = 0;
1035
        do {
1036
            y--;
1037
            scr_ptr += pRender_screen->row_bytes;
1038
            depth_ptr += pDepth_buffer->row_bytes / 2;
1039
            for (r_squared -= (2 * y - 1) * aspect_squared; max_r_squared < r_squared && inc < 0; r_squared += 2 * inc - 1) {
1040
                inc++;
1041
                scr_ptr++;
1042
                depth_ptr++;
1043
                l -= 2;
1044
            }
1045
            x = inc + gOffset;
1046
            if (min_x <= inc + gOffset && l + x - 1 <= max_x) {
1047
                line(l, inc, zbuff, r_squared, scr_ptr, depth_ptr, shade_ptr, r_multiplier, z_multiplier, shade_offset);
1048
            } else {
1049
                if (max_x < x || l + x < min_x) {
1050
                    return;
1051
                }
1052
                x2 = l;
1053
                if (l + x - 1 - max_x > 0) {
1054
                    x2 = max_x - (x - 1);
1055
                }
1056
                if (min_x - x <= 0) {
1057
                    line(x2, inc, zbuff, r_squared, scr_ptr, depth_ptr, shade_ptr, r_multiplier, z_multiplier, shade_offset);
1058
                } else {
1059
                    line(x2 - (min_x - x), min_x - x + inc, zbuff, y * y + (min_x - x + inc) * (min_x - x + inc), &scr_ptr[min_x - x], &depth_ptr[min_x - x], shade_ptr, r_multiplier, z_multiplier, shade_offset);
1060
                }
1061
            }
1062
            gOffset += IRandomBetween(-1, 1);
1063
            if (gOffset > r / 5.0) {
1064
                gOffset = r / 5.0;
1065
            }
1066
            if (gOffset < -(r / 5.0)) {
1067
                gOffset = -r / 5.0;
1068
            }
1069
        } while (-y < y_limit);
1070
    }
1071
}
1072
 
1073
// IDA: int __cdecl CmpSmokeZ(void *p1, void *p2)
1074
int CmpSmokeZ(void* p1, void* p2) {
1075
    //tBRender_smoke** a; // Pierre-Marie Baty -- unused variable
1076
    //tBRender_smoke** b; // Pierre-Marie Baty -- unused variable
1077
    LOG_TRACE("(%p, %p)", p1, p2);
1078
    NOT_IMPLEMENTED();
1079
}
1080
 
1081
// IDA: void __cdecl RenderRecordedSmokeCircles()
1082
void RenderRecordedSmokeCircles(void) {
1083
    //int i; // Pierre-Marie Baty -- unused variable
1084
    //tBRender_smoke* smoke; // Pierre-Marie Baty -- unused variable
1085
    //tU8 red; // Pierre-Marie Baty -- unused variable
1086
    //tU8 grn; // Pierre-Marie Baty -- unused variable
1087
    //tU8 blu; // Pierre-Marie Baty -- unused variable
1088
    LOG_TRACE("()");
1089
    NOT_IMPLEMENTED();
1090
}
1091
 
1092
// IDA: void __usercall RecordSmokeCircle(br_vector3 *pCent@<EAX>, br_scalar pR, br_scalar pStrength, br_pixelmap *pShade, br_scalar pAspect)
1093
void RecordSmokeCircle(br_vector3* pCent, br_scalar pR, br_scalar pStrength, br_pixelmap* pShade, br_scalar pAspect) {
1094
    //tU8 shade_index; // Pierre-Marie Baty -- unused variable
1095
    //br_colour shade_rgb; // Pierre-Marie Baty -- unused variable
1096
    LOG_TRACE("(%p, %f, %f, %p, %f)", pCent, pR, pStrength, pShade, pAspect);
1097
    NOT_IMPLEMENTED();
1098
}
1099
 
1100
// IDA: void __usercall SmokeCircle3D(br_vector3 *o@<EAX>, br_scalar r, br_scalar strength, br_scalar pAspect, br_pixelmap *pRender_screen, br_pixelmap *pDepth_buffer, br_pixelmap *pShade_table, br_actor *pCam)
1101
void SmokeCircle3D(br_vector3* o, br_scalar r, br_scalar strength, br_scalar pAspect, br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_pixelmap* pShade_table, br_actor* pCam) {
1102
    br_vector3 tv;
1103
    br_vector3 p;
1104
    br_vector4 o2;
1105
    br_camera* cam;
1106
    int scaled_r;
1107
    br_scalar extra_z;
1108
    LOG_TRACE("(%p, %f, %f, %f, %p, %p, %p, %p)", o, r, strength, pAspect, pRender_screen, pDepth_buffer, pShade_table, pCam);
1109
 
1110
    cam = pCam->type_data;
1111
    srand(o->v[2] * 16777216.0f + o->v[1] * 65536.0f + o->v[0] * 256.0f + r);
1112
    BrVector3Sub(&tv, o, (br_vector3*)gCamera_to_world.m[3]);
1113
    BrMatrix34TApplyV(&p, &tv, &gCamera_to_world);
1114
 
1115
    if (-p.v[2] >= cam->hither_z && -p.v[2] <= cam->yon_z) {
1116
        scaled_r = gCameraToScreen.m[0][0] * r / -p.v[2];
1117
        extra_z = gCameraToScreen.m[3][2] * r / (p.v[2] * p.v[2]);
1118
        BrMatrix4ApplyP(&o2, &p, &gCameraToScreen);
1119
        BrVector3InvScale(&p, &o2, o2.v[3]);
1120
        SmokeCircle(&p, (br_scalar)scaled_r, extra_z, strength, pAspect, pRender_screen, pDepth_buffer, pShade_table);
1121
    }
1122
}
1123
 
1124
// IDA: void __usercall ReplaySmoke(br_pixelmap *pRender_screen@<EAX>, br_pixelmap *pDepth_buffer@<EDX>, br_actor *pCamera@<EBX>)
1125
void ReplaySmoke(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_actor* pCamera) {
1126
    br_scalar aspect;
1127
    int i;
1128
    LOG_TRACE("(%p, %p, %p)", pRender_screen, pDepth_buffer, pCamera);
1129
 
1130
    for (i = 0; i < COUNT_OF(gSmoke_column); i++) {
1131
        if (gSmoke_flags & (1 << i)) {
1132
            aspect = 1.f + (gSmoke[i].radius - .05f) / .25f * .5f;
1133
            if (gSmoke[i].type & 0x10) {
1134
                SmokeCircle3D(&gSmoke[i].pos, gSmoke[i].radius / aspect, gSmoke[i].strength, 1.f,
1135
                    pRender_screen, pDepth_buffer, gShade_list[gSmoke[i].type & 0xf], pCamera);
1136
            } else {
1137
                SmokeCircle3D(&gSmoke[i].pos, gSmoke[i].radius, gSmoke[i].strength, aspect,
1138
                    pRender_screen, pDepth_buffer, gShade_list[gSmoke[i].type & 0xf], pCamera);
1139
            }
1140
        }
1141
    }
1142
}
1143
 
1144
// IDA: void __usercall GenerateContinuousSmoke(tCar_spec *pCar@<EAX>, int wheel@<EDX>, tU32 pTime@<EBX>)
1145
void GenerateContinuousSmoke(tCar_spec* pCar, int wheel, tU32 pTime) {
1146
    br_vector3 pos;
1147
    br_vector3 v;
1148
    br_vector3 vcs;
1149
    br_vector3 tv;
1150
    br_scalar decay_factor;
1151
    br_scalar ts;
1152
    br_scalar alpha;
1153
    br_scalar beta;
1154
    int colour;
1155
    LOG_TRACE("(%p, %d, %d)", pCar, wheel, pTime);
1156
 
1157
    if (pCar->dust_time[wheel] > (int) pTime) { // Pierre-Marie Baty -- added type cast
1158
        pCar->dust_time[wheel] -= pTime;
1159
        return;
1160
    }
1161
    pCar->dust_time[wheel] += IRandomBetween(200, 400) - pTime;
1162
    if (pCar->dust_time[wheel] < 0) {
1163
        pCar->dust_time[wheel] = 0;
1164
    }
1165
    BrVector3Cross(&tv, &pCar->omega, &pCar->wpos[wheel]);
1166
    BrVector3Scale(&vcs, &pCar->velocity_car_space, WORLD_SCALE * 1000.0f);
1167
    BrVector3Accumulate(&vcs, &tv);
1168
    ts = BrVector3LengthSquared(&vcs);
1169
    if (ts < 25.0f) {
1170
        return;
1171
    }
1172
    decay_factor = sqrtf(ts) / 25.0f;
1173
    if (decay_factor > 1.0f) {
1174
        decay_factor = 1.0f;
1175
    }
1176
    BrVector3InvScale(&tv, &pCar->wpos[wheel], WORLD_SCALE);
1177
    tv.v[1] -= pCar->oldd[wheel] / WORLD_SCALE;
1178
 
1179
    alpha = -1000.0f;
1180
    if (vcs.v[2] > 0.0f) {
1181
        alpha = (pCar->bounds[0].min.v[2] - tv.v[2]) / vcs.v[2];
1182
    } else if (vcs.v[2] < 0.0f) {
1183
        alpha = (pCar->bounds[0].max.v[2] - tv.v[2]) / vcs.v[2];
1184
    }
1185
 
1186
    beta = -1000.0f;
1187
    if (vcs.v[0] > 0.0f) {
1188
        beta = (pCar->bounds[0].min.v[0] - tv.v[0]) / vcs.v[0];
1189
    } else if (vcs.v[0] < 0.0f) {
1190
        beta = (pCar->bounds[0].max.v[0] - tv.v[0]) / vcs.v[0];
1191
    }
1192
 
1193
    ts = MAX(alpha, beta);
1194
    BrVector3Scale(&pos, &vcs, ts);
1195
    BrVector3Accumulate(&tv, &pos);
1196
    BrMatrix34ApplyP(&pos, &tv, &pCar->car_master_actor->t.t.mat);
1197
    BrMatrix34ApplyV(&v, &vcs, &pCar->car_master_actor->t.t.mat);
1198
 
1199
    colour = gDust_rotate + gCurrent_race.material_modifiers[pCar->material_index[wheel]].smoke_type - 2;
1200
    while (colour >= gNum_dust_tables) {
1201
        colour -= gNum_dust_tables;
1202
    }
1203
    CreatePuffOfSmoke(&pos, &v, decay_factor, decay_factor * 2, colour + 8, pCar);
1204
}
1205
 
1206
// IDA: void __cdecl DustRotate()
1207
void DustRotate(void) {
1208
    LOG_TRACE("()");
20 pmbaty 1209
    NOT_IMPLEMENTED();
1 pmbaty 1210
}
1211
 
1212
// IDA: void __usercall RenderSmoke(br_pixelmap *pRender_screen@<EAX>, br_pixelmap *pDepth_buffer@<EDX>, br_actor *pCamera@<EBX>, br_matrix34 *pCamera_to_world@<ECX>, tU32 pTime)
1213
void RenderSmoke(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_actor* pCamera, br_matrix34* pCamera_to_world, tU32 pTime) {
1214
    int i;
1215
    int j;
1216
    br_vector3 tv;
1217
    br_scalar aspect;
1218
    br_scalar ts;
1219
    tU32 seed;
1220
    tU32 not_lonely;
1221
    LOG_TRACE("(%p, %p, %p, %p, %d)", pRender_screen, pDepth_buffer, pCamera, pCamera_to_world, pTime);
1222
 
1223
    not_lonely = 0;
1224
    DrawTheGlow(pRender_screen, pDepth_buffer, pCamera);
1225
 
1226
    if (gSmoke_flags != 0) {
1227
        seed = rand();
1228
        if (gAction_replay_mode) {
1229
            ReplaySmoke(pRender_screen, pDepth_buffer, pCamera);
1230
            srand(seed);
1231
        } else {
1232
            StartPipingSession(ePipe_chunk_smoke);
1233
            for (i = 0; i < COUNT_OF(gSmoke); i++) {
1234
                if ((gSmoke_flags & (1u << i)) != 0) {
1235
                    if (gSmoke[i].strength > 0.0) {
1236
                        if (gSmoke[i].time_sync) {
1237
                            BrVector3Scale(&tv, &gSmoke[i].v, gSmoke[i].time_sync / 1000.0);
1238
                            gSmoke[i].time_sync = 0;
1239
                        } else {
1240
                            BrVector3Scale(&tv, &gSmoke[i].v, pTime / 1000.0);
1241
                        }
1242
                        BrVector3Accumulate(&gSmoke[i].pos, &tv);
1243
                    } else {
1244
                        gSmoke_flags &= ~(1u << i);
1245
                    }
1246
                }
1247
            }
1248
            for (i = 0; i < COUNT_OF(gSmoke); i++) {
1249
                if ((gSmoke_flags & (1u << i)) != 0) {
1250
                    if ((gSmoke[i].type & 0xf) == 7) {
1251
                        not_lonely |= 1u << i;
1252
                    } else if ((not_lonely & (1u << i)) == 0) {
1253
                        for (j = i + 1; j < COUNT_OF(gSmoke); j++) {
1254
                            if ((gSmoke_flags & (1u << j)) != 0) {
1255
                                BrVector3Sub(&tv, &gSmoke[i].pos, &gSmoke[i].pos);
1256
                                ts = BrVector3LengthSquared(&tv);
1257
                                if ((gSmoke[i].radius + gSmoke[j].radius) * (gSmoke[i].radius + gSmoke[j].radius) > ts) {
1258
                                    not_lonely |= (1u << j) | (1u << i);
1259
                                    break;
1260
                                }
1261
                            }
1262
                        }
1263
                    }
1264
                    if (((1u << i) & not_lonely) == 0) {
1265
                        gSmoke[i].strength = gSmoke[i].strength / 2.0;
1266
                    }
1267
                    aspect = (gSmoke[i].radius - 0.05f) / 0.25f * 0.5f + 1.0f;
1268
                    if ((gSmoke[i].type & 0x10) != 0) {
1269
                        SmokeCircle3D(&gSmoke[i].pos, gSmoke[i].radius / aspect, gSmoke[i].strength, 1.0, pRender_screen, pDepth_buffer, gShade_list[gSmoke[i].type & 0xf], pCamera);
1270
                    } else {
1271
                        SmokeCircle3D(&gSmoke[i].pos, gSmoke[i].radius, gSmoke[i].strength, aspect, pRender_screen, pDepth_buffer, gShade_list[gSmoke[i].type & 0xf], pCamera);
1272
                    }
1273
                    if (gSmoke[i].pipe_me) {
1274
                        AddSmokeToPipingSession(i, gSmoke[i].type, &gSmoke[i].pos, gSmoke[i].radius, gSmoke[i].strength);
1275
                    }
1276
                    gSmoke[i].radius = (double)pTime / 1000.0 * gSmoke[i].strength * 0.5 + gSmoke[i].radius;
1277
                    gSmoke[i].strength = gSmoke[i].strength - (double)pTime * gSmoke[i].decay_factor / 1000.0;
1278
                    if (gSmoke[i].radius > 0.3f) {
1279
                        gSmoke[i].radius = 0.3f;
1280
                    }
1281
                    if (gSmoke[i].strength > 0.0) {
1282
                        ts = 1.0f - (double)pTime * 0.002f;
1283
                        if (ts < 0.5f) {
1284
                            ts = 0.5f;
1285
                        }
1286
                        BrVector3Scale(&gSmoke[i].v, &gSmoke[i].v, ts);
1287
                        if (fabs(gSmoke[i].v.v[1]) < 0.43478259f && (gSmoke[i].type & 0xFu) < 7) {
1288
                            if (gSmoke[i].v.v[1] >= 0.0) {
1289
                                gSmoke[i].v.v[1] = 0.43478259f;
1290
                            } else {
1291
                                gSmoke[i].v.v[1] += 0.43478259f;
1292
                            }
1293
                        }
1294
                    } else {
1295
                        gSmoke_flags &= ~(1u << i);
1296
                    }
1297
                }
1298
            }
1299
            EndPipingSession();
1300
            srand(seed);
1301
        }
1302
    }
1303
}
1304
 
1305
// IDA: void __usercall CreatePuffOfSmoke(br_vector3 *pos@<EAX>, br_vector3 *v@<EDX>, br_scalar strength, br_scalar pDecay_factor, int pType, tCar_spec *pC)
1306
void CreatePuffOfSmoke(br_vector3* pos, br_vector3* v, br_scalar strength, br_scalar pDecay_factor, int pType, tCar_spec* pC) {
1307
    br_vector3 tv;
1308
    //int pipe_me; // Pierre-Marie Baty -- unused variable
1309
    LOG_TRACE("(%p, %p, %f, %f, %d, %p)", pos, v, strength, pDecay_factor, pType, pC);
1310
 
1311
    if (!gSmoke_on) {
1312
        return;
1313
    }
1314
    // if we are too far away from the current car...
1315
    BrVector3Sub(&tv, pos, &gProgram_state.current_car.pos);
1316
    if (BrVector3LengthSquared(&tv) > 625.0) {
1317
        // check the distance from the car we are viewing and if it is too far away also, just return
1318
        BrVector3Sub(&tv, pos, &gCar_to_view->pos);
1319
        if (&gProgram_state.current_car != gCar_to_view && BrVector3LengthSquared(&tv) > 625.0) {
1320
            return;
1321
        }
1322
    }
1323
 
1324
    BrVector3InvScale(&gSmoke[gSmoke_num].v, v, WORLD_SCALE);
1325
    gSmoke[gSmoke_num].v.v[1] += (1.0f / WORLD_SCALE);
1326
    BrVector3Copy(&gSmoke[gSmoke_num].pos, pos);
1327
    gSmoke[gSmoke_num].radius = 0.05f;
1328
    if ((pType & 0xF) == 7) {
1329
        gSmoke[gSmoke_num].radius *= 2.0f;
1330
    } else {
1331
        gSmoke[gSmoke_num].pos.v[1] += (br_scalar) 0.04; // Pierre-Marie Baty -- added type cast
1332
    }
1333
    gSmoke[gSmoke_num].pos.v[1] += (br_scalar) 0.04; // Pierre-Marie Baty -- added type cast
1334
    if (strength > 1.0) {
1335
        strength = 1.0;
1336
    }
1337
    gSmoke[gSmoke_num].strength = strength;
1338
    gSmoke_flags |= 1u << gSmoke_num;
1339
    gSmoke[gSmoke_num].time_sync = gMechanics_time_sync;
1340
    gSmoke[gSmoke_num].type = pType;
1341
    gSmoke[gSmoke_num].decay_factor = pDecay_factor;
1342
    gSmoke[gSmoke_num].pipe_me = 1;
1343
    gSmoke_num++;
1344
    if (gSmoke_num >= COUNT_OF(gSmoke)) {
1345
        gSmoke_num = 0;
1346
    }
1347
}
1348
 
1349
// IDA: void __cdecl ResetSmoke()
1350
void ResetSmoke(void) {
1351
    LOG_TRACE("()");
1352
 
1353
    gSmoke_flags = 0;
1354
    ;
1355
}
1356
 
1357
// IDA: void __usercall AdjustSmoke(int pIndex@<EAX>, tU8 pType@<EDX>, br_vector3 *pPos@<EBX>, br_scalar pRadius, br_scalar pStrength)
1358
void AdjustSmoke(int pIndex, tU8 pType, br_vector3* pPos, br_scalar pRadius, br_scalar pStrength) {
1359
    LOG_TRACE("(%d, %d, %p, %f, %f)", pIndex, pType, pPos, pRadius, pStrength);
1360
 
1361
    gSmoke[pIndex].type = pType;
1362
    gSmoke[pIndex].radius = pRadius;
1363
    gSmoke[pIndex].strength = pStrength;
1364
    BrVector3Copy(&gSmoke[pIndex].pos, pPos);
1365
    gSmoke_flags |= 1 << pIndex;
1366
}
1367
 
1368
// IDA: void __cdecl ActorError()
1369
void ActorError(void) {
1370
    LOG_TRACE("()");
1371
}
1372
 
1373
// IDA: void __usercall AdjustSmokeColumn(int pIndex@<EAX>, tCar_spec *pCar@<EDX>, int pVertex@<EBX>, int pColour@<ECX>)
1374
void AdjustSmokeColumn(int pIndex, tCar_spec* pCar, int pVertex, int pColour) {
1375
    int i;
1376
    //br_actor* actor; // Pierre-Marie Baty -- unused variable
1377
    LOG_TRACE("(%d, %p, %d, %d)", pIndex, pCar, pVertex, pColour);
1378
 
1379
    gColumn_flags ^= 1 << pIndex;
1380
    gSmoke_column[pIndex].car = pCar;
1381
    gSmoke_column[pIndex].vertex_index = pVertex;
1382
    gSmoke_column[pIndex].colour = pColour;
1383
    for (i = 0; i < COUNT_OF(gSmoke_column->frame_count); i++) {
1384
        gSmoke_column[pIndex].frame_count[i] = 100;
1385
    }
1386
    if (pColour == 0) {
1387
        if ((gColumn_flags & (1 << pIndex)) != 0) {
1388
            if (gSmoke_column[pIndex].flame_actor->depth != 0) {
1389
                ActorError();
1390
            }
1391
            BrActorAdd(gNon_track_actor, gSmoke_column[pIndex].flame_actor);
1392
        } else {
1393
            if (gSmoke_column[pIndex].flame_actor->depth == 0) {
1394
                ActorError();
1395
            }
1396
            BrActorRemove(gSmoke_column[pIndex].flame_actor);
1397
        }
1398
    }
1399
}
1400
 
1401
// IDA: void __usercall CreateSmokeColumn(tCar_spec *pCar@<EAX>, int pColour@<EDX>, int pVertex_index@<EBX>, tU32 pLifetime@<ECX>)
1402
void CreateSmokeColumn(tCar_spec* pCar, int pColour, int pVertex_index, tU32 pLifetime) {
1403
    int i;
1404
    br_actor* actor;
1405
    tSmoke_column* col;
1406
    LOG_TRACE("(%p, %d, %d, %d)", pCar, pColour, pVertex_index, pLifetime);
1407
 
1408
    col = &gSmoke_column[gNext_column];
1409
    if (pCar->last_special_volume != NULL && pCar->last_special_volume->gravity_multiplier < 1.0f) {
1410
        return;
1411
    }
1412
    SmudgeCar(pCar, pVertex_index);
1413
    if (pCar->knackered) {
1414
        SmudgeCar(pCar, pCar->fire_vertex[IRandomBetween(0, 11)]);
1415
    }
1416
    if (!gSmoke_on) {
1417
        return;
1418
    }
1419
    if (((1u << gNext_column) & gColumn_flags) != 0) {
1420
        if (gSmoke_column[gNext_column].car != NULL) {
1421
            gSmoke_column[gNext_column].car->num_smoke_columns--;
1422
        }
1423
        StartPipingSession(ePipe_chunk_smoke_column);
1424
        AddSmokeColumnToPipingSession(gNext_column, gSmoke_column[gNext_column].car, gSmoke_column[gNext_column].vertex_index, gSmoke_column[gNext_column].colour);
1425
        EndPipingSession();
1426
        if (gSmoke_column[gNext_column].colour == 0) {
1427
            StartPipingSession(ePipe_chunk_flame);
1428
            actor = col->flame_actor->children;
1429
            for (i = 0; i < 3; i++) {
1430
                if (actor->type == BR_ACTOR_MODEL) {
1431
                    AddFlameToPipingSession(i + 16 * gNext_column, col->frame_count[i], col->scale_x[i], col->scale_y[i], col->offset_x[i], col->offset_z[i]);
1432
                }
1433
                actor->type = BR_ACTOR_NONE;
1434
                actor = actor->next;
1435
            }
1436
            EndPipingSession();
1437
        }
1438
    }
1439
    if (pColour == 0 && (((1u << gNext_column) & gColumn_flags) == 0 || gSmoke_column[gNext_column].colour != 0)) {
1440
        BrActorAdd(gNon_track_actor, gSmoke_column[gNext_column].flame_actor);
1441
    }
1442
    if (pColour != 0 && ((1u << gNext_column) & gColumn_flags) != 0 && gSmoke_column[gNext_column].colour == 0) {
1443
        BrActorRemove(gSmoke_column[gNext_column].flame_actor);
1444
    }
1445
    StartPipingSession(ePipe_chunk_smoke_column);
1446
    AddSmokeColumnToPipingSession(gNext_column, pCar, pVertex_index, pColour);
1447
    EndPipingSession();
1448
    gSmoke_column[gNext_column].car = pCar;
1449
    gSmoke_column[gNext_column].colour = pColour;
1450
    gSmoke_column[gNext_column].lifetime = pLifetime;
1451
    gSmoke_column[gNext_column].time = 0;
1452
    gSmoke_column[gNext_column].smudge_timer = 1000;
1453
    gSmoke_column[gNext_column].vertex_index = pVertex_index;
1454
    gSmoke_column[gNext_column].upright = 1;
1455
    gColumn_flags |= 1u << gNext_column;
1456
    pCar->num_smoke_columns++;
1457
    for (i = 0; i < COUNT_OF(gSmoke_column[gNext_column].frame_count); i++) {
1458
        gSmoke_column[gNext_column].frame_count[i] = 100;
1459
    }
1460
    gNext_column++;
1461
    if (gNext_column >= MAX_SMOKE_COLUMNS) {
1462
        gNext_column = 0;
1463
    }
1464
}
1465
 
1466
// IDA: void __cdecl GenerateSmokeShades()
1467
void GenerateSmokeShades(void) {
1468
    static int rb = 0x00;
1469
    static int gb = 0x00;
1470
    static int bb = 0x00;
1471
    static int rd = 0x40;
1472
    static int gd = 0x40;
1473
    static int bd = 0x40;
1474
    static int rg = 0x80;
1475
    static int gg = 0x80;
1476
    static int bg = 0x80;
1477
    LOG_TRACE("()");
1478
 
1479
    gBlack_smoke_shade_table = GenerateShadeTable(16, gRender_palette, rb, gb, bb, .25f, .6f, .9f);
1480
    gDark_smoke_shade_table = GenerateShadeTable(16, gRender_palette, rd, gd, bd, .25f, .6f, .9f);
1481
    gGrey_smoke_shade_table = GenerateShadeTable(16, gRender_palette, rg, gg, bg, .25f, .6f, .9f);
1482
    gIt_shade_table = GenerateDarkenedShadeTable(16, gRender_palette, 0, 255, 254, .25f, .5f, .75f, .6f);
1483
 
1484
    gShade_list[0] = gBlack_smoke_shade_table;
1485
    gShade_list[1] = gDark_smoke_shade_table;
1486
    gShade_list[2] = gGrey_smoke_shade_table;
1487
    gShade_list[3] = gFog_shade_table;
1488
    gShade_list[4] = gFog_shade_table;
1489
    gShade_list[7] = gAcid_shade_table;
1490
}
1491
 
1492
// IDA: void __cdecl GenerateItFoxShadeTable()
1493
void GenerateItFoxShadeTable(void) {
1494
    LOG_TRACE("()");
1495
 
1496
    if (gIt_shade_table == NULL) {
1497
        gIt_shade_table = GenerateDarkenedShadeTable(16, gRender_palette, 0, 255, 254, .25f, .5f, .75f, .6f);
1498
    }
1499
}
1500
 
1501
// IDA: void __usercall AdjustFlame(int pIndex@<EAX>, int pFrame_count@<EDX>, br_scalar pScale_x, br_scalar pScale_y, br_scalar pOffset_x, br_scalar pOffset_z)
1502
void AdjustFlame(int pIndex, int pFrame_count, br_scalar pScale_x, br_scalar pScale_y, br_scalar pOffset_x, br_scalar pOffset_z) {
1503
    int i;
1504
    int j;
1505
    tSmoke_column* col;
1506
    //br_actor* actor; // Pierre-Marie Baty -- unused variable
1507
    LOG_TRACE("(%d, %d, %f, %f, %f, %f)", pIndex, pFrame_count, pScale_x, pScale_y, pOffset_x, pOffset_z);
1508
 
1509
    i = pIndex >> 4;
1510
    j = pIndex & 0xf;
1511
    col = &gSmoke_column[i];
1512
    col->frame_count[j] = pFrame_count;
1513
    col->scale_x[j] = pScale_x;
1514
    col->scale_y[j] = pScale_y;
1515
    col->offset_x[j] = pOffset_x;
1516
    col->offset_z[j] = pOffset_z;
1517
}
1518
 
1519
// IDA: void __usercall ReplayFlame(tSmoke_column *col@<EAX>, br_actor *actor@<EDX>)
1520
void ReplayFlame(tSmoke_column* col, br_actor* actor) {
1521
    int i;
1522
    LOG_TRACE("(%p, %p)", col, actor);
1523
 
1524
    for (i = 0; i < COUNT_OF(col->frame_count); i++, actor = actor->next) {
1525
        col->frame_count[i] += GetReplayRate();
1526
        if (col->frame_count[i] < 0 || col->frame_count[i] >= COUNT_OF(gFlame_map)) {
1527
            actor->type = BR_ACTOR_NONE;
1528
        } else {
1529
            actor->type = BR_ACTOR_MODEL;
1530
            actor->material->colour_map = gFlame_map[col->frame_count[i]];
1531
            BrMaterialUpdate(actor->material, BR_MATU_ALL);
1532
            BrMatrix34Scale(&actor->t.t.mat,
1533
                col->scale_x[i] * gFlame_map[col->frame_count[i]]->width,
1534
                col->scale_y[i] * gFlame_map[col->frame_count[i]]->height,
1535
                1.f);
1536
            actor->t.t.translate.t.v[0] = col->offset_x[i];
1537
            actor->t.t.translate.t.v[2] = col->offset_z[i];
1538
        }
1539
    }
1540
}
1541
 
1542
// IDA: void __usercall FlameAnimate(int c@<EAX>, br_vector3 *pPos@<EDX>, tU32 pTime@<EBX>)
1543
void FlameAnimate(int c, br_vector3* pPos, tU32 pTime) {
1544
    tSmoke_column* col;
1545
    br_actor* actor;
1546
    int i;
1547
    LOG_TRACE("(%d, %p, %d)", c, pPos, pTime);
1548
 
1549
    col = &gSmoke_column[c];
1550
    actor = col->flame_actor;
1551
    DRMatrix34RotateY(&actor->t.t.mat, FastScalarArcTan2Angle(gCamera_to_world.m[2][0], gCamera_to_world.m[2][2]));
1552
    actor->t.t.translate.t = *pPos;
1553
    actor = actor->children;
1554
 
1555
    if (gAction_replay_mode) {
1556
        ReplayFlame(col, actor);
1557
        return;
1558
    }
1559
    for (i = 0; i < COUNT_OF(col->frame_count); i++) {
1560
 
1561
#ifdef DETHRACE_FIX_BUGS
1562
        col->frame_time[i] += pTime;
1563
        if (col->frame_time[i] > FLAME_ANIMATION_FRAME_INTERVAL) {
1564
            col->frame_time[i] = 0;
1565
            col->frame_count[i]++;
1566
        }
1567
#else
1568
        col->frame_count[i]++;
1569
#endif
1570
 
1571
        if (col->frame_count[i] >= COUNT_OF(gFlame_map)) {
1572
            StartPipingSession(ePipe_chunk_flame);
1573
            AddFlameToPipingSession(i + 16 * c, col->frame_count[i] + 1, col->scale_x[i], col->scale_y[i], col->offset_x[i], col->offset_z[i]);
1574
            EndPipingSession();
1575
            col->frame_count[i] = IRandomBetween(-5, -1);
1576
            col->scale_x[i] = (2 * IRandomBetween(0, 1) - 1) * SRandomBetween(1.0f, 1.5f) * 0.003f;
1577
            col->scale_y[i] = SRandomBetween(0.5f, 1.0f) * 0.003f;
1578
            col->offset_x[i] = SRandomPosNeg(0.03f);
1579
            col->offset_z[i] = SRandomBetween(-0.03f, 0.0);
1580
            actor->type = BR_ACTOR_NONE;
1581
        }
1582
        if (col->frame_count[i] == 0) {
1583
            if (BrVector3LengthSquared(&col->car->v) >= 80.0f || col->lifetime <= 30 * pTime) {
1584
                col->frame_count[i] = -5;
1585
            } else {
1586
                actor->type = BR_ACTOR_MODEL;
1587
                StartPipingSession(ePipe_chunk_flame);
1588
                AddFlameToPipingSession(i + 16 * c, col->frame_count[i] - 1, col->scale_x[i], col->scale_y[i], col->offset_x[i], col->offset_z[i]);
1589
                EndPipingSession();
1590
            }
1591
        }
1592
        if (col->frame_count[i] >= 0) {
1593
            actor->material->colour_map = gFlame_map[col->frame_count[i]];
1594
            BrMaterialUpdate(actor->material, BR_MATU_ALL);
1595
            BrMatrix34Scale(&actor->t.t.mat, gFlame_map[col->frame_count[i]]->width * col->scale_x[i], gFlame_map[col->frame_count[i]]->height * col->scale_y[i], 1.0);
1596
            actor->t.t.mat.m[3][0] = col->offset_x[i];
1597
            actor->t.t.mat.m[3][2] = col->offset_z[i];
1598
        }
1599
        actor = actor->next;
1600
    }
1601
}
1602
 
1603
// IDA: void __usercall DoSmokeColumn(int i@<EAX>, tU32 pTime@<EDX>, br_vector3 *pRet_car_pos@<EBX>)
1604
void DoSmokeColumn(int i, tU32 pTime, br_vector3* pRet_car_pos) {
1605
    tCar_spec* c;
1606
    br_actor* actor;
1607
    br_actor* bonny;
1608
    //int group; // Pierre-Marie Baty -- unused variable
1609
    LOG_TRACE("(%d, %d, %p)", i, pTime, pRet_car_pos);
1610
 
1611
    c = gSmoke_column[i].car;
1612
    if (c->car_master_actor->t.t.mat.m[1][1] > 0.1f) {
1613
        gSmoke_column[i].upright = 1;
1614
    }
1615
    if (c->car_master_actor->t.t.mat.m[1][1] < -0.1f) {
1616
        gSmoke_column[i].upright = 0;
1617
    }
1618
    actor = c->car_model_actors[c->principal_car_actor].actor;
1619
    bonny = c->car_model_actors[c->car_actor_count - 1].actor;
1620
 
20 pmbaty 1621
    BrVector3Add(pRet_car_pos, &V11MODEL(actor->model)->groups->vertices[gSmoke_column[i].vertex_index].p, &actor->t.t.translate.t);
1 pmbaty 1622
    if (gProgram_state.cockpit_on && c->driver == eDriver_local_human) {
1623
        if (c->driver_z_offset + 0.2f <= pRet_car_pos->v[2]) {
1624
            pRet_car_pos->v[1] -= -0.07f;
1625
        } else {
20 pmbaty 1626
            BrMatrix34ApplyP(pRet_car_pos, &V11MODEL(actor->model)->groups->vertices[gSmoke_column[i].vertex_index].p, &bonny->t.t.mat);
1 pmbaty 1627
        }
1628
    }
1629
    if (!gSmoke_column[i].upright) {
1630
        pRet_car_pos->v[1] = c->bounds[1].min.v[1] / WORLD_SCALE;
1631
    }
1632
    BrMatrix34ApplyP(&gSmoke_column[i].pos, pRet_car_pos, &c->car_master_actor->t.t.mat);
1633
    gSmoke_column[i].pos.v[1] -= 0.03f;
1634
}
1635
 
1636
// IDA: void __usercall ReplaySmokeColumn(tU32 pTime@<EAX>)
1637
void ReplaySmokeColumn(tU32 pTime) {
1638
    int i;
1639
    br_vector3 dummy;
1640
    LOG_TRACE("(%d)", pTime);
1641
 
1642
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
1643
        if ((gColumn_flags & (1 << i)) != 0) {
1644
            DoSmokeColumn(i, pTime, &dummy);
1645
            if (gSmoke_column[i].colour == 0) {
1646
                FlameAnimate(i, &gSmoke_column[i].pos, pTime);
1647
            }
1648
        }
1649
    }
1650
}
1651
 
1652
// IDA: void __usercall MungeSmokeColumn(tU32 pTime@<EAX>)
1653
void MungeSmokeColumn(tU32 pTime) {
1654
    int i;
1655
    int plane;
1656
    //br_actor* actor; // Pierre-Marie Baty -- unused variable
1657
    //br_actor* bonny; // Pierre-Marie Baty -- unused variable
1658
    br_vector3 car_pos;
1659
    br_vector3 pos;
1660
    br_vector3 v;
1661
    //br_vector3 up; // Pierre-Marie Baty -- unused variable
1662
    //br_vector3 start; // Pierre-Marie Baty -- unused variable
1663
    //br_vector3 end; // Pierre-Marie Baty -- unused variable
1664
    //br_scalar ts; // Pierre-Marie Baty -- unused variable
1665
    br_scalar decay_factor;
1666
    tCar_spec* c;
1667
    LOG_TRACE("(%d)", pTime);
1668
 
1669
    if (gColumn_flags == 0) {
1670
        return;
1671
    }
1672
    if (gAction_replay_mode) {
1673
        ReplaySmokeColumn(pTime);
1674
        return;
1675
    }
1676
 
1677
    gMechanics_time_sync = 1;
1678
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
1679
        if (((1u << i) & gColumn_flags) == 0) {
1680
            continue;
1681
        }
1682
        if (gSmoke_column[i].lifetime >= pTime) {
1683
            gSmoke_column[i].lifetime -= pTime;
1684
            c = gSmoke_column[i].car;
1685
            DoSmokeColumn(i, pTime, &car_pos);
1686
            if (gSmoke_column[i].colour == 0) {
1687
                FlameAnimate(i, &gSmoke_column[i].pos, pTime);
1688
                if (gSmoke_column[i].smudge_timer >= pTime) {
1689
                    gSmoke_column[i].smudge_timer -= pTime;
1690
                } else {
1691
                    gSmoke_column[i].smudge_timer += 2000;
1692
                    SmudgeCar(gSmoke_column[i].car, gSmoke_column[i].vertex_index);
1693
                    if (gSmoke_column[i].car->knackered) {
1694
                        plane = IRandomBetween(0, COUNT_OF(gSmoke_column[i].car->fire_vertex) - 1);
1695
                        SmudgeCar(gSmoke_column[i].car, gSmoke_column[i].car->fire_vertex[plane]);
1696
                    }
1697
                }
1698
            }
1699
            gSmoke_column[i].time += pTime;
1700
            if (gSmoke_column[i].time > 200) {
1701
#ifdef DETHRACE_FIX_BUGS
1702
                gSmoke_column[i].time -= fmaxf(SMOKE_COLUMN_NEW_PUFF_INTERVAL, pTime);
1703
#else
1704
                gSmoke_column[i].time -= pTime;
1705
#endif
1706
                gSmoke_column[i].count++;
1707
                BrVector3Cross(&v, &c->omega, &car_pos);
1708
                BrMatrix34ApplyV(&car_pos, &v, &c->car_master_actor->t.t.mat);
1709
                BrVector3Add(&v, &c->v, &car_pos);
1710
                v.v[1] = v.v[1] + 2.898550724637681f; // 100 / 34.5 ?
1711
                pos.v[0] = SRandomBetween(-0.03f, 0.03f) + gSmoke_column[i].pos.v[0];
1712
                pos.v[1] = (gSmoke_column[i].colour == 0) * 0.05f + gSmoke_column[i].pos.v[1];
1713
                pos.v[2] = SRandomBetween(-0.03f, 0.03f) + gSmoke_column[i].pos.v[2];
1714
                if ((gSmoke_column[i].whiter & 2) == 0 || IRandomBetween(0, 3)) {
1715
                    if (gSmoke_column[i].whiter < 1) {
1716
                        gSmoke_column[i].whiter = -1;
1717
                    } else {
1718
                        gSmoke_column[i].whiter = 2;
1719
                    }
1720
                } else {
1721
                    gSmoke_column[i].whiter &= 1;
1722
                }
1723
                decay_factor = ((gSmoke_column[i].whiter > 0) + 1.0f) / 2.0f;
1724
                if (gSmoke_column[i].lifetime < 4000) {
1725
                    decay_factor = gSmoke_column[i].lifetime * decay_factor / 4000.0f;
1726
                }
1727
                CreatePuffOfSmoke(&pos, &v, decay_factor, decay_factor, gSmoke_column[i].colour + 16, c);
1728
            }
1729
        } else {
1730
            if (gSmoke_column[i].car != NULL) {
1731
                StartPipingSession(ePipe_chunk_smoke_column);
1732
                AddSmokeColumnToPipingSession(i, gSmoke_column[i].car, gSmoke_column[i].vertex_index, gSmoke_column[i].colour);
1733
                EndPipingSession();
1734
            }
1735
            gColumn_flags &= ~(1u << i);
1736
            if (gSmoke_column[i].colour == 0) {
1737
                BrActorRemove(gSmoke_column[i].flame_actor);
1738
            }
1739
            if (gSmoke_column[i].car != NULL) {
1740
                if (gSmoke_column[i].car->num_smoke_columns != 0) {
1741
                    gSmoke_column[i].car->num_smoke_columns--;
1742
                }
1743
            }
1744
        }
1745
    }
1746
}
1747
 
1748
// IDA: void __cdecl DisposeFlame()
1749
void DisposeFlame(void) {
1750
    int i;
1751
    int j;
1752
    br_actor* actor;
1753
    //br_material* material; // Pierre-Marie Baty -- unused variable
1754
    LOG_TRACE("()");
1755
 
1756
    for (i = 0; i < COUNT_OF(gFlame_map); i++) {
1757
        BrMapRemove(gFlame_map[i]);
1758
        BrPixelmapFree(gFlame_map[i]);
1759
    }
1760
 
1761
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
1762
        if ((gColumn_flags & (1u << i)) && (gSmoke_column[i].colour == 0)) {
1763
            BrActorRemove(gSmoke_column[i].flame_actor);
1764
        }
1765
        actor = gSmoke_column[i].flame_actor->children;
1766
        for (j = 0; j < COUNT_OF(gSmoke_column[0].frame_count); j++) {
1767
            BrMaterialRemove(actor->material);
1768
            BrMaterialFree(actor->material);
1769
            actor = actor->next;
1770
        }
1771
        BrActorFree(gSmoke_column[i].flame_actor);
1772
    }
1773
    BrModelRemove(gLollipop_model);
1774
    BrModelFree(gLollipop_model);
1775
}
1776
 
1777
// IDA: void __cdecl InitFlame()
1778
void InitFlame(void) {
1779
    int i;
1780
    int j;
1781
    int num;
1782
    char the_path[256];
1783
    br_actor* actor;
1784
    br_material* material;
1785
    LOG_TRACE("()");
1786
 
1787
    gColumn_flags = 0;
1788
    gLollipop_model = BrModelAllocate("Lollipop", 4, 2);
1789
    PathCat(the_path, gApplication_path, "PIXELMAP");
1790
    PathCat(the_path, the_path, "FLAMES.PIX");
1791
    num = DRPixelmapLoadMany(the_path, gFlame_map, COUNT_OF(gFlame_map));
1792
    if (num != COUNT_OF(gFlame_map)) {
1793
        FatalError(kFatalError_LoadPixelmapFile_S, the_path);
1794
    }
1795
    BrMapAddMany(gFlame_map, COUNT_OF(gFlame_map));
1796
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
1797
        gSmoke_column[i].flame_actor = BrActorAllocate(BR_ACTOR_NONE, NULL);
1798
        for (j = 0; j < COUNT_OF(gSmoke_column[0].frame_count); j++) {
1799
            actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
1800
            material = BrMaterialAllocate(NULL);
1801
            BrActorAdd(gSmoke_column[i].flame_actor, actor);
1802
            actor->model = gLollipop_model;
1803
            actor->material = material;
1804
            material->flags &= ~BR_MATF_LIGHT;
1805
            material->flags |= BR_MATF_ALWAYS_VISIBLE;
1806
            material->colour_map = gFlame_map[0];
1807
            BrMaterialAdd(material);
1808
            gSmoke_column[i].frame_count[j] = 100;
1809
        }
1810
    }
1811
    gLollipop_model->nvertices = 4;
1812
    BrVector3SetFloat(&gLollipop_model->vertices[0].p, -.5f, 0.f, .0f);
1813
    BrVector3SetFloat(&gLollipop_model->vertices[1].p, .5f, 0.f, .0f);
1814
    BrVector3SetFloat(&gLollipop_model->vertices[2].p, .5f, 1.f, .0f);
1815
    BrVector3SetFloat(&gLollipop_model->vertices[3].p, -.5f, 1.f, .0f);
1816
    gLollipop_model->vertices[0].map.v[0] = 0.f;
1817
    gLollipop_model->vertices[0].map.v[1] = 1.f;
1818
    gLollipop_model->vertices[1].map.v[0] = 1.f;
1819
    gLollipop_model->vertices[1].map.v[1] = 1.f;
1820
    gLollipop_model->vertices[2].map.v[0] = 1.f;
1821
    gLollipop_model->vertices[2].map.v[1] = 0.f;
1822
    gLollipop_model->vertices[3].map.v[0] = 0.f;
1823
    gLollipop_model->vertices[3].map.v[1] = 0.f;
1824
 
1825
    gLollipop_model->nfaces = 2;
1826
    gLollipop_model->faces[0].vertices[0] = 0;
1827
    gLollipop_model->faces[0].vertices[1] = 1;
1828
    gLollipop_model->faces[0].vertices[2] = 2;
1829
    gLollipop_model->faces[1].vertices[0] = 0;
1830
    gLollipop_model->faces[1].vertices[1] = 2;
1831
    gLollipop_model->faces[1].vertices[2] = 3;
1832
    gLollipop_model->faces[0].smoothing = 1;
1833
    gLollipop_model->faces[1].smoothing = 1;
1834
    BrModelAdd(gLollipop_model);
1835
}
1836
 
1837
// IDA: void __usercall InitSplash(FILE *pF@<EAX>)
1838
void InitSplash(FILE* pF) {
1839
    int i;
1840
    int num_files;
1841
    int num;
1842
    br_actor* actor;
1843
    char the_path[256];
1844
    char s[256];
1845
    br_pixelmap* splash_maps[20];
1846
    LOG_TRACE("(%p)", pF);
1847
 
1848
    gSplash_flags = 0;
1849
    gSplash_model = BrModelAllocate("Splash", 4, 2);
1850
    if (pF != NULL) {
1851
        num = GetAnInt(pF);
1852
        gNum_splash_types = 0;
1853
        for (i = 0; num > i; ++i) {
1854
            GetAString(pF, s);
1855
            PathCat(the_path, gApplication_path, "PIXELMAP");
1856
            PathCat(the_path, the_path, s);
1857
            num_files = DRPixelmapLoadMany(the_path, &splash_maps[gNum_splash_types], 20 - gNum_splash_types);
1858
            if (num_files == 0) {
1859
                FatalError(kFatalError_LoadPixelmapFile_S, the_path);
1860
            }
1861
            gNum_splash_types += num_files;
1862
        }
1863
    } else {
1864
        PathCat(the_path, gApplication_path, "PIXELMAP");
1865
        PathCat(the_path, the_path, "SPLSHBLU.PIX");
1866
        gNum_splash_types = DRPixelmapLoadMany(the_path, splash_maps, 0x14u);
1867
    }
1868
    BrMapAddMany(splash_maps, gNum_splash_types);
1869
    for (i = 0; i < gNum_splash_types; ++i) {
1870
        gSplash_material[i] = BrMaterialAllocate(0);
1871
        gSplash_material[i]->flags &= ~(BR_MATF_LIGHT | BR_MATF_PRELIT);
1872
        gSplash_material[i]->flags |= BR_MATF_ALWAYS_VISIBLE | BR_MATF_PERSPECTIVE;
1873
        gSplash_material[i]->index_blend = LoadSingleShadeTable(&gTrack_storage_space, "BLEND50.TAB");
1874
        gSplash_material[i]->colour_map = splash_maps[i];
1875
        BrMaterialAdd(gSplash_material[i]);
1876
    }
1877
    gSplash_model->nvertices = 4;
1878
    BrVector3SetFloat(&gSplash_model->vertices[0].p, -0.5f, 0.0f, 0.0f);
1879
    BrVector3SetFloat(&gSplash_model->vertices[1].p, 0.5f, 0.0f, 0.0f);
1880
    BrVector3SetFloat(&gSplash_model->vertices[2].p, 0.5f, 1.0f, 0.0f);
1881
    BrVector3SetFloat(&gSplash_model->vertices[3].p, -0.5f, 1.0f, 0.0f);
1882
    gSplash_model->vertices[0].map.v[0] = 0.0f;
1883
    gSplash_model->vertices[0].map.v[1] = 1.0f;
1884
    gSplash_model->vertices[1].map.v[0] = 1.0f;
1885
    gSplash_model->vertices[1].map.v[1] = 1.0f;
1886
    gSplash_model->vertices[2].map.v[0] = 1.0f;
1887
    gSplash_model->vertices[2].map.v[1] = 0.0f;
1888
    gSplash_model->vertices[3].map.v[0] = 0.0f;
1889
    gSplash_model->vertices[3].map.v[1] = 0.0f;
1890
    gSplash_model->nfaces = 2;
1891
    gSplash_model->faces[0].vertices[0] = 0;
1892
    gSplash_model->faces[0].vertices[1] = 1;
1893
    gSplash_model->faces[0].vertices[2] = 2;
1894
    gSplash_model->faces[1].vertices[0] = 0;
1895
    gSplash_model->faces[1].vertices[1] = 2;
1896
    gSplash_model->faces[1].vertices[2] = 3;
1897
    gSplash_model->faces[0].smoothing = 1;
1898
    gSplash_model->faces[1].smoothing = 1;
1899
    BrModelAdd(gSplash_model);
1900
    for (i = 0; i < COUNT_OF(gSplash); i++) {
1901
        gSplash[i].actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
1902
        actor = gSplash[i].actor;
1903
        actor->model = gSplash_model;
1904
        if (gNum_splash_types != 0) {
1905
            actor->material = gSplash_material[IRandomBetween(0, gNum_splash_types - 1)];
1906
        } else {
1907
            actor->material = NULL;
1908
        }
1909
        gSplash[i].scale_x = SRandomBetween(0.9f, 1.1f) * (2 * IRandomBetween(0, 1) - 1);
1910
    }
1911
}
1912
 
1913
// IDA: void __cdecl DisposeSplash()
1914
void DisposeSplash(void) {
1915
    int i;
1916
    LOG_TRACE("()");
1917
 
1918
    for (i = 0; i < gNum_splash_types; i++) {
1919
        BrMapRemove(gSplash_material[i]->colour_map);
1920
        BrPixelmapFree(gSplash_material[i]->colour_map);
1921
        BrMaterialRemove(gSplash_material[i]);
1922
        BrMaterialFree(gSplash_material[i]);
1923
    }
1924
    for (i = 0; i < COUNT_OF(gSplash); i++) {
1925
        if (gSplash_flags & (1u << i)) {
1926
            BrActorRemove(gSplash[i].actor);
1927
        }
1928
        BrActorFree(gSplash[i].actor);
1929
    }
1930
    BrModelRemove(gSplash_model);
1931
    BrModelFree(gSplash_model);
1932
}
1933
 
1934
// IDA: void __usercall DrawTheGlow(br_pixelmap *pRender_screen@<EAX>, br_pixelmap *pDepth_buffer@<EDX>, br_actor *pCamera@<EBX>)
1935
void DrawTheGlow(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_actor* pCamera) {
1936
    int i;
1937
    br_scalar strength;
1938
    br_vector3 tv;
1939
    tU32 seed;
1940
    LOG_TRACE("(%p, %p, %p)", pRender_screen, pDepth_buffer, pCamera);
1941
 
1942
    if (gColumn_flags) {
1943
        seed = rand();
1944
        srand(GetTotalTime());
1945
        for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
1946
            if (((1u << i) & gColumn_flags) != 0 && gSmoke_column[i].colour <= 1) {
1947
                strength = 0.5f;
1948
                if (gSmoke_column[i].lifetime < 4000) {
1949
                    strength = gSmoke_column[i].lifetime * 0.5f / 4000.f;
1950
                }
1951
                BrVector3Set(&tv, gSmoke_column[i].pos.v[0], gSmoke_column[i].pos.v[1] + 0.02f, gSmoke_column[i].pos.v[2]);
1952
                SmokeCircle3D(&tv, 0.07f, strength, SRandomBetween(0.5f, 0.99f), pRender_screen, pDepth_buffer, gAcid_shade_table, pCamera);
1953
            }
1954
        }
1955
        srand(seed);
1956
    }
1957
}
1958
 
1959
// IDA: void __usercall PipeInstantUnSmudge(tCar_spec *pCar@<EAX>)
1960
void PipeInstantUnSmudge(tCar_spec* pCar) {
1961
    br_model* model;
1962
    br_model* b_model;
1963
    br_actor* actor;
1964
    br_actor* bonny;
1965
    int j;
1966
    int n;
1967
    int v;
1968
    int group;
1969
    tSmudged_vertex data[1000];
1970
    LOG_TRACE("(%p)", pCar);
1971
 
1972
    if (!gAction_replay_mode) {
1973
        return;
1974
    }
1975
    actor = pCar->car_model_actors[pCar->principal_car_actor].actor;
1976
    model = actor->model;
1977
    bonny = pCar->car_model_actors[pCar->car_actor_count - 1].actor;
1978
    n = 0;
1979
    if ((model->flags & BR_MODF_KEEP_ORIGINAL) != 0 || (model->flags & BR_MODF_UPDATEABLE) != 0) {
1980
        StartPipingSession(ePipe_chunk_smudge);
1981
        j = 0;
1982
        for (group = 0; group < V11MODEL(model)->ngroups; group++) {
1983
            for (v = 0; v < V11MODEL(model)->groups[group].nvertices; v++) {
1984
                if ((V11MODEL(model)->groups[group].vertex_colours[v] >> 24) != 0) {
1985
                    data[n].vertex_index = j;
1986
                    data[n].light_index = 0 -(V11MODEL(model)->groups[group].vertex_colours[v] >> 24); // Pierre-Marie Baty -- prefixed 0 to silence signedness warning
1987
                    n += 1;
1988
                    V11MODEL(model)->groups[group].vertex_colours[v] = 0;
1989
                    if ((model->flags & 0x80) != 0) {
1990
                        model->vertices[V11MODEL(model)->groups[group].vertex_user[v]].index = 0;
1991
                    }
1992
                    if (n >= COUNT_OF(data)) {
1993
                        group = V11MODEL(model)->ngroups;
1994
                        break;
1995
                    }
1996
                }
1997
                j += 1;
1998
            }
1999
        }
2000
        if (n != 0) {
2001
            AddSmudgeToPipingSession(pCar->car_ID, pCar->principal_car_actor, n, data);
2002
        }
2003
        if (bonny != actor) {
2004
            b_model = bonny->model;
2005
            n = 0;
2006
            j = 0;
2007
            for (group = 0; group < V11MODEL(model)->ngroups; group++) {
2008
                for (v = 0; v < V11MODEL(model)->groups[group].nvertices; v++) {
2009
                    if ((V11MODEL(model)->groups[group].vertex_colours[v] >> 24) != 0) {
2010
                        data[n].vertex_index = j;
2011
                        data[n].light_index = -V11MODEL(model)->groups[group].nvertices;
2012
                        n += 1;
2013
                        V11MODEL(model)->groups[group].vertex_colours[v] = 0;
2014
                        if ((b_model->flags & BR_MODF_UPDATEABLE) != 0) {
2015
                            b_model->vertices[V11MODEL(model)->groups[group].vertex_user[v]].index = 0;
2016
                        }
2017
                        if (n >= COUNT_OF(data)) {
2018
                            group = V11MODEL(model)->groups[group].nvertices;
2019
                            break;
2020
                        }
2021
                    }
2022
                    j += 1;
2023
                }
2024
            }
2025
            if (n != 0) {
2026
                AddSmudgeToPipingSession(pCar->car_ID, pCar->car_actor_count - 1, n, data);
2027
            }
2028
        }
2029
        EndPipingSession();
2030
    }
2031
}
2032
 
2033
// IDA: void __usercall SmudgeCar(tCar_spec *pCar@<EAX>, int fire_point@<EDX>)
2034
void SmudgeCar(tCar_spec* pCar, int fire_point) {
2035
    int v;
2036
    int j;
2037
    int real_vertex_number;
2038
    br_model* model;
2039
    br_model* b_model;
2040
    br_actor* actor;
2041
    br_actor* bonny;
2042
    br_scalar ts;
2043
    br_vector3 tv;
2044
    br_vector3 bonny_pos;
2045
    int group;
2046
    br_vector3 point;
2047
    tSmudged_vertex data[30];
2048
    int n;
2049
    LOG_TRACE("(%p, %d)", pCar, fire_point);
2050
 
2051
    if (gAusterity_mode) {
2052
        return;
2053
    }
2054
 
2055
    v = fire_point;
2056
    group = 0;
2057
    actor = pCar->car_model_actors[pCar->principal_car_actor].actor;
2058
    model = actor->model;
2059
    bonny = pCar->car_model_actors[pCar->car_actor_count - 1].actor;
2060
    n = 0;
2061
    real_vertex_number = 0;
2062
    if ((model->flags & BR_MODF_KEEP_ORIGINAL) != 0 || (model->flags & BR_MODF_UPDATEABLE) != 0) {
20 pmbaty 2063
        point = V11MODEL(model)->groups[group].vertices[fire_point].p;
1 pmbaty 2064
        StartPipingSession(ePipe_chunk_smudge);
2065
        for (group = 0; group < V11MODEL(model)->ngroups; group++) {
2066
            for (j = 0; j < V11MODEL(model)->groups[group].nvertices; j++) {
20 pmbaty 2067
                BrVector3Sub(&tv, &V11MODEL(model)->groups[group].vertices[j].p, &point);
1 pmbaty 2068
                ts = (.0144f - BrVector3LengthSquared(&tv) / SRandomBetween(.5f, 1.f)) / .0144f * 127.f;
2069
                if (ts > 0.f) {
2070
                    ts += BR_ALPHA(V11MODEL(model)->groups[group].vertex_colours[j]);
2071
                    if (ts > 255.f) {
2072
                        ts = 255.f;
2073
                    }
2074
                    if (BR_ALPHA(V11MODEL(model)->groups[group].vertex_colours[j]) != (int)ts) {
2075
                        data[n].vertex_index = real_vertex_number;
2076
                        data[n].light_index = (int)ts - BR_ALPHA(V11MODEL(model)->groups[group].vertex_colours[j]);
2077
                        V11MODEL(model)->groups[group].vertex_colours[j] = (int)ts << 24;
2078
                        if ((model->flags & BR_MODF_UPDATEABLE) != 0) {
2079
                            model->vertices[V11MODEL(model)->groups[group].vertex_user[j]].index = (int)ts;
2080
                        }
2081
                        n++;
2082
                        if (n >= COUNT_OF(data)) {
2083
                            break;
2084
                        }
2085
                    }
2086
                }
2087
                real_vertex_number++;
2088
            }
2089
            if (n >= COUNT_OF(data)) {
2090
                break;
2091
            }
2092
        }
2093
        if (n > 0) {
2094
            AddSmudgeToPipingSession(pCar->car_ID, pCar->principal_car_actor, n, data);
2095
            // Added by dethrace to update gpu-buffered vertices
20 pmbaty 2096
            model->flags |= BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE;
1 pmbaty 2097
        }
2098
 
2099
        n = 0;
2100
        real_vertex_number = 0;
2101
        if (actor != bonny) {
2102
            b_model = bonny->model;
2103
            BrVector3Add(&tv, &actor->t.t.translate.t, &point);
2104
            BrVector3Accumulate(&tv, &bonny->t.t.translate.t);
2105
            BrMatrix34TApplyV(&bonny_pos, &tv, &bonny->t.t.mat);
2106
            for (group = 0; group < V11MODEL(b_model)->ngroups; group++) {
2107
                j = 0;
2108
                for (j = 0; j < V11MODEL(b_model)->groups[group].nvertices; j++) {
20 pmbaty 2109
                    BrVector3Sub(&tv, &V11MODEL(b_model)->groups[group].vertices[j].p, &bonny_pos);
1 pmbaty 2110
                    ts = (.0144f - BrVector3LengthSquared(&tv) / SRandomBetween(.5f, 1.f)) / .0144f * 127.f;
2111
                    if (ts > 0.f) {
2112
                        ts += BR_ALPHA(V11MODEL(b_model)->groups[group].vertex_colours[j]);
2113
                        if (ts > 255.f) {
2114
                            ts = 255.f;
2115
                        }
2116
                        if (BR_ALPHA(V11MODEL(b_model)->groups[group].vertex_colours[j]) != (int)ts) {
2117
                            data[n].vertex_index = real_vertex_number;
2118
                            data[n].light_index = (int)ts - BR_ALPHA(V11MODEL(b_model)->groups[group].vertex_colours[j]);
2119
                            V11MODEL(b_model)->groups[group].vertex_colours[j] = (int)ts << 24;
2120
                            if ((b_model->flags & BR_MODF_UPDATEABLE) != 0) {
2121
                                b_model->vertices[V11MODEL(b_model)->groups[group].vertex_user[j]].index = (int)ts;
2122
                            }
2123
                            n++;
2124
                            if (n >= COUNT_OF(data)) {
2125
                                break;
2126
                            }
2127
                        }
2128
                    }
2129
                    real_vertex_number++;
2130
                }
2131
                if (n >= COUNT_OF(data)) {
2132
                    break;
2133
                }
2134
            }
2135
            if (n > 0) {
2136
                AddSmudgeToPipingSession(pCar->car_ID, pCar->car_actor_count - 1, n, data);
20 pmbaty 2137
 
1 pmbaty 2138
                // Added by dethrace to update gpu-buffered vertices
20 pmbaty 2139
                b_model->flags |= BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE;
1 pmbaty 2140
            }
2141
        }
2142
        EndPipingSession();
2143
    }
2144
}
2145
 
2146
// IDA: void __cdecl ResetSmokeColumns()
2147
void ResetSmokeColumns(void) {
20 pmbaty 2148
    //int i; // Pierre-Marie Baty -- unused variable
1 pmbaty 2149
    LOG_TRACE("()");
20 pmbaty 2150
    NOT_IMPLEMENTED();
1 pmbaty 2151
}
2152
 
2153
// IDA: void __usercall SetSmokeOn(int pSmoke_on@<EAX>)
2154
void SetSmokeOn(int pSmoke_on) {
2155
    LOG_TRACE("(%d)", pSmoke_on);
2156
 
2157
    gSmoke_on = pSmoke_on;
2158
}
2159
 
2160
// IDA: void __usercall ReallySetSmokeOn(int pSmoke_on@<EAX>)
2161
void ReallySetSmokeOn(int pSmoke_on) {
2162
    LOG_TRACE("(%d)", pSmoke_on);
20 pmbaty 2163
    NOT_IMPLEMENTED();
1 pmbaty 2164
}
2165
 
2166
// IDA: void __usercall SetSmoke(int pSmoke_on@<EAX>)
2167
void SetSmoke(int pSmoke_on) {
2168
    LOG_TRACE("(%d)", pSmoke_on);
2169
 
2170
    ReallySetSmokeOn(pSmoke_on);
2171
    SetSmokeOn(pSmoke_on);
2172
}
2173
 
2174
// IDA: int __cdecl GetSmokeOn()
2175
int GetSmokeOn(void) {
2176
    LOG_TRACE("()");
2177
 
2178
    return gSmoke_on;
2179
}
2180
 
2181
// IDA: void __usercall StopCarSmoking(tCar_spec *pCar@<EAX>)
2182
void StopCarSmoking(tCar_spec* pCar) {
2183
    int i;
2184
    LOG_TRACE("(%p)", pCar);
2185
 
2186
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
2187
        if (gSmoke_column[i].car == pCar && gSmoke_column[i].lifetime > 2000) {
2188
            gSmoke_column[i].lifetime = 2000;
2189
        }
2190
    }
2191
}
2192
 
2193
// IDA: void __usercall StopCarSmokingInstantly(tCar_spec *pCar@<EAX>)
2194
void StopCarSmokingInstantly(tCar_spec* pCar) {
2195
    int i;
2196
    LOG_TRACE("(%p)", pCar);
2197
 
2198
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
2199
        if (gSmoke_column[i].car == pCar) {
2200
            gSmoke_column[i].lifetime = 0;
2201
        }
2202
    }
2203
}
2204
 
2205
// IDA: void __usercall ConditionalSmokeColumn(tCar_spec *pCar@<EAX>, int pDamage_index@<EDX>, int pColour@<EBX>)
2206
void ConditionalSmokeColumn(tCar_spec* pCar, int pDamage_index, int pColour) {
2207
    int i;
2208
    LOG_TRACE("(%p, %d, %d)", pCar, pDamage_index, pColour);
2209
 
2210
    if (!pColour) {
2211
        pColour = pCar->driver < eDriver_net_human;
2212
    }
2213
    if (pCar->num_smoke_columns != 0) {
2214
        for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
2215
            if (gSmoke_column[i].car == pCar) {
2216
                if (((1u << i) & gColumn_flags) != 0 && gSmoke_column[i].colour <= pColour && gSmoke_column[i].lifetime) {
2217
                    return;
2218
                }
2219
                gSmoke_column[i].lifetime = 2000;
2220
            }
2221
        }
2222
    }
2223
    CreateSmokeColumn(pCar, pColour, pCar->fire_vertex[pDamage_index], 10000000u);
2224
}
2225
 
2226
// IDA: void __usercall SingleSplash(tCar_spec *pCar@<EAX>, br_vector3 *sp@<EDX>, br_vector3 *normal@<EBX>, tU32 pTime@<ECX>)
2227
void SingleSplash(tCar_spec* pCar, br_vector3* sp, br_vector3* normal, tU32 pTime) {
2228
    br_matrix34* mat;
2229
    br_matrix34* c_mat;
2230
    br_vector3 tv;
2231
    br_vector3 vel;
2232
    br_scalar size;
2233
    br_scalar speed;
2234
    br_scalar ts;
2235
    LOG_TRACE("(%p, %p, %p, %d)", pCar, sp, normal, pTime);
2236
 
2237
    mat = &gSplash[gNext_splash].actor->t.t.mat;
2238
    c_mat = &pCar->car_master_actor->t.t.mat;
2239
    BrMatrix34ApplyP(&gSplash[gNext_splash].actor->t.t.euler.t, sp, c_mat);
2240
    tv.v[0] = sp->v[2] * pCar->omega.v[1] - pCar->omega.v[2] * sp->v[1];
2241
    tv.v[1] = pCar->omega.v[2] * sp->v[0] - sp->v[2] * pCar->omega.v[0];
2242
    tv.v[2] = sp->v[1] * pCar->omega.v[0] - pCar->omega.v[1] * sp->v[0];
2243
    BrMatrix34ApplyV(&vel, &tv, c_mat);
2244
    BrVector3Accumulate(&vel, &pCar->v);
2245
    ts = BrVector3Length(&vel);
2246
    size = (fabs(BrVector3Dot(normal, &vel)) * 5.0 + ts) / 150.0 + 0.047826085;
2247
    if (size > 0.5) {
2248
        size = 0.5;
2249
    }
2250
    if (BrVector3Dot(&pCar->velocity_car_space, sp) < 0.0) {
2251
        size = size / 2.0;
2252
    }
2253
 
2254
    gSplash[gNext_splash].size = SRandomBetween(size / 2.0, size);
2255
    if (((1u << gNext_splash) & gSplash_flags) == 0) {
2256
        BrActorAdd(gDont_render_actor, gSplash[gNext_splash].actor);
2257
    }
2258
    gSplash_flags |= 1u << gNext_splash;
2259
    gSplash[gNext_splash].just_done = 1;
2260
    if ((double)pTime * 0.003 > SRandomBetween(0.0, 1.0) && !gAction_replay_mode) {
2261
        BrVector3InvScale(&vel, &vel, WORLD_SCALE);
2262
        BrVector3Scale(&tv, &vel, 0.1f);
2263
        speed = sqrt(ts / 70.0) * 15.0;
2264
        if (speed > 15.0f) {
2265
            speed = 15.0f;
2266
        }
2267
        tv.v[1] += SRandomBetween(5.0, speed) / WORLD_SCALE;
2268
        BrMatrix34TApplyV(&vel, &tv, &pCar->car_master_actor->t.t.mat);
2269
 
2270
        BrVector3Cross(&tv, &vel, &pCar->water_normal);
2271
        BrVector3Scale(&tv, &tv, 0.5f);
2272
        if (BrVector3Dot(sp, &tv) <= 0.0) {
2273
            BrVector3Sub(&vel, &vel, &tv);
2274
        } else {
2275
            BrVector3Accumulate(&vel, &tv);
2276
        }
2277
        CreateSingleSpark(pCar, sp, &vel);
2278
    }
2279
    gNext_splash++;
2280
    if (gNext_splash >= COUNT_OF(gSplash)) {
2281
        gNext_splash = 0;
2282
    }
2283
}
2284
 
2285
// IDA: void __usercall CreateSplash(tCar_spec *pCar@<EAX>, tU32 pTime@<EDX>)
2286
void CreateSplash(tCar_spec* pCar, tU32 pTime) {
2287
    br_vector3 normal_car_space;
2288
    br_vector3 pos2;
2289
    br_vector3 v_plane;
2290
    br_vector3 p;
2291
    br_vector3 tv;
2292
    br_vector3 tv2;
2293
    br_vector3 cm;
2294
    int i;
2295
    int j;
2296
    int mask;
2297
    int axis1;
2298
    int axis2;
2299
    int axis3;
2300
    //br_bounds bnds; // Pierre-Marie Baty -- unused variable
2301
    br_scalar min;
2302
    br_scalar max;
2303
    br_scalar d;
2304
    //br_scalar d2; // Pierre-Marie Baty -- unused variable
2305
    br_scalar dist;
2306
    br_scalar dist2;
2307
    br_scalar ts;
2308
    br_vector3 back_point[2];
2309
    br_scalar back_val[2];
2310
    LOG_TRACE("(%p, %d)", pCar, pTime);
2311
 
2312
    back_val[0] = 0.0;
2313
    back_val[1] = 0.0;
2314
#ifdef DETHRACE_FIX_BUGS
2315
    BrVector3Set(&p, 0.f, 0.f, 0.f);
2316
#endif
2317
    if (pCar->v.v[2] * pCar->v.v[2] + pCar->v.v[1] * pCar->v.v[1] + pCar->v.v[0] * pCar->v.v[0] >= 1.0) {
2318
        BrMatrix34TApplyV(&normal_car_space, &pCar->water_normal, &pCar->car_master_actor->t.t.mat);
2319
        BrMatrix34ApplyP(&tv, &pCar->bounds[0].min, &pCar->car_master_actor->t.t.mat);
2320
        min = BrVector3Dot(&pCar->water_normal, &tv) - pCar->water_d;
2321
        max = min;
2322
        for (i = 0; i < 3; ++i) {
2323
            if (normal_car_space.v[i] <= 0.0) {
2324
                max = (pCar->bounds[0].max.v[i] - pCar->bounds[0].min.v[i]) * normal_car_space.v[i] + max;
2325
            } else {
2326
                min = (pCar->bounds[0].max.v[i] - pCar->bounds[0].min.v[i]) * normal_car_space.v[i] + min;
2327
            }
2328
        }
2329
        if (min * max <= 0.0) {
2330
            BrVector3InvScale(&back_point[0], &pCar->bounds[1].min, WORLD_SCALE);
2331
            BrVector3InvScale(&back_point[1], &pCar->bounds[1].max, WORLD_SCALE);
2332
            back_point[0].v[1] = (br_scalar) 0.01; // Pierre-Marie Baty -- added type cast
2333
            ts = BrVector3Dot(&pCar->velocity_car_space, &normal_car_space);
2334
            BrVector3Scale(&tv, &normal_car_space, ts);
2335
            BrVector3Sub(&v_plane, &pCar->velocity_car_space, &tv);
2336
            d = pCar->water_d
2337
                - (pCar->car_master_actor->t.t.mat.m[3][1] * pCar->water_normal.v[1]
2338
                    + pCar->car_master_actor->t.t.mat.m[3][2] * pCar->water_normal.v[2]
2339
                    + pCar->car_master_actor->t.t.mat.m[3][0] * pCar->water_normal.v[0]);
2340
            mask = IRandomBetween(0, 3);
2341
            axis2 = 2;
2342
            for (axis1 = 0; axis1 < 3; ++axis1) {
2343
                axis3 = 3 - axis1 - axis2;
2344
                for (j = 0; j < 4; ++j) {
2345
                    i = j ^ mask;
2346
                    if (((j ^ mask) & 1) != 0) {
2347
                        tv2.v[axis3] = back_point[0].v[axis3];
2348
                    } else {
2349
                        tv2.v[axis3] = back_point[1].v[axis3];
2350
                    }
2351
                    if (((j ^ mask) & 1) != 0) {
2352
                        tv2.v[axis2] = back_point[0].v[axis2];
2353
                    } else {
2354
                        tv2.v[axis2] = back_point[1].v[axis2];
2355
                    }
2356
 
2357
                    ts = d - tv2.v[axis3] * normal_car_space.v[axis3] - tv2.v[axis2] * normal_car_space.v[axis2];
2358
                    ts = ts / normal_car_space.v[axis1];
2359
                    if (ts >= back_point[0].v[axis1] && back_point[1].v[axis1] >= ts) {
2360
                        tv2.v[axis1] = ts;
2361
                        ts = BrVector3Dot(&pCar->velocity_car_space, &tv2);
2362
                        if (ts >= back_val[0]) {
2363
                            if (back_val[1] <= ts) {
2364
                                SingleSplash(pCar, &tv2, &normal_car_space, pTime);
2365
                            } else {
2366
                                if (back_val[1] < 0.0) {
2367
                                    SingleSplash(pCar, &pos2, &normal_car_space, pTime);
2368
                                }
2369
                                back_val[1] = ts;
2370
                                pos2 = tv2;
2371
                            }
2372
                        } else {
2373
                            if (back_val[1] < 0.0) {
2374
                                SingleSplash(pCar, &pos2, &normal_car_space, pTime);
2375
                            }
2376
                            back_val[1] = back_val[0];
2377
                            back_val[0] = ts;
2378
                            pos2 = p;
2379
                            p = tv2;
2380
                        }
2381
                    }
2382
                }
2383
                axis2 = axis1;
2384
            }
2385
            if (back_val[1] >= 0.0) {
2386
                if (back_val[0] < 0.0) {
2387
                    SingleSplash(pCar, &p, &normal_car_space, pTime);
2388
                }
2389
            } else {
2390
                tv.v[0] = pos2.v[0] - p.v[0];
2391
                tv.v[1] = pos2.v[1] - p.v[1];
2392
                tv.v[2] = pos2.v[2] - p.v[2];
2393
                BrVector3Sub(&tv, &pos2, &p);
2394
                ts = SRandomBetween((br_scalar) 0.4, (br_scalar) 0.6); // Pierre-Marie Baty -- added type casts
2395
                BrVector3Scale(&tv2, &tv, ts);
2396
                BrVector3Accumulate(&tv2, &p);
2397
                ts = SRandomBetween((br_scalar) 0.2, (br_scalar) 0.3); // Pierre-Marie Baty -- added type casts
2398
                BrVector3Scale(&cm, &tv, ts);
2399
                BrVector3Accumulate(&p, &cm);
2400
                ts = -SRandomBetween((br_scalar) 0.2, (br_scalar) 0.3); // Pierre-Marie Baty -- added type casts
2401
                BrVector3Scale(&cm, &tv, ts);
2402
                BrVector3Accumulate(&pos2, &cm);
2403
                ts = BrVector3Dot(&pCar->velocity_car_space, &normal_car_space);
2404
                BrVector3Scale(&tv, &normal_car_space, -ts);
2405
                BrVector3Add(&v_plane, &pCar->velocity_car_space, &tv);
2406
                BrVector3Normalise(&tv, &v_plane);
2407
                BrVector3Scale(&tv, &tv, -0.028985508f);
2408
                BrVector3Accumulate(&tv2, &tv);
2409
                BrVector3Scale(&tv, &tv, 0.5f);
2410
                BrVector3Accumulate(&p, &tv);
2411
                BrVector3Accumulate(&pos2, &tv);
2412
                SingleSplash(pCar, &tv2, &normal_car_space, pTime);
2413
                SingleSplash(pCar, &p, &normal_car_space, pTime);
2414
                SingleSplash(pCar, &pos2, &normal_car_space, pTime);
2415
            }
2416
            d = d * WORLD_SCALE;
2417
            dist = d - BrVector3Dot(&pCar->cmpos, &normal_car_space);
2418
            for (i = 0; pCar->extra_point_num > i; ++i) {
2419
                dist2 = d
2420
                    - (pCar->extra_points[i].v[1] * normal_car_space.v[1]
2421
                        + pCar->extra_points[i].v[2] * normal_car_space.v[2]
2422
                        + pCar->extra_points[i].v[0] * normal_car_space.v[0]);
2423
                if ((dist > 0.0) != (dist2 > 0.0)) {
2424
                    ts = dist / (dist - dist2);
2425
                    BrVector3Sub(&tv, &pCar->extra_points[i], &pCar->cmpos);
2426
                    BrVector3Scale(&tv, &tv, ts);
2427
                    BrVector3Accumulate(&tv, &pCar->cmpos);
2428
                    if (pCar->bounds[1].max.v[1] - 0.028985508 > tv.v[1]
2429
                        || pCar->bounds[1].min.v[0] > tv.v[0]
2430
                        || pCar->bounds[1].max.v[0] < tv.v[1]
2431
                        || pCar->bounds[1].min.v[2] > tv.v[2]
2432
                        || pCar->bounds[1].max.v[2] < tv.v[2]) {
2433
                        BrVector3InvScale(&tv, &tv, WORLD_SCALE);
2434
                        SingleSplash(pCar, &tv, &normal_car_space, pTime);
2435
                    }
2436
                }
2437
            }
2438
            for (i = 0; i < 4; ++i) {
2439
                if ((i & 1) != 0) {
2440
                    tv.v[0] = pCar->bounds[1].max.v[0];
2441
                } else {
2442
                    tv.v[0] = pCar->bounds[1].min.v[0];
2443
                }
2444
                tv.v[1] = pCar->bounds[1].max.v[1];
2445
                tv.v[2] = pCar->wpos[i].v[2];
2446
                dist = d - BrVector3Dot(&tv, &normal_car_space);
2447
                dist2 = (pCar->bounds[1].max.v[1] - 0.01) * normal_car_space.v[1] + dist;
2448
                if ((dist > 0.0) != (dist2 > 0.0)) {
2449
                    ts = dist / (dist - dist2);
2450
                    tv.v[1] -= (pCar->bounds[1].max.v[1] - 0.01) * ts;
2451
                    BrVector3InvScale(&tv, &tv, WORLD_SCALE);
2452
                    SingleSplash(pCar, &tv, &normal_car_space, pTime);
2453
                }
2454
            }
2455
        } else {
2456
            min = min + 1.0;
2457
        }
2458
    }
2459
}
2460
 
2461
// IDA: void __usercall MungeSplash(tU32 pTime@<EAX>)
2462
void MungeSplash(tU32 pTime) {
2463
    int i;
2464
    //br_vector3 tv; // Pierre-Marie Baty -- unused variable
2465
    br_scalar dt;
2466
    br_scalar ts;
2467
    tCar_spec* car;
2468
    tVehicle_type type;
2469
    LOG_TRACE("(%d)", pTime);
2470
 
2471
    if (gNum_splash_types == 0) {
2472
        return;
2473
    }
2474
    if (!gAction_replay_mode || GetReplayRate() == 0.0) {
2475
        if (!gAction_replay_mode) {
2476
            for (i = 0; i < gNum_cars_and_non_cars; i++) {
2477
                if (gActive_car_list[i]->water_d != 10000.0 && gActive_car_list[i]->driver != eDriver_local_human) {
2478
                    CreateSplash(gActive_car_list[i], pTime);
2479
                }
2480
            }
2481
            if (gProgram_state.current_car.water_d != 10000.0) {
2482
                CreateSplash(&gProgram_state.current_car, 100);
2483
            }
2484
        }
2485
    } else {
2486
        for (type = eVehicle_net_player; type <= eVehicle_rozzer; type++) {
2487
            for (i = 0;; i++) {
2488
                if (i >= type ? GetCarCount(type) : 1) {
2489
                    break;
2490
                }
2491
                if (type) {
2492
                    car = GetCarSpec(type, i);
2493
                } else {
2494
                    car = &gProgram_state.current_car;
2495
                }
2496
                if (car->water_d != 10000.0 && car->driver != eDriver_local_human) {
2497
                    CreateSplash(car, pTime);
2498
                }
2499
            }
2500
        }
2501
        if (gProgram_state.current_car.water_d != 10000.0) {
2502
            CreateSplash(&gProgram_state.current_car, 0x64u);
2503
        }
2504
    }
2505
    if (!gSplash_flags) {
2506
        return;
2507
    }
2508
    for (i = 0; i < COUNT_OF(gSplash); i++) {
2509
        if (((1u << i) & gSplash_flags) == 0) {
2510
            continue;
2511
        }
2512
        if (gSplash[i].just_done || (gAction_replay_mode && GetReplayRate() == 0.0f)) {
2513
            dt = gSplash[i].size * gSplash[i].scale_x;
2514
            gSplash[i].actor->t.t.mat.m[0][0] = gCamera_to_world.m[0][0] * dt;
2515
            gSplash[i].actor->t.t.mat.m[0][1] = gCamera_to_world.m[0][1] * dt;
2516
            gSplash[i].actor->t.t.mat.m[0][2] = gCamera_to_world.m[0][2] * dt;
2517
            gSplash[i].actor->t.t.mat.m[1][0] = gSplash[i].size * gCamera_to_world.m[1][0];
2518
            gSplash[i].actor->t.t.mat.m[1][1] = gSplash[i].size * gCamera_to_world.m[1][1];
2519
            gSplash[i].actor->t.t.mat.m[1][2] = gSplash[i].size * gCamera_to_world.m[1][2];
2520
            gSplash[i].actor->t.t.mat.m[2][0] = gSplash[i].size * gCamera_to_world.m[2][0];
2521
            gSplash[i].actor->t.t.mat.m[2][1] = gSplash[i].size * gCamera_to_world.m[2][1];
2522
            gSplash[i].actor->t.t.mat.m[2][2] = gSplash[i].size * gCamera_to_world.m[2][2];
2523
            if (gProgram_state.cockpit_on) {
2524
                ts = sqrt(gCamera_to_world.m[0][2] * gCamera_to_world.m[0][2] + gCamera_to_world.m[0][0] * gCamera_to_world.m[0][0]);
2525
                DRMatrix34PreRotateZ(&gSplash[i].actor->t.t.mat, -FastScalarArcTan2Angle(gCamera_to_world.m[0][1], ts));
2526
            }
2527
            gSplash[i].just_done = 0;
2528
        } else {
2529
            gSplash_flags &= ~(1u << i);
2530
            BrActorRemove(gSplash[i].actor);
2531
        }
2532
    }
2533
}
2534
 
2535
// IDA: void __cdecl RenderSplashes()
2536
void RenderSplashes(void) {
2537
    int i;
2538
    LOG_TRACE("()");
2539
 
2540
    for (i = 0; i < COUNT_OF(gSplash); i++) {
2541
        if (gSplash_flags & (1u << i)) {
2542
            BrActorRelink(gNon_track_actor, gSplash[i].actor);
2543
            BrZbSceneRenderAdd(gSplash[i].actor);
2544
            BrActorRelink(gDont_render_actor, gSplash[i].actor);
2545
        }
2546
    }
2547
}
2548
 
2549
// IDA: void __usercall GetSmokeShadeTables(FILE *f@<EAX>)
2550
void GetSmokeShadeTables(FILE* f) {
2551
    int i;
2552
    int red;
2553
    int green;
2554
    int blue;
2555
    br_scalar quarter;
2556
    br_scalar half;
2557
    br_scalar three_quarter;
2558
    LOG_TRACE("(%p)", f);
2559
 
2560
    gNum_dust_tables = GetAnInt(f);
2561
    if (gNum_dust_tables > 8) {
2562
        gNum_dust_tables = 8;
2563
    }
2564
    for (i = 0; gNum_dust_tables > i; ++i) {
2565
        PossibleService();
2566
        GetThreeInts(f, &red, &green, &blue);
2567
        GetThreeScalars(f, &quarter, &half, &three_quarter);
2568
        gDust_table[i] = GenerateShadeTable(16, gRender_palette, red, green, blue, quarter, half, three_quarter);
2569
    }
2570
}
2571
 
2572
// IDA: void __cdecl FreeSmokeShadeTables()
2573
void FreeSmokeShadeTables(void) {
2574
    int i;
2575
    LOG_TRACE("()");
2576
 
2577
    for (i = 0; i < gNum_dust_tables; i++) {
2578
        PossibleService();
2579
        BrTableRemove(gDust_table[i]);
2580
        BrPixelmapFree(gDust_table[i]);
2581
    }
2582
}
2583
 
2584
// IDA: void __usercall LoadInKevStuff(FILE *pF@<EAX>)
2585
void LoadInKevStuff(FILE* pF) {
2586
    LOG_TRACE("(%p)", pF);
2587
 
2588
    PossibleService();
2589
    LoadInShrapnel();
2590
    PossibleService();
2591
    InitShrapnel();
2592
    PossibleService();
2593
    InitFlame();
2594
    PossibleService();
2595
    InitSplash(pF);
2596
}
2597
 
2598
// IDA: void __cdecl DisposeKevStuff()
2599
void DisposeKevStuff(void) {
2600
    LOG_TRACE("()");
2601
 
2602
    DisposeShrapnel();
2603
    DisposeFlame();
2604
    DisposeSplash();
2605
}
2606
 
2607
// IDA: void __usercall DisposeKevStuffCar(tCar_spec *pCar@<EAX>)
2608
void DisposeKevStuffCar(tCar_spec* pCar) {
2609
    int i;
2610
    LOG_TRACE("(%p)", pCar);
2611
 
2612
    for (i = 0; i < MAX_SMOKE_COLUMNS; i++) {
2613
        if (gSmoke_column[i].car == pCar) {
2614
            gSmoke_column[i].lifetime = 0;
2615
            gSmoke_column[i].car = NULL;
2616
        }
2617
    }
2618
    for (i = 0; i < COUNT_OF(gSparks); i++) {
2619
        if ((gSpark_flags & (1u << i)) && gSparks[i].car == pCar) {
2620
            gSparks[i].count = 0;
2621
            gSpark_flags &= ~(1u << i);
2622
        }
2623
        if (gCar_to_view == pCar) {
2624
            gCamera_yaw = 0;
2625
            gCar_to_view = &gProgram_state.current_car;
2626
            InitialiseExternalCamera();
2627
            PositionExternalCamera(gCar_to_view, 200);
2628
            gCar_to_view = &gProgram_state.current_car;
2629
        }
2630
    }
2631
}
2632
 
2633
// IDA: void __cdecl DoTrueColModelThing(br_actor *actor, br_model *pModel, br_material *material, void *render_data, br_uint_8 style, int on_screen)
2634
void DoTrueColModelThing(br_actor* actor, br_model* pModel, br_material* material, void* render_data, br_uint_8 style, int on_screen) {
20 pmbaty 2635
    //int group; // Pierre-Marie Baty -- unused variable
2636
    //int j; // Pierre-Marie Baty -- unused variable
2637
    //int val; // Pierre-Marie Baty -- unused variable
1 pmbaty 2638
    LOG_TRACE("(%p, %p, %p, %p, %d, %d)", actor, pModel, material, render_data, style, on_screen);
2639
    NOT_IMPLEMENTED();
2640
}
2641
 
2642
// IDA: void __cdecl DoModelThing(br_actor *actor, br_model *pModel, br_material *material, void *render_data, br_uint_8 style, int on_screen)
2643
void DoModelThing(br_actor* actor, br_model* pModel, br_material* material, void* render_data, br_uint_8 style, int on_screen) {
20 pmbaty 2644
    //int j; // Pierre-Marie Baty -- unused variable
2645
    //int i; // Pierre-Marie Baty -- unused variable
2646
    //int group; // Pierre-Marie Baty -- unused variable
2647
    //tU32 t; // Pierre-Marie Baty -- unused variable
2648
    //int val; // Pierre-Marie Baty -- unused variable
1 pmbaty 2649
    LOG_TRACE("(%p, %p, %p, %p, %d, %d)", actor, pModel, material, render_data, style, on_screen);
20 pmbaty 2650
    NOT_IMPLEMENTED();
1 pmbaty 2651
}
2652
 
2653
// IDA: void __usercall SetModelShade(br_actor *pActor@<EAX>, br_pixelmap *pShade@<EDX>)
2654
void SetModelShade(br_actor* pActor, br_pixelmap* pShade) {
2655
    int i;
2656
    br_material* material;
2657
    br_model* model;
2658
    LOG_TRACE("(%p, %p)", pActor, pShade);
2659
 
2660
    model = pActor->model;
2661
    if (pActor->material != NULL && pActor->material->index_shade != pShade) {
2662
        pActor->material->index_shade = pShade;
2663
        BrMaterialUpdate(pActor->material, BR_MATU_ALL);
2664
    }
2665
    for (i = 0; i < model->nfaces; i++) {
2666
        material = model->faces[i].material;
2667
        if (material != NULL && material->index_shade != pShade) {
2668
            material->index_shade = pShade;
2669
            BrMaterialUpdate(material, BR_MATU_ALL);
2670
        }
2671
    }
2672
}
2673
 
2674
// IDA: void __usercall MakeCarIt(tCar_spec *pCar@<EAX>)
2675
void MakeCarIt(tCar_spec* pCar) {
2676
    br_actor* actor;
2677
    br_actor* bonny;
2678
    br_pixelmap* shade[6];
2679
    static int shade_num = 0;
2680
    //int i; // Pierre-Marie Baty -- unused variable
2681
    LOG_TRACE("(%p)", pCar);
2682
 
20 pmbaty 2683
    STUB();
2684
    return;
2685
 
1 pmbaty 2686
    shade[0] = gIt_shade_table;
2687
    shade[1] = gFog_shade_table;
2688
    shade[2] = gShade_list[0];
2689
    shade[3] = gShade_list[1];
2690
    shade[4] = gShade_list[2];
2691
    shade[5] = NULL;
2692
 
2693
    actor = pCar->car_model_actors[pCar->principal_car_actor].actor;
2694
    bonny = pCar->car_model_actors[pCar->car_actor_count - 1].actor;
20 pmbaty 2695
    if (((actor->model->flags & BR_MODF_CUSTOM) == 0) || actor->model->custom != DoTrueColModelThing) {
1 pmbaty 2696
        SetModelShade(actor, shade[shade_num]);
20 pmbaty 2697
        actor->model->user = DoTrueColModelThing;
2698
        actor->model->custom = DoTrueColModelThing;
1 pmbaty 2699
        actor->model->flags |= BR_MODF_CUSTOM;
2700
        if (bonny != actor) {
20 pmbaty 2701
            bonny->model->user = DoTrueColModelThing;
2702
            bonny->model->custom = DoTrueColModelThing;
1 pmbaty 2703
            bonny->model->flags |= BR_MODF_CUSTOM;
2704
            SetModelShade(bonny, shade[shade_num]);
2705
        }
2706
    }
2707
}
2708
 
2709
// IDA: void __usercall StopCarBeingIt(tCar_spec *pCar@<EAX>)
2710
void StopCarBeingIt(tCar_spec* pCar) {
20 pmbaty 2711
    //int i; // Pierre-Marie Baty -- unused variable
2712
    //int group; // Pierre-Marie Baty -- unused variable
2713
    //br_actor* actor; // Pierre-Marie Baty -- unused variable
2714
    //br_actor* bonny; // Pierre-Marie Baty -- unused variable
1 pmbaty 2715
    LOG_TRACE("(%p)", pCar);
20 pmbaty 2716
    STUB();
1 pmbaty 2717
}