This is a sub-page of Proto:Hydro Thunder (Dreamcast).
|
|
This prototype is documented on Hidden Palace. |
Compiled on July 16th, 1999, about a month before the first final build, dumped by Laurent.
General Differences#
- The graphical and sound issues have been toned down from the previous build, but there are still some, especially for sound in two player mode.
- The disc contains two extra executables, both dating earlier than the one that's used. HYDRO.BIN, dated 8 days earlier, and HYDRO.ELF, dated a week earlier. The latter is 25mb in size and appears to contain a significant amount of debug information.
- Like the previous build, the game crashes upon finishing a race in two player mode.
- Razorback and Nile Adventure are still assigned the Ultra-Hard difficulty. And....Hydro Speedway?? Extra-Easy has been rendered unused at this point.
- In the boat select, the graphic for BONUS does not flash in the center when highlighting the respective boats.
- Catacomb is designated as an Easy track.
- The game will crash upon crossing the finish line in Catacomb.
The HUD radar for Catacomb is glitched.
The flashing PRESS START has been added from the previous build, but the website name hasn't been removed yet.
|
Proto |
Final |
|---|---|
|
|
|
The countdown timer still shows in Trainer.
|
Proto |
Final |
|---|---|
|
|
|
Unused Graphics#
Contained in PURUSEL.R2, among the the few brighter text graphics is one for BACK.
Title Screen Differences#
The first graphic you see upon booting the game is not copyright information, rather, the Midway logo.
|
Proto |
Final |
|---|---|
|
|
|
The title screen has a considerable amount of copyright info at the bottom, and the Sega Dreamcast logo is not present. "PRESS START" is placed lower, and the blue "EUROCOM" text is not present.
|
Proto |
Final(v1.002) |
|---|---|
|
|
|
Menu Differences#
In the main menu, the VMU option for saving and loading data is absent. In all menus, (nearly)every red text graphic is darker colored, and the graphics in the very top right only include the A button, instead of having both A and B. The ACCEPT text used in the proto's graphic appears to be from the arcade version. It is not possible to return from a menu with B in this build, the menu must include an option to do so with A in order to go back.
|
Proto |
Final |
|---|---|
|
|
|
In the controller settings menu, the Dreamcast controller is positioned more to the left. In the first configuration option, the graphic says that X, Y, and B are all mapped for camera control. This may have been like the original arcade version, which also had three camera buttons. However, in-game, this isn't the case, Y is mapped to cycle between the different camera positions, like the final.
|
Proto |
Final |
|---|---|
|
|
|
The second configuration option differs pretty significantly. In what the graphics imply, anyway. Notably, there was intended to be two camera bindings, the D-Pad isn't used at all, and both triggers are mapped as boost. Selecting this option does not change the controls at all.
|
Proto |
Final |
|---|---|
|
|
|
In the jump pack config menu, the jump pack is referred to as "PURU-PURU" instead, which is what it was called in Japan. The graphic at the top-right says to use the X and B buttons to adjust the sliders instead of left and right, respectively. However, pressing X does not do anything, meaning it's only possible to turn up the sliders and not down.
|
Proto |
Final |
|---|---|
|
|
|
In the volume adjustment menu, a sound plays on loop while highlighting SFX. The final uses a sound played while adjusting the slider to let you hear your changes instead. The default SFX slider setting is much lower in this build. Oddly, the SELECT and CHANGE text graphics are colored as bright as the final's.
|
Proto |
Final |
|---|---|
|
|
|
In the Hydro Champions screen from the main menu, the floating characters to represent the initials and time appear much duller colored than they should be. The default times set on the leaderboard are also much higher.
|
Proto |
Final |
|---|---|
|
|
|
In the Hydro Champions screen after finishing a race, the glitched flashing graphic next to "YOUR TIME" is still present, but everything else has been removed, including "PLAY AGAIN!", which would be added back in time for the final.
|
Proto |
Final |
|---|---|
|
|
|
In the track select, Trainer can be seen placed in the Easy category. Every track is also unlocked by default for this build.
|
Proto |
Final |
|---|---|
|
|
|
Every boat is unlocked by default for this build. The difficulty text graphic that flashes in the center is larger.
|
Proto |
Final |
|---|---|
|
|
|
Uncompiled Source Code#
This build also includes more uncompiled source code that is not in the final.
dcHydro.c#
Found at 0x16978810.
/******************************************************************************
* dcHydro.c
*******************************************************************************
*
*
* Hydro dreamcast specific stuff
*
* David Long '98
*
*******************************************************************************/
#include "dcInclude.h"
#include "dcHydro.h"
//#include "fcntl.h"
static BOOL _bFileLogging;
float dcViewportYTrans = 240.0f;
float dcViewportH = 480.0f;
float dcViewportYMin = 0;
float dcViewportYMax = 639.0f;
float dcOrthoYOffset = 0;
int bEUROCOM_WORLDOBWORKFUNCS = 1; /* Only process work functions for objects in view! */
int bEUROCOM_SECTORSCANPHYS = 1; /* Scan to see if water collision mesh is inside a single sector */
int bEUROCOM_COLLSCANINVIEW = 1; /* Only scan collision objects in view */
int bEUROCOM_PHYSSKIPHULLPOINTS = 1; /* Skip every other hull point */
int bEUROCOM_NODRONES = 0;
int bEUROCOM_SPLITSCREEN = 0; /* Split screen version - ouch!!! */
int bEUROCOM_NOTIMERS = 0;
DCWATERTRISPAN WaterTriLists[16+1][16+1]; /* Mostly NULL's or pointer to above list for correct [][] index */
typedef struct {
int Lod1,Lod2;
int *pInds;
int Size;
} WATER_TRILIST_TABLE;
/* 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 (Lod2) */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (Lod1) */
int Lod16_16[] = {
16,17,1,0, 1,0,16,0,
17,18,2,0, 2,1,17,0,
18,19,3,0, 3,2,18,0,
19,20,4,0, 4,3,19,0,
20,21,5,0, 5,4,20,0,
21,22,6,0, 6,5,21,0,
22,23,7,0, 7,6,22,0,
23,24,8,0, 8,7,23,0,
24,25,9,0, 9,8,24,0,
25,26,10,0, 10,9,25,0,
26,27,11,0, 11,10,26,0,
27,28,12,0, 12,11,27,0,
28,29,13,0, 13,12,28,0,
29,30,14,0, 14,13,29,0,
30,31,15,0, 15,14,30,0
};
/* 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 (Lod2) */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 (Lod1) */
int Lod15_15[] = {
15,16,1,0, 1,0,15,0,
16,17,2,0, 2,1,16,0,
17,18,3,0, 3,2,17,0,
18,19,4,0, 4,3,18,0,
19,20,5,0, 5,4,19,0,
20,21,6,0, 6,5,20,0,
21,22,7,0, 7,6,21,0,
22,23,8,0, 8,7,22,0,
23,24,9,0, 9,8,23,0,
24,25,10,0, 10,9,24,0,
25,26,11,0, 11,10,25,0,
26,27,12,0, 12,11,26,0,
27,28,13,0, 13,12,27,0,
28,29,14,0, 14,13,28,0
};
/* 14 15 16 17 18 19 20 21 22 23 24 25 26 27 (Lod2) */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 (Lod1) */
int Lod14_14[] = {
14,15,1,0, 1,0,14,0,
15,16,2,0, 2,1,15,0,
16,17,3,0, 3,2,16,0,
17,18,4,0, 4,3,17,0,
18,19,5,0, 5,4,18,0,
19,20,6,0, 6,5,19,0,
20,21,7,0, 7,6,20,0,
21,22,8,0, 8,7,21,0,
22,23,9,0, 9,8,22,0,
23,24,10,0, 10,9,23,0,
24,25,11,0, 11,10,24,0,
25,26,12,0, 12,11,25,0,
26,27,13,0, 13,12,26,0
};
/* 13 14 15 16 17 18 19 20 21 22 23 24 25 (Lod2) */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 (Lod1) */
int Lod13_13[] = {
13,14,1,0, 1,0,13,0,
14,15,2,0, 2,1,14,0,
15,16,3,0, 3,2,15,0,
16,17,4,0, 4,3,16,0,
17,18,5,0, 5,4,17,0,
18,19,6,0, 6,5,18,0,
19,20,7,0, 7,6,19,0,
20,21,8,0, 8,7,20,0,
21,22,9,0, 9,8,21,0,
22,23,10,0, 10,9,22,0,
23,24,11,0, 11,10,23,0,
24,25,12,0, 12,11,24,0
};
/* 12 13 14 15 16 17 18 19 20 21 22 23 (Lod2) */
/* 0 1 2 3 4 5 6 7 8 9 10 11 (Lod1) */
int Lod12_12[] = {
12,13,1,0, 1,0,12,0,
13,14,2,0, 2,1,13,0,
14,15,3,0, 3,2,14,0,
15,16,4,0, 4,3,15,0,
16,17,5,0, 5,4,16,0,
17,18,6,0, 6,5,17,0,
18,19,7,0, 7,6,18,0,
19,20,8,0, 8,7,19,0,
20,21,9,0, 9,8,20,0,
21,22,10,0, 10,9,21,0,
22,23,11,0, 11,10,22,0
};
/* 11 12 13 14 15 16 17 18 19 20 21 (Lod2) */
/* 0 1 2 3 4 5 6 7 8 9 10 (Lod1) */
int Lod11_11[] = {
11,12,1,0, 1,0,11,0,
12,13,2,0, 2,1,12,0,
13,14,3,0, 3,2,13,0,
14,15,4,0, 4,3,14,0,
15,16,5,0, 5,4,15,0,
16,17,6,0, 6,5,16,0,
17,18,7,0, 7,6,17,0,
18,19,8,0, 8,7,18,0,
19,20,9,0, 9,8,19,0,
20,21,10,0, 10,9,20,0
};
/* 10 11 12 13 14 15 16 17 18 19 (Lod2) */
/* 0 1 2 3 4 5 6 7 8 9 (Lod1) */
int Lod10_10[] = {
10,11,1,0, 1,0,10,0,
11,12,2,0, 2,1,11,0,
12,13,3,0, 3,2,12,0,
13,14,4,0, 4,3,13,0,
14,15,5,0, 5,4,14,0,
15,16,6,0, 6,5,15,0,
16,17,7,0, 7,6,16,0,
17,18,8,0, 8,7,17,0,
18,19,9,0, 9,8,18,0
};
/* 9 10 11 12 13 14 15 16 17 (Lod2) */
/* 0 1 2 3 4 5 6 7 8 (Lod1) */
int Lod9_9[] = {
9,10,1,0, 1,0,9,0,
10,11,2,0, 2,1,10,0,
11,12,3,0, 3,2,11,0,
12,13,4,0, 4,3,12,0,
13,14,5,0, 5,4,13,0,
14,15,6,0, 6,5,14,0,
15,16,7,0, 7,6,15,0,
16,17,8,0, 8,7,16,0
};
/* 8 9 10 11 12 13 14 15 (Lod2) */
/* 0 1 2 3 4 5 6 7 (Lod1) */
int Lod8_8[] = {
8,9,1,0, 1,0,8,0,
9,10,2,0, 2,1,9,0,
10,11,3,0, 3,2,10,0,
11,12,4,0, 4,3,11,0,
12,13,5,0, 5,4,12,0,
13,14,6,0, 6,5,13,0,
14,15,7,0, 7,6,14,0
};
/* 7 8 9 10 11 12 13 (Lod2) */
/* 0 1 2 3 4 5 6 (Lod1) */
int Lod7_7[] = {
7,8,1,0, 1,0,7,0,
8,9,2,0, 2,1,8,0,
9,10,3,0, 3,2,9,0,
10,11,4,0, 4,3,10,0,
11,12,5,0, 5,4,11,0,
12,13,6,0, 6,5,12,0
};
/* 6 7 8 9 10 11 (Lod2) */
/* 0 1 2 3 4 5 (Lod1) */
int Lod6_6[] = {
6,7,1,0, 1,0,6,0,
7,8,2,0, 2,1,7,0,
8,9,3,0, 3,2,8,0,
9,10,4,0, 4,3,9,0,
10,11,5,0, 5,4,10,0
};
/* 5 6 7 8 9 (Lod2) */
/* 0 1 2 3 4 (Lod1) */
int Lod5_5[] = {
5,6,1,0, 1,0,5,0,
6,7,2,0, 2,1,6,0,
7,8,3,0, 3,2,7,0,
8,9,4,0, 4,3,8,0
};
/* 4 5 6 7 (Lod2) */
/* 0 1 2 3 (Lod1) */
int Lod4_4[] = {
4,5,1,0, 1,0,4,0,
5,6,2,0, 2,1,5,0,
6,7,3,0, 3,2,6,0
};
/* 3 4 5 (Lod2) */
/* 0 1 2 (Lod1) */
int Lod3_3[] = {
3,4,1,0, 1,0,3,0,
4,5,2,0, 2,1,4,0
};
/* 2 3 (Lod2) */
/* 0 1 (Lod1) */
int Lod2_2[] = {
2,3,1,0, 1,0,2,0
};
/*------------------------------------------------------------------------------------------------ */
/* Down one */
/* 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 (Lod2) */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (Lod1) */
int Lod16_15[] = {
16,1,0,0, 16,17,1,0,
17,2,1,0, 17,18,2,0,
18,3,2,0, 18,19,3,0,
19,4,3,0, 19,20,4,0,
20,5,4,0, 20,21,5,0,
21,6,5,0, 21,22,6,0,
22,7,6,0, 22,23,7,0,
23,8,7,0, 23,24,8,0,
24,9,8,0, 24,25,9,0,
25,10,9,0, 25,26,10,0,
26,11,10,0, 26,27,11,0,
27,12,11,0, 27,28,12,0,
28,13,12,0, 28,29,13,0,
29,14,13,0, 29,30,14,0,
30,15,14,0
};
/* 15 16 17 18 19 20 21 22 23 24 25 26 27 28 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 (Lod1 */
int Lod15_14[] = {
15,1,0,0, 15,16,1,0,
16,2,1,0, 16,17,2,0,
17,3,2,0, 17,18,3,0,
18,4,3,0, 18,19,4,0,
19,5,4,0, 19,20,5,0,
20,6,5,0, 20,21,6,0,
21,7,6,0, 21,22,7,0,
22,8,7,0, 22,23,8,0,
23,9,8,0, 23,24,9,0,
24,10,9,0, 24,25,10,0,
25,11,10,0, 25,26,11,0,
26,12,11,0, 26,27,12,0,
27,13,12,0, 27,28,13,0,
28,14,13,0
};
/* 14 15 16 17 18 19 20 21 22 23 24 25 26 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 (Lod1 */
int Lod14_13[] = {
14,1,0,0, 14,15,1,0,
15,2,1,0, 15,16,2,0,
16,3,2,0, 16,17,3,0,
17,4,3,0, 17,18,4,0,
18,5,4,0, 18,19,5,0,
19,6,5,0, 19,20,6,0,
20,7,6,0, 20,21,7,0,
21,8,7,0, 21,22,8,0,
22,9,8,0, 22,23,9,0,
23,10,9,0, 23,24,10,0,
24,11,10,0, 24,25,11,0,
25,12,11,0, 25,26,12,0,
26,13,12,0
};
/* 13 14 15 16 17 18 19 20 21 22 23 24 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 (Lod1 */
int Lod13_12[] = {
13,1,0,0, 13,14,1,0,
14,2,1,0, 14,15,2,0,
15,3,2,0, 15,16,3,0,
16,4,3,0, 16,17,4,0,
17,5,4,0, 17,18,5,0,
18,6,5,0, 18,19,6,0,
19,7,6,0, 19,20,7,0,
20,8,7,0, 20,21,8,0,
21,9,8,0, 21,22,9,0,
22,10,9,0, 22,23,10,0,
23,11,10,0, 23,24,11,0,
24,12,11,0
};
/* 12 13 14 15 16 17 18 19 20 21 22 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 11 (Lod1 */
int Lod12_11[] = {
12,1,0,0, 12,13,1,0,
13,2,1,0, 13,14,2,0,
14,3,2,0, 14,15,3,0,
15,4,3,0, 15,16,4,0,
16,5,4,0, 16,17,5,0,
17,6,5,0, 17,18,6,0,
18,7,6,0, 18,19,7,0,
19,8,7,0, 19,20,8,0,
20,9,8,0, 20,21,9,0,
21,10,9,0, 21,22,10,0,
22,11,10,0
};
/* 11 12 13 14 15 16 17 18 19 20 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 (Lod1 */
int Lod11_10[] = {
11,1,0,0, 11,12,1,0,
12,2,1,0, 12,13,2,0,
13,3,2,0, 13,14,3,0,
14,4,3,0, 14,15,4,0,
15,5,4,0, 15,16,5,0,
16,6,5,0, 16,17,6,0,
17,7,6,0, 17,18,7,0,
18,8,7,0, 18,19,8,0,
19,9,8,0, 19,20,9,0,
20,10,9,0
};
/* 10 11 12 13 14 15 16 17 18 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 (Lod1 */
int Lod10_9[] = {
10,1,0,0, 10,11,1,0,
11,2,1,0, 11,12,2,0,
12,3,2,0, 12,13,3,0,
13,4,3,0, 13,14,4,0,
14,5,4,0, 14,15,5,0,
15,6,5,0, 15,16,6,0,
16,7,6,0, 16,17,7,0,
17,8,7,0, 17,18,8,0,
18,9,8,0
};
/* 9 10 11 12 13 14 15 16 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 (Lod1 */
int Lod9_8[] = {
9,1,0,0, 9,10,1,0,
10,2,1,0, 10,11,2,0,
11,3,2,0, 11,12,3,0,
12,4,3,0, 12,13,4,0,
13,5,4,0, 13,14,5,0,
14,6,5,0, 14,15,6,0,
15,7,6,0, 15,16,7,0,
16,8,7,0
};
/* 8 9 10 11 12 13 14 (Lod2 */
/* 0 1 2 3 4 5 6 7 (Lod1 */
int Lod8_7[] = {
8,1,0,0, 8,9,1,0,
9,2,1,0, 9,10,2,0,
10,3,2,0, 10,11,3,0,
11,4,3,0, 11,12,4,0,
12,5,4,0, 12,13,5,0,
13,6,5,0, 13,14,6,0,
14,7,6,0
};
/* 7 8 9 10 11 12 (Lod2 */
/* 0 1 2 3 4 5 6 (Lod1 */
int Lod7_6[] = {
7,1,0,0, 7,8,1,0,
8,2,1,0, 8,9,2,0,
9,3,2,0, 9,10,3,0,
10,4,3,0, 10,11,4,0,
11,5,4,0, 11,12,5,0,
12,6,5,0
};
/* 6 7 8 9 10 (Lod2 */
/* 0 1 2 3 4 5 (Lod1 */
int Lod6_5[] = {
6,1,0,0, 6,7,1,0,
7,2,1,0, 7,8,2,0,
8,3,2,0, 8,9,3,0,
9,4,3,0, 9,10,4,0,
10,5,4,0
};
/* 5 6 7 8 (Lod2 */
/* 0 1 2 3 4 (Lod1 */
int Lod5_4[] = {
5,1,0,0, 5,6,1,0,
6,2,1,0, 6,7,2,0,
7,3,2,0, 7,8,3,0,
8,4,3,0
};
/* 4 5 6 (Lod2 */
/* 0 1 2 3 (Lod1 */
int Lod4_3[] = {
4,1,0,0, 4,5,1,0,
5,2,1,0, 5,6,2,0,
6,3,2,0
};
/* 3 4 (Lod2 */
/* 0 1 2 (Lod1 */
int Lod3_2[] = {
3,1,0,0, 3,4,1,0,
4,2,1,0
};
/*------------------------------------------------------------------------------------------------ */
/* Up one */
/* 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 (Lod1 */
int Lod15_16[] = {
15,16,0,0, 16,1,0,0,
16,17,1,0, 17,2,1,0,
17,18,2,0, 18,3,2,0,
18,19,3,0, 19,4,3,0,
19,20,4,0, 20,5,4,0,
20,21,5,0, 21,6,5,0,
21,22,6,0, 22,7,6,0,
22,23,7,0, 23,8,7,0,
23,24,8,0, 24,9,8,0,
24,25,9,0, 25,10,9,0,
25,26,10,0, 26,11,10,0,
26,27,11,0, 27,12,11,0,
27,28,12,0, 28,13,12,0,
28,29,13,0, 29,14,13,0,
29,30,14,0
};
/* 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 (Lod1 */
int Lod14_15[] = {
14,15,0,0, 15,1,0,0,
15,16,1,0, 16,2,1,0,
16,17,2,0, 17,3,2,0,
17,18,3,0, 18,4,3,0,
18,19,4,0, 19,5,4,0,
19,20,5,0, 20,6,5,0,
20,21,6,0, 21,7,6,0,
21,22,7,0, 22,8,7,0,
22,23,8,0, 23,9,8,0,
23,24,9,0, 24,10,9,0,
24,25,10,0, 25,11,10,0,
25,26,11,0, 26,12,11,0,
26,27,12,0, 27,13,12,0,
27,28,13,0
};
/* 13 14 15 16 17 18 19 20 21 22 23 24 25 26 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 (Lod1 */
int Lod13_14[] = {
13,14,0,0, 14,1,0,0,
14,15,1,0, 15,2,1,0,
15,16,2,0, 16,3,2,0,
16,17,3,0, 17,4,3,0,
17,18,4,0, 18,5,4,0,
18,19,5,0, 19,6,5,0,
19,20,6,0, 20,7,6,0,
20,21,7,0, 21,8,7,0,
21,22,8,0, 22,9,8,0,
22,23,9,0, 23,10,9,0,
23,24,10,0, 24,11,10,0,
24,25,11,0, 25,12,11,0,
25,26,12,0
};
/* 12 13 14 15 16 17 18 19 20 21 22 23 24 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 11 (Lod1 */
int Lod12_13[] = {
12,13,0,0, 13,1,0,0,
13,14,1,0, 14,2,1,0,
14,15,2,0, 15,3,2,0,
15,16,3,0, 16,4,3,0,
16,17,4,0, 17,5,4,0,
17,18,5,0, 18,6,5,0,
18,19,6,0, 19,7,6,0,
19,20,7,0, 20,8,7,0,
20,21,8,0, 21,9,8,0,
21,22,9,0, 22,10,0,0,
22,23,10,0, 23,11,10,0,
23,24,11,0
};
/* 11 12 13 14 15 16 17 18 19 20 21 22 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 (Lod1 */
int Lod11_12[] = {
11,12,0,0, 12,1,0,0,
12,13,1,0, 13,2,1,0,
13,14,2,0, 14,3,2,0,
14,15,3,0, 15,4,3,0,
15,16,4,0, 16,5,4,0,
16,17,5,0, 17,6,5,0,
17,18,6,0, 18,7,6,0,
18,19,7,0, 19,8,7,0,
19,20,8,0, 20,9,8,0,
20,21,9,0, 21,10,9,0,
21,22,10,0
};
/* 10 11 12 13 14 15 16 17 18 19 20 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 (Lod1 */
int Lod10_11[] = {
10,11,0,0, 11,1,0,0,
11,12,1,0, 12,2,1,0,
12,13,2,0, 13,3,2,0,
13,14,3,0, 14,4,3,0,
14,15,4,0, 15,5,4,0,
15,16,5,0, 16,6,5,0,
16,17,6,0, 17,7,6,0,
17,18,7,0, 18,8,7,0,
18,19,8,0, 19,9,8,0,
19,20,9,0
};
/* 9 10 11 12 13 14 15 16 17 18 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 (Lod1 */
int Lod9_10[] = {
9,10,0,0, 10,1,0,0,
10,11,1,0, 11,2,1,0,
11,12,2,0, 12,3,2,0,
12,13,3,0, 13,4,3,0,
13,14,4,0, 14,5,4,0,
14,15,5,0, 15,6,5,0,
15,16,6,0, 16,7,6,0,
16,17,7,0, 17,8,7,0,
17,18,8,0
};
/* 8 9 10 11 12 13 14 15 16 (Lod2 */
/* 0 1 2 3 4 5 6 7 (Lod1 */
int Lod8_9[] = {
8,9,0,0, 9,1,0,0,
9,10,1,0, 10,2,1,0,
10,11,2,0, 11,3,2,0,
11,12,3,0, 12,4,3,0,
12,13,4,0, 13,5,4,0,
13,14,5,0, 14,6,5,0,
14,15,6,0, 15,7,6,0,
15,16,7,0
};
/* 7 8 9 10 11 12 13 14 (Lod2 */
/* 0 1 2 3 4 5 6 (Lod1 */
int Lod7_8[] = {
7,8,0,0, 8,1,0,0,
8,9,1,0, 9,2,1,0,
9,10,2,0, 10,3,2,0,
10,11,3,0, 11,4,3,0,
11,12,4,0, 12,5,4,0,
12,13,5,0, 13,6,5,0,
13,14,6,0
};
/* 6 7 8 9 10 11 12 (Lod2 */
/* 0 1 2 3 4 5 (Lod1 */
int Lod6_7[] = {
6,7,0,0, 7,1,0,0,
7,8,1,0, 8,2,1,0,
8,9,2,0, 9,3,2,0,
9,10,3,0, 10,4,3,0,
10,11,4,0, 11,5,4,0,
11,12,5,0
};
/* 5 6 7 8 9 10 (Lod2 */
/* 0 1 2 3 4 (Lod1 */
int Lod5_6[] = {
5,6,0,0, 6,1,0,0,
6,7,1,0, 7,2,1,0,
7,8,2,0, 8,3,2,0,
8,9,3,0, 9,4,3,0,
9,10,4,0
};
/* 4 5 6 7 8 (Lod2 */
/* 0 1 2 3 (Lod1 */
int Lod4_5[] = {
4,5,0,0, 5,1,0,0,
5,6,1,0, 6,2,1,0,
6,7,2,0, 7,3,2,0,
7,8,3,0
};
/* 3 4 5 6 (Lod2 */
/* 0 1 2 (Lod1 */
int Lod3_4[] = {
3,4,0,0, 4,1,0,0,
4,5,1,0, 5,2,1,0,
5,6,2,0
};
/* 2 3 4 (Lod2 */
/* 0 1 (Lod1 */
int Lod2_3[] = {
2,3,0,0, 3,1,0,0,
3,4,1,0
};
/*------------------------------------------------------------------------------------------------ */
/* Down two */
/* 16 17 18 19 20 21 22 23 24 25 26 27 28 29 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (Lod1 */
int Lod16_14[] = {
16,1,0,0, 16,17,1,0,
17,2,1,0, 17,18,2,0,
18,3,2,0, 18,19,3,0,
19,4,3,0, 19,20,4,0,
20,5,4,0, 20,21,5,0,
21,6,5,0, 21,22,6,0,
22,7,6,0, 22,8,7,0,
22,23,8,0, 23,9,8,0,
23,24,9,0, 24,10,9,0,
24,25,10,0, 25,11,10,0,
25,26,11,0, 26,12,11,0,
26,27,12,0, 27,13,12,0,
27,28,13,0, 28,14,13,0,
28,29,14,0, 29,15,14,0
};
/* 14 15 16 17 18 19 20 21 22 23 24 25 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 (Lod1 */
int Lod14_12[] = {
14,1,0,0, 14,15,1,0,
15,2,1,0, 15,16,2,0,
16,3,2,0, 16,17,3,0,
17,4,3,0, 17,18,4,0,
18,5,4,0, 18,19,5,0,
19,6,5,0, 19,7,6,0,
19,20,7,0, 20,8,7,0,
20,21,8,0, 21,9,8,0,
21,22,9,0, 22,10,9,0,
22,23,10,0, 23,11,10,0,
23,24,11,0, 24,12,11,0,
24,25,12,0, 25,13,12,0
};
/* 12 13 14 15 16 17 18 19 20 21 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 11 (Lod1 */
int Lod12_10[] = {
12,1,0,0, 12,13,1,0,
13,2,1,0, 13,14,2,0,
14,3,2,0, 14,15,3,0,
15,4,3,0, 15,16,4,0,
16,5,4,0, 16,6,5,0,
16,17,6,0, 17,7,6,0,
17,18,7,0, 18,8,7,0,
18,19,8,0, 19,9,8,0,
19,20,9,0, 20,10,9,0,
20,21,10,0, 21,11,10,0
};
/* 10 11 12 13 14 15 16 17 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 (Lod1 */
int Lod10_8[] = {
10,1,0,0, 10,11,1,0,
11,2,1,0, 11,12,2,0,
12,3,2,0, 12,13,3,0,
13,4,3,0, 13,5,4,0,
13,14,5,0, 14,6,5,0,
14,15,6,0, 15,7,6,0,
15,16,7,0, 16,8,7,0,
16,17,8,0, 17,9,8,0
};
/* 8 9 10 11 12 13 (Lod2 */
/* 0 1 2 3 4 5 6 7 (Lod1 */
int Lod8_6[] = {
8,1,0,0, 8,9,1,0,
9,2,1,0, 9,10,2,0,
10,3,2,0, 10,4,3,0,
10,11,4,0, 11,5,4,0,
11,12,5,0, 12,6,5,0,
12,13,6,0, 13,7,6,0
};
/* 6 7 8 9 (Lod2 */
/* 0 1 2 3 4 5 (Lod1 */
int Lod6_4[] = {
6,1,0,0, 6,7,1,0,
7,2,1,0, 7,3,2,0,
7,8,3,0, 8,4,3,0,
8,9,4,0, 9,5,4,0
};
/* 4 5 (Lod2 */
/* 0 1 2 3 (Lod1 */
int Lod4_2[] = {
4,1,0,0, 4,2,1,0,
4,5,2,0, 5,3,2,0
};
/*------------------------------------------------------------------------------------------------ */
/* Up two */
/* 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 (Lod1 */
int Lod14_16[] = {
14,15,0,0, 15,1,0,0,
15,16,1,0, 16,2,1,0,
16,17,2,0, 17,3,2,0,
17,18,3,0, 18,4,3,0,
18,19,4,0, 19,5,4,0,
19,20,5,0, 20,6,5,0,
20,21,6,0, 21,7,6,0,
21,22,7,0, 22,23,7,0,
23,8,7,0, 23,24,8,0,
24,9,8,0, 24,25,9,0,
25,10,9,0, 25,26,10,0,
26,11,10,0, 26,27,11,0,
27,12,11,0, 27,28,12,0,
28,13,12,0, 28,29,13,0
};
/* 12 13 14 15 16 17 18 19 20 21 22 23 24 25 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 10 11 (Lod1 */
int Lod12_14[] = {
12,13,0,0, 13,1,0,0,
13,14,1,0, 14,2,1,0,
14,15,2,0, 15,3,2,0,
15,16,3,0, 16,4,3,0,
16,17,4,0, 17,5,4,0,
17,18,5,0, 18,6,5,0,
18,19,6,0, 19,20,6,0,
20,7,6,0, 20,21,7,0,
21,8,7,0, 21,22,8,0,
22,9,8,0, 22,23,9,0,
23,10,9,0, 23,24,10,0,
24,11,10,0, 24,25,11,0
};
/* 10 11 12 13 14 15 16 17 18 19 20 21 (Lod2 */
/* 0 1 2 3 4 5 6 7 8 9 (Lod1 */
int Lod10_12[] = {
10,11,0,0, 11,1,0,0,
11,12,1,0, 12,2,1,0,
12,13,2,0, 13,3,2,0,
13,14,3,0, 14,4,3,0,
14,15,4,0, 15,5,4,0,
15,16,5,0, 16,17,5,0,
17,6,5,0, 17,18,6,0,
18,7,6,0, 18,19,7,0,
19,8,7,0, 19,20,8,0,
20,9,8,0, 20,21,9,0
};
/* 8 9 10 11 12 13 14 15 16 17 (Lod2 */
/* 0 1 2 3 4 5 6 7 (Lod1 */
int Lod8_10[] = {
8,9,0,0, 9,1,0,0,
9,10,1,0, 10,2,1,0,
10,11,2,0, 11,3,2,0,
11,12,3,0, 12,4,3,0,
12,13,4,0, 13,14,4,0,
14,5,4,0, 14,15,5,0,
15,6,5,0, 15,16,6,0,
16,7,6,0, 16,17,7,0
};
/* 6 7 8 9 10 11 12 13 (Lod2 */
/* 0 1 2 3 4 5 (Lod1 */
int Lod6_8[] = {
6,7,0,0, 7,1,0,0,
7,8,1,0, 8,2,1,0,
8,9,2,0, 9,3,2,0,
9,10,3,0, 10,11,3,0,
11,4,3,0, 11,12,4,0,
12,5,4,0, 12,13,5,0
};
/* 4 5 6 7 8 9 (Lod2 */
/* 0 1 2 3 (Lod1 */
int Lod4_6[] = {
4,5,0,0, 5,1,0,0,
5,6,1,0, 6,2,1,0,
6,7,2,0, 7,8,2,0,
8,3,2,0, 8,9,3,0
};
/* 2 3 4 5 (Lod2 */
/* 0 1 (Lod1 */
int Lod2_4[] = {
2,3,0,0, 3,1,0,0,
3,4,1,0, 4,5,1,0
};
WATER_TRILIST_TABLE WaterTriListTable[] = {
16,16, Lod16_16,sizeof(Lod16_16),
15,15, Lod15_15,sizeof(Lod15_15),
14,14, Lod14_14,sizeof(Lod14_14),
13,13, Lod13_13,sizeof(Lod13_13),
12,12, Lod12_12,sizeof(Lod12_12),
11,11, Lod11_11,sizeof(Lod11_11),
10,10, Lod10_10,sizeof(Lod10_10),
9,9, Lod9_9,sizeof(Lod9_9),
8,8, Lod8_8,sizeof(Lod8_8),
7,7, Lod7_7,sizeof(Lod7_7),
6,6, Lod6_6,sizeof(Lod6_6),
5,5, Lod5_5,sizeof(Lod5_5),
4,4, Lod4_4,sizeof(Lod4_4),
3,3, Lod3_3,sizeof(Lod3_3),
2,2, Lod2_2,sizeof(Lod2_2),
16,15, Lod16_15,sizeof(Lod16_15),
15,14, Lod15_14,sizeof(Lod15_14),
14,13, Lod14_13,sizeof(Lod14_13),
13,12, Lod13_12,sizeof(Lod13_12),
12,11, Lod12_11,sizeof(Lod12_11),
11,10, Lod11_10,sizeof(Lod11_10),
10,9, Lod10_9,sizeof(Lod10_9),
9,8, Lod9_8,sizeof(Lod9_8),
8,7, Lod8_7,sizeof(Lod8_7),
7,6, Lod7_6,sizeof(Lod7_6),
6,5, Lod6_5,sizeof(Lod6_5),
5,4, Lod5_4,sizeof(Lod5_4),
4,3, Lod4_3,sizeof(Lod4_3),
3,2, Lod3_2,sizeof(Lod3_2),
15,16, Lod15_16,sizeof(Lod15_16),
14,15, Lod14_15,sizeof(Lod14_15),
13,14, Lod13_14,sizeof(Lod13_14),
12,13, Lod12_13,sizeof(Lod12_13),
11,12, Lod11_12,sizeof(Lod11_12),
10,11, Lod10_11,sizeof(Lod10_11),
9,10, Lod9_10,sizeof(Lod9_10),
8,9, Lod8_9,sizeof(Lod8_9),
7,8, Lod7_8,sizeof(Lod7_8),
6,7, Lod6_7,sizeof(Lod6_7),
5,6, Lod5_6,sizeof(Lod5_6),
4,5, Lod4_5,sizeof(Lod4_5),
3,4, Lod3_4,sizeof(Lod3_4),
2,3, Lod2_3,sizeof(Lod2_3),
16,14, Lod16_14,sizeof(Lod16_14),
14,12, Lod14_12,sizeof(Lod14_12),
12,10, Lod12_10,sizeof(Lod12_10),
10,8, Lod10_8,sizeof(Lod10_8),
8,6, Lod8_6,sizeof(Lod8_6),
6,4, Lod6_4,sizeof(Lod6_4),
4,2, Lod4_2,sizeof(Lod4_2),
14,16, Lod14_16,sizeof(Lod14_16),
12,14, Lod12_14,sizeof(Lod12_14),
10,12, Lod10_12,sizeof(Lod10_12),
8,10, Lod8_10,sizeof(Lod8_10),
6,8, Lod6_8,sizeof(Lod6_8),
4,6, Lod4_6,sizeof(Lod4_6),
2,4, Lod2_4,sizeof(Lod2_4),
0,0, NULL, 0
};
static void _InitWaterTriLists()
{
int i=0,Lod1,Lod2;
while(WaterTriListTable[i].Lod1!=0)
{
Lod1 = WaterTriListTable[i].Lod1;
Lod2 = WaterTriListTable[i].Lod2;
WaterTriLists[Lod1][Lod2].pInds = WaterTriListTable[i].pInds;
WaterTriLists[Lod1][Lod2].nTris = WaterTriListTable[i].Size / (sizeof(int) * 4);
i++;
}
}
/************** TEMP LEVEL SELECTION VIA LEVEL.TXT *************/
/*
static char *_Levels[]={
"TRACKS_SHIPGRAVEYARD",
"TRACKS_AMAZON",
"TRACKS_VENICE",
"TRACKS_LAKEPOWELL",
"TRACKS_ARTIC",
"TRACKS_NILE",
"TRACKS_NY",
"TRACKS_GREECE",
"TRACKS_CHINA",
"TRACKS_TEST",
"TRACKS_LOOP1",
"TRACKS_LOOP2",
};
static char *_Boats[]={
"BOATS_TYPE_BANSHEE",
"BOATS_TYPE_TIDAL_BLADE",
"BOATS_TYPE_RAD_HAZARD",
"BOATS_TYPE_MISS_BEHAVE",
"BOATS_TYPE_DAMN_THE_TORPEDOES",
"BOATS_TYPE_CUT_THROAT",
"BOATS_TYPE_RAZORBACK",
"BOATS_TYPE_THRESHER",
"BOATS_TYPE_MIDWAY"
};
static char _FileBuffer[1024*128];
static int _FilePos;
static char _Name[128];
int Track;
int Boat;
static void _GetName()
{
int i = 0;
int c;
for(;;)
{
c = _FileBuffer[_FilePos++];
if(c==' ')
continue;
if(c==0x0d){
_FilePos++;
_Name[i++] = 0;
return;
}
_Name[i++] = c;
}
}
void HydroSetLevelAndBoat()
{
char LevelStr;
void *fh;
int i;
fh = (void*)dcOpen("level.txt", NULL);
if(fh == NULL)
return;
dcRead(_FileBuffer, dcGetFileSize(fh), fh);
dcClose(fh);
_GetName();
for(i = 0; i < 12; i++){
if(!strncmp(_Name, _Levels[i], strlen(_Levels[i])))
break;
}
if(i == 12)
Track = 0;
Track = i;
_GetName();
for(i = 0; i < 9; i++){
if(!strncmp(_Name, _Boats[i], strlen(_Boats[i])))
break;
}
if(i == 9)
Boat = 0;
Boat = i;
}
*/
#define STDOUT 1
#define STDERR 2
void HydroDCInit()
{
// Open a log file to log file reads
dcInitSystem();
_InitWaterTriLists();
dcSetViewport(256.0f, 224.0f);
}
void dcStartFileLogging(char *pLogFilename)
{
dcPrintStr("Opening Log File: %s\n", pLogFilename);
dcPCOpenForWriting(pLogFilename);
_bFileLogging = TRUE;
}
void dcStopFileLogging()
{
dcPrintStr("Closing Log file\n");
dcPCClose();
_bFileLogging = FALSE;
}
void dcLogFilename(char *pName)
{
char s[64];
if(_bFileLogging){
sprintf(s, "%s \n", pName);
dcPCWrite(s, strlen(s));
}
}
void dcSetViewport(float Height, float y)
{
dcViewportYMin = y;
dcViewportYMax = y + Height - 1.0f;
if(y > 0)
dcViewportYTrans = (dcViewportYMin + Height / 2.0f) - 32;
else
dcViewportYTrans = (dcViewportYMin + Height / 2.0f);
dcViewportH = Height;
}
#define PIXTOTILE(a) (a / 32.0f)
void dcSetViewportClipping()
{
KMSTATUS kms;
int Lines;
kmSetUserClipping(&VertexBufferDesc, KM_OPAQUE_POLYGON, 0, PIXTOTILE(dcViewportYMin), 19, PIXTOTILE(dcViewportYMax));
kmSetUserClipping(&VertexBufferDesc, KM_TRANS_POLYGON, 0, PIXTOTILE(dcViewportYMin), 19, PIXTOTILE(dcViewportYMax));
kmSetUserClipping(&VertexBufferDesc, KM_PUNCHTHROUGH_POLYGON, 0, PIXTOTILE(dcViewportYMin), 19, PIXTOTILE(dcViewportYMax));
}
#if 0
int write (int file, char *ptr, int len)
{
return 0;
}
#endif
waterfall.c#
Found at 0x1697F650.
/*////////////////////////////////////////////////////////////////////////////////////*/
/* waterfall.c - Waterfall generator (extension of water.c).*/
/**/
/* Author: Steve Ranck */
/*////////////////////////////////////////////////////////////////////////////////////*/
/* THIS CODE IS PROPRIETARY PROPERTY OF MIDWAY HOME ENTERTAINMENT.*/
/* Copyright (c) 1997*/
/**/
/* The contents of this file may not be disclosed to third*/
/* parties, copied or duplicated in any form, in whole or in part,*/
/* without the prior written permission of Midway Home Entertainment.*/
/*////////////////////////////////////////////////////////////////////////////////////*/
/* Modification History:*/
/**/
/* Date Who Description*/
/* -------- ---------- --------------------------------------------------------------*/
/* 11/24/97 Ranck Created.*/
/*////////////////////////////////////////////////////////////////////////////////////*/
#include "gendefs.h"
#include "waterfall.h"
#include "world.h"
#include "vec2.h"
#include "vec3.h"
#include "xfm.h"
#include "gutil.h"
#include "viewport.h"
#include "mesh3d.h"
#include "material.h"
#include "glide.h"
#include "xmath.h"
#include "tmem.h"
#include "gfxdefs.h"
#include "glcount.h"
#include "mesh3d.h"
#include "race.h"
#if !SYS_WINLIB_TARGET
#include "audio.h"
#include "soundcall.h"
#endif
#define _MAX_WATERFALLS 25 /* Maximum waterfall instances*/
#define _SHAPE_DEF_SPEED 40.0f /* Worldspace feet/second*/
#define _GRAVITY_FEET_PER_SECSEC 32.0f /* Gravity*/
#define _PANEL_Z_LENGTH 15.0f
#define _PANEL_TARGET_X_WIDTH 60.0f
#define _MAX_TILES_ACROSS 20
#define _TARGET_SECONDS_PER_FRAME (1.0f/30.0f)
#define _EDGEBEND_PERCENT_TO_END 0.20f
#define _EDGEBEND_MULTIPLIER 30.0f
#define _FLAIR_PERCENT_TO_END 0.50f
#define _FLAIR_MAX 30.0f
#define _ST_DISTORT_AMPLITUDE 40.0f
#define _TEX_S_TILE_DELTA 256.0f
#if !WATERFALL_CALIBRATION_TOOL_ENABLED
#define _WHITESAT_PERCENT_TO_END 0.34f
#define _WHITESAT_MULTIPLIER 220.0f
#define _TEX_T_SCROLL_DELTA1 12.91f
#define _TEX_T_SCROLL_DELTA2 20.55f
#define _TEX_T_TILE_DELTA1 -79.63f
#define _TEX_T_TILE_DELTA2 -66.02f
#define _TEX_S_RIGHTEDGE_DELTA -10.0f
#else
float _WHITESAT_PERCENT_TO_END;
float _WHITESAT_MULTIPLIER;
float _TEX_T_SCROLL_DELTA1;
float _TEX_T_SCROLL_DELTA2;
float _TEX_T_TILE_DELTA1;
float _TEX_T_TILE_DELTA2;
float _TEX_S_RIGHTEDGE_DELTA;
#endif
#define _MIST_MAX_ACROSS 100
#define _MIST_GAP_X 50.0f
#define _MIST_FADEIN_UNITPERC 0.10f
#define _MIST_FADEOUT_UNITPERC 0.60f
#define _MIST_DISTORT_AMPLITUDE 10.0f
#define _MIST_VTX_X 0.75f
#define _MIST_VTX_Y 0.5f
#define _MIST_LIFE_LOG2 5
#define _MIST_COUNT 2
#define _MIST_TRAVEL_Y_UNITPERC 0.20f
#define _MIST_TRAVEL_Z -15.0f
#define _MIST_TRAVEL_ATTEN 0.2f
#define _MIST_MIN_SIZE 0.20f
#define _MIST_MAX_SIZE 0.40f
#define _MIST_START_DELTA_Z -5.0f
#define _CAMSPACE_ITERATOR_COUNT 10
#define _MAX_WATERFALL_SOUND_DIST 1000.0f
#define _MAX_WATERFALL_SOUND_DIST2 (_MAX_WATERFALL_SOUND_DIST*_MAX_WATERFALL_SOUND_DIST)
#define _MAX_WATERFALL_SOUND_VOL 255.0f
#define _MAX_WATERFALL_SOUND_PAN 145.0f /* 0=full-time main, 128=up to 50/50 mix, 255=up to 100% woofer*/
#define _WATERFALL_SOUNDCALL SOUNDCALL_1ST_WATERFALL_NEAR /*SOUNDCALL_1ST_WATERFALL_CLOSE*/
typedef struct {
u32 nGameframeDrawn; /* The game frame this waterfall was last drawn*/
Xfm_t Xfm; /* Transformation matrix from model to world space*/
Tws_t Tws; /* Waterfall's tracking world sphere*/
Mesh3dBound_t Bound; /* 8-point bounding volume*/
Vec2_t FlowVelocity; /* Model space: [0]=y, [1]=z*/
float fDecentSecs; /* Time it takes water to decend from top to bottom*/
BOOL8 bUsedSlot; /* TRUE: This entry used, FALSE=unused*/
u8 nTileCountAcross; /* Number of horizonal tiles*/
u16 nTileCountDown; /* Number of vertical tiles*/
float fTileWidthX; /* Tile width (x directin), feet*/
float fLoopSecs; /* Waterfall animation cycle time*/
float fScrollT1; /* Scrolling texture T coordinate*/
float fScrollT2; /* Scrolling texture T coordinate*/
float fOneOverHeight; /* 1/fHeight*/
float fUnitPercIntoSector; /* Unit percentage into the sector that the TWS center lies in*/
float fLowerMistStartZ; /* Modelspace mist start z coordinate*/
TexDef_t *apTexDef[WATERFALL_TEXTURE_COUNT];
Vec3_t WorldOrigin; /* Origin in world space*/
Vec3_t WorldFlowUnitDir; /* Flow unit direction in world space*/
float fWidth; /* Width*/
float fHeight; /* Height*/
WaterfallFlags_t nFlags; /* See WaterfallFlags_t for info*/
} WaterfallDef_t;
typedef struct {
Vec3_t ModelspaceVtx;
float afCamspaceVtx[_CAMSPACE_ITERATOR_COUNT]; /* x, y, z, r, g, b, a, s, t1, t2*/
GrVertex ScreenspaceVtx;
u32 anOutsidePlaneCode[4];
BOOL bScreenspaceVtxValid;
BOOL bSensedLight;
u32 Specular;
} TileVtx_t;
#define _OUTCODE_UNKNOWN 0
#define _OUTCODE_OUTSIDE 1
#define _OUTCODE_INSIDE 2
static TexDef_t *_apDefaultTexDef[WATERFALL_TEXTURE_COUNT];
static WaterfallDef_t _aWaterfallDefs[_MAX_WATERFALLS];
static TileVtx_t _aTileGrVtx[2][_MAX_TILES_ACROSS+1];
static int _nBoundCrossesPlanesMask;
static int _nTriCrossesPlanesMask;
static u32 _nLightFrame;
static float _fWhiteSat;
static int _nWhiteSat;
static float _afRandomFloatTable[_MIST_MAX_ACROSS];
static TileVtx_t _aTileClippedVtx[40], *_apTileClippedVtx[40]; /* Frustum clipping*/
static BOOL _bPlayingWaterfallSound;
static ARGBf_t _MistColor;
static u32 _nMistDrawListCount;
static WaterfallDef_t *_apMistDrawList[_MAX_WATERFALLS];
static Xfm_t _XfmPosterY;
static Xfm_t _XfmMistPos;
static TileVtx_t _aMistTileVtx[4];
static TileVtx_t *_apMistTileVtx[4] = {
&_aMistTileVtx[0], &_aMistTileVtx[1], &_aMistTileVtx[2], &_aMistTileVtx[3]
};
static const Vec2_t _aMistVtx[4] = {
_MIST_VTX_X, -_MIST_VTX_Y, /* lower-right corner*/
_MIST_VTX_X, _MIST_VTX_Y, /* upper-right corner*/
-_MIST_VTX_X, -_MIST_VTX_Y, /* lower-left corner*/
-_MIST_VTX_X, _MIST_VTX_Y, /* upper-left corner*/
};
static const Vec2_t _aMistSt[4] = {
256.0f, 0.0f, /* lower-right corner*/
256.0f, 256.0f, /* upper-right corner*/
0.0f, 0.0f, /* lower-left corner*/
0.0f, 256.0f, /* upper-left corner*/
};
static void _InitWaterfall( WaterfallDef_t *pWfDef,
const Vec3_t *pOrigin, float fWidth, float fHeight,
const Vec3_t *pFlowUnitDir, WaterfallFlags_t nFlags );
static void _DrawWaterfall( WaterfallDef_t *pWfDef );
static BOOL _TestTriAgainstFrustum( TileVtx_t **ppTileVtx );
static void _ClipTriToFrustumAndDraw( TileVtx_t **ppTileVtx, BOOL bTestForScreenCoverage );
static void _BuildLightList( WaterfallDef_t *pWfDef );
static void _DrawMist( WaterfallDef_t *pWfDef );
#if MESH3D_TRI_HIGHLIGHT_CODE
static void _DrawConvexPolyWireframe( int nNumVerts, TileVtx_t *apVtxCache[] );
#endif
static void _CalcWaterfallLighting(WaterfallDef_t *pWfDef);
BOOL waterfall_ModuleInit( void ) {
u32 i;
for( i=0; i<_MAX_WATERFALLS; i++ ) {
_aWaterfallDefs[i].bUsedSlot = FALSE;
}
_nMistDrawListCount = 0;
_bPlayingWaterfallSound = FALSE;
xfm_Identity( &_XfmPosterY );
_XfmPosterY.type = XFM_TYPE_R;
xfm_Identity( &_XfmMistPos );
for( i=0; i<_MIST_MAX_ACROSS; i++ ) {
_afRandomFloatTable[i] = 2.0f*xmath_RandomFloat() - 1.0f;
}
for( i=0; i<WATERFALL_TEXTURE_COUNT; i++ ) {
_apDefaultTexDef[i] = NULL;
}
waterfall_SetWhiteSat( 0.0f );
return TRUE;
}
/* Returns the current white saturation level (0=none, 1=fully white).*/
float waterfall_GetWhiteSat( void ) {
return _fWhiteSat;
}
/* Sets the current white saturation level (0=none, 1=fully white).*/
void waterfall_SetWhiteSat( float fWhiteSat ) {
XASSERT_UNIT_FLOAT( fWhiteSat );
_fWhiteSat = fWhiteSat;
_nWhiteSat = (int)( fWhiteSat * 255.0f );
}
/* Allocates and initializes a waterfall object. After this call, the engine*/
/* will automatically attach the waterfall to the world, display it and animate it.*/
/**/
/* pOrigin, fHeight, fWidth, and pFlowUnitDir are in world space. The origin of*/
/* the waterfall is the midpoint of the emission line (the upper line from where*/
/* the waterfall starts). fWidth is the total width of the waterfall at the*/
/* emission line. fHeight is the total height of the waterfall, from the emission*/
/* line to the bottom. Both fWidth and fHeight must be positive. pFlowUnitDir*/
/* indicates the initial direction that the water flows from the origin. It must*/
/* be unit and must not point directly up or directly down. */
/**/
/* Returns a non-zero handle that may be used in other waterfall functions.*/
/* If there is no available resources for the waterfall, or if an illegal parameter*/
/* is passed in, this function returns NULL.*/
WaterfallHandle_t waterfall_InitInstance( const Vec3_t *pOrigin, float fWidth, float fHeight,
const Vec3_t *pFlowUnitDir, WaterfallFlags_t nFlags ) {
WaterfallDef_t *pWfDef;
u32 i, j;
if( fWidth<=0.0f || fHeight<=0.0f ) {
return NULL;
}
/* Find empty waterfall definition slot...*/
for( i=0; i<_MAX_WATERFALLS; i++ ) {
pWfDef = &_aWaterfallDefs[i];
if( !pWfDef->bUsedSlot ) {
/* Found empty slot...*/
pWfDef->bUsedSlot = TRUE;
for( j=0; j<WATERFALL_TEXTURE_COUNT; j++ ) {
pWfDef->apTexDef[j] = _apDefaultTexDef[j];
}
_InitWaterfall( pWfDef, pOrigin, fWidth, fHeight, pFlowUnitDir, nFlags );
return (WaterfallHandle_t)pWfDef;
}
}
/* No free waterfall definition slot...*/
return NULL;
}
/* Specifies the default textures for the waterfall system.*/
void waterfall_SpecifyDefaultTextures( TexDef_t **ppTexDef, u32 nTextureCount ) {
u32 i;
for( i=0; i<WATERFALL_TEXTURE_COUNT; i++ ) {
_apDefaultTexDef[i] = NULL;
}
if( nTextureCount == 0 ) {
/* Exit if no textures specified...*/
return;
}
if( ppTexDef[WATERFALL_TEXINDEX_MAIN] ) {
_apDefaultTexDef[WATERFALL_TEXINDEX_MAIN] = ppTexDef[WATERFALL_TEXINDEX_MAIN];
if( nTextureCount > WATERFALL_TEXINDEX_LEFT ) {
_apDefaultTexDef[WATERFALL_TEXINDEX_LEFT] = ppTexDef[WATERFALL_TEXINDEX_LEFT]
? ppTexDef[WATERFALL_TEXINDEX_LEFT]
: ppTexDef[WATERFALL_TEXINDEX_MAIN];
}
if( nTextureCount > WATERFALL_TEXINDEX_RIGHT ) {
_apDefaultTexDef[WATERFALL_TEXINDEX_RIGHT] = ppTexDef[WATERFALL_TEXINDEX_RIGHT]
? ppTexDef[WATERFALL_TEXINDEX_RIGHT]
: ppTexDef[WATERFALL_TEXINDEX_MAIN];
}
}
if( nTextureCount > WATERFALL_TEXINDEX_MIST ) {
_apDefaultTexDef[WATERFALL_TEXINDEX_MIST] = ppTexDef[WATERFALL_TEXINDEX_MIST];
}
}
/* Specifies the textures for the waterfall object.*/
void waterfall_SpecifyTextures( WaterfallHandle_t Handle,
TexDef_t **ppTexDef, u32 nTextureCount ) {
WaterfallDef_t *pWfDef;
u32 i;
if( Handle ) {
pWfDef = (WaterfallDef_t *)Handle;
for( i=0; i<WATERFALL_TEXTURE_COUNT; i++ ) {
pWfDef->apTexDef[i] = NULL;
}
if( nTextureCount == 0 ) {
/* Exit if no textures specified...*/
return;
}
if( ppTexDef[WATERFALL_TEXINDEX_MAIN] ) {
pWfDef->apTexDef[WATERFALL_TEXINDEX_MAIN] = ppTexDef[WATERFALL_TEXINDEX_MAIN];
if( nTextureCount > WATERFALL_TEXINDEX_LEFT ) {
pWfDef->apTexDef[WATERFALL_TEXINDEX_LEFT] = ppTexDef[WATERFALL_TEXINDEX_LEFT]
? ppTexDef[WATERFALL_TEXINDEX_LEFT]
: ppTexDef[WATERFALL_TEXINDEX_MAIN];
}
if( nTextureCount > WATERFALL_TEXINDEX_RIGHT ) {
pWfDef->apTexDef[WATERFALL_TEXINDEX_RIGHT] = ppTexDef[WATERFALL_TEXINDEX_RIGHT]
? ppTexDef[WATERFALL_TEXINDEX_RIGHT]
: ppTexDef[WATERFALL_TEXINDEX_MAIN];
}
}
if( nTextureCount > WATERFALL_TEXINDEX_MIST ) {
pWfDef->apTexDef[WATERFALL_TEXINDEX_MIST] = ppTexDef[WATERFALL_TEXINDEX_MIST];
}
}
}
/* Enables or disables drawing of the specified waterfall. Returns the*/
/* prior state. Simply returns FALSE if a NULL handle is passed in.*/
BOOL waterfall_EnableDraw( WaterfallHandle_t Handle, BOOL bEnableDraw ) {
WaterfallDef_t *pWfDef;
if( Handle ) {
pWfDef = (WaterfallDef_t *)Handle;
if( pWfDef->nFlags & WATERFALL_FLAG_DONT_DRAW ) {
/* Waterfall is currently flagged to not draw...*/
if( bEnableDraw ) {
pWfDef->nFlags &= ~WATERFALL_FLAG_DONT_DRAW;
}
return FALSE;
} else {
/* Waterfall is currently flagged to draw...*/
if( !bEnableDraw ) {
pWfDef->nFlags |= ~WATERFALL_FLAG_DONT_DRAW;
}
return TRUE;
}
}
return FALSE;
}
/* Deletes the specified waterfall from the world.*/
/* Simply returns if passed a NULL handle.*/
void waterfall_Delete( WaterfallHandle_t Handle ) {
WaterfallDef_t *pWfDef;
if( Handle ) {
pWfDef = (WaterfallDef_t *)Handle;
tws_DeleteIntersectListFromTws( &pWfDef->Tws );
pWfDef->bUsedSlot = FALSE;
}
}
/* Deletes all waterfall objects.*/
void waterfall_DeleteAll( void ) {
WaterfallDef_t *pWfDef;
u32 i;
for( i=0; i<_MAX_WATERFALLS; i++ ) {
pWfDef = &_aWaterfallDefs[i];
if( pWfDef->bUsedSlot ) {
waterfall_Delete( (WaterfallHandle_t)pWfDef );
}
}
}
/* Updates the waterfall sound.*/
void waterfall_UpdateSound( Vec3_t *pWorldspaceEarPoint ) {
#if !SYS_WINLIB_TARGET
WaterfallDef_t *pWfDef;
u32 i;
float fDx, fDz, fDist2, fClosestDist2, fTemp1, fTemp2, fTemp3;
float fUnitIntensity, fUnitVolume, fUnitPanToWoofer;
BOOL bNoSound;
bNoSound = TRUE;
fClosestDist2 = -1.0f;
for( i=0; i<_MAX_WATERFALLS; i++ ) {
pWfDef = &_aWaterfallDefs[i];
if( pWfDef->bUsedSlot && !(pWfDef->nFlags & WATERFALL_FLAG_NO_SOUND)) {
/* Found a waterfall that makes sound...*/
fDx = pWfDef->WorldOrigin.p[0] - pWorldspaceEarPoint->p[0];
fDz = pWfDef->WorldOrigin.p[2] - pWorldspaceEarPoint->p[2];
fDist2 = fDx*fDx + fDz*fDz;
if( fDist2 <= _MAX_WATERFALL_SOUND_DIST2 ) {
/* Waterfall is within hearing distance...*/
if( fDist2<fClosestDist2 || fClosestDist2==-1 ) {
/* This waterfall is the closest we've found...*/
fClosestDist2 = fDist2;
}
}
}
}
if( fClosestDist2 >= 0.0f ) {
/* We've found the closest waterfall that makes sound...*/
fUnitIntensity = (_MAX_WATERFALL_SOUND_DIST - xmath_sqrt( fClosestDist2 )) * (1.0f/_MAX_WATERFALL_SOUND_DIST);
/* Generate a linear curve for the volume...*/
fUnitVolume = (_MAX_WATERFALL_SOUND_DIST2 - fClosestDist2) * (1.0f/_MAX_WATERFALL_SOUND_DIST2);
if( fUnitIntensity > 0.0f ) {
/* Generate a nice curve for the panning...*/
fTemp1 = 1.24f * fUnitIntensity;
fTemp2 = fTemp1 * fTemp1 * fTemp1 * fTemp1;
fTemp3 = fTemp1 * fTemp2 * fTemp2 + 1.0f;
fTemp1 = 1.29f * fUnitIntensity;
fTemp2 = fTemp1 * fTemp1 * fTemp1 * fTemp1;
fUnitPanToWoofer = fTemp2 * fTemp2 / fTemp3;
fUnitVolume = fUnitVolume*0.5f + fUnitPanToWoofer;
XMATH_CLAMPMAX( fUnitVolume, 1.0f );
if( _bPlayingWaterfallSound ) {
audio_GameIDVolume( _WATERFALL_SOUNDCALL, (u32)(_MAX_WATERFALL_SOUND_VOL * fUnitVolume) );
audio_GameIDPan( _WATERFALL_SOUNDCALL, (u32)(_MAX_WATERFALL_SOUND_PAN * fUnitPanToWoofer) );
} else {
audio_Trigger( _WATERFALL_SOUNDCALL,
(u32)(_MAX_WATERFALL_SOUND_VOL * fUnitVolume),
(u32)(_MAX_WATERFALL_SOUND_PAN * fUnitPanToWoofer),
AUDIO_TYPE_WATERFALL,
AUDIO_PRIORITY_WATERFALL );
_bPlayingWaterfallSound = TRUE;
}
bNoSound = FALSE;
}
}
if( bNoSound && _bPlayingWaterfallSound ) {
audio_GameIDStop( _WATERFALL_SOUNDCALL );
_bPlayingWaterfallSound = FALSE;
}
#endif
}
/* Stops all waterfall sound effects.*/
void waterfall_KillAllSound( void ) {
#if !SYS_WINLIB_TARGET
if( _bPlayingWaterfallSound ) {
audio_GameIDStop( _WATERFALL_SOUNDCALL );
_bPlayingWaterfallSound = FALSE;
}
#endif
}
/* Draws the specified waterfall.*/
void waterfall_Draw( WaterfallHandle_t Handle ) {
WaterfallDef_t *pWfDef;
if( Handle ) {
pWfDef = (WaterfallDef_t *)Handle;
_CalcWaterfallLighting(pWfDef);
if( pWfDef->bUsedSlot && !(pWfDef->nFlags & WATERFALL_FLAG_DONT_DRAW) ) {
/* Valid waterfall object...*/
if( pWfDef->nGameframeDrawn != Gameloop_n2xPausedFrameCounter) { //PAB
pWfDef->nGameframeDrawn = Gameloop_n2xPausedFrameCounter; //PAB
xfm_Push( &pWfDef->Xfm );
_nBoundCrossesPlanesMask = mesh3d_TestBoundWithFrustum( &pWfDef->Bound, 0xf );
if( _nBoundCrossesPlanesMask != -1 ) {
/* Waterfall must be drawn...*/
_DrawWaterfall( pWfDef );
if( !(pWfDef->nFlags & WATERFALL_FLAG_NO_MIST) ) {
_apMistDrawList[_nMistDrawListCount++] = pWfDef;
}
}
xfm_Pop();
}
}
}
}
/* Draws all mist created by waterfalls.*/
#if TARGET!=DREAMCAST
void waterfall_DrawWaterfallMist( void ) {
u32 i, nPriorZbufState, nLightFrame;
float fIntensity;
WaterfallDef_t *pWfDef;
WorldSector_t *pSector;
static const Vec3_t Normal = { 0.0f, 0.707107f, -0.707107f };
static const Vec3_t Pos = { 0.0f, 0.0f, 0.0f };
if( _nMistDrawListCount ) {
/* Compare, but don't update zbuffer...*/
nPriorZbufState = gutil_ZbufferSetState( GUTIL_ZBUFFER_STATE_COMPARE_ONLY );
/* Set graphics modes...*/
material_PushState();
material_Defaults();
material_AlphaTestReferenceValue( 0 );
grDitherMode( GR_DITHER_2x2 );
for( i=0; i<_nMistDrawListCount; i++ ) {
pWfDef = _apMistDrawList[i];
if( pWfDef->apTexDef[WATERFALL_TEXINDEX_MIST] && !(pWfDef->nFlags & WATERFALL_FLAG_DONT_DRAW) ) {
/* Texture specified...*/
material_ColorCombine(
GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL,
GR_COMBINE_FACTOR_ONE,
GR_COMBINE_LOCAL_CONSTANT,
GR_COMBINE_OTHER_ITERATED,
FXFALSE
);
material_AlphaCombine(
GR_COMBINE_FUNCTION_SCALE_OTHER,
GR_COMBINE_FACTOR_LOCAL,
GR_COMBINE_LOCAL_ITERATED,
GR_COMBINE_OTHER_TEXTURE,
FXFALSE
);
material_ConstantColorValue( GFXDEFS_INTARGB_TO_GRCOLOR( 0, _nWhiteSat, _nWhiteSat, _nWhiteSat ) );
tmem_Select( pWfDef->apTexDef[WATERFALL_TEXINDEX_MIST] );
tmem_SetTileMode( pWfDef->apTexDef[WATERFALL_TEXINDEX_MIST], FALSE, FALSE );
pSector = pWfDef->Tws.pCenterSector;
if( pSector ) {
nLightFrame = mesh3d_StartLightFrame();
/*mesh3d_ClearLightFrame();*/
xfm_Push( &pWfDef->Xfm );
world_BuildObjectAmbient( pSector->pSectorCache, pWfDef->fUnitPercIntoSector );
world_BuildObjectCelestial( pSector->pSectorCache, pWfDef->fUnitPercIntoSector );
mesh3d_LightSensor( &Pos, &Normal, &_MistColor, FALSE );
fIntensity = 255.0f * gutil_RgbToIntensity( _MistColor.argb[2], _MistColor.argb[1], _MistColor.argb[0] );
fIntensity = XMATH_FPOT( 0.35f, fIntensity, 255.0f );
_MistColor.argb[2] = fIntensity;
_MistColor.argb[1] = fIntensity;
_MistColor.argb[0] = fIntensity;
_DrawMist( pWfDef );
xfm_Pop();
mesh3d_ReleaseLightFrame( nLightFrame );
}
}
}
/* Restore graphics state...*/
grDitherMode( GR_DITHER_4x4 );
material_PopState(1);
/* Restore state and reset buffer index...*/
gutil_ZbufferSetState( nPriorZbufState );
_nMistDrawListCount = 0;
}
}
#endif
static void _InitWaterfall( WaterfallDef_t *pWfDef,
const Vec3_t *pOrigin, float fWidth, float fHeight,
const Vec3_t *pFlowUnitDir, WaterfallFlags_t nFlags ) {
Vec2_t FlowUnitDirXZ, UpperLineUnitVec, BoundCenter, TwsRadiusVec, UpperEndPoint;
Vec3_t FrustumRadiusVec;
Mesh3dBound_t *pBound;
float fLength, fDecentSecs, fFrustumHeight, fFrustumWidth, fFrustumLength, fTime;
Xfm_t *pXfm;
Tws_t *pTws;
/* Init simple waterfall fields...*/
pWfDef->nGameframeDrawn = Gameloop_n2xFrameCounter - 2; //PAB (was -1)
vec3_Copy( &pWfDef->WorldOrigin, pOrigin );
vec3_Copy( &pWfDef->WorldFlowUnitDir, pFlowUnitDir );
pWfDef->fWidth = fWidth;
pWfDef->fHeight = fHeight;
pWfDef->fOneOverHeight = 1.0f / fHeight;
pWfDef->nFlags = nFlags;
/* Build needed vectors...*/
FlowUnitDirXZ.p[0] = pFlowUnitDir->p[0];
FlowUnitDirXZ.p[1] = pFlowUnitDir->p[2];
vec2_Unit( &FlowUnitDirXZ );
UpperLineUnitVec.p[0] = -FlowUnitDirXZ.p[1];
UpperLineUnitVec.p[1] = FlowUnitDirXZ.p[0];
/* Build model-to-world transformation matrix...*/
pXfm = &pWfDef->Xfm;
xfm_Identity( pXfm );
pXfm->mf[0][0] = pXfm->mr[0][0] = UpperLineUnitVec.p[0];
pXfm->mf[2][0] = pXfm->mr[0][2] = -UpperLineUnitVec.p[1];
pXfm->mf[0][2] = pXfm->mr[2][0] = UpperLineUnitVec.p[1];
pXfm->mf[2][2] = pXfm->mr[2][2] = UpperLineUnitVec.p[0];
pXfm->mf[3][0] = pOrigin->p[0];
pXfm->mr[3][0] = pXfm->mf[2][0]*pOrigin->p[2] - pXfm->mf[0][0]*pOrigin->p[0];
pXfm->mf[3][1] = pOrigin->p[1];
pXfm->mr[3][1] = -pOrigin->p[1];
pXfm->mf[3][2] = pOrigin->p[2];
pXfm->mr[3][2] = pXfm->mf[0][2]*pOrigin->p[0] - pXfm->mf[0][0]*pOrigin->p[2];
pXfm->type = XFM_TYPE_GEN;
/* Compute flow velocity in YZ model space...*/
pWfDef->FlowVelocity.p[0] = pXfm->mr[0][1]*pFlowUnitDir->p[0] + pXfm->mr[1][1]*pFlowUnitDir->p[1] + pXfm->mr[2][1]*pFlowUnitDir->p[2];
pWfDef->FlowVelocity.p[1] = pXfm->mr[0][2]*pFlowUnitDir->p[0] + pXfm->mr[1][2]*pFlowUnitDir->p[1] + pXfm->mr[2][2]*pFlowUnitDir->p[2];
vec2_Unit( &pWfDef->FlowVelocity );
vec2_Scale1( &pWfDef->FlowVelocity, _SHAPE_DEF_SPEED );
/* Compute time it takes for water to decend from top to bottom...*/
fDecentSecs = ( xmath_sqrt( pWfDef->FlowVelocity.p[0]*pWfDef->FlowVelocity.p[0] + _GRAVITY_FEET_PER_SECSEC*2.0f*fHeight ) + pWfDef->FlowVelocity.p[0] ) * (1.0f / _GRAVITY_FEET_PER_SECSEC);
pWfDef->fDecentSecs = fDecentSecs;
/* Compute waterfall length...*/
fLength = -pWfDef->FlowVelocity.p[1]*fDecentSecs;
/* Init TWS...*/
pTws = &pWfDef->Tws;
tws_Init( pTws, pWfDef, WORLD_INTERSECT_TYPE_WATERFALL );
vec2_CalcScale1( &BoundCenter, &FlowUnitDirXZ, fLength*0.5f );
pTws->CurrentWorldPos.p[0] = pOrigin->p[0] + BoundCenter.p[0];
pTws->CurrentWorldPos.p[1] = pOrigin->p[2] + BoundCenter.p[1];
vec2_CalcScale1( &UpperEndPoint, &UpperLineUnitVec, fWidth*0.5f );
UpperEndPoint.p[0] += pOrigin->p[0];
UpperEndPoint.p[1] += pOrigin->p[2];
TwsRadiusVec.p[0] = UpperEndPoint.p[0] - pTws->CurrentWorldPos.p[0];
TwsRadiusVec.p[1] = UpperEndPoint.p[1] - pTws->CurrentWorldPos.p[1];
pTws->fCurrentRadius = vec2_CalcMag( &TwsRadiusVec );
world_BuildTwsIntersectList( pTws );
/* Compute flow characteristics...*/
pWfDef->fLoopSecs = (-_PANEL_Z_LENGTH) / pWfDef->FlowVelocity.p[1];
pWfDef->nTileCountAcross = (u32)(fWidth * (1.0f / _PANEL_TARGET_X_WIDTH)) + 1;
if( pWfDef->nTileCountAcross < 2 ) {
pWfDef->nTileCountAcross = 2;
}
if( pWfDef->nTileCountAcross > _MAX_TILES_ACROSS ) {
pWfDef->nTileCountAcross = _MAX_TILES_ACROSS;
xprintferr( "WARNING: Waterfall too wide. Truncated.\n" );
}
pWfDef->fTileWidthX = fWidth / pWfDef->nTileCountAcross;
pWfDef->nTileCountDown = (u32)(fDecentSecs / pWfDef->fLoopSecs) + 2;
pWfDef->fScrollT1 = 0.0f;
pWfDef->fScrollT2 = 0.0f;
/* Compute frustum width and height...*/
fTime = pWfDef->fLoopSecs * (float)(pWfDef->nTileCountDown+1);
fFrustumHeight = -(pWfDef->FlowVelocity.p[0]*fTime - (_GRAVITY_FEET_PER_SECSEC*0.5f)*fTime*fTime);
fFrustumLength = (float)(pWfDef->nTileCountDown+1) * _PANEL_Z_LENGTH;
if( nFlags & WATERFALL_FLAG_FLAIR_EDGES ) {
fFrustumWidth = fWidth + _FLAIR_MAX;
} else {
fFrustumWidth = fWidth;
}
/* Init frustum bounds (model space)...*/
pBound = &pWfDef->Bound;
pBound->BoundCenter.p[0] = 0.0f;
pBound->BoundCenter.p[1] = fFrustumHeight * -0.5f;
pBound->BoundCenter.p[2] = fFrustumLength * -0.5f;
FrustumRadiusVec.p[0] = pBound->BoundCenter.p[0] - fFrustumWidth*0.5f;
FrustumRadiusVec.p[1] = pBound->BoundCenter.p[1];
FrustumRadiusVec.p[2] = pBound->BoundCenter.p[2];
pBound->fBoundRadius = vec3_CalcMag( &FrustumRadiusVec );
pBound->nNumBoundPoints = 8;
pBound->aBoundPoints[0].p[0] = fFrustumWidth * -0.5f;
pBound->aBoundPoints[0].p[1] = 0.0f;
pBound->aBoundPoints[0].p[2] = -fFrustumLength;
pBound->aBoundPoints[1].p[0] = fFrustumWidth * -0.5f;
pBound->aBoundPoints[1].p[1] = 0.0f;
pBound->aBoundPoints[1].p[2] = 0.0f;
pBound->aBoundPoints[2].p[0] = fFrustumWidth * 0.5f;
pBound->aBoundPoints[2].p[1] = 0.0f;
pBound->aBoundPoints[2].p[2] = 0.0f;
pBound->aBoundPoints[3].p[0] = fFrustumWidth * 0.5f;
pBound->aBoundPoints[3].p[1] = 0.0f;
pBound->aBoundPoints[3].p[2] = -fFrustumLength;
pBound->aBoundPoints[4].p[0] = fFrustumWidth * -0.5f;
pBound->aBoundPoints[4].p[1] = -fFrustumHeight;
pBound->aBoundPoints[4].p[2] = -fFrustumLength;
pBound->aBoundPoints[5].p[0] = fFrustumWidth * -0.5f;
pBound->aBoundPoints[5].p[1] = -fFrustumHeight;
pBound->aBoundPoints[5].p[2] = 0.0f;
pBound->aBoundPoints[6].p[0] = fFrustumWidth * 0.5f;
pBound->aBoundPoints[6].p[1] = -fFrustumHeight;
pBound->aBoundPoints[6].p[2] = 0.0f;
pBound->aBoundPoints[7].p[0] = fFrustumWidth * 0.5f;
pBound->aBoundPoints[7].p[1] = -fFrustumHeight;
pBound->aBoundPoints[7].p[2] = -fFrustumLength;
}
#if TARGET!=ULTRA64 && TARGET!=DREAMCAST /* New waterfall drawing code */
static void _DrawWaterfall( WaterfallDef_t *pWfDef ) {
float fTime, fLoopSecs, fVelY, fVelZ, fStartX, fEndX, q, oow;
float *pfCamspaceVtx, fClipRatio, fTexS, fTexT1, fTexT2, fWhiteSat, fOneOverHeight;
float fUnitDuration, fStartTexT1, fStartTexT2, fTemp, fPrevY, fPrevZ, fDy, fDz, fEdgeBend;
float fTileWidthX, fAnimateK1, fAnimateK2, fFlairAmount;
u32 nTileTopVtxArrayIndex, nTileCountAcross, nTileCountDown;
u32 nVtxCountDown, nVtxCountAcross;
u32 i, j, k, n;
s32 nAnimateAngle;
Vec3_t ModelspacePoint, BentModelspacePoint, *pModelspaceVtx, Normal;
GrVertex *pScreenspaceVtx;
TileVtx_t *pTileVtx, *apTileVtx[4];
ARGBf_t SensedColor;
BOOL bFlairEdges;
/* Set graphics modes...*/
_nLightFrame = mesh3d_StartLightFrame();
/*mesh3d_ClearLightFrame();*/
material_PushState();
material_Defaults();
grHints( GR_HINT_STWHINT, GR_STWHINT_ST_DIFF_TMU1 );
if( pWfDef->apTexDef[WATERFALL_TEXINDEX_MAIN] == NULL ) {
/* No texture specified...*/
MATERIAL_COMBINE_CONSTANT_COLOR;
material_ConstantColorValue( GFXDEFS_INTARGB_TO_GRCOLOR( 255, 0, 0, 255 ) );
} else {
/* Texture specified...*/
material_ColorCombine(
GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL_ALPHA,
GR_COMBINE_FACTOR_LOCAL,
GR_COMBINE_LOCAL_ITERATED,
GR_COMBINE_OTHER_TEXTURE,
FXFALSE
);
material_AlphaCombine(
GR_COMBINE_FUNCTION_SCALE_OTHER,
GR_COMBINE_FACTOR_ONE,
GR_COMBINE_LOCAL_ITERATED,
GR_COMBINE_OTHER_TEXTURE,
FXFALSE
);
tmem_SelectTextureTwins( pWfDef->apTexDef[WATERFALL_TEXINDEX_MAIN] );
tmem_SetTileMode( pWfDef->apTexDef[WATERFALL_TEXINDEX_MAIN], TRUE, TRUE );
tmem_SetTexCombineMode(
GR_COMBINE_FUNCTION_SCALE_OTHER, GR_COMBINE_FACTOR_LOCAL,
GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL, GR_COMBINE_FACTOR_ONE,
FXFALSE, FXFALSE
);
}
_BuildLightList( pWfDef );
pWfDef->fScrollT1 += _TEX_T_SCROLL_DELTA1*Gameloop_fDeltaFrames;
pWfDef->fScrollT2 += _TEX_T_SCROLL_DELTA2*Gameloop_fDeltaFrames;
fVelY = pWfDef->FlowVelocity.p[0];
fVelZ = pWfDef->FlowVelocity.p[1];
fStartX = pWfDef->fWidth * -0.5f;
fEndX = -fStartX;
fTileWidthX = pWfDef->fTileWidthX;
nTileCountAcross = pWfDef->nTileCountAcross;
nTileCountDown = pWfDef->nTileCountDown;
nVtxCountAcross = nTileCountAcross + 1;
nVtxCountDown = nTileCountDown + 1;
fOneOverHeight = -pWfDef->fOneOverHeight;
fAnimateK1 = Gameloop_fFrameCounter * 512.0f;
bFlairEdges = pWfDef->nFlags & WATERFALL_FLAG_FLAIR_EDGES;
fFlairAmount = 0.0f;
fLoopSecs = pWfDef->fLoopSecs;
fTime = -fLoopSecs;
nTileTopVtxArrayIndex = 0;
for( i=0; i<nVtxCountDown; i++, nTileTopVtxArrayIndex^=1, fTime+=fLoopSecs ) {
/* Each row of vertices...*/
ModelspacePoint.p[0] = fStartX;
if( i == 0 ) {
/* Clip upper row...*/
fClipRatio = fVelZ*fTime * (1.0f/_PANEL_Z_LENGTH);
fStartTexT1 = _TEX_T_TILE_DELTA1*fClipRatio + pWfDef->fScrollT1;
fStartTexT2 = _TEX_T_TILE_DELTA2*fClipRatio + pWfDef->fScrollT2;
ModelspacePoint.p[1] = 0.0f;
ModelspacePoint.p[2] = 0.0f;
fUnitDuration = 0.0f;
vec3_Set( &Normal, 0.0f, 1.0f, 0.0f );
} else {
fPrevY = ModelspacePoint.p[1];
fPrevZ = ModelspacePoint.p[2];
fTemp = (float)i;
fStartTexT1 = pWfDef->fScrollT1 + i*_TEX_T_TILE_DELTA1;
fStartTexT2 = pWfDef->fScrollT2 + i*_TEX_T_TILE_DELTA2;
ModelspacePoint.p[1] = fVelY*fTime - (_GRAVITY_FEET_PER_SECSEC*0.5f)*fTime*fTime;
fUnitDuration = ModelspacePoint.p[1]*fOneOverHeight;
if( fUnitDuration > 1.0f ) {
fUnitDuration = 1.0f;
}
fTemp = fVelZ*fTime;
ModelspacePoint.p[2] = (fPrevZ-fTemp)*fUnitDuration*0.8f + fTemp;
/* Calc normal...*/
fDy = fPrevY - ModelspacePoint.p[1];
fDz = fPrevZ - ModelspacePoint.p[2];
Normal.p[0] = 0.0f;
Normal.p[1] = fDz;
Normal.p[2] = -fDy;
vec3_Unit( &Normal );
}
if( i == nTileCountDown ) {
pWfDef->fLowerMistStartZ = ModelspacePoint.p[2];
}
if( fUnitDuration >= _WHITESAT_PERCENT_TO_END ) {
fWhiteSat = (fUnitDuration - _WHITESAT_PERCENT_TO_END) * (1.0f/(1.0f-_WHITESAT_PERCENT_TO_END));
fWhiteSat *= _WHITESAT_MULTIPLIER;
} else {
fWhiteSat = 0.0f;
}
if( fUnitDuration >= _EDGEBEND_PERCENT_TO_END ) {
fEdgeBend = (fUnitDuration - _EDGEBEND_PERCENT_TO_END) * (1.0f/(1.0f-_EDGEBEND_PERCENT_TO_END));
fEdgeBend *= _EDGEBEND_MULTIPLIER;
} else {
fEdgeBend = 0.0f;
}
if( bFlairEdges ) {
if( fUnitDuration >= _FLAIR_PERCENT_TO_END ) {
fFlairAmount = _FLAIR_MAX;
} else {
fFlairAmount = fUnitDuration * (1.0f/(1.0f-_FLAIR_PERCENT_TO_END));
fFlairAmount *= _FLAIR_MAX;
}
}
fAnimateK2 = fAnimateK1 + ModelspacePoint.p[1]*385.0f;
fTexS = 0.0f;
for( j=0; j<nVtxCountAcross; j++, ModelspacePoint.p[0]+=fTileWidthX, fTexS+=_TEX_S_TILE_DELTA ) {
/* Each vertex across...*/
BentModelspacePoint.p[0] = ModelspacePoint.p[0];
BentModelspacePoint.p[1] = ModelspacePoint.p[1];
BentModelspacePoint.p[2] = ModelspacePoint.p[2];
if( j==0 || j==nTileCountAcross ) {
if( fEdgeBend ) {
BentModelspacePoint.p[1] -= Normal.p[1]*fEdgeBend;
BentModelspacePoint.p[2] -= Normal.p[2]*fEdgeBend;
}
if( fFlairAmount ) {
if( j == 0 ) {
BentModelspacePoint.p[0] -= fFlairAmount;
} else {
BentModelspacePoint.p[0] += fFlairAmount;
}
}
}
nAnimateAngle = (int)( BentModelspacePoint.p[0]*475.0f + fAnimateK2 );
fTexT1 = fStartTexT1 + _ST_DISTORT_AMPLITUDE*xmath_sin( nAnimateAngle );
fTexT2 = fStartTexT2 + _ST_DISTORT_AMPLITUDE*xmath_cos( nAnimateAngle );
pTileVtx = &_aTileGrVtx[nTileTopVtxArrayIndex][j];
pModelspaceVtx = &pTileVtx->ModelspaceVtx;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
pTileVtx->bScreenspaceVtxValid = FALSE;
pTileVtx->bSensedLight = FALSE;
pTileVtx->anOutsidePlaneCode[0]
= pTileVtx->anOutsidePlaneCode[1]
= pTileVtx->anOutsidePlaneCode[2]
= pTileVtx->anOutsidePlaneCode[3]
= _OUTCODE_UNKNOWN;
pModelspaceVtx->p[0] = BentModelspacePoint.p[0];
pModelspaceVtx->p[1] = BentModelspacePoint.p[1];
pModelspaceVtx->p[2] = BentModelspacePoint.p[2];
pfCamspaceVtx[0] = Xfm_pStackTop->mf[0][0] * BentModelspacePoint.p[0]
+ Xfm_pStackTop->mf[1][0] * BentModelspacePoint.p[1]
+ Xfm_pStackTop->mf[2][0] * BentModelspacePoint.p[2]
+ Xfm_pStackTop->mf[3][0];
pfCamspaceVtx[1] = Xfm_pStackTop->mf[0][1] * BentModelspacePoint.p[0]
+ Xfm_pStackTop->mf[1][1] * BentModelspacePoint.p[1]
+ Xfm_pStackTop->mf[2][1] * BentModelspacePoint.p[2]
+ Xfm_pStackTop->mf[3][1];
pfCamspaceVtx[2] = Xfm_pStackTop->mf[0][2] * BentModelspacePoint.p[0]
+ Xfm_pStackTop->mf[1][2] * BentModelspacePoint.p[1]
+ Xfm_pStackTop->mf[2][2] * BentModelspacePoint.p[2]
+ Xfm_pStackTop->mf[3][2];
pfCamspaceVtx[7] = (j!=nTileCountAcross) ? fTexS : fTexS + _TEX_S_RIGHTEDGE_DELTA;
pfCamspaceVtx[8] = fTexT1;
pfCamspaceVtx[9] = fTexT2;
if( i>0 && j>0 ) {
/* Draw a tile...*/
apTileVtx[0] = &_aTileGrVtx[nTileTopVtxArrayIndex][j];
apTileVtx[1] = &_aTileGrVtx[1-nTileTopVtxArrayIndex][j];
apTileVtx[2] = &_aTileGrVtx[nTileTopVtxArrayIndex][j-1];
apTileVtx[3] = &_aTileGrVtx[1-nTileTopVtxArrayIndex][j-1];
if( j==1 ) {
/* Left endcap...*/
tmem_SelectTextureTwins( pWfDef->apTexDef[WATERFALL_TEXINDEX_LEFT] );
tmem_SetTileMode( pWfDef->apTexDef[WATERFALL_TEXINDEX_LEFT], FALSE, TRUE );
} else if( j==nTileCountAcross ) {
/* Right endcap...*/
tmem_SelectTextureTwins( pWfDef->apTexDef[WATERFALL_TEXINDEX_RIGHT] );
tmem_SetTileMode( pWfDef->apTexDef[WATERFALL_TEXINDEX_RIGHT], TRUE, TRUE );
}
for( k=0; k<2; k++ ) {
if( _TestTriAgainstFrustum( &apTileVtx[k] ) ) {
/* Triangle is at least partially visible...*/
for( n=0; n<3; n++ ) {
pTileVtx = apTileVtx[n+k];
if( !pTileVtx->bSensedLight ) {
pTileVtx->bSensedLight = TRUE;
pModelspaceVtx = &pTileVtx->ModelspaceVtx;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
mesh3d_LightSensor( pModelspaceVtx, &Normal, &SensedColor, FALSE );
if( fWhiteSat ) {
pScreenspaceVtx->a
= fWhiteSat*gutil_RgbToIntensity(
SensedColor.argb[2],
SensedColor.argb[1],
SensedColor.argb[0]
) + (SensedColor.argb[3] + _fWhiteSat)*255.0f;
if( pScreenspaceVtx->a > 255.0f ) {
pScreenspaceVtx->a = 255.0f;
}
} else {
pScreenspaceVtx->a = SensedColor.argb[3]*255.0f;
}
pScreenspaceVtx->r = SensedColor.argb[2] * 255.0f;
pScreenspaceVtx->g = SensedColor.argb[1] * 255.0f;
pScreenspaceVtx->b = SensedColor.argb[0] * 255.0f;
}
}
if( !_nTriCrossesPlanesMask ) {
/* Triangle doesn't require frustum clipping...*/
for( n=0; n<3; n++ ) {
pTileVtx = apTileVtx[n+k];
if( !pTileVtx->bScreenspaceVtxValid ) {
pTileVtx->bScreenspaceVtxValid = TRUE;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
oow = pScreenspaceVtx->oow = VIEWPORT_CALC_OOW( pfCamspaceVtx[2] );
q = VIEWPORT_CALC_Q( oow );
#if GFXDEFS_USE_ZBUFFER
pScreenspaceVtx->ooz = VIEWPORT_CALC_OOZ( pfCamspaceVtx[2] );
#endif
pScreenspaceVtx->x = GFXDEFS_SNAPVERT( q*pfCamspaceVtx[0] - Viewport_frustum_limit[0] );
pScreenspaceVtx->y = GFXDEFS_SNAPVERT( q*pfCamspaceVtx[1] - Viewport_frustum_limit[2] );
GUTIL_CLAMP_OOW( pScreenspaceVtx->oow );
pTileVtx->ScreenspaceVtx.tmuvtx[0].sow =
pTileVtx->ScreenspaceVtx.tmuvtx[1].sow =
pTileVtx->afCamspaceVtx[7]*pScreenspaceVtx->oow;
pTileVtx->ScreenspaceVtx.tmuvtx[0].tow =
pTileVtx->afCamspaceVtx[8]*pScreenspaceVtx->oow;
pTileVtx->ScreenspaceVtx.tmuvtx[1].tow =
pTileVtx->afCamspaceVtx[9]*pScreenspaceVtx->oow;
}
}
GUTIL_DRAW_TRIANGLE(
&apTileVtx[k]->ScreenspaceVtx,
&apTileVtx[k+1]->ScreenspaceVtx,
&apTileVtx[k+2]->ScreenspaceVtx
);
#if MESH3D_TRI_HIGHLIGHT_CODE
if( pWfDef->nFlags & WATERFALL_FLAG_SELECTED ) {
_DrawConvexPolyWireframe( 3, &apTileVtx[k] );
}
#endif
} else {
/* Must clip triangle to frustum...*/
_ClipTriToFrustumAndDraw( &apTileVtx[k], FALSE );
}
}
}
if( j==1 || j==nTileCountAcross ) {
/* Endcap...*/
tmem_SelectTextureTwins( pWfDef->apTexDef[WATERFALL_TEXINDEX_MAIN] );
tmem_SetTileMode( pWfDef->apTexDef[WATERFALL_TEXINDEX_MAIN], TRUE, TRUE );
}
}
}
}
/* Restore graphics state...*/
grHints( GR_HINT_STWHINT, 0 );
material_PopState(1);
mesh3d_ReleaseLightFrame( _nLightFrame );
}
#endif /*ULTRA64 */
/* Returns TRUE if triangle is at least partially visible.*/
static BOOL _TestTriAgainstFrustum( TileVtx_t **ppTileVtx ) {
u32 j, k, nWalkingBit, nOutsideCount;
Vec3_t *pNormVec;
float *pfCamspace, fDot;
_nTriCrossesPlanesMask = _nBoundCrossesPlanesMask;
if( _nBoundCrossesPlanesMask ) {
for( j=0; j<3; j++ ) {
if( ppTileVtx[j]->afCamspaceVtx[2] >= 0.0f ) {
break;
}
}
if( j == 3 ) {
/* All vertices are behind the camera.*/
/* Cull this triangle...*/
return FALSE;
}
for( j=0, nWalkingBit=1; j<4; j++, nWalkingBit<<=1 ) {
if( _nBoundCrossesPlanesMask & nWalkingBit ) {
pNormVec = &Viewport_rect_norm[j];
nOutsideCount = 0;
for( k=0; k<3; k++ ) {
if( ppTileVtx[k]->anOutsidePlaneCode[j] == _OUTCODE_UNKNOWN ) {
pfCamspace = &ppTileVtx[k]->afCamspaceVtx[0];
fDot = pfCamspace[0]*pNormVec->p[0]
+ pfCamspace[1]*pNormVec->p[1]
+ pfCamspace[2]*pNormVec->p[2]
- GUTIL_CLIPPING_AVOID_DIVZERO_MARGIN;
if( fDot < 0.0f ) {
ppTileVtx[k]->anOutsidePlaneCode[j] = _OUTCODE_OUTSIDE;
nOutsideCount++;
} else {
ppTileVtx[k]->anOutsidePlaneCode[j] = _OUTCODE_INSIDE;
}
} else if( ppTileVtx[k]->anOutsidePlaneCode[j] == _OUTCODE_OUTSIDE ) {
nOutsideCount++;
}
}
if( nOutsideCount == 3 ) {
/* All vertices are outside this plane...*/
break;
}
if( nOutsideCount == 0 ) {
/* All vertices are inside this plane...*/
_nTriCrossesPlanesMask ^= nWalkingBit;
}
}
}
if( nOutsideCount == 3 ) {
/* Indicate that triangle is entirely outside frustum...*/
return FALSE;
}
}
return TRUE;
}
#if TARGET!=DREAMCAST
static void _ClipTriToFrustumAndDraw( TileVtx_t **ppTileVtx, BOOL bTestForScreenCoverage ) {
u32 i, j, k, nVtxInCount, nVtxStartOutIndex, nNumTris, nVtxInIndex, nVtxOutIndex;
float fPrevDot, fThisDot, fScale, x, y, w, q;
u32 nWalkingBit;
TileVtx_t *pVtxIn, *pPrevVtxIn, *pVtxBuf;
for( i=0; i<3; i++ ) {
ppTileVtx[i]->afCamspaceVtx[3] = ppTileVtx[i]->ScreenspaceVtx.r;
ppTileVtx[i]->afCamspaceVtx[4] = ppTileVtx[i]->ScreenspaceVtx.g;
ppTileVtx[i]->afCamspaceVtx[5] = ppTileVtx[i]->ScreenspaceVtx.b;
ppTileVtx[i]->afCamspaceVtx[6] = ppTileVtx[i]->ScreenspaceVtx.a;
_apTileClippedVtx[i] = ppTileVtx[i];
}
pVtxBuf = _aTileClippedVtx;
nVtxInIndex = 0;
nVtxOutIndex = 3;
nVtxInCount = 3;
for( j=0, nWalkingBit=1; j<4; j++, nWalkingBit<<=1 ) {
if( _nTriCrossesPlanesMask & nWalkingBit ) {
nVtxStartOutIndex = nVtxOutIndex;
pVtxIn = _apTileClippedVtx[ nVtxInIndex + nVtxInCount - 1 ];
fPrevDot = pVtxIn->afCamspaceVtx[0]*Viewport_rect_norm[j].p[0]
+ pVtxIn->afCamspaceVtx[1]*Viewport_rect_norm[j].p[1]
+ pVtxIn->afCamspaceVtx[2]*Viewport_rect_norm[j].p[2]
- GUTIL_CLIPPING_AVOID_DIVZERO_MARGIN;
for( i=0; i<nVtxInCount; i++ ) {
pPrevVtxIn = pVtxIn;
if( fPrevDot>0.0f ) {
_apTileClippedVtx[nVtxOutIndex++] = pVtxIn;
}
pVtxIn = _apTileClippedVtx[nVtxInIndex++];
fThisDot = pVtxIn->afCamspaceVtx[0]*Viewport_rect_norm[j].p[0]
+ pVtxIn->afCamspaceVtx[1]*Viewport_rect_norm[j].p[1]
+ pVtxIn->afCamspaceVtx[2]*Viewport_rect_norm[j].p[2]
- GUTIL_CLIPPING_AVOID_DIVZERO_MARGIN;
if( fPrevDot*fThisDot < 0.0f ) {
/* vertices are on opposite sides of nearplane*/
fScale = fPrevDot / (fPrevDot-fThisDot);
for( k=0; k<_CAMSPACE_ITERATOR_COUNT; k++ ) {
pVtxBuf->afCamspaceVtx[k] = pPrevVtxIn->afCamspaceVtx[k]
+ (pVtxIn->afCamspaceVtx[k] - pPrevVtxIn->afCamspaceVtx[k] )*fScale;
}
_apTileClippedVtx[ nVtxOutIndex++ ] = pVtxBuf++;
}
fPrevDot = fThisDot;
}
nVtxInCount = nVtxOutIndex - nVtxStartOutIndex;
if( nVtxInCount==0 ) {
return;
}
}
}
for( i=0, j=nVtxInIndex; i<nVtxInCount; i++, j++ ) {
pVtxIn = _apTileClippedVtx[j];
w = pVtxIn->ScreenspaceVtx.oow = VIEWPORT_CALC_OOW( pVtxIn->afCamspaceVtx[2] );
q = VIEWPORT_CALC_Q( w );
#if GFXDEFS_USE_ZBUFFER
pVtxIn->ScreenspaceVtx.ooz = VIEWPORT_CALC_OOZ( pVtxIn->afCamspaceVtx[2] );
#endif
x = q*pVtxIn->afCamspaceVtx[0] - Viewport_frustum_limit[0];
y = q*pVtxIn->afCamspaceVtx[1] - Viewport_frustum_limit[2];
pVtxIn->ScreenspaceVtx.x = GFXDEFS_SNAPVERT(x);
pVtxIn->ScreenspaceVtx.y = GFXDEFS_SNAPVERT(y);
pVtxIn->ScreenspaceVtx.r = pVtxIn->afCamspaceVtx[3];
pVtxIn->ScreenspaceVtx.g = pVtxIn->afCamspaceVtx[4];
pVtxIn->ScreenspaceVtx.b = pVtxIn->afCamspaceVtx[5];
pVtxIn->ScreenspaceVtx.a = pVtxIn->afCamspaceVtx[6];
GUTIL_CLAMP_OOW( pVtxIn->ScreenspaceVtx.oow );
}
nNumTris = nVtxInCount - 2;
/* Draw base water...*/
for( j=0; j<nVtxInCount; j++ ) {
pVtxIn = _apTileClippedVtx[j + nVtxInIndex];
w = pVtxIn->ScreenspaceVtx.oow;
pVtxIn->ScreenspaceVtx.tmuvtx[0].sow = pVtxIn->ScreenspaceVtx.tmuvtx[1].sow = pVtxIn->afCamspaceVtx[7] * w;
pVtxIn->ScreenspaceVtx.tmuvtx[0].tow = pVtxIn->afCamspaceVtx[8] * w;
pVtxIn->ScreenspaceVtx.tmuvtx[1].tow = pVtxIn->afCamspaceVtx[9] * w;
}
if( bTestForScreenCoverage ) {
for( i=0; i<nNumTris; i++ ) {
gutil_ScreenCoverage_DrawTriangleIfUnderLimit(
&_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx,
&_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx,
&_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx
);
}
} else {
for( i=0; i<nNumTris; i++ ) {
GUTIL_DRAW_TRIANGLE(
&_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx,
&_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx,
&_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx
);
}
}
}
#endif // DREAMCAST
static void _BuildLightList( WaterfallDef_t *pWfDef ) {
WorldSector_t *pSector;
WorldSectorCache_t *pSectorCache;
TwsIntersect_t *pSectorIntersect, *pLightIntersect;
LinkRoot_t *pSectorIntersectList, *pLightIntersectList;
WorldLt_t *pWorldLight;
MidwayLight_t *pLight;
Vec3_t CameraspaceBoundCenter;
Vec2_t WorldspaceBoundCenter;
Xfm_t *pCamXfm;
float fDx, fDz, fRadiusSum, fWaterfallBoundRadius, fUnitPercentageIntoSector;
u32 i;
fWaterfallBoundRadius = pWfDef->Bound.fBoundRadius;
pCamXfm = &Xfm_pStackCam->xfm;
for( i=0; i<3; i++ ) {
CameraspaceBoundCenter.p[i] = Xfm_pStackTop->mf[0][i] * pWfDef->Bound.BoundCenter.p[0]
+ Xfm_pStackTop->mf[1][i] * pWfDef->Bound.BoundCenter.p[1]
+ Xfm_pStackTop->mf[2][i] * pWfDef->Bound.BoundCenter.p[2]
+ Xfm_pStackTop->mf[3][i];
}
WorldspaceBoundCenter.p[0] = pCamXfm->mr[0][0] * CameraspaceBoundCenter.p[0]
+ pCamXfm->mr[1][0] * CameraspaceBoundCenter.p[1]
+ pCamXfm->mr[2][0] * CameraspaceBoundCenter.p[2]
+ pCamXfm->mr[3][0];
WorldspaceBoundCenter.p[1] = pCamXfm->mr[0][2] * CameraspaceBoundCenter.p[0]
+ pCamXfm->mr[1][2] * CameraspaceBoundCenter.p[1]
+ pCamXfm->mr[2][2] * CameraspaceBoundCenter.p[2]
+ pCamXfm->mr[3][2];
pSectorIntersectList = &pWfDef->Tws.IntersectList;
pSectorIntersect = linklist_GetHead( pSectorIntersectList );
while( pSectorIntersect ) {
pSector = pSectorIntersect->pSector;
pSectorCache = pSector->pSectorCache;
/* Each sector intersecting waterfall...*/
pLightIntersectList = &pSectorCache->aIntersectList[WORLD_INTERSECT_TYPE_LIGHT];
pLightIntersect = linklist_GetHead( pLightIntersectList );
while( pLightIntersect ) {
/* Each light intersecting sector that waterfall intersects...*/
pWorldLight = (WorldLt_t *)pLightIntersect->pTws->pOwner;
pLight = &pWorldLight->Light;
fDx = pLight->WorldSpacePos.p[0] - WorldspaceBoundCenter.p[0];
fDz = pLight->WorldSpacePos.p[2] - WorldspaceBoundCenter.p[1];
fRadiusSum = pLight->fWorldSpaceRadius + fWaterfallBoundRadius;
if( (fDx*fDx + fDz*fDz) < fRadiusSum*fRadiusSum ) {
/* Light possibly intersects waterfall...*/
mesh3d_AddLight( &pWorldLight->Light );
}
pLightIntersect = linklist_GetNext( pLightIntersectList, pLightIntersect );
}
pSectorIntersect = linklist_GetNext( pSectorIntersectList, pSectorIntersect );
}
pSector = pWfDef->Tws.pCenterSector;
if( pSector ) {
fUnitPercentageIntoSector = world_CalcUnitPercentageIntoSector(
pSector,
pWfDef->Tws.CurrentWorldPos.p[0],
pWfDef->Tws.CurrentWorldPos.p[1]
);
pWfDef->fUnitPercIntoSector = fUnitPercentageIntoSector;
world_BuildObjectAmbient( pSector->pSectorCache, fUnitPercentageIntoSector );
world_BuildObjectCelestial( pSector->pSectorCache, fUnitPercentageIntoSector );
}
}
#if TARGET!=DREAMCAST
static void _DrawMist( WaterfallDef_t *pWfDef ) {
float fGapsAcross, fExcessX, fStartX, fEndX, fStartZ, fStartY;
float fModelCamX, fModelCamZ, fModelCamR, fModelCamOor, fSin, fCos;
float fI, fUnitLife, *pfCamspaceVtx, fSize, oow, q;
float fOpaqueness, fMinUnitLife, fOrigIndex, fSteppingX, fTemp, fTravelY;
float fStartSize, fEndSize, fDeltaSize;
TileVtx_t *pTileVtx;
u32 i, j, k, n, nNumAcross, nNumPops, nAdjustedFrameCounter, nMinUnitLifeIndex;
Vec3_t MistOrigin, *pModelspaceVtx;
GrVertex *pScreenspaceVtx;
static float afUnsortedUnitLife[_MIST_COUNT][_MIST_MAX_ACROSS];
static float afUnitLife[_MIST_COUNT][_MIST_MAX_ACROSS];
static float afOrigIndex[_MIST_COUNT][_MIST_MAX_ACROSS];
/* Calculate the number of gaps across and mist particles across...*/
fGapsAcross = pWfDef->fWidth*(1.0f/_MIST_GAP_X) + 1.0f;
nNumAcross = (int)fGapsAcross + 1;
if( nNumAcross > _MIST_MAX_ACROSS ) {
/* Too much mist. Don't draw it...*/
return;
}
/* Calculate particle start and end coordinates...*/
fExcessX = fGapsAcross*_MIST_GAP_X - pWfDef->fWidth;
fEndX = (pWfDef->fWidth + fExcessX)*0.5f;
fStartX = -fEndX;
fStartY = -pWfDef->fHeight;
fStartZ = pWfDef->fLowerMistStartZ + _MIST_START_DELTA_Z;
fTravelY = _MIST_TRAVEL_Y_UNITPERC * pWfDef->fHeight;
fStartSize = _MIST_MIN_SIZE * pWfDef->fHeight;
fEndSize = _MIST_MAX_SIZE * pWfDef->fHeight;
fDeltaSize = fEndSize - fStartSize;
/* Clip against all frustum planes...*/
_nBoundCrossesPlanesMask = 0xf;
/* Calculate unit life values for each particle...*/
for( i=0, fI=0.0f; i<_MIST_COUNT; i++, fI+=1.0f ) {
/* Calculate base life...*/
nAdjustedFrameCounter =
(u32)( Gameloop_fFrameCounter
+ fI * ( (float)(1<<_MIST_LIFE_LOG2) / (float)_MIST_COUNT ) );
fUnitLife = (nAdjustedFrameCounter & ( (1<<_MIST_LIFE_LOG2)-1 ))
* (1.0f/(float)(1<<_MIST_LIFE_LOG2));
for( j=0; j<nNumAcross; j++ ) {
/* Now adjust life by random amount based on the column number...*/
fTemp = fUnitLife + (0.5f/(float)_MIST_COUNT) * _afRandomFloatTable[j];
if( fTemp >= 1.0f ) {
fTemp -= 1.0f;
} else if( fTemp < 0.0f ) {
fTemp += 1.0f;
}
afUnsortedUnitLife[i][j] = fTemp;
}
}
/* Sort unit life values from lowest to highest for each column...*/
for( j=0; j<nNumAcross; j++ ) {
/* Each column...*/
/* Find lowest life value for this column...*/
fMinUnitLife = 2.0f;
for( i=0; i<_MIST_COUNT; i++ ) {
if( afUnsortedUnitLife[i][j] < fMinUnitLife ) {
fMinUnitLife = afUnsortedUnitLife[i][j];
nMinUnitLifeIndex = i;
}
}
/* Copy unit lifes, sorted from lowest to highest...*/
for( i=0; i<_MIST_COUNT; i++ ) {
afUnitLife[i][j] = afUnsortedUnitLife[nMinUnitLifeIndex][j];
afOrigIndex[i][j] = (float)nMinUnitLifeIndex;
if( ++nMinUnitLifeIndex >= _MIST_COUNT ) {
nMinUnitLifeIndex = 0;
}
}
}
for( i=0; i<_MIST_COUNT; i++ ) {
/* Each row of mist objects...*/
fSteppingX = fStartX;
for( j=0; j<nNumAcross; j++, fSteppingX+=_MIST_GAP_X ) {
/* Each mist object across...*/
fUnitLife = afUnitLife[i][j];
fOrigIndex = afOrigIndex[i][j];
fSize = fDeltaSize*fUnitLife + fStartSize;
if( fUnitLife < _MIST_FADEIN_UNITPERC ) {
fOpaqueness = (255.0f * (1.0f/_MIST_FADEIN_UNITPERC)) * fUnitLife;
} else if( fUnitLife > _MIST_FADEOUT_UNITPERC ) {
fOpaqueness = 255.0f - ( (255.0f * (1.0f/(1.0f-_MIST_FADEOUT_UNITPERC))) * (fUnitLife-_MIST_FADEOUT_UNITPERC) );
} else {
fOpaqueness = 255.0f;
}
MistOrigin.p[0] = fSteppingX;
fTemp = fTravelY + (fTravelY*0.2f)*_afRandomFloatTable[j];
MistOrigin.p[1] = fTemp*fUnitLife + fStartY;
MistOrigin.p[2] = _MIST_TRAVEL_Z*fUnitLife + fStartZ;
/* Create and push mist particle transformation matrix...*/
_XfmMistPos.mf[3][0] = MistOrigin.p[0];
_XfmMistPos.mf[3][1] = MistOrigin.p[1];
_XfmMistPos.mf[3][2] = MistOrigin.p[2];
_XfmMistPos.mr[3][0] = -MistOrigin.p[0];
_XfmMistPos.mr[3][1] = -MistOrigin.p[1];
_XfmMistPos.mr[3][2] = -MistOrigin.p[2];
xfm_Push( &_XfmMistPos );
nNumPops = 1;
/* Create poster transformation matrix...*/
fModelCamX = Xfm_pStackTop->mr[3][0];
fModelCamZ = Xfm_pStackTop->mr[3][2];
fModelCamR = xmath_sqrt( fModelCamX*fModelCamX + fModelCamZ*fModelCamZ );
if( fModelCamR ) {
fModelCamOor = 1.0f / fModelCamR;
fSin = fModelCamOor * fModelCamX;
fCos = fModelCamOor * fModelCamZ;
_XfmPosterY.mf[0][0] = _XfmPosterY.mf[2][2] = fCos;
_XfmPosterY.mr[0][0] = _XfmPosterY.mr[2][2] = fCos;
_XfmPosterY.mf[0][2] = _XfmPosterY.mr[2][0] = -fSin;
_XfmPosterY.mf[2][0] = _XfmPosterY.mr[0][2] = fSin;
xfm_Push( &_XfmPosterY );
nNumPops++;
/* Transform vertices...*/
for( k=0; k<4; k++ ) {
pTileVtx = &_aMistTileVtx[k];
pModelspaceVtx = &pTileVtx->ModelspaceVtx;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
pTileVtx->bScreenspaceVtxValid = FALSE;
pTileVtx->anOutsidePlaneCode[0]
= pTileVtx->anOutsidePlaneCode[1]
= pTileVtx->anOutsidePlaneCode[2]
= pTileVtx->anOutsidePlaneCode[3]
= _OUTCODE_UNKNOWN;
pModelspaceVtx->p[0] = _aMistVtx[k].p[0]*fSize;
pModelspaceVtx->p[1] = _aMistVtx[k].p[1]*fSize;
pModelspaceVtx->p[2] = 0.0f;
pfCamspaceVtx[0] = Xfm_pStackTop->mf[0][0] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][0] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][0];
pfCamspaceVtx[1] = Xfm_pStackTop->mf[0][1] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][1] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][1];
pfCamspaceVtx[2] = Xfm_pStackTop->mf[0][2] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][2] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][2];
pfCamspaceVtx[7] = _aMistSt[k].p[0];
pfCamspaceVtx[8] = _aMistSt[k].p[1];
pfCamspaceVtx[9] = 0.0f;
pScreenspaceVtx->a = fOpaqueness;
pScreenspaceVtx->r = _MistColor.argb[2];
pScreenspaceVtx->g = _MistColor.argb[1];
pScreenspaceVtx->b = _MistColor.argb[0];
}
/* Draw triangles...*/
for( k=0; k<2; k++ ) {
if( _TestTriAgainstFrustum( &_apMistTileVtx[k] ) ) {
/* Triangle is at least partially visible...*/
if( !_nTriCrossesPlanesMask ) {
/* Triangle doesn't require frustum clipping...*/
for( n=0; n<3; n++ ) {
pTileVtx = _apMistTileVtx[n+k];
if( !pTileVtx->bScreenspaceVtxValid ) {
pTileVtx->bScreenspaceVtxValid = TRUE;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
oow = pScreenspaceVtx->oow = VIEWPORT_CALC_OOW( pfCamspaceVtx[2] );
q = VIEWPORT_CALC_Q( pScreenspaceVtx->oow );
#if GFXDEFS_USE_ZBUFFER
pScreenspaceVtx->ooz = VIEWPORT_CALC_OOZ( pfCamspaceVtx[2] );
#endif
pScreenspaceVtx->x = GFXDEFS_SNAPVERT( q*pfCamspaceVtx[0] - Viewport_frustum_limit[0] );
pScreenspaceVtx->y = GFXDEFS_SNAPVERT( q*pfCamspaceVtx[1] - Viewport_frustum_limit[2] );
GUTIL_CLAMP_OOW( pScreenspaceVtx->oow );
pTileVtx->ScreenspaceVtx.tmuvtx[0].sow =
pTileVtx->ScreenspaceVtx.tmuvtx[1].sow =
pTileVtx->afCamspaceVtx[7]*pScreenspaceVtx->oow;
pTileVtx->ScreenspaceVtx.tmuvtx[0].tow =
pTileVtx->ScreenspaceVtx.tmuvtx[1].tow =
pTileVtx->afCamspaceVtx[8]*pScreenspaceVtx->oow;
}
}
gutil_ScreenCoverage_DrawTriangleIfUnderLimit(
&_apMistTileVtx[k]->ScreenspaceVtx,
&_apMistTileVtx[k+1]->ScreenspaceVtx,
&_apMistTileVtx[k+2]->ScreenspaceVtx
);
} else {
/* Must clip triangle to frustum...*/
_ClipTriToFrustumAndDraw( &_apMistTileVtx[k], TRUE );
}
}
}
}
xfm_PopN( nNumPops );
}
}
}
#endif
#if MESH3D_TRI_HIGHLIGHT_CODE
static void _DrawConvexPolyWireframe( int nNumVerts, TileVtx_t *apVtxCache[] ) {
int i;
material_PushState();
material_Defaults();
material_ColorCombine(
GR_COMBINE_FUNCTION_SCALE_OTHER,
GR_COMBINE_FACTOR_ONE,
GR_COMBINE_LOCAL_NONE,
GR_COMBINE_OTHER_CONSTANT,
FXFALSE
);
material_AlphaCombine(
GR_COMBINE_FUNCTION_SCALE_OTHER,
GR_COMBINE_FACTOR_ONE,
GR_COMBINE_LOCAL_ITERATED,
GR_COMBINE_OTHER_CONSTANT,
FXFALSE
);
material_ConstantColorValue( GFXDEFS_INTARGB_TO_GRCOLOR( 255, Mesh3d_WireframeColor.rgb[2], Mesh3d_WireframeColor.rgb[1], Mesh3d_WireframeColor.rgb[0] ) );
gutil_DeltaDepthBiasLevel( 2 );
for( i=0; i<nNumVerts-1; i++ ) {
GUTIL_DRAW_AALINE( &apVtxCache[i]->ScreenspaceVtx, &apVtxCache[i+1]->ScreenspaceVtx );
}
GUTIL_DRAW_AALINE( &apVtxCache[nNumVerts-1]->ScreenspaceVtx, &apVtxCache[0]->ScreenspaceVtx );
gutil_DeltaDepthBiasLevel( -2 );
material_PopState(1);
}
#endif
#if TARGET==ULTRA64 /* New waterfall drawing code */
#include "u64draw.h"
#include "u64sprite.h"
#include "math.h"
extern u32 TexAddr;
extern float N64CameraXPosFrac,N64CameraYPosFrac,N64CameraZPosFrac;
/*
Draw waterfall for N64
We draws the waterfall twice to give a similar effect to the arcade(merges two textures). We draw
a normal texture for the underlay and then overlay a white hi-light on top, which scrolls faster.
NOTE - This seems to be called even when NO waterfalls are in view - why?? Check this out!
*/
static void _DrawWaterfall( WaterfallDef_t *pWfDef ) {
float fTime, fLoopSecs, fVelY, fVelZ, fStartX, fEndX, q, oow;
float *pfCamspaceVtx, fClipRatio, fTexS, fTexT1, fTexT2, fWhiteSat, fOneOverHeight;
float fUnitDuration, fStartTexT1, fStartTexT2, fTemp, fPrevY, fPrevZ, fDy, fDz, fEdgeBend;
float fTileWidthX, fAnimateK1, fAnimateK2, fFlairAmount;
u32 nTileTopVtxArrayIndex, nTileCountAcross, nTileCountDown;
u32 nVtxCountDown, nVtxCountAcross;
u32 i, j, k, n;
s32 nAnimateAngle;
Vec3_t ModelspacePoint, BentModelspacePoint, *pModelspaceVtx, Normal;
GrVertex *pScreenspaceVtx;
TileVtx_t *pTileVtx, *apTileVtx[4];
ARGBf_t SensedColor;
BOOL bFlairEdges;
/* new stuff */
TexDef_t *TexDef;
Vtx *VtxPtr;
Vec3_t FallModelspacePoints[16][2];
Vec2_t FallModelspaceUVs[32][2];
float Stash[4][4],Stash2[4][4];
int StartN64Vertex;
Xfm_t OldNewStackTop;
unsigned char WaterShade;
pWfDef->fScrollT1 += _TEX_T_SCROLL_DELTA1*Gameloop_fDeltaFrames;
pWfDef->fScrollT2 += _TEX_T_SCROLL_DELTA2*Gameloop_fDeltaFrames;
fVelY = pWfDef->FlowVelocity.p[0];
fVelZ = pWfDef->FlowVelocity.p[1];
fStartX = pWfDef->fWidth * -0.5f;
fEndX = -fStartX;
fTileWidthX = pWfDef->fTileWidthX;
nTileCountAcross = pWfDef->nTileCountAcross;
nTileCountDown = pWfDef->nTileCountDown;
nVtxCountAcross = nTileCountAcross + 1;
nVtxCountDown = nTileCountDown + 1;
fOneOverHeight = -pWfDef->fOneOverHeight;
bFlairEdges = pWfDef->nFlags & WATERFALL_FLAG_FLAIR_EDGES;
fFlairAmount = 0.0f;
fLoopSecs = pWfDef->fLoopSecs;
fTime = -fLoopSecs;
nTileTopVtxArrayIndex = 0;
/* N64 CANNOT have more than 16 vertices in water-fall as assumes fits in one cache */
if (nVtxCountDown>=16) {
/* CONSOLE("Oops, nVtxCountDown OVERFLOW!!\n"); */
/* for(;;); */
nVtxCountDown = 16; /* For height of waterfalls!!! */
}
for( i=0; i<nVtxCountDown; i++, nTileTopVtxArrayIndex^=1, fTime+=fLoopSecs ) {
/* Each row of vertices... */
ModelspacePoint.p[0] = fStartX;
if( i == 0 ) {
/* Clip upper row... */
fClipRatio = fVelZ*fTime * (1.0f/_PANEL_Z_LENGTH);
fStartTexT1 = _TEX_T_TILE_DELTA1*fClipRatio + pWfDef->fScrollT1;
fStartTexT2 = _TEX_T_TILE_DELTA2*fClipRatio + pWfDef->fScrollT2;
ModelspacePoint.p[1] = 0.0f;
ModelspacePoint.p[2] = 0.0f;
fUnitDuration = 0.0f;
vec3_Set( &Normal, 0.0f, 1.0f, 0.0f );
} else {
fPrevY = ModelspacePoint.p[1];
fPrevZ = ModelspacePoint.p[2];
fTemp = (float)i;
fStartTexT1 = pWfDef->fScrollT1 + i*_TEX_T_TILE_DELTA1;
fStartTexT2 = pWfDef->fScrollT2 + i*_TEX_T_TILE_DELTA2;
ModelspacePoint.p[1] = fVelY*fTime - (_GRAVITY_FEET_PER_SECSEC*0.5f)*fTime*fTime;
fUnitDuration = ModelspacePoint.p[1]*fOneOverHeight;
if( fUnitDuration > 1.0f ) {
fUnitDuration = 1.0f;
}
fTemp = fVelZ*fTime;
ModelspacePoint.p[2] = (fPrevZ-fTemp)*fUnitDuration*0.8f + fTemp;
/* Calc normal... */
fDy = fPrevY - ModelspacePoint.p[1];
fDz = fPrevZ - ModelspacePoint.p[2];
Normal.p[0] = 0.0f;
Normal.p[1] = fDz;
Normal.p[2] = -fDy;
vec3_Unit( &Normal );
}
if( fUnitDuration >= _EDGEBEND_PERCENT_TO_END ) {
fEdgeBend = (fUnitDuration - _EDGEBEND_PERCENT_TO_END) * (1.0f/(1.0f-_EDGEBEND_PERCENT_TO_END));
fEdgeBend *= _EDGEBEND_MULTIPLIER;
} else
fEdgeBend = 0.0f;
fAnimateK2 = ModelspacePoint.p[1]*385.0f;
/* New stuff!!!!! */
/* Store 2 edge points down sides of waterfall */
FallModelspacePoints[i][0].p[0] = ModelspacePoint.p[0];
FallModelspacePoints[i][0].p[1] = ModelspacePoint.p[1] - (Normal.p[1]*fEdgeBend);
FallModelspacePoints[i][0].p[2] = ModelspacePoint.p[2] - (Normal.p[2]*fEdgeBend);
FallModelspacePoints[i][1].p[0] = ModelspacePoint.p[0] + (nVtxCountAcross*fTileWidthX);
FallModelspacePoints[i][1].p[1] = FallModelspacePoints[i][0].p[1];
FallModelspacePoints[i][1].p[2] = FallModelspacePoints[i][0].p[2];
nAnimateAngle = (int)( FallModelspacePoints[i][0].p[0]*475.0f + fAnimateK2 );
fTexT2 = fStartTexT2 + _ST_DISTORT_AMPLITUDE*xmath_cos( nAnimateAngle );
FallModelspaceUVs[i][0].p[0] = 0<<6;
FallModelspaceUVs[i][1].p[0] = 32<<6;
FallModelspaceUVs[i][1].p[1] =
FallModelspaceUVs[i][0].p[1] = fTexT2;
FallModelspaceUVs[i+16][0].p[0] = 0<<6;
FallModelspaceUVs[i+16][1].p[0] = 48<<6;
FallModelspaceUVs[i+16][1].p[1] =
FallModelspaceUVs[i+16][0].p[1] = 2048-(((i*16)-(((int)Gameloop_fFrameCounter<<1)&127))<<2);
}
/* Now draw!!!! */
Stash[1][1] =
Stash[2][2] = 1.0;
Stash[0][0] = -1.0;
Stash[3][0] = -Xfm_pStackTop->mf[3][0]+N64CameraXPosFrac;
Stash[3][1] = Xfm_pStackTop->mf[3][1]-N64CameraYPosFrac;
Stash[3][2] = Xfm_pStackTop->mf[3][2]-N64CameraZPosFrac;
Stash[3][3] = 1.0;
Stash[1][0] = Stash[2][0] =
Stash[0][1] = Stash[2][1] =
Stash[0][2] = Stash[1][2] =
Stash[0][3] = Stash[1][3] = Stash[2][3] = 0.0;
/* Copy arcade matrix, remove any scale/translate */
Stash2[0][0] = Xfm_pStackTop->mf[0][0];
Stash2[1][0] = Xfm_pStackTop->mf[1][0];
Stash2[2][0] = Xfm_pStackTop->mf[2][0];
Stash2[0][1] = Xfm_pStackTop->mf[0][1];
Stash2[1][1] = Xfm_pStackTop->mf[1][1];
Stash2[2][1] = Xfm_pStackTop->mf[2][1];
Stash2[0][2] = Xfm_pStackTop->mf[0][2];
Stash2[1][2] = Xfm_pStackTop->mf[1][2];
Stash2[2][2] = Xfm_pStackTop->mf[2][2];
Stash2[3][3] = 1.0;
Stash2[3][0] = Stash2[3][1] = Stash2[3][2] =
Stash2[0][3] = Stash2[1][3] = Stash2[2][3] = 0.0;
guMtxCatF(Stash2,Stash,Stash);
guMtxF2L(Stash,&dynamicp->object_matrix[NumN64Objects]);
SetNewgrMatrix(&dynamicp->object_matrix[NumN64Objects]);
NumN64Objects++;
/* Store first N64 vertex index, ready for triangle create */
VtxPtr = &N64Vertices[draw_buffer][NumN64Vertices];
/* First, create our Vtx structures!! */
/* Do under water fall first */
WaterShade = 0xff-(0x10*nVtxCountDown);
for(i=0; i<nVtxCountDown; i++) {
for(j=0; j<2; j++) {
VtxPtr->v.ob[0] = (s16)FallModelspacePoints[i][j].p[0]; /*XYZ */
VtxPtr->v.ob[1] = (s16)FallModelspacePoints[i][j].p[1];
VtxPtr->v.ob[2] = (s16)FallModelspacePoints[i][j].p[2];
VtxPtr->v.flag = 0;
VtxPtr->v.tc[0] = (s16)FallModelspaceUVs[i+16][j].p[0]<<1; /*UV */
VtxPtr->v.tc[1] = (s16)FallModelspaceUVs[i+16][j].p[1]<<1;
VtxPtr->v.cn[0] = /*RGB */
VtxPtr->v.cn[1] =
VtxPtr->v.cn[2] = WaterShade; /*0xff; */
VtxPtr->v.cn[3] = 0xC0;
VtxPtr++;
}
WaterShade += 0x10;
}
/* And overlay(alpha texture) */
for(i=0; i<nVtxCountDown; i++) {
for(j=0; j<2; j++) {
VtxPtr->v.ob[0] = (s16)FallModelspacePoints[i][j].p[0]; /*XYZ */
VtxPtr->v.ob[1] = (s16)FallModelspacePoints[i][j].p[1];
VtxPtr->v.ob[2] = (s16)FallModelspacePoints[i][j].p[2];
VtxPtr->v.flag = 0;
VtxPtr->v.tc[0] = (s16)FallModelspaceUVs[i][j].p[0]<<1; /*UV */
VtxPtr->v.tc[1] = (s16)FallModelspaceUVs[i][j].p[1]<<1;
VtxPtr->v.cn[0] = /*RGB */
VtxPtr->v.cn[1] =
VtxPtr->v.cn[2] = 0xff;
VtxPtr->v.cn[3] = 0xC0;
VtxPtr++;
}
}
/* Draw waterfall */
gDPSetCombineMode(N64GfxDlPtr++, G_CC_MODULATERGB, G_CC_MODULATERGB);
TexDef = pWfDef->apTexDef[WATERFALL_TEXINDEX_RIGHT];
TexAddr = TexDef->aTMUTexInfo[0].nTmemStartAddress;
gDPLoadTextureBlock_4bS(N64GfxDlPtr++, TexAddr, G_IM_FMT_I, 32,32,0,
G_TX_WRAP, G_TX_WRAP, /* No mirror! */
5, 5, G_TX_NOLOD, G_TX_NOLOD);
TexAddr += ((32*32)/2);
*((u16 *)TexAddr) &= 0xfffe;
gDPLoadTLUT_pal256(N64GfxDlPtr++, TexAddr);
gSPVertex(N64GfxDlPtr++, &N64Vertices[draw_buffer][NumN64Vertices], nVtxCountDown*2, 0);
NumN64Vertices += nVtxCountDown*2;
for(i=0; i<(nVtxCountDown-1); i++) {
gSP2Triangles(N64GfxDlPtr++, 0+(i*2),1+(i*2),2+(i*2),0, 1+(i*2),3+(i*2),2+(i*2),0);
}
/* Add hi-lights */
gDPSetCombineMode(N64GfxDlPtr++, ASH_G_CC_BLENDPEDECALA, ASH_G_CC_BLENDPEDECALA);
gDPSetTextureLUT(N64GfxDlPtr++,G_TT_NONE);
gDPSetEnvColor(N64GfxDlPtr++, 36,78,117,0x0); /* Trans */
gDPSetPrimColor(N64GfxDlPtr++, 0,0, 212,241,255,0xc0); /* Opaque */
TexDef = pWfDef->apTexDef[WATERFALL_TEXINDEX_MAIN];
TexAddr = TexDef->aTMUTexInfo[0].nTmemStartAddress;
gDPLoadTextureBlock_4bS(N64GfxDlPtr++, TexAddr, G_IM_FMT_I, 32,32,0,
G_TX_WRAP, G_TX_WRAP, /* No mirror! */
5, 5, G_TX_NOLOD, G_TX_NOLOD);
gSPVertex(N64GfxDlPtr++, &N64Vertices[draw_buffer][NumN64Vertices], nVtxCountDown*2, 0);
NumN64Vertices += nVtxCountDown*2;
for(i=0; i<(nVtxCountDown-1); i++) {
gSP2Triangles(N64GfxDlPtr++, 0+(i*2),1+(i*2),2+(i*2),0, 1+(i*2),3+(i*2),2+(i*2),0);
}
/* Reset render modes */
gDPSetTextureLUT(N64GfxDlPtr++,G_TT_RGBA16);
}
/* 1. Show texture UV's correctly */
/* 2. Download correct-ish texture! */
/* 3. Do we draw x2 of these??? Then could take as different UV's(just scale) */
/* Print one(facing, and other away to give 1 cycle mip-map!! Yes? */
/* 4. Remove crap and clean - does work OK? */
#endif /*ULTRA64 */
#if TARGET==DREAMCAST
static void _DrawWaterfall( WaterfallDef_t *pWfDef ) {
float fTime, fLoopSecs, fVelY, fVelZ, fStartX, fEndX, q, oow, zoff;
float *pfCamspaceVtx, fClipRatio, fTexS, fTexT1, fTexT2, fWhiteSat, fOneOverHeight;
float fUnitDuration, fStartTexT1, fStartTexT2, fTemp, fPrevY, fPrevZ, fDy, fDz, fEdgeBend;
float fTileWidthX, fAnimateK1, fAnimateK2, fFlairAmount;
u32 nTileTopVtxArrayIndex, nTileCountAcross, nTileCountDown;
u32 nVtxCountDown, nVtxCountAcross,nWhiteSat;
u32 i, j, k, n, nTexture, layer, NumLayers, rgba;
s32 nAnimateAngle;
Vec3_t ModelspacePoint, BentModelspacePoint, *pModelspaceVtx, Normal;
GrVertex *pScreenspaceVtx;
TileVtx_t *pTileVtx, *apTileVtx[4];
ARGBf_t SensedColor;
BOOL bFlairEdges;
rgba = dcRGBA(
(unsigned int)_MistColor.argb[2],
(unsigned int)_MistColor.argb[1],
(unsigned int)_MistColor.argb[0],
0xffffffff
);
/* Set graphics modes... */
_nLightFrame = mesh3d_StartLightFrame();
/*mesh3d_ClearLightFrame(); */
nTexture = pWfDef->apTexDef[WATERFALL_TEXINDEX_MAIN]->aTMUTexInfo[0].nTmemStartAddress;
if(nTexture != -1)
dcSetCurrentVertexContext(&dcTextureTable[nTexture].AlphaBlendedTexVC);
// _BuildLightList( pWfDef );
// Don't animate water if paused !!
if(!bPaused){
pWfDef->fScrollT1 += _TEX_T_SCROLL_DELTA1*Gameloop_fDeltaFrames;
pWfDef->fScrollT2 += _TEX_T_SCROLL_DELTA2*Gameloop_fDeltaFrames;
}
fVelY = pWfDef->FlowVelocity.p[0];
fVelZ = pWfDef->FlowVelocity.p[1];
fStartX = pWfDef->fWidth * -0.5f;
fEndX = -fStartX;
fTileWidthX = pWfDef->fTileWidthX;
nTileCountAcross = pWfDef->nTileCountAcross;
nTileCountDown = pWfDef->nTileCountDown;
nVtxCountAcross = nTileCountAcross + 1;
nVtxCountDown = nTileCountDown + 1;
fOneOverHeight = -pWfDef->fOneOverHeight;
fAnimateK1 = Gameloop_fFrameCounter * 512.0f;
bFlairEdges = pWfDef->nFlags & WATERFALL_FLAG_FLAIR_EDGES;
fFlairAmount = 0.0f;
fLoopSecs = pWfDef->fLoopSecs;
fTime = -fLoopSecs;
nTileTopVtxArrayIndex = 0;
for( i=0; i<nVtxCountDown; i++, nTileTopVtxArrayIndex^=1, fTime+=fLoopSecs ) {
/* Each row of vertices... */
ModelspacePoint.p[0] = fStartX;
if( i == 0 ) {
/* Clip upper row... */
fClipRatio = fVelZ*fTime * (1.0f/_PANEL_Z_LENGTH);
fStartTexT1 = _TEX_T_TILE_DELTA1*fClipRatio + pWfDef->fScrollT1;
fStartTexT2 = _TEX_T_TILE_DELTA2*fClipRatio + pWfDef->fScrollT2;
ModelspacePoint.p[1] = 0.0f;
ModelspacePoint.p[2] = 0.0f;
fUnitDuration = 0.0f;
vec3_Set( &Normal, 0.0f, 1.0f, 0.0f );
}
else {
fPrevY = ModelspacePoint.p[1];
fPrevZ = ModelspacePoint.p[2];
fTemp = (float)i;
fStartTexT1 = pWfDef->fScrollT1 + i*_TEX_T_TILE_DELTA1;
fStartTexT2 = pWfDef->fScrollT2 + i*_TEX_T_TILE_DELTA2;
ModelspacePoint.p[1] = fVelY*fTime - (_GRAVITY_FEET_PER_SECSEC*0.5f)*fTime*fTime;
fUnitDuration = ModelspacePoint.p[1]*fOneOverHeight;
if( fUnitDuration > 1.0f ) {
fUnitDuration = 1.0f;
}
fTemp = fVelZ*fTime;
ModelspacePoint.p[2] = (fPrevZ-fTemp)*fUnitDuration*0.8f + fTemp;
/* Calc normal... */
fDy = fPrevY - ModelspacePoint.p[1];
fDz = fPrevZ - ModelspacePoint.p[2];
Normal.p[0] = 0.0f;
Normal.p[1] = fDz;
Normal.p[2] = -fDy;
vec3_Unit( &Normal );
}
if( i == nTileCountDown ) {
pWfDef->fLowerMistStartZ = ModelspacePoint.p[2];
}
if( fUnitDuration >= _WHITESAT_PERCENT_TO_END ) {
fWhiteSat = (fUnitDuration - _WHITESAT_PERCENT_TO_END) * (1.0f/(1.0f-_WHITESAT_PERCENT_TO_END));
fWhiteSat *= _WHITESAT_MULTIPLIER;
}
else {
fWhiteSat = 0.0f;
}
nWhiteSat = (u32)fWhiteSat;
if( fUnitDuration >= _EDGEBEND_PERCENT_TO_END ) {
fEdgeBend = (fUnitDuration - _EDGEBEND_PERCENT_TO_END) * (1.0f/(1.0f-_EDGEBEND_PERCENT_TO_END));
fEdgeBend *= _EDGEBEND_MULTIPLIER;
}
else {
fEdgeBend = 0.0f;
}
if( bFlairEdges ) {
if( fUnitDuration >= _FLAIR_PERCENT_TO_END ) {
fFlairAmount = _FLAIR_MAX;
} else {
fFlairAmount = fUnitDuration * (1.0f/(1.0f-_FLAIR_PERCENT_TO_END));
fFlairAmount *= _FLAIR_MAX;
}
}
fAnimateK2 = fAnimateK1 + ModelspacePoint.p[1]*385.0f;
fTexS = 0.0f;
for( j=0; j<nVtxCountAcross; j++, ModelspacePoint.p[0]+=fTileWidthX, fTexS+=_TEX_S_TILE_DELTA ) {
/* Each vertex across... */
BentModelspacePoint.p[0] = ModelspacePoint.p[0];
BentModelspacePoint.p[1] = ModelspacePoint.p[1];
BentModelspacePoint.p[2] = ModelspacePoint.p[2];
if( j==0 || j==nTileCountAcross ) {
if( fEdgeBend ) {
BentModelspacePoint.p[1] -= Normal.p[1]*fEdgeBend;
BentModelspacePoint.p[2] -= Normal.p[2]*fEdgeBend;
}
if( fFlairAmount ) {
if( j == 0 ) {
BentModelspacePoint.p[0] -= fFlairAmount;
} else {
BentModelspacePoint.p[0] += fFlairAmount;
}
}
}
nAnimateAngle = (int)( BentModelspacePoint.p[0]*475.0f + fAnimateK2 );
fTexT1 = fStartTexT1 + _ST_DISTORT_AMPLITUDE*xmath_sin( nAnimateAngle );
fTexT2 = fStartTexT2 + _ST_DISTORT_AMPLITUDE*xmath_cos( nAnimateAngle );
pTileVtx = &_aTileGrVtx[nTileTopVtxArrayIndex][j];
pModelspaceVtx = &pTileVtx->ModelspaceVtx;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
pTileVtx->bScreenspaceVtxValid = FALSE;
pTileVtx->bSensedLight = FALSE;
pTileVtx->anOutsidePlaneCode[0]
= pTileVtx->anOutsidePlaneCode[1]
= pTileVtx->anOutsidePlaneCode[2]
= pTileVtx->anOutsidePlaneCode[3]
= _OUTCODE_UNKNOWN;
pModelspaceVtx->p[0] = BentModelspacePoint.p[0];
pModelspaceVtx->p[1] = BentModelspacePoint.p[1];
pModelspaceVtx->p[2] = BentModelspacePoint.p[2];
pfCamspaceVtx[0] = Xfm_pStackTop->mf[0][0] * BentModelspacePoint.p[0]
+ Xfm_pStackTop->mf[1][0] * BentModelspacePoint.p[1]
+ Xfm_pStackTop->mf[2][0] * BentModelspacePoint.p[2]
+ Xfm_pStackTop->mf[3][0];
pfCamspaceVtx[1] = Xfm_pStackTop->mf[0][1] * BentModelspacePoint.p[0]
+ Xfm_pStackTop->mf[1][1] * BentModelspacePoint.p[1]
+ Xfm_pStackTop->mf[2][1] * BentModelspacePoint.p[2]
+ Xfm_pStackTop->mf[3][1];
pfCamspaceVtx[2] = Xfm_pStackTop->mf[0][2] * BentModelspacePoint.p[0]
+ Xfm_pStackTop->mf[1][2] * BentModelspacePoint.p[1]
+ Xfm_pStackTop->mf[2][2] * BentModelspacePoint.p[2]
+ Xfm_pStackTop->mf[3][2];
pfCamspaceVtx[7] = (j!=nTileCountAcross) ? fTexS : fTexS + _TEX_S_RIGHTEDGE_DELTA;
pfCamspaceVtx[8] = fTexT1;
pfCamspaceVtx[9] = fTexT2;
pTileVtx->Specular = dcRGB(nWhiteSat, nWhiteSat, nWhiteSat);
if( i>0 && j>0 ) {
/* Draw a tile... */
apTileVtx[0] = &_aTileGrVtx[nTileTopVtxArrayIndex][j];
apTileVtx[1] = &_aTileGrVtx[1-nTileTopVtxArrayIndex][j];
apTileVtx[2] = &_aTileGrVtx[nTileTopVtxArrayIndex][j-1];
apTileVtx[3] = &_aTileGrVtx[1-nTileTopVtxArrayIndex][j-1];
for(k = 0; k < 4; k++){
pTileVtx = apTileVtx[k];
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
if(pfCamspaceVtx[2] > 0){
pScreenspaceVtx->ooz = 1.0f / pfCamspaceVtx[2];
q = Viewport_zscreen * pScreenspaceVtx->ooz;
pScreenspaceVtx->x = 320.0f + q*pfCamspaceVtx[0];
pScreenspaceVtx->y = dcViewportYTrans - q*pfCamspaceVtx[1];
pTileVtx->ScreenspaceVtx.tmuvtx[0].sow =
pfCamspaceVtx[7] * DCRECIP256;
pTileVtx->ScreenspaceVtx.tmuvtx[0].tow =
pfCamspaceVtx[8] * DCRECIP256;
pTileVtx->ScreenspaceVtx.tmuvtx[1].tow =
pfCamspaceVtx[9] * DCRECIP256;
}
// if(!pTileVtx->bSensedLight){
// pTileVtx->bSensedLight = TRUE;
// pModelspaceVtx = &pTileVtx->ModelspaceVtx;
// mesh3d_LightSensor( pModelspaceVtx, &Normal, &SensedColor, FALSE );
// pScreenspaceVtx->a = 255.0f;
// pScreenspaceVtx->r = SensedColor.argb[0] * 255.0f;
// pScreenspaceVtx->g = SensedColor.argb[1] * 255.0f;
// pScreenspaceVtx->b = SensedColor.argb[2] * 255.0f;
// }
}
if( j==1 ) {
nTexture = pWfDef->apTexDef[WATERFALL_TEXINDEX_LEFT]->aTMUTexInfo[0].nTmemStartAddress;
}
else if( j==nTileCountAcross ) {
nTexture = pWfDef->apTexDef[WATERFALL_TEXINDEX_RIGHT]->aTMUTexInfo[0].nTmemStartAddress;
}
else{
nTexture = pWfDef->apTexDef[WATERFALL_TEXINDEX_MAIN]->aTMUTexInfo[0].nTmemStartAddress;
}
if(nTexture != -1)
dcSetCurrentVertexContext(&dcTextureTable[nTexture].AlphaBlendedTexVC);
kmxxGetCurrentPtr(&VertexBufferDesc);
kmxxStartVertexStrip(&VertexBufferDesc);
kmxxReleaseCurrentPtr(&VertexBufferDesc);
if(bEUROCOM_SPLITSCREEN)
NumLayers = 1;
else
NumLayers = 2;
for(layer = 0, zoff = 0; layer < NumLayers; layer++){
for(k=0; k<2; k++ ){
if( apTileVtx[k]->afCamspaceVtx[2] < 0 &&
apTileVtx[k+1]->afCamspaceVtx[2] < 0 &&
apTileVtx[k+2]->afCamspaceVtx[2] < 0){
continue;
}
if( apTileVtx[k]->afCamspaceVtx[2] < 0 ||
apTileVtx[k+1]->afCamspaceVtx[2] < 0 ||
apTileVtx[k+2]->afCamspaceVtx[2] < 0){
_nTriCrossesPlanesMask = 0xf;
_ClipTriToFrustumAndDraw( &apTileVtx[k], FALSE );
}
else{
kmxxGetCurrentPtr(&VertexBufferDesc);
// rgba = dcRGBA(
// (u32)apTileVtx[k]->ScreenspaceVtx.r,
// (u32)apTileVtx[k]->ScreenspaceVtx.g,
// (u32)apTileVtx[k]->ScreenspaceVtx.b,
// 255
// );
kmxxSetVertex_3( KM_VERTEXPARAM_NORMAL,
apTileVtx[k]->ScreenspaceVtx.x,
apTileVtx[k]->ScreenspaceVtx.y,
apTileVtx[k]->ScreenspaceVtx.ooz + zoff,
apTileVtx[k]->ScreenspaceVtx.tmuvtx[0].sow,
apTileVtx[k]->ScreenspaceVtx.tmuvtx[layer].tow,
rgba,
apTileVtx[k]->Specular
);
// rgba = dcRGBA(
// (u32)apTileVtx[k+1]->ScreenspaceVtx.r,
// (u32)apTileVtx[k+1]->ScreenspaceVtx.g,
// (u32)apTileVtx[k+1]->ScreenspaceVtx.b,
// 255
// );
kmxxSetVertex_3( KM_VERTEXPARAM_NORMAL,
apTileVtx[k+1]->ScreenspaceVtx.x,
apTileVtx[k+1]->ScreenspaceVtx.y,
apTileVtx[k+1]->ScreenspaceVtx.ooz + zoff,
apTileVtx[k+1]->ScreenspaceVtx.tmuvtx[0].sow,
apTileVtx[k+1]->ScreenspaceVtx.tmuvtx[layer].tow,
rgba,
apTileVtx[k+1]->Specular
);
// rgba = dcRGBA(
// (u32)apTileVtx[k+2]->ScreenspaceVtx.r,
// (u32)apTileVtx[k+2]->ScreenspaceVtx.g,
// (u32)apTileVtx[k+2]->ScreenspaceVtx.b,
// 255
// );
kmxxSetVertex_3( KM_VERTEXPARAM_ENDOFSTRIP,
apTileVtx[k+2]->ScreenspaceVtx.x,
apTileVtx[k+2]->ScreenspaceVtx.y,
apTileVtx[k+2]->ScreenspaceVtx.ooz + zoff,
apTileVtx[k+2]->ScreenspaceVtx.tmuvtx[0].sow,
apTileVtx[k+2]->ScreenspaceVtx.tmuvtx[layer].tow,
rgba,
apTileVtx[k+2]->Specular
);
kmxxReleaseCurrentPtr(&VertexBufferDesc);
}
}
}
}
}
}
mesh3d_ReleaseLightFrame( _nLightFrame );
}
static void _ClipTriToFrustumAndDraw( TileVtx_t **ppTileVtx, BOOL bTestForScreenCoverage ) {
u32 i, j, k, nVtxInCount, nVtxStartOutIndex, nNumTris, nVtxInIndex, nVtxOutIndex;
float fPrevDot, fThisDot, fScale, x, y, w, q;
u32 nWalkingBit;
TileVtx_t *pVtxIn, *pPrevVtxIn, *pVtxBuf;
for( i=0; i<3; i++ ) {
ppTileVtx[i]->afCamspaceVtx[3] = ppTileVtx[i]->ScreenspaceVtx.r;
ppTileVtx[i]->afCamspaceVtx[4] = ppTileVtx[i]->ScreenspaceVtx.g;
ppTileVtx[i]->afCamspaceVtx[5] = ppTileVtx[i]->ScreenspaceVtx.b;
ppTileVtx[i]->afCamspaceVtx[6] = ppTileVtx[i]->ScreenspaceVtx.a;
_apTileClippedVtx[i] = ppTileVtx[i];
}
pVtxBuf = _aTileClippedVtx;
nVtxInIndex = 0;
nVtxOutIndex = 3;
nVtxInCount = 3;
for( j=0, nWalkingBit=1; j<4; j++, nWalkingBit<<=1 ) {
if( _nTriCrossesPlanesMask & nWalkingBit ) {
nVtxStartOutIndex = nVtxOutIndex;
pVtxIn = _apTileClippedVtx[ nVtxInIndex + nVtxInCount - 1 ];
fPrevDot = pVtxIn->afCamspaceVtx[0]*Viewport_rect_norm[j].p[0]
+ pVtxIn->afCamspaceVtx[1]*Viewport_rect_norm[j].p[1]
+ pVtxIn->afCamspaceVtx[2]*Viewport_rect_norm[j].p[2]
- GUTIL_CLIPPING_AVOID_DIVZERO_MARGIN;
for( i=0; i<nVtxInCount; i++ ) {
pPrevVtxIn = pVtxIn;
if( fPrevDot>0.0f ) {
_apTileClippedVtx[nVtxOutIndex++] = pVtxIn;
}
pVtxIn = _apTileClippedVtx[nVtxInIndex++];
fThisDot = pVtxIn->afCamspaceVtx[0]*Viewport_rect_norm[j].p[0]
+ pVtxIn->afCamspaceVtx[1]*Viewport_rect_norm[j].p[1]
+ pVtxIn->afCamspaceVtx[2]*Viewport_rect_norm[j].p[2]
- GUTIL_CLIPPING_AVOID_DIVZERO_MARGIN;
if( fPrevDot*fThisDot < 0.0f ) {
/* vertices are on opposite sides of nearplane*/
fScale = fPrevDot / (fPrevDot-fThisDot);
for( k=0; k<_CAMSPACE_ITERATOR_COUNT; k++ ) {
pVtxBuf->afCamspaceVtx[k] = pPrevVtxIn->afCamspaceVtx[k]
+ (pVtxIn->afCamspaceVtx[k] - pPrevVtxIn->afCamspaceVtx[k] )*fScale;
}
_apTileClippedVtx[ nVtxOutIndex++ ] = pVtxBuf++;
}
fPrevDot = fThisDot;
}
nVtxInCount = nVtxOutIndex - nVtxStartOutIndex;
if( nVtxInCount==0 ) {
return;
}
}
}
for( i=0, j=nVtxInIndex; i<nVtxInCount; i++, j++ ) {
pVtxIn = _apTileClippedVtx[j];
pVtxIn->ScreenspaceVtx.ooz = 1.0f / pVtxIn->afCamspaceVtx[2];
q = Viewport_zscreen * pVtxIn->ScreenspaceVtx.ooz;
pVtxIn->ScreenspaceVtx.x = 320.0f + q*pVtxIn->afCamspaceVtx[0];
pVtxIn->ScreenspaceVtx.y = dcViewportYTrans - q*pVtxIn->afCamspaceVtx[1];
pVtxIn->ScreenspaceVtx.r = pVtxIn->afCamspaceVtx[3];
pVtxIn->ScreenspaceVtx.g = pVtxIn->afCamspaceVtx[4];
pVtxIn->ScreenspaceVtx.b = pVtxIn->afCamspaceVtx[5];
pVtxIn->ScreenspaceVtx.a = pVtxIn->afCamspaceVtx[6];
}
nNumTris = nVtxInCount - 2;
/* Draw base water...*/
for( j=0; j<nVtxInCount; j++ ) {
pVtxIn = _apTileClippedVtx[j + nVtxInIndex];
w = pVtxIn->ScreenspaceVtx.oow;
pVtxIn->ScreenspaceVtx.tmuvtx[0].sow = pVtxIn->afCamspaceVtx[7] * DCRECIP256;
pVtxIn->ScreenspaceVtx.tmuvtx[0].tow = pVtxIn->afCamspaceVtx[8] * DCRECIP256;
}
for( i=0; i<nNumTris; i++ ) {
kmxxGetCurrentPtr(&VertexBufferDesc);
kmxxStartVertexStrip(&VertexBufferDesc);
kmxxSetVertex_3( KM_VERTEXPARAM_NORMAL,
_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.x,
_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.y,
_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.ooz,
_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.tmuvtx[0].sow,
_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.tmuvtx[0].tow,
dcRGBA((unsigned int)_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.r,
(unsigned int)_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.g,
(unsigned int)_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.b,
(unsigned int)_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.a),
0xff000000
);
kmxxSetVertex_3( KM_VERTEXPARAM_NORMAL,
_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.x,
_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.y,
_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.ooz,
_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.tmuvtx[0].sow,
_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.tmuvtx[0].tow,
dcRGBA((unsigned int)_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.r,
(unsigned int)_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.g,
(unsigned int)_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.b,
(unsigned int)_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.a),
0xff000000
);
kmxxSetVertex_3( KM_VERTEXPARAM_ENDOFSTRIP,
_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.x,
_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.y,
_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.ooz,
_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.tmuvtx[0].sow,
_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.tmuvtx[0].tow,
dcRGBA((unsigned int)_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.r,
(unsigned int)_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.g,
(unsigned int)_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.b,
(unsigned int)_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.a),
0xff000000
);
kmxxReleaseCurrentPtr(&VertexBufferDesc);
}
}
static void _CalcWaterfallLighting(WaterfallDef_t *pWfDef)
{
u32 nLightFrame;
WorldSector_t *pSector;
float fIntensity;
static const Vec3_t Normal = { 0.0f, 0.707107f, -0.707107f };
static const Vec3_t Pos = { 0.0f, 0.0f, 0.0f };
pSector = pWfDef->Tws.pCenterSector;
world_BuildObjectAmbient( pSector->pSectorCache, pWfDef->fUnitPercIntoSector );
world_BuildObjectCelestial( pSector->pSectorCache, pWfDef->fUnitPercIntoSector );
mesh3d_LightSensor( &Pos, &Normal, &_MistColor, FALSE );
fIntensity = 255.0f * gutil_RgbToIntensity( _MistColor.argb[2], _MistColor.argb[1], _MistColor.argb[0] );
fIntensity = XMATH_FPOT( 0.35f, fIntensity, 255.0f );
_MistColor.argb[2] = fIntensity;
_MistColor.argb[1] = fIntensity;
_MistColor.argb[0] = fIntensity;
}
void waterfall_DrawWaterfallMist( void ) {
u32 i, nPriorZbufState, nLightFrame, nTexture;
// float fIntensity;
WaterfallDef_t *pWfDef;
WorldSector_t *pSector;
// static const Vec3_t Normal = { 0.0f, 0.707107f, -0.707107f };
// static const Vec3_t Pos = { 0.0f, 0.0f, 0.0f };
if( _nMistDrawListCount ) {
for( i=0; i<_nMistDrawListCount; i++ ) {
pWfDef = _apMistDrawList[i];
if( pWfDef->apTexDef[WATERFALL_TEXINDEX_MIST] && !(pWfDef->nFlags & WATERFALL_FLAG_DONT_DRAW) ) {
nTexture = pWfDef->apTexDef[WATERFALL_TEXINDEX_MIST]->aTMUTexInfo[0].nTmemStartAddress;
if(nTexture != -1)
dcSetCurrentVertexContext(&dcTextureTable[nTexture].AlphaBlendedTexVC);
pSector = pWfDef->Tws.pCenterSector;
if( pSector ) {
// nLightFrame = mesh3d_StartLightFrame();
/*mesh3d_ClearLightFrame();*/
xfm_Push( &pWfDef->Xfm );
// world_BuildObjectAmbient( pSector->pSectorCache, pWfDef->fUnitPercIntoSector );
// world_BuildObjectCelestial( pSector->pSectorCache, pWfDef->fUnitPercIntoSector );
// mesh3d_LightSensor( &Pos, &Normal, &_MistColor, FALSE );
// fIntensity = 255.0f * gutil_RgbToIntensity( _MistColor.argb[2], _MistColor.argb[1], _MistColor.argb[0] );
// fIntensity = XMATH_FPOT( 0.35f, fIntensity, 255.0f );
// _MistColor.argb[2] = fIntensity;
// _MistColor.argb[1] = fIntensity;
// _MistColor.argb[0] = fIntensity;
_DrawMist( pWfDef );
xfm_Pop();
// mesh3d_ReleaseLightFrame( nLightFrame );
}
}
}
_nMistDrawListCount = 0;
}
}
static void _DrawMist( WaterfallDef_t *pWfDef ) {
float fGapsAcross, fExcessX, fStartX, fEndX, fStartZ, fStartY;
float fModelCamX, fModelCamZ, fModelCamR, fModelCamOor, fSin, fCos;
float fI, fUnitLife, *pfCamspaceVtx, fSize, oow, q;
float fMinUnitLife, fOrigIndex, fSteppingX, fTemp, fTravelY;
float fStartSize, fEndSize, fDeltaSize;
TileVtx_t *pTileVtx;
u32 i, j, k, n, nNumAcross, nNumPops, nAdjustedFrameCounter, nMinUnitLifeIndex,argb,nOpaqueness;
Vec3_t MistOrigin, *pModelspaceVtx;
GrVertex *pScreenspaceVtx;
static float afUnsortedUnitLife[_MIST_COUNT][_MIST_MAX_ACROSS];
static float afUnitLife[_MIST_COUNT][_MIST_MAX_ACROSS];
static float afOrigIndex[_MIST_COUNT][_MIST_MAX_ACROSS];
argb = dcRGBA(
(unsigned int)_MistColor.argb[2],
(unsigned int)_MistColor.argb[1],
(unsigned int)_MistColor.argb[0],
0
);
/* Calculate the number of gaps across and mist particles across...*/
fGapsAcross = pWfDef->fWidth*(1.0f/_MIST_GAP_X) + 1.0f;
nNumAcross = (int)fGapsAcross + 1;
if( nNumAcross > _MIST_MAX_ACROSS ) {
/* Too much mist. Don't draw it...*/
return;
}
/* Calculate particle start and end coordinates...*/
fExcessX = fGapsAcross*_MIST_GAP_X - pWfDef->fWidth;
fEndX = (pWfDef->fWidth + fExcessX)*0.5f;
fStartX = -fEndX;
fStartY = -pWfDef->fHeight;
fStartZ = pWfDef->fLowerMistStartZ + _MIST_START_DELTA_Z;
fTravelY = _MIST_TRAVEL_Y_UNITPERC * pWfDef->fHeight;
fStartSize = _MIST_MIN_SIZE * pWfDef->fHeight;
fEndSize = _MIST_MAX_SIZE * pWfDef->fHeight;
fDeltaSize = fEndSize - fStartSize;
/* Clip against all frustum planes...*/
_nBoundCrossesPlanesMask = 0xf;
/* Calculate unit life values for each particle...*/
for( i=0, fI=0.0f; i<_MIST_COUNT; i++, fI+=1.0f ) {
/* Calculate base life...*/
nAdjustedFrameCounter =
(u32)( Gameloop_fFrameCounter
+ fI * ( (float)(1<<_MIST_LIFE_LOG2) / (float)_MIST_COUNT ) );
fUnitLife = (nAdjustedFrameCounter & ( (1<<_MIST_LIFE_LOG2)-1 ))
* (1.0f/(float)(1<<_MIST_LIFE_LOG2));
for( j=0; j<nNumAcross; j++ ) {
/* Now adjust life by random amount based on the column number...*/
fTemp = fUnitLife + (0.5f/(float)_MIST_COUNT) * _afRandomFloatTable[j];
if( fTemp >= 1.0f ) {
fTemp -= 1.0f;
} else if( fTemp < 0.0f ) {
fTemp += 1.0f;
}
afUnsortedUnitLife[i][j] = fTemp;
}
}
/* Sort unit life values from lowest to highest for each column...*/
for( j=0; j<nNumAcross; j++ ) {
/* Each column...*/
/* Find lowest life value for this column...*/
fMinUnitLife = 2.0f;
for( i=0; i<_MIST_COUNT; i++ ) {
if( afUnsortedUnitLife[i][j] < fMinUnitLife ) {
fMinUnitLife = afUnsortedUnitLife[i][j];
nMinUnitLifeIndex = i;
}
}
/* Copy unit lifes, sorted from lowest to highest...*/
for( i=0; i<_MIST_COUNT; i++ ) {
afUnitLife[i][j] = afUnsortedUnitLife[nMinUnitLifeIndex][j];
afOrigIndex[i][j] = (float)nMinUnitLifeIndex;
if( ++nMinUnitLifeIndex >= _MIST_COUNT ) {
nMinUnitLifeIndex = 0;
}
}
}
for( i=0; i<_MIST_COUNT; i++ ) {
/* Each row of mist objects...*/
fSteppingX = fStartX;
for( j=0; j<nNumAcross; j++, fSteppingX+=_MIST_GAP_X ) {
/* Each mist object across...*/
fUnitLife = afUnitLife[i][j];
fOrigIndex = afOrigIndex[i][j];
fSize = fDeltaSize*fUnitLife + fStartSize;
if( fUnitLife < _MIST_FADEIN_UNITPERC ) {
nOpaqueness = (u32)(255.0f * (1.0f/_MIST_FADEIN_UNITPERC)) * fUnitLife;
} else if( fUnitLife > _MIST_FADEOUT_UNITPERC ) {
nOpaqueness = (u32)(255.0f - ( (255.0f * (1.0f/(1.0f-_MIST_FADEOUT_UNITPERC))) * (fUnitLife-_MIST_FADEOUT_UNITPERC) ));
} else {
nOpaqueness = (u32)255.0f;
}
MistOrigin.p[0] = fSteppingX;
fTemp = fTravelY + (fTravelY*0.2f)*_afRandomFloatTable[j];
MistOrigin.p[1] = fTemp*fUnitLife + fStartY;
MistOrigin.p[2] = _MIST_TRAVEL_Z*fUnitLife + fStartZ;
/* Create and push mist particle transformation matrix...*/
_XfmMistPos.mf[3][0] = MistOrigin.p[0];
_XfmMistPos.mf[3][1] = MistOrigin.p[1];
_XfmMistPos.mf[3][2] = MistOrigin.p[2];
_XfmMistPos.mr[3][0] = -MistOrigin.p[0];
_XfmMistPos.mr[3][1] = -MistOrigin.p[1];
_XfmMistPos.mr[3][2] = -MistOrigin.p[2];
xfm_Push( &_XfmMistPos );
nNumPops = 1;
/* Create poster transformation matrix...*/
fModelCamX = Xfm_pStackTop->mr[3][0];
fModelCamZ = Xfm_pStackTop->mr[3][2];
fModelCamR = xmath_sqrt( fModelCamX*fModelCamX + fModelCamZ*fModelCamZ );
if( fModelCamR ) {
fModelCamOor = 1.0f / fModelCamR;
fSin = fModelCamOor * fModelCamX;
fCos = fModelCamOor * fModelCamZ;
_XfmPosterY.mf[0][0] = _XfmPosterY.mf[2][2] = fCos;
_XfmPosterY.mr[0][0] = _XfmPosterY.mr[2][2] = fCos;
_XfmPosterY.mf[0][2] = _XfmPosterY.mr[2][0] = -fSin;
_XfmPosterY.mf[2][0] = _XfmPosterY.mr[0][2] = fSin;
xfm_Push( &_XfmPosterY );
nNumPops++;
/* Transform vertices...*/
for( k=0; k<4; k++ ) {
pTileVtx = &_aMistTileVtx[k];
pModelspaceVtx = &pTileVtx->ModelspaceVtx;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
pTileVtx->bScreenspaceVtxValid = FALSE;
pTileVtx->anOutsidePlaneCode[0]
= pTileVtx->anOutsidePlaneCode[1]
= pTileVtx->anOutsidePlaneCode[2]
= pTileVtx->anOutsidePlaneCode[3]
= _OUTCODE_UNKNOWN;
pModelspaceVtx->p[0] = _aMistVtx[k].p[0]*fSize;
pModelspaceVtx->p[1] = _aMistVtx[k].p[1]*fSize;
pModelspaceVtx->p[2] = 0.0f;
pfCamspaceVtx[0] = Xfm_pStackTop->mf[0][0] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][0] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][0];
pfCamspaceVtx[1] = Xfm_pStackTop->mf[0][1] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][1] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][1];
pfCamspaceVtx[2] = Xfm_pStackTop->mf[0][2] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][2] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][2];
pfCamspaceVtx[7] = _aMistSt[k].p[0];
pfCamspaceVtx[8] = _aMistSt[k].p[1];
pfCamspaceVtx[9] = 0.0f;
// pScreenspaceVtx->a = fOpaqueness;
// pScreenspaceVtx->r = _MistColor.argb[2];
// pScreenspaceVtx->g = _MistColor.argb[1];
// pScreenspaceVtx->b = _MistColor.argb[0];
}
/* Draw triangles...*/
for( k=0; k<2; k++ ) {
if( _TestTriAgainstFrustum( &_apMistTileVtx[k] ) ) {
/* Triangle is at least partially visible...*/
if( !_nTriCrossesPlanesMask ) {
/* Triangle doesn't require frustum clipping...*/
for( n=0; n<3; n++ ) {
pTileVtx = _apMistTileVtx[n+k];
if( !pTileVtx->bScreenspaceVtxValid ) {
pTileVtx->bScreenspaceVtxValid = TRUE;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
pScreenspaceVtx->ooz = 1.0f / pfCamspaceVtx[2];
q = Viewport_zscreen * pScreenspaceVtx->ooz;
pScreenspaceVtx->x = 320.0f + q*pfCamspaceVtx[0];
pScreenspaceVtx->y = dcViewportYTrans - q*pfCamspaceVtx[1];
pTileVtx->ScreenspaceVtx.tmuvtx[0].sow =
pTileVtx->afCamspaceVtx[7];
pTileVtx->ScreenspaceVtx.tmuvtx[0].tow =
pTileVtx->afCamspaceVtx[8];
}
}
// gutil_ScreenCoverage_DrawTriangleIfUnderLimit(
// &_apMistTileVtx[k]->ScreenspaceVtx,
// &_apMistTileVtx[k+1]->ScreenspaceVtx,
// &_apMistTileVtx[k+2]->ScreenspaceVtx
// );
kmxxGetCurrentPtr(&VertexBufferDesc);
kmxxStartVertexStrip(&VertexBufferDesc);
kmxxSetVertex_3( KM_VERTEXPARAM_NORMAL,
_apMistTileVtx[k]->ScreenspaceVtx.x,
_apMistTileVtx[k]->ScreenspaceVtx.y,
_apMistTileVtx[k]->ScreenspaceVtx.ooz,
_apMistTileVtx[k]->ScreenspaceVtx.tmuvtx[0].sow * DCRECIP256,
_apMistTileVtx[k]->ScreenspaceVtx.tmuvtx[0].tow * DCRECIP256,
argb | (nOpaqueness << 24),
0xff000000
);
kmxxSetVertex_3( KM_VERTEXPARAM_NORMAL,
_apMistTileVtx[k+1]->ScreenspaceVtx.x,
_apMistTileVtx[k+1]->ScreenspaceVtx.y,
_apMistTileVtx[k+1]->ScreenspaceVtx.ooz,
_apMistTileVtx[k+1]->ScreenspaceVtx.tmuvtx[0].sow * DCRECIP256,
_apMistTileVtx[k+1]->ScreenspaceVtx.tmuvtx[0].tow * DCRECIP256,
argb | (nOpaqueness << 24),
0xff000000
);
kmxxSetVertex_3( KM_VERTEXPARAM_ENDOFSTRIP,
_apMistTileVtx[k+2]->ScreenspaceVtx.x,
_apMistTileVtx[k+2]->ScreenspaceVtx.y,
_apMistTileVtx[k+2]->ScreenspaceVtx.ooz,
_apMistTileVtx[k+2]->ScreenspaceVtx.tmuvtx[0].sow * DCRECIP256,
_apMistTileVtx[k+2]->ScreenspaceVtx.tmuvtx[0].tow * DCRECIP256,
argb | (nOpaqueness << 24),
0xff000000
);
kmxxReleaseCurrentPtr(&VertexBufferDesc);
} else {
/* Must clip triangle to frustum...*/
_ClipTriToFrustumAndDraw( &_apMistTileVtx[k], TRUE );
}
}
}
}
xfm_PopN( nNumPops );
}
}
}
#endif /* DREAMCAST */
waterspray.c#
Found at 0x170433D0.
/*////////////////////////////////////////////////////////////////////////////////////*/
/* waterspray.c - Functions to generate water spray and rooster tails.*/
/**/
/* Author: Steve Ranck */
/*////////////////////////////////////////////////////////////////////////////////////*/
/* THIS CODE IS PROPRIETARY PROPERTY OF MIDWAY HOME ENTERTAINMENT.*/
/* Copyright (c) 1997*/
/**/
/* The contents of this file may not be disclosed to third*/
/* parties, copied or duplicated in any form, in whole or in part,*/
/* without the prior written permission of Midway Home Entertainment.*/
/*////////////////////////////////////////////////////////////////////////////////////*/
/* Modification History:*/
/**/
/* Date Who Description*/
/* -------- ---------- --------------------------------------------------------------*/
/* 12/09/97 Ranck Created.*/
/*////////////////////////////////////////////////////////////////////////////////////*/
#include "gendefs.h"
#include "waterspray.h"
#include "water.h"
#include "world.h"
#include "worldob.h"
#include "vec2.h"
#include "vec3.h"
#include "xfm.h"
#include "orient.h"
#include "mtx3.h"
#include "gutil.h"
#include "viewport.h"
#include "mesh3d.h"
#include "material.h"
#include "glide.h"
#include "xmath.h"
#include "tmem.h"
#include "gfxdefs.h"
#include "glcount.h"
#include "obsys.h"
#include "mesh3d.h"
#include "sysmem.h"
#if !SYS_WINLIB_TARGET
#include "gamecam.h"
#endif
#if TARGET==ULTRA64
#include "u64draw.h"
#include "silib.h"
//#define DISABLE_WATERSPRAY // Turn the bugger off!
typedef struct { /* Triangle definition: */
Vec3_t BoundCenter; /* center of bounding sphere */
float fBoundRadius; /* radius of bounding sphere */
Vec3_t FaceUnitNorm; /* face unit normal */
Mesh3dTriVtx_t TriVtx[3];/* indices into Mesh3dVtxDef_t, Mesh3dTcDef_t, Mesh3dVnDef_t */
} Orig_Mesh3dTriDef_t;
typedef struct { /* Triangle list definition: */
u32 nTriCount; /* number of triangles in this list */
Orig_Mesh3dTriDef_t *pTri; /* pointer to first triangle in this material (from Mesh3d_t *) */
Mesh3dMtlDef_t *pMtl; /* pointer to material used for this tlist */
} Orig_Mesh3dTlDef_t;
int WaterSprayNumVertsInCache;
int WaterSprayStartVertex;
#endif /*ULTRA64*/
#if TARGET==ULTRA64
/* Loads less spray - just on play boat! */
#define _MAX_SPRAY_OBJECTS 20 // ie 20 polygons max
#define _MAX_EMITTERS_PER_OBJECT 1
#else /*ULTRA64*/
#define _MAX_SPRAY_OBJECTS 50
#define _MAX_EMITTERS_PER_OBJECT 20
#endif /*ULTRA64*/
#define _PARTICLE_VTX_X 1.0f
#define _PARTICLE_VTX_Y 1.0f
#define _MIN_PARTICLE_SPEED 0.5f
#define _MAX_PARTICLE_SPEED 3.0f
#define _CAMSPACE_ITERATOR_COUNT 6
#define _ITERATOR_X 0
#define _ITERATOR_Y 1
#define _ITERATOR_Z 2
#define _ITERATOR_A 3
#define _ITERATOR_S 4
#define _ITERATOR_T 5
#define _OUTCODE_UNKNOWN 0
#define _OUTCODE_OUTSIDE 1
#define _OUTCODE_INSIDE 2
#define _MAX_TRIS_FOR_SPRAY_TYPE 32
#define _ORIGIN_MARGIN 0.5f
enum _MESH3D_MTL_ID {
_MESH3D_MTL_ID_IMPACT = 0,
_MESH3D_MTL_ID_DRAG,
_MESH3D_MTL_ID_COUNT
};
typedef struct {
Vec3_t ModelPoint; /* Modelspace point*/
Vec3_t ModelUnitNormal; /* Modelspace unit normal (X/Z plane)*/
} EmitterDef_t;
typedef struct { /* One of these per unique WorldOb*/
u32 nUsedCount; /* Number of SprayDef's using this FlumeTransform*/
BOOL bUseMtx; /* TRUE: Particle velocities must be run through Mtx (flumes only)*/
u32 nGameFrame; /* Gameloop_nFrameCounter when Mtx was last calculated*/
Mtx3_t Mtx; /* Rotation matrix (valid only when bUseMtx is TRUE)*/
} FlumeTransform_t;
typedef struct {
WatersprayType_t nType; /* Indicates the type of particle*/
WatersprayControlFlags_t *pnControlFlags;/* Points to control flags (must NOT be NULL)*/
WorldOb_t *pWorldOb; /* Pointer to world object*/
FlumeTransform_t *pFlumeTransform; /* Points to flume transformation information*/
BOOL8 bUsedSlot; /* TRUE: This entry used, FALSE=unused*/
u8 nEmitterCount;
u8 nNextArrayIndex; /* Next index into aWorldPoints[here][], 2=none*/
u8 nEmitterLaunchTimer; /* Gameloops before another emitter can fire*/
float fRoosterUnitPower;
const float *pfSprayEmitterHeightAdjust;/* Points to value added to all emitter model point Y coordinates*/
const float *pfMistEmitterHeightAdjust; /* Points to value multiplied by all emitter model XZ coordinates*/
EmitterDef_t aEmitters[_MAX_EMITTERS_PER_OBJECT];
Vec2_t aWorldPoints[2][_MAX_EMITTERS_PER_OBJECT];
} SprayDef_t;
typedef struct {
WatersprayType_t nType;
WatersprayControlFlags_t nControlFlags;
Vec3_t HeadPoint;
Vec3_t TailPoint;
Vec3_t HeadVelocity;
Vec3_t TailVelocity;
float fScaleX;
float fDeltaScaleX;
WorldSector_t *pSector;
const WorldOb_t *pWorldOb;
float fOpacity;
float fDistToCam2; /* Distance to camera squared*/
BOOL bCulledByFrustum;
} ParticleDef_t;
typedef struct {
Vec3_t ModelspaceVtx;
float afCamspaceVtx[_CAMSPACE_ITERATOR_COUNT]; /* x, y, z, a, s, t*/
GrVertex ScreenspaceVtx;
u8 anOutsidePlaneCode[4];
BOOL8 bScreenspaceVtxValid;
BOOL8 bSensedLight;
} TileVtx_t;
static struct _TriList_s {
#if TARGET!=ULTRA64
Mesh3dTriDef_t *pTri;
#else /*ULTRA64*/
Orig_Mesh3dTriDef_t *pTri;
#endif /*ULTRA64*/
BOOL bVisited;
Vec3_t *apVtx[2];
float afIntensity[2];
} _aTriList[_MAX_TRIS_FOR_SPRAY_TYPE];
static WatersprayJunctionDef_t _aWatersprayJunctions[_MAX_TRIS_FOR_SPRAY_TYPE*2];
static u32 _nTriListCount;
static u32 _nWatersprayJunctionCount;
static const float _fOne = 1.0f;
static const float _fZero = 0.0f;
static BOOL _bDrawEnabled;
static SprayDef_t _aSprayDefs[_MAX_SPRAY_OBJECTS];
static FlumeTransform_t _aFlumeTransforms[_MAX_SPRAY_OBJECTS];
static ParticleDef_t *_pParticleBuf;
static u32 _nMaxParticles;
static u32 _nParticleCount;
static Xfm_t _ParticleXfm;
static TexDef_t *_pSprayTexDef;
static Xfm_t _XfmIdentity;
static ParticleDef_t *_pParticleFarthestFromCamera; /* NULL=none*/
static Cam_t *_pCameraLastUsed;
static Mtx3_t *_pFlumeMtx;
static TileVtx_t _aParticleTileVtx[4];
static TileVtx_t *_apParticleTileVtx[4] = {
&_aParticleTileVtx[0], &_aParticleTileVtx[1], &_aParticleTileVtx[2], &_aParticleTileVtx[3]
};
static TileVtx_t _aTileClippedVtx[40], *_apTileClippedVtx[40]; /* Frustum clipping*/
static int _nBoundCrossesPlanesMask;
static int _nTriCrossesPlanesMask;
static const Vec3_t _WorldUpUnitVector = { 0.0f, 1.0f, 0.0f };
static const Vec2_t _aParticleVtx[4] = {
_PARTICLE_VTX_X, -_PARTICLE_VTX_Y, /* lower-right corner*/
_PARTICLE_VTX_X, _PARTICLE_VTX_Y, /* upper-right corner*/
-_PARTICLE_VTX_X, -_PARTICLE_VTX_Y, /* lower-left corner*/
-_PARTICLE_VTX_X, _PARTICLE_VTX_Y, /* upper-left corner*/
};
static const Vec2_t _aParticleSt[4] = {
128.0f, 0.0f, /* lower-right corner*/
128.0f, 256.0f, /* upper-right corner*/
0.0f, 0.0f, /* lower-left corner*/
0.0f, 256.0f, /* upper-left corner*/
};
static const u32 _anGameloopsPerEmitterFire[WATERSPRAY_TYPE_COUNT] = {
2, /* WATERSPRAY_TYPE_IMPACT*/
2, /* WATERSPRAY_TYPE_DRAG*/
};
static const float _afDistanceBetweenEmitters[WATERSPRAY_TYPE_COUNT] = {
5.0f, /* WATERSPRAY_TYPE_IMPACT*/
2.5f, /* WATERSPRAY_TYPE_DRAG*/
};
static const float _afGravity[WATERSPRAY_TYPE_COUNT] = {
-0.1f, /* WATERSPRAY_TYPE_IMPACT*/
-0.025f, /* WATERSPRAY_TYPE_DRAG*/
};
static const float _afDamper[WATERSPRAY_TYPE_COUNT] = {
0.95f, /* WATERSPRAY_TYPE_IMPACT*/
0.95f, /* WATERSPRAY_TYPE_DRAG*/
};
static WatersprayControlFlags_t _nDefaultControlFlags = WATERSPRAY_CONTROLFLAG_NONE;
static void _ComputeFlumeMtx( Mtx3_t *pRetFlumeMtx, Vec3_t *pWaterSurfaceUnitNormal );
static void _InitSprayObject( SprayDef_t *pSprayDef, WatersprayType_t nType,
u32 nJunctionCount, WatersprayJunctionDef_t *pJunctionList,
const float *pfSprayEmitterHeightAdjust,
const float *pfMistEmitterHeightAdjust,
const float fMeshScale );
static void _FindParticleFarthestFromCamera( void );
static ParticleDef_t *_FindNewParticleForEmitter( Vec2_t *pWorldPointXZ );
static void _CalcSegmentUnitNormal( Vec3_t *pUnitNormal, const Vec3_t *pSegmentVector );
static void _CalcCornerUnitNormal( Vec3_t *pUnitNormal, const Vec3_t *pPrevPoint,
const Vec3_t *pCurPoint, const Vec3_t *pNextPoint );
static void _PushParticleXfm( const ParticleDef_t *pParticle );
static void _DrawParticle( ParticleDef_t *pParticle );
static BOOL _TestTriAgainstFrustum( TileVtx_t **ppTileVtx );
static void _ClipTriToFrustumAndDraw( TileVtx_t **ppTileVtx );
static void _KillParticle( ParticleDef_t *pParticle );
static void _EmitNewParticle( const SprayDef_t *pSprayDef, const WorldOb_t *pWorldOb,
Vec2_t *pWorldPointXZ, u32 nEmitterIndex, float fEmitterY, float fWaterY );
static void _BuildEmitterSegmentsFromTriList( WatersprayType_t nWatersprayType,
WatersprayControlFlags_t *pnControlFlags,
WorldOb_t *pWorldOb,
const float *pfSprayEmitterHeightAdjust,
const float *pfMistEmitterHeightAdjust );
static void _OrderJunctionList( void );
static void _RecursivelyAddAdjacentJunctions( u32 nTriIndex, u32 nEdgeIndex );
static void _AddJunction( u32 nTriIndex, u32 nEdgeIndex );
static BOOL _ReturnIndexOfUnvisitedTriWithUnsharedEdge( u32 *pnTriIndex, u32 *pnUnsharedEdgeIndex );
BOOL waterspray_ModuleInit( void ) {
waterspray_ResetSystem();
xfm_Identity( &_XfmIdentity );
xfm_Identity( &_ParticleXfm );
_ParticleXfm.type = XFM_TYPE_GEN;
_pSprayTexDef = NULL;
_nBoundCrossesPlanesMask = 0xf;
_bDrawEnabled = TRUE;
_pParticleBuf = NULL;
_nMaxParticles = 0;
_pParticleFarthestFromCamera = NULL;
_pCameraLastUsed = NULL;
return TRUE;
}
/* Initializes the waterspray system, allocating memory to hold nMaxParticles.*/
/* If nMaxParticles is 0, the default WATERSPRAY_DEFAULT_MAX_PARTICLES is used.*/
/* The caller must handle memory frames to deallocate the memory allocated by*/
/* this call.*/
/**/
/* Returns the amount of memory allocated by the waterspray system, in bytes.*/
/* Returns 0 if the systems could not be initialized.*/
u32 waterspray_InitSystem( u32 nMaxParticles ) {
u32 nMemRequired;
_pParticleBuf = NULL;
_nMaxParticles = 0;
_nMaxParticles = (nMaxParticles==0) ? WATERSPRAY_DEFAULT_MAX_PARTICLES : nMaxParticles;
nMemRequired = sizeof(ParticleDef_t) * _nMaxParticles;
if( sysmem_GetSize() >= nMemRequired ) {
/* Enough room to fit particles*/
_pParticleBuf = (ParticleDef_t *)SYSMEM_ALLOC( nMemRequired );
xprintf( "Allocated %i bytes for waterspray system.\n", nMemRequired );
return nMemRequired;
} else {
/* Not enough heap memory for the particles...*/
return 0;
}
}
/* Enables or disables water spray effects. Used for development purposes.*/
/* Returns the prior state.*/
BOOL waterspray_SetDrawMode( BOOL bEnable ) {
BOOL bPriorState;
bPriorState = _bDrawEnabled;
_bDrawEnabled = bEnable;
return bPriorState;
}
/* Returns the current draw state.*/
BOOL waterspray_GetDrawMode( void ) {
return _bDrawEnabled;
}
/* Specifies the texture to be used for the spray. If NULL is specified, no spray*/
/* will be generated.*/
void waterspray_SpecifyTextures( TexDef_t *pSprayTexDef ) {
_pSprayTexDef = pSprayTexDef;
}
/* Initializes a spray object defined by lines with the specified junction points for ends.*/
/* Note that the junction list is not required to persist.*/
/**/
/* Returns a non-zero handle used to identify the spray object. If there is insufficient*/
/* resources, or if there is an invalid parameter, a NULL handle is returned.*/
/**/
/* pnControlFlags points to flags that can be used by an external module to dynamically change*/
/* the state of the spray. If pnControlFlags is NULL, WATERSPRAY_CONTROLFLAG_NONE is used.*/
/**/
/* pfSprayEmitterHeightAdjust points to a value which will be added to all emitter model Y coordinates.*/
/* pfMistEmitterHeightAdjust points to a value which will be multiplied by all emitter model XZ coordinates.*/
/**/
/* Any of the above three values may be NULL, in which the default is used:*/
/* pfSprayEmitterHeightAdjust: 0.0f*/
/* pfMistEmitterHeightAdjust: 1.0f*/
WatersprayHandle_t waterspray_InitInstance( WatersprayType_t nType,
WatersprayControlFlags_t *pnControlFlags,
WorldOb_t *pWorldOb,
u32 nJunctionCount,
WatersprayJunctionDef_t *pJunctionList,
const float *pfSprayEmitterHeightAdjust,
const float *pfMistEmitterHeightAdjust ) {
SprayDef_t *pSprayDef;
u32 i, j;
if( nJunctionCount < 2 ) {
/* Not enough junction points specified...*/
return NULL;
}
if( pnControlFlags == NULL ) {
/* No control flags pointer specified.*/
/* Use default flags...*/
pnControlFlags = &_nDefaultControlFlags;
}
/* Find unused slot...*/
for( i=0; i<_MAX_SPRAY_OBJECTS; i++ ) {
pSprayDef = &_aSprayDefs[i];
if( !pSprayDef->bUsedSlot ) {
/* Unused slot found...*/
pSprayDef->bUsedSlot = TRUE;
pSprayDef->pWorldOb = pWorldOb;
_InitSprayObject(
pSprayDef,
nType,
nJunctionCount,
pJunctionList,
pfSprayEmitterHeightAdjust,
pfMistEmitterHeightAdjust,
pWorldOb->fScale
);
pSprayDef->pnControlFlags = pnControlFlags;
/* Assign pFlumeTransform...*/
for( j=0; j<_MAX_SPRAY_OBJECTS; j++ ) {
if( j != i ) {
if( _aSprayDefs[j].bUsedSlot ) {
if( _aSprayDefs[j].pWorldOb == pSprayDef->pWorldOb ) {
/* Another SprayDef has been found attached to the same WorldOb...*/
pSprayDef->pFlumeTransform = _aSprayDefs[j].pFlumeTransform;
pSprayDef->pFlumeTransform->nUsedCount++;
break;
}
}
}
}
if( j == _MAX_SPRAY_OBJECTS ) {
/* No other SprayDef's are attached to our WorldOb.*/
/* Find free FlumeTransform slot and use it...*/
for( j=0; j<_MAX_SPRAY_OBJECTS; j++ ) {
if( !_aFlumeTransforms[j].nUsedCount ) {
/* Found a free slot...*/
pSprayDef->pFlumeTransform = &_aFlumeTransforms[j];
_aFlumeTransforms[j].nUsedCount++;
_aFlumeTransforms[j].nGameFrame = Gameloop_nFrameCounter - 1;
_aFlumeTransforms[j].bUseMtx = FALSE;
break;
}
}
XASSERT( j < _MAX_SPRAY_OBJECTS );
}
return (WatersprayHandle_t)pSprayDef;
}
}
/* No empty slot found...*/
return NULL;
}
/* Uses the data in a special mesh3d to generate waterspray emitters.*/
/* The fVtxIntensity field for each vertex (except the vertex at the origin) controls the spray intensity at*/
/* that point (0=none, 1=max). The material nID field controls the type of spray (0=impact, 1=drag).*/
/* Returns TRUE if successful, or FALSE if not.*/
/**/
/* pnControlFlags points to flags that can be used by an external module to dynamically change*/
/* the state of the spray. If pnControlFlags is NULL, WATERSPRAY_CONTROLFLAG_NONE is used.*/
/**/
/* pfSprayEmitterHeightAdjust points to a value which will be added to all emitter model Y coordinates.*/
/* pfMistEmitterHeightAdjust points to a value which will be multiplied by all emitter model XZ coordinates.*/
/**/
/* Any of the above three values may be NULL, in which the default is used:*/
/* pfSprayEmitterHeightAdjust: 0.0f*/
/* pfMistEmitterHeightAdjust: 1.0f*/
#if TARGET!=ULTRA64
BOOL waterspray_InitFromMesh( const char *pszObName,
WatersprayControlFlags_t *pnControlFlags,
WorldOb_t *pWorldOb,
const float *pfSprayEmitterHeightAdjust,
const float *pfMistEmitterHeightAdjust ) {
ObsysFrame_t ObsysFrame;
Mesh3d_t *pMesh;
Mesh3dGrpDef_t *pGrp;
Mesh3dTlDef_t *pTl;
Mesh3dTriDef_t *pTri;
BOOL bSuccessFlag;
u32 i, j, nMeshID, nTl, nTri;
Vec3_t *pVec;
WatersprayType_t nWatersprayType;
bSuccessFlag = FALSE;
/* Start obsys frame and load mesh3d object...*/
ObsysFrame = obsys_StartFrame();
pMesh = (Mesh3d_t *)obsys_Load( pszObName );
if( pMesh == NULL ) {
/* Mesh not found...*/
xprintferr( "Waterspray mesh '%s' could not be loaded.\n", pszObName );
goto _ExitInitFromMesh;
}
/* Mesh was successfully loaded...*/
if( pMesh->nGrpCount == 0 ) {
xprintferr( "Waterspray mesh '%s' has no groups.\n", pszObName );
goto _ExitInitFromMesh;
}
if( pMesh->nGrpCount > 1 ) {
xprintferr( "Waterspray mesh '%s' has more than one group.\n", pszObName );
goto _ExitInitFromMesh;
}
if( pMesh->nTlCount == 0 ) {
xprintferr( "Waterspray mesh '%s' has no triange lists.\n", pszObName );
goto _ExitInitFromMesh;
}
if( pMesh->nMtlCount == 0 ) {
xprintferr( "Waterspray mesh '%s' has no materials.\n", pszObName );
goto _ExitInitFromMesh;
}
if( pMesh->nTriCount == 0 ) {
xprintferr( "Waterspray mesh '%s' has no trianges.\n", pszObName );
goto _ExitInitFromMesh;
}
if( pMesh->nVtxCount == 0 ) {
xprintferr( "Waterspray mesh '%s' has no vertices.\n", pszObName );
goto _ExitInitFromMesh;
}
pGrp = pMesh->pGrp;
pTl = pMesh->pTl;
for( nMeshID=0; nMeshID<_MESH3D_MTL_ID_COUNT; nMeshID++ ) {
/* Impact, then Drag...*/
switch( nMeshID ) {
case 0:
nWatersprayType = WATERSPRAY_TYPE_IMPACT;
break;
case 1:
nWatersprayType = WATERSPRAY_TYPE_DRAG;
break;
default:
XASSERT_NOW;
}
_nTriListCount = 0;
for( nTl=0; nTl<pMesh->nTlCount; nTl++ ) {
if( pMesh->pTl[nTl].pMtl->nID >= _MESH3D_MTL_ID_COUNT ) {
/* Invalid material ID...*/
xprintferr( "Waterspray mesh '%s' has an invalid material ID %i.\n", pszObName, pMesh->pTl[nTl].pMtl->nID );
goto _ExitInitFromMesh;
}
if( pMesh->pTl[nTl].pMtl->nID == nMeshID ) {
/* ID matches...*/
for( nTri=0; nTri<pMesh->pTl[nTl].nTriCount; nTri++ ) {
pTri = &pMesh->pTl[nTl].pTri[nTri];
if( _nTriListCount >= _MAX_TRIS_FOR_SPRAY_TYPE ) {
/* No room to add another triangle...*/
xprintferr( "Waterspray mesh '%s' has too many triangles in material ID %i.\n", pszObName, nMeshID );
goto _ExitInitFromMesh;
}
/* Verify that the triangle has exactly one vertex at the origin...*/
j = 0;
for( i=0; i<3; i++ ) {
pVec = &pMesh->pVtx[ pTri->TriVtx[i].nVtxIndex ].Point;
if( XMATH_FABS(pVec->p[0])>_ORIGIN_MARGIN
|| XMATH_FABS(pVec->p[1])>_ORIGIN_MARGIN
|| XMATH_FABS(pVec->p[2])>_ORIGIN_MARGIN ) {
/* This vertex is not at the origin...*/
if( j < 2 ) {
_aTriList[_nTriListCount].apVtx[j] = pVec;
// _aTriList[_nTriListCount].afIntensity[j] = pMesh->pVtx[ pTri->TriVtx[i].nVtxIndex ].fVtxIntensity;
}
j++;
}
}
if( j == 3 ) {
xprintferr( "Waterspray mesh '%s' has a triangle with no vertex at the origin.\n", pszObName );
goto _ExitInitFromMesh;
}
if( j < 2 ) {
xprintferr( "Waterspray mesh '%s' has a triangle with more than one vertex at the origin.\n", pszObName );
goto _ExitInitFromMesh;
}
/* Verify that this triangle doesn't already exist in our list...*/
for( i=0; i<_nTriListCount; i++ ) {
if( _aTriList[i].pTri == pTri ) {
/* Triangle already exists in our list...*/
xprintferr( "Waterspray mesh '%s' has duplicate triangles.\n", pszObName );
goto _ExitInitFromMesh;
}
}
/* Valid triangle...*/
_aTriList[_nTriListCount].pTri = pTri;
_aTriList[_nTriListCount].bVisited = FALSE;
_nTriListCount++;
}
}
}
/* We've built the list of triangles for this waterspray type...*/
if( _nTriListCount ) {
_BuildEmitterSegmentsFromTriList(
nWatersprayType,
pnControlFlags,
pWorldOb,
pfSprayEmitterHeightAdjust,
pfMistEmitterHeightAdjust
);
}
}
bSuccessFlag = TRUE;
_ExitInitFromMesh:
obsys_ReleaseFrame( ObsysFrame );
return bSuccessFlag;
}
#else /*ULTRA64 */
#define MAX_TEMPTLS 2
#define MAX_TEMPVTX 64
#define MAX_TEMPTRIS 32
#define MAX_TEMPMTLS 2
Orig_Mesh3dTlDef_t TempTl[MAX_TEMPTLS]; // One for each material
Mesh3dVtxDef_t *TempVtx[MAX_TEMPVTX]; // Pointers to vertices in mesh
int TempVtxIndex[MAX_TEMPVTX];
Orig_Mesh3dTriDef_t TempTris[MAX_TEMPTRIS];
Mesh3dMtlDef_t TempMtls[MAX_TEMPMTLS]; // Two materials - Impact and Drag?
int NumTempTris;
Vec3_t CalcMeshNormal(Mesh3dVtxDef_t *TempVtx0, Mesh3dVtxDef_t *TempVtx1,Mesh3dVtxDef_t *TempVtx2)
{
Vec3_t vtx0,vtx1,vtx2,v1,v2,normal;
// Get face v0,v1,v2
vtx0.p[0] = TempVtx0->Point.p[0];
vtx0.p[1] = TempVtx0->Point.p[1];
vtx0.p[2] = TempVtx0->Point.p[2];
vtx1.p[0] = TempVtx1->Point.p[0];
vtx1.p[1] = TempVtx1->Point.p[1];
vtx1.p[2] = TempVtx1->Point.p[2];
vtx2.p[0] = TempVtx2->Point.p[0];
vtx2.p[1] = TempVtx2->Point.p[1];
vtx2.p[2] = TempVtx2->Point.p[2];
// CONSOLE("%f,%f,%f %f,%f,%f %f,%f,%f\n",vtx0.p[0],vtx0.p[1],vtx0.p[2], vtx1.p[0],vtx1.p[1],vtx1.p[2], vtx2.p[0],vtx2.p[1],vtx2.p[2]);
vec3_CalcSub( &v1, &vtx1, &vtx0 );
vec3_CalcSub( &v2, &vtx2, &vtx0 );
vec3_CalcUnitCross( &normal, &v2, &v1 );
return(normal);
}
void AddOrigTri(int v0,int v1,int v2,Vec3_t *pUnitNormal,int MaterialID)
{
// Fill in triangle
TempTris[NumTempTris].TriVtx[0].nVtxIndex = v0;
TempTris[NumTempTris].TriVtx[1].nVtxIndex = v1;
TempTris[NumTempTris].TriVtx[2].nVtxIndex = v2;
TempTris[NumTempTris].FaceUnitNorm = *pUnitNormal;
// CONSOLE("AddOrigTri %d,%d,%d(%d)\n",v0,v1,v2,MaterialID);
if ( (MaterialID<0) || (MaterialID>1) ) {
CONSOLE("oops, material id out of range for waterspray\n");
}
if (TempTl[MaterialID].pTri==NULL)
TempTl[MaterialID].pTri = &TempTris[NumTempTris];
TempTl[MaterialID].nTriCount++;
NumTempTris++;
}
void BuildArcadeMesh(Mesh3d_t *pMesh) // Assumes only 1 group
{
Mesh3dGrpDef_t *pGrp;
Mesh3dVtxDef_t *pVtxList;
unsigned short int *TriStripStream;
Vec3_t UnitNormal;
int NumStripVertices,NumStripFaces,StreamCode,Red,Green,Blue;
unsigned int i1,i2;
int v[3];
int i,j,k,MaterialID=-1;
memset(TempTl,0x00,sizeof(Orig_Mesh3dTlDef_t)*MAX_TEMPTLS);
pGrp = pMesh->pGrp;
// So, 'pVtxList' is list of vertices - good
pVtxList = &pMesh->pVtx[ pGrp->nStartingVtx ];
// Now build up list of faces
NumTempTris = 0;
memset(TempTris,0x00,sizeof(Orig_Mesh3dTriDef_t)*MAX_TEMPTRIS);
memset(TempMtls,0x00,sizeof(Mesh3dMtlDef_t)*MAX_TEMPMTLS);
TempTl[0].nTriCount = 0;
TempTl[0].pTri = NULL;//TempTris;
TempTl[0].pMtl = TempMtls;
TempTl[1].nTriCount = 0;
TempTl[1].pTri = NULL;//TempTris;
TempTl[1].pMtl = TempMtls;
// Scale vertices back down!!! Need to fix this....
for(i=0; i<pMesh->nVtxCount; i++) {
pVtxList[i].Point.p[0] /= 16.0;
pVtxList[i].Point.p[1] /= 16.0;
pVtxList[i].Point.p[2] /= 16.0;
}
// Run through stream!
TriStripStream = (unsigned short int *)pGrp->pTl;
TriStripStream+=2; // Skip GfxDl/Vtx sizes
StreamCode = *TriStripStream++;
while(StreamCode!=0) {
switch(StreamCode&0xc000) {
case 0x4000: /* Short int vertices */
/* Now read/download vertices */
NumStripVertices = StreamCode&0x0fff;
/* Vertices as top short TcIndex, bottom short VcIndex */
for(i=0; i<NumStripVertices; i++) {
k = *TriStripStream++; /* Vertex list for cache */
j = *TriStripStream++; /* Tc */
// Store into temporary cache(32 max)
TempVtx[i] = pVtxList + k;
TempVtxIndex[i] = k;
}
break;
case 0x8000: /* Faces */
NumStripFaces = StreamCode&0x0fff;
for(i=0; i<(NumStripFaces>>1); i++) {
i1 = *TriStripStream++;
i2 = *TriStripStream++;
v[0] = (i1>>10)&0x1f;
v[1] = (i1>>5)&0x1f;
v[2] = i1&0x1f;
UnitNormal = CalcMeshNormal(TempVtx[ v[0] ], TempVtx[ v[1] ], TempVtx[ v[2] ]);
// Add to list, use vertex index in list(no cache!)
AddOrigTri(TempVtxIndex[v[0]],TempVtxIndex[v[1]],TempVtxIndex[v[2]],&UnitNormal,MaterialID);
v[0] = (i2>>10)&0x1f;
v[1] = (i2>>5)&0x1f;
v[2] = i2&0x1f;
UnitNormal = CalcMeshNormal(TempVtx[ v[0] ], TempVtx[ v[1] ], TempVtx[ v[2] ]);
AddOrigTri(TempVtxIndex[v[0]],TempVtxIndex[v[1]],TempVtxIndex[v[2]],&UnitNormal,MaterialID);
}
if (NumStripFaces&0x1) {
i1 = *TriStripStream++;
v[0] = (i1>>10)&0x1f;
v[1] = (i1>>5)&0x1f;
v[2] = i1&0x1f;
UnitNormal = CalcMeshNormal(TempVtx[ v[0] ], TempVtx[ v[1] ], TempVtx[ v[2] ]);
AddOrigTri(TempVtxIndex[v[0]],TempVtxIndex[v[1]],TempVtxIndex[v[2]],&UnitNormal,MaterialID);
}
break;
case 0xc000: /* Material */
/* Download material, if have one */
if (StreamCode&0x0800) {
/* Set primitive shade */
CONSOLE("Found primtive colour in waterspray mesh - bugger\n");
for(;;);
Red = StreamCode&0xff; // Bit 11 is shade(no texture)
Blue = Green = *TriStripStream++;
Blue &= 0xff;
Green >>= 8;
}
else { // Else is a normal texture
i = StreamCode&0x0fff;
MaterialID = i;
}
break;
default:
CONSOLE("oh dear - Invalid Tri stream code\n");
for(;;);
break;
}
StreamCode = *TriStripStream++;
}
}
BOOL waterspray_InitFromMesh( const char *pszObName,
WatersprayControlFlags_t *pnControlFlags,
WorldOb_t *pWorldOb,
const float *pfEmitterHeightAdjust,
const float *pfEmitterScaleXZ ) {
ObsysFrame_t ObsysFrame;
Mesh3d_t *pMesh;
Mesh3dGrpDef_t *pGrp;
Orig_Mesh3dTlDef_t *pTl, *pTl2;
Orig_Mesh3dTriDef_t *pTri;
BOOL bSuccessFlag;
u32 i, j, nMeshID, nTl, nTri;
Vec3_t *pVec;
WatersprayType_t nWatersprayType;
bSuccessFlag = FALSE;
/* Start obsys frame and load mesh3d object... */
ObsysFrame = obsys_StartFrame();
pMesh = (Mesh3d_t *)obsys_Load( pszObName );
CONSOLE("Load that waterspray mesh:%s\n",pszObName);
if( pMesh == NULL ) {
/* Mesh not found... */
xprintferr( "Waterspray mesh '%s' could not be loaded.\n", pszObName );
goto _ExitInitFromMesh;
}
/* Mesh was successfully loaded... */
if( pMesh->nGrpCount == 0 ) {
xprintferr( "Waterspray mesh '%s' has no groups.\n", pszObName );
goto _ExitInitFromMesh;
}
if( pMesh->nGrpCount > 1 ) {
xprintferr( "Waterspray mesh '%s' has more than one group.\n", pszObName );
goto _ExitInitFromMesh;
}
if( pMesh->nTlCount == 0 ) {
xprintferr( "Waterspray mesh '%s' has no triange lists.\n", pszObName );
goto _ExitInitFromMesh;
}
if( pMesh->nMtlCount == 0 ) {
xprintferr( "Waterspray mesh '%s' has no materials.\n", pszObName );
goto _ExitInitFromMesh;
}
if( pMesh->nTriCount == 0 ) {
xprintferr( "Waterspray mesh '%s' has no trianges.\n", pszObName );
goto _ExitInitFromMesh;
}
if( pMesh->nVtxCount == 0 ) {
xprintferr( "Waterspray mesh '%s' has no vertices.\n", pszObName );
goto _ExitInitFromMesh;
}
// Could uncompress tri-strips into arcade mesh format!!???
// Better idea!
// Get group(only 1)
// Run through triangle lists
// Build up tri cache, and when get 'face' scan for material match on each one
// Store off into arcade structure! Phew!
BuildArcadeMesh(pMesh);
// for(;;);
pGrp = pMesh->pGrp;
pTl = TempTl; //pMesh->pTl;
for( nMeshID=0; nMeshID<_MESH3D_MTL_ID_COUNT; nMeshID++ ) {
// Impact, then Drag...
switch( nMeshID ) {
case 0:
nWatersprayType = WATERSPRAY_TYPE_IMPACT;
break;
case 1:
nWatersprayType = WATERSPRAY_TYPE_DRAG;
break;
default:
XASSERT_NOW;
}
_nTriListCount = 0;
for( nTl=0; nTl<pMesh->nTlCount; nTl++ ) {
if( pTl[nTl].pMtl->nID >= _MESH3D_MTL_ID_COUNT ) {
// Invalid material ID...
xprintferr( "Waterspray mesh '%s' has an invalid material ID %i.\n", pszObName, pTl[nTl].pMtl->nID );
goto _ExitInitFromMesh;
}
if( pTl[nTl].pMtl->nID == nMeshID ) {
// ID matches...
for( nTri=0; nTri<pTl[nTl].nTriCount; nTri++ ) {
// PAB Oops, actually wrong type so get correct offset
pTl2 = (Orig_Mesh3dTlDef_t *)( (u32)pTl+nTl*sizeof(Orig_Mesh3dTlDef_t) );
pTri = &pTl2->pTri[nTri];
if( _nTriListCount >= _MAX_TRIS_FOR_SPRAY_TYPE ) {
// No room to add another triangle...
xprintferr( "Waterspray mesh '%s' has too many triangles in material ID %i.\n", pszObName, nMeshID );
goto _ExitInitFromMesh;
}
// Verify that the triangle has exactly one vertex at the origin...
j = 0;
for( i=0; i<3; i++ ) {
pVec = &pMesh->pVtx[ pTri->TriVtx[i].nVtxIndex ].Point;
// CONSOLE("%f %f %f\n",pVec->p[0],pVec->p[1],pVec->p[2]);
if( XMATH_FABS(pVec->p[0])>_ORIGIN_MARGIN
|| XMATH_FABS(pVec->p[1])>_ORIGIN_MARGIN
|| XMATH_FABS(pVec->p[2])>_ORIGIN_MARGIN ) {
// This vertex is not at the origin...
if( j < 2 ) {
_aTriList[_nTriListCount].apVtx[j] = pVec;
//? _aTriList[_nTriListCount].afIntensity[j] = pMesh->pVtx[ pTri->TriVtx[i].nVtxIndex ].fVtxIntensity;
}
j++;
}
}
if( j == 3 ) {
xprintferr( "Waterspray mesh '%s' has a triangle with no vertex at the origin.\n", pszObName );
goto _ExitInitFromMesh;
}
if( j < 2 ) {
xprintferr( "Waterspray mesh '%s' has a triangle with more than one vertex at the origin.\n", pszObName );
goto _ExitInitFromMesh;
}
// Verify that this triangle doesn't already exist in our list...
for( i=0; i<_nTriListCount; i++ ) {
if( _aTriList[i].pTri == pTri ) {
// Triangle already exists in our list...
xprintferr( "Waterspray mesh '%s' has duplicate triangles.\n", pszObName );
goto _ExitInitFromMesh;
}
}
// Valid triangle...
_aTriList[_nTriListCount].pTri = pTri;
_aTriList[_nTriListCount].bVisited = FALSE;
_nTriListCount++;
}
}
}
// We've built the list of triangles for this waterspray type...
if( _nTriListCount ) {
_BuildEmitterSegmentsFromTriList(
nWatersprayType,
pnControlFlags,
pWorldOb,
pfEmitterHeightAdjust,
pfEmitterScaleXZ
);
}
}
bSuccessFlag = TRUE;
_ExitInitFromMesh:
obsys_ReleaseFrame( ObsysFrame );
return bSuccessFlag;
}
#endif /*ULTRA64 */
/* Deletes all spray objects and destroys all particles.*/
/* Does not reset textures.*/
void waterspray_ResetSystem( void ) {
waterspray_DeleteAll();
waterspray_DestroyAllParticles();
_pParticleBuf = NULL;
_nMaxParticles = 0;
}
/* Deletes a spray object. Does not reset the particles.*/
/* Does nothing if a NULL handle is passed in.*/
void waterspray_Delete( WatersprayHandle_t hWaterspray ) {
SprayDef_t *pSprayDef;
if( hWaterspray ) {
pSprayDef = (SprayDef_t *)hWaterspray;
--pSprayDef->pFlumeTransform->nUsedCount;
pSprayDef->bUsedSlot = FALSE;
}
}
/* Same as waterspray_Delete(), but deletes all waterspray handles*/
/* attached to the specified worldob.*/
void waterspray_DeleteFromWorldObPtr( WorldOb_t *pWorldOb ) {
SprayDef_t *pSprayDef;
for( pSprayDef=_aSprayDefs; pSprayDef < &_aSprayDefs[_MAX_SPRAY_OBJECTS]; pSprayDef++ ) {
if( pSprayDef->bUsedSlot && pSprayDef->pWorldOb==pWorldOb ) {
waterspray_Delete( (WatersprayHandle_t)pSprayDef );
}
}
}
/* Deletes all spray objects. Does not reset the particles.*/
void waterspray_DeleteAll( void ) {
u32 i;
for( i=0; i<_MAX_SPRAY_OBJECTS; i++ ) {
_aSprayDefs[i].bUsedSlot = FALSE;
_aFlumeTransforms[i].nUsedCount = 0;
}
}
/* Destroys all particles.*/
void waterspray_DestroyAllParticles( void ) {
_nParticleCount = 0;
}
void waterspray_Work( void ) {
ParticleDef_t *pParticle;
SprayDef_t *pSprayDef;
WorldOb_t *pWorldOb;
Orient_t *pOrient;
WorldSector_t *pSector;
Vec3_t *pModelPoint, *pHighestPoint, BiasedModelPoint, WaterSurfaceUnitNormal;
Vec2_t *pWorldPoint;
EmitterDef_t *pEmitter;
float fEmitterY, fWaterY, fDamper, fHeightAboveWater;
const Vec3_t *pPos;
const Mtx3_t *pMtx;
u32 i, j, nNextArrayIndex;
WatersprayType_t nType;
#if !SYS_WINLIB_TARGET
WorldSector_t **ppDisplaySectorList;
u32 nDisplaySectorCount, k;
#endif
/* Move all particles...*/
pParticle = _pParticleBuf;
for( i=0; i<_nParticleCount; i++, pParticle++ ) {
nType = pParticle->nType;
/* Update head and tail points...*/
pParticle->HeadPoint.p[0] += pParticle->HeadVelocity.p[0];
pParticle->HeadPoint.p[1] += pParticle->HeadVelocity.p[1];
pParticle->HeadPoint.p[2] += pParticle->HeadVelocity.p[2];
pParticle->TailPoint.p[0] += pParticle->TailVelocity.p[0];
pParticle->TailPoint.p[1] += pParticle->TailVelocity.p[1];
pParticle->TailPoint.p[2] += pParticle->TailVelocity.p[2];
/* Update head and tail velocities...*/
fDamper = _afDamper[nType];
pParticle->HeadVelocity.p[0] *= fDamper;
pParticle->HeadVelocity.p[1] *= fDamper;
pParticle->HeadVelocity.p[2] *= fDamper;
pParticle->TailVelocity.p[0] *= fDamper;
pParticle->TailVelocity.p[1] *= fDamper;
pParticle->TailVelocity.p[2] *= fDamper;
pParticle->HeadVelocity.p[1] += _afGravity[nType];
pParticle->TailVelocity.p[1] += _afGravity[nType];
/* Update fScaleX...*/
pParticle->fScaleX += pParticle->fDeltaScaleX;
/* Determine highest point...*/
pHighestPoint = (pParticle->HeadPoint.p[1] > pParticle->TailPoint.p[1]) ? &pParticle->HeadPoint : &pParticle->TailPoint;
/* Update particle's center sector...*/
pParticle->pSector = world_FindSectorContainingPoint(
pParticle->pSector,
pHighestPoint->p[0],
pHighestPoint->p[2]
);
#if TARGET==ULTRA64 || TARGET==DREAMCAST/*Bodge bug fix*/
if ((u32)pParticle->pSector<0x80000000)
pParticle->pSector = NULL;
#endif
/* Kill particle if it's under water or outside a sector boundary...*/
if( pParticle->pSector ) {
fWaterY = water_GetHeight(
pParticle->pSector,
pHighestPoint->p[0],
pHighestPoint->p[2]
);
if( pHighestPoint->p[1] < fWaterY ) {
/* Particle is under water.*/
if( pParticle->HeadVelocity.p[1]<0.0f && pParticle->TailVelocity.p[1]<0.0f ) {
/* Kill it...*/
_KillParticle( pParticle );
i--;
continue;
}
}
} else {
/* Particle is out of a sector boundary.*/
/* Kill it...*/
_KillParticle( pParticle );
i--;
continue;
}
}
_FindParticleFarthestFromCamera();
/* Launch new particles...*/
for( i=0; i<_MAX_SPRAY_OBJECTS; i++ ) {
pSprayDef = &_aSprayDefs[i];
if( pSprayDef->bUsedSlot ) {
if( *pSprayDef->pnControlFlags & WATERSPRAY_CONTROLFLAG_DISABLE_EMISSIONS ) {
/* Emissions have been disabled...*/
pSprayDef->nNextArrayIndex = 2;
continue;
}
pWorldOb = pSprayDef->pWorldOb;
/*PAB These flags don't seem right on N64... */
if( pWorldOb->nFlags & (WORLDOB_FLAG_DISABLE|WORLDOB_FLAG_DONT_DRAW) ) {
pSprayDef->nNextArrayIndex = 2;
continue;
}
pSector = pWorldOb->Tws.pCenterSector;
#if TARGET==ULTRA64 || TARGET==DREAMCAST/*Bodge bug fix*/
if ((u32)pSector<0x80000000)
pSector = NULL;
#endif
if( pSector == NULL ) {
pSprayDef->nNextArrayIndex = 2;
continue;
}
pOrient = &pWorldOb->Orient;
fWaterY = water_GetHeight(
pSector,
pOrient->Pos.p[0],
pOrient->Pos.p[2]
);
fHeightAboveWater = pOrient->Pos.p[1] - fWaterY;
if( fHeightAboveWater > pWorldOb->Tws.fCurrentRadius ) {
pSprayDef->nNextArrayIndex = 2;
continue;
}
#if !SYS_WINLIB_TARGET
#if TARGET!=ULTRA64 && TARGET!=DREAMCAST
if( Gamecam_pSector ) {
/* Determine if this WorldOb is in the displaylist...*/
nDisplaySectorCount = Gamecam_pSector->display[SECTOR_DIRECTION_FORWARD].count;
ppDisplaySectorList = Gamecam_pSector->display[SECTOR_DIRECTION_FORWARD].sectors;
for( k=0; k<nDisplaySectorCount; k++, ppDisplaySectorList++ ) {
if( pSector == *ppDisplaySectorList ) {
break;
}
}
if( k >= nDisplaySectorCount ) {
/* Object is not in display list...*/
pSprayDef->nNextArrayIndex = 2;
continue;
}
}
#endif /*ULTRA64*/
#endif
nType = pSprayDef->nType;
pMtx = &pOrient->Rot;
pPos = &pOrient->Pos;
nNextArrayIndex = pSprayDef->nNextArrayIndex;
if( nNextArrayIndex == 2 ) {
nNextArrayIndex = 0;
}
/* Transform all emitters into world space...*/
pWorldPoint = &pSprayDef->aWorldPoints[ nNextArrayIndex ][0];
for( j=0; j<pSprayDef->nEmitterCount; j++, pWorldPoint++ ) {
pModelPoint = &pSprayDef->aEmitters[j].ModelPoint;
if( nType == WATERSPRAY_TYPE_IMPACT ) {
BiasedModelPoint.p[0] = pModelPoint->p[0];
BiasedModelPoint.p[1] = pModelPoint->p[1] + *pSprayDef->pfSprayEmitterHeightAdjust;
BiasedModelPoint.p[2] = pModelPoint->p[2];
} else {
BiasedModelPoint.p[0] = pModelPoint->p[0];
BiasedModelPoint.p[1] = pModelPoint->p[1] + *pSprayDef->pfMistEmitterHeightAdjust;
BiasedModelPoint.p[2] = pModelPoint->p[2];
}
pWorldPoint->p[0] = pMtx->m[0][0] * BiasedModelPoint.p[0]
+ pMtx->m[1][0] * BiasedModelPoint.p[1]
+ pMtx->m[2][0] * BiasedModelPoint.p[2]
+ pPos->p[0];
pWorldPoint->p[1] = pMtx->m[0][2] * BiasedModelPoint.p[0]
+ pMtx->m[1][2] * BiasedModelPoint.p[1]
+ pMtx->m[2][2] * BiasedModelPoint.p[2]
+ pPos->p[2];
}
if( pSprayDef->nNextArrayIndex != 2 ) {
if( --pSprayDef->nEmitterLaunchTimer == 0 ) {
pSprayDef->nEmitterLaunchTimer = _anGameloopsPerEmitterFire[nType];
pWorldPoint = &pSprayDef->aWorldPoints[ nNextArrayIndex ][0];
if( pSprayDef->pFlumeTransform->nGameFrame != Gameloop_nFrameCounter ) {
pSprayDef->pFlumeTransform->nGameFrame = Gameloop_nFrameCounter;
pSprayDef->pFlumeTransform->bUseMtx = FALSE;
world_GetSectorUnitNormal(
&WaterSurfaceUnitNormal,
pSector,
pWorldOb->Tws.CurrentWorldPos.p[0],
pWorldOb->Tws.CurrentWorldPos.p[1]
);
if( WaterSurfaceUnitNormal.p[0]!=0.0f || WaterSurfaceUnitNormal.p[1]!=1.0f || WaterSurfaceUnitNormal.p[2]!=0.0f ) {
/* Need flume matrix...*/
pSprayDef->pFlumeTransform->bUseMtx = TRUE;
_ComputeFlumeMtx( &pSprayDef->pFlumeTransform->Mtx, &WaterSurfaceUnitNormal );
}
}
if( !pSprayDef->pFlumeTransform->bUseMtx ) {
/* No flume matrix required...*/
_pFlumeMtx = NULL;
} else {
/* Flume matrix required...*/
_pFlumeMtx = &pSprayDef->pFlumeTransform->Mtx;
}
pEmitter = pSprayDef->aEmitters;
for( j=0; j<pSprayDef->nEmitterCount; j++, pEmitter++, pWorldPoint++ ) {
#if 0
{
static u32 nMaxParticle;
if( _nParticleCount > nMaxParticle ) {
nMaxParticle = _nParticleCount;
xprintf( "Max particles: %i\n", nMaxParticle );
}
}
#endif
pModelPoint = &pEmitter->ModelPoint;
if( nType == WATERSPRAY_TYPE_IMPACT ) {
fEmitterY = pMtx->m[0][1] * pModelPoint->p[0]
+ pMtx->m[1][1] * (pModelPoint->p[1] + *pSprayDef->pfSprayEmitterHeightAdjust)
+ pMtx->m[2][1] * pModelPoint->p[2]
+ pPos->p[1];
} else {
fEmitterY = pMtx->m[0][1] * pModelPoint->p[0]
+ pMtx->m[1][1] * (pModelPoint->p[1] + *pSprayDef->pfMistEmitterHeightAdjust)
+ pMtx->m[2][1] * pModelPoint->p[2]
+ pPos->p[1];
}
fWaterY = water_GetHeight( pSector, pWorldPoint->p[0], pWorldPoint->p[1] );
if( fEmitterY < fWaterY ) {
/* Emitter is below waterline.*/
/* Create new spray particle...*/
_EmitNewParticle( pSprayDef, pWorldOb, pWorldPoint, j, fEmitterY, fWaterY );
}
}
}
pSprayDef->nNextArrayIndex = 1 - pSprayDef->nNextArrayIndex;
} else {
pSprayDef->nNextArrayIndex = 1;
}
}
}
}
#if TARGET!=ULTRA64 /* Remove waterspray work */
#if TARGET!=DREAMCAST
void waterspray_Draw( WorldSector_t **ppSectorList, u32 nSectorCount ) {
ParticleDef_t *pParticle;
u32 i, j, nPriorZbufState;
float fDx, fDz;
_pCameraLastUsed = Xfm_pStackCam;
if( !_bDrawEnabled ) {
/* Spray drawing is disabled...*/
return;
}
if( _nParticleCount && _pSprayTexDef ) {
/* Compare, but don't update zbuffer...*/
nPriorZbufState = gutil_ZbufferSetState( GUTIL_ZBUFFER_STATE_COMPARE_ONLY );
/* Set graphics modes...*/
material_PushState();
material_Defaults();
material_AlphaTestReferenceValue( 0 );
grDitherMode( GR_DITHER_2x2 );
/* Set texture...*/
material_ColorCombine(
GR_COMBINE_FUNCTION_SCALE_OTHER,
GR_COMBINE_FACTOR_LOCAL,
GR_COMBINE_LOCAL_CONSTANT,
GR_COMBINE_OTHER_TEXTURE,
FXFALSE
);
material_AlphaCombine(
GR_COMBINE_FUNCTION_SCALE_OTHER,
GR_COMBINE_FACTOR_LOCAL,
GR_COMBINE_LOCAL_CONSTANT,
GR_COMBINE_OTHER_TEXTURE,
FXFALSE
);
tmem_Select( _pSprayTexDef );
tmem_SetTileMode( _pSprayTexDef, FALSE, FALSE );
pParticle = _pParticleBuf;
for( i=0; i<_nParticleCount; i++, pParticle++ ) {
fDx = pParticle->HeadPoint.p[0] - _pCameraLastUsed->xfm.mr[3][0];
fDz = pParticle->HeadPoint.p[2] - _pCameraLastUsed->xfm.mr[3][2];
pParticle->fDistToCam2 = fDx*fDx + fDz*fDz;
if( !(pParticle->pWorldOb->nFlags & WORLDOB_FLAG_DONT_DRAW) ) {
for( j=0; j<nSectorCount; j++ ) {
if( pParticle->pSector == ppSectorList[j] ) {
/* Particle is in a sector that is being displayed...*/
_PushParticleXfm( pParticle );
_DrawParticle( pParticle );
xfm_Pop();
break;
}
}
}
}
/* Restore graphics state...*/
grDitherMode( GR_DITHER_4x4 );
material_PopState(1);
/* Restore state and reset buffer index...*/
gutil_ZbufferSetState( nPriorZbufState );
}
}
#endif /* DREAMCAST */
#else /*ULTRA64*/
u64 WaterSpraySprite[] = { /* 2048 bytes */
0x0000000000000011,0x1111212122222122,0x2222222222212111,0x1011000000000000,
0x0000000000011111,0x1222233333333333,0x3333333333332322,0x1111110000000000,
0x0000000000111122,0x3343444454545555,0x5555555555544443,0x3322211110000000,
0x0000000001122334,0x4445566667777777,0x7878777777776655,0x4443321111000000,
0x0000000111234455,0x5677778898999999,0xA9A9999999988887,0x7655443221100000,
0x0000011113344567,0x7889999AABABABBC,0xBCCCBBAAAAAAAA99,0x8876554432110000,
0x0000112233456688,0x99AABBCBCCCBCCDD,0xDDEDDCBBBBBBBBBA,0xA997766553311000,
0x000112234456789A,0xABBCCDDDDDDDDDDE,0xEFEFEDCCCCCCCCCC,0xBB99887765321100,
0x00112244557789AB,0xCCDDEEEEEEEDEEEE,0xFFFFFEDCDDDDDDDC,0xCBB9988775422110,
0x1111244567789ABC,0xDDEEEFFFFFEEEEEF,0xFFFFFEDDDDDEDEDD,0xDCBB998865432111,
0x111234567889AACD,0xEEFFFFFFFFFEFEFF,0xFFFFFEEEEEEEEEEE,0xDDCBA98765433111,
0x111334577899ABCD,0xEEEFFFFFFFFFFFFF,0xFFFFFFEEEEEEEEEE,0xDDCBA98765443211,
0x11234567899AABDD,0xEEFFFFFFFFFFFFFF,0xFFFFFFFFFEFFEEEE,0xDCCBA98775543221,
0x11234568899AACDE,0xEFFFFFFFFFFFFFFF,0xFFFFFFFFFFEEEEDD,0xDCBBAA9876543321,
0x1123457899AABCDE,0xEFFFFFFFFFFFFFFF,0xFFFFFFFFFEEEEDDD,0xDCCCBAA876544321,
0x112345788AAABCDE,0xEFFFFFFFFFFFFFFF,0xFFFFFFFFFFEEDDDD,0xDDCCBBA986554321,
0x1123457899AABCDE,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFFEEEDDDD,0xDDCCCAA886654321,
0x112346689AAABCDE,0xEFEFEFFFFFFFFFFF,0xFFFFFFFFFFEEDDDD,0xDDCCBBA987654321,
0x1123457899AABCDE,0xEEEEEEFFFFFFFEFF,0xFFFFFFFFFFEDDDDD,0xDCCCBAA887654321,
0x1123467899ABBDDE,0xEEDEEEFFFFFFEEEF,0xFFFFFFFFFFEDCDCD,0xCCCBBA9877654321,
0x1123557899AABCDE,0xEDDDEEFFFFFEEEEF,0xFFFFFFFFFEECCCCC,0xCCBAA99887654321,
0x11234678899AACDE,0xEDDDEFFFFFFEEEEF,0xFFFFFFFFEEDCBCCC,0xCBBAA99877654321,
0x112345788999BBDD,0xEDDDEEFFFFFEEEEF,0xFFFEEEEEEDDCBBBB,0xCBBAA99887654321,
0x11234678899AABCD,0xDDDDEFFFFFFFEEEF,0xFFFEDDEEEECCBBBC,0xCBBA998877654321,
0x112355778899ABCD,0xDDDDEEFFFFFFFFFF,0xFFFEEDEEEEDCCBBB,0xCBBA998887654321,
0x1123466888899BCD,0xDDDDDEEFEFEEEFFF,0xFFEEEEEEEEDDCCCC,0xBBBA998877654321,
0x112345778899AACC,0xDDDDEEFFFEEEEEFF,0xFFEEEEEEFEEDCCBB,0xBBBAA98876654321,
0x1123456789999BBD,0xDDDDDEEFEEDDDEEF,0xFFEEEEEFFEEDCCBB,0xBBBBA98877654321,
0x112345678899ABCD,0xDDDDDEEEEDDDDDEE,0xFFFEEEFFFEEDCBBB,0xBBBBA99887654321,
0x11234567899ABCDD,0xDDCDDEEEDDDDDDDE,0xEFEEEFFFFFEDCCBB,0xBBBBAA9877654321,
0x1123446688AACCDD,0xDCDDEEEEEDDCCCDD,0xEEEEFFFFFFEDDCCC,0xCBBBAA9887654321,
0x11133557789ABCCC,0xCCCDDEEEDDCCCCCD,0xDEEFFFFFFFEEDDDD,0xCCBBAA9877654321,
0x11223466789ABBCC,0xCCCCDDEEDDDCCCDD,0xEEFFFFFFFFEDDDDD,0xCCBBAA9877654321,
0x111334567889ABBC,0xCCCCDDEEDDCCCCDD,0xDEEFFFFFFFEDDDDD,0xCCBBAA9877654321,
0x111234567899AABB,0xCCCCDDEDDDDCDDDD,0xDEEFFFFFFEEDDDDD,0xDCBAA99876654321,
0x11123456789AAABB,0xCCCCCDDEDDDDDDDD,0xDEEFFFFFEEDDDDCD,0xCCBA998877554321,
0x111234567899AABB,0xCCCCDDDDDDDDDDDD,0xDEEFFFFEEDDCCBCC,0xCCBAA98776544321,
0x01112446789AAAAB,0xCCBCCDDDDDDDDDDD,0xDEEEEFEEEDCCBBBB,0xCCBA998776553321,
0x0011234578999AAB,0xCBBBCDDDDDDDDDDD,0xDDEEEEEEEDCBBABB,0xCCBA988765544221,
0x00112345688999AB,0xBBBCCDDDDDDDDDDD,0xDDDEDEEEDDCBBBBB,0xBBBA987765443311,
0x00112244678899AA,0xBBBBCCDDDDDDDDDD,0xDDDDDDDDDDCCCBBB,0xBAAA977665433211,
0x000112345778899A,0xABABBCCCCDDDDEDD,0xCCCCCDCDDDCCCCBA,0xAAA9876654432211,
0x000012345677889A,0xAAAABCCCCCDDDDDC,0xCCCCCCCCCCCCCBBA,0xA998876554433111,
0x0001122456677899,0x999AABBCCCCDDDCC,0xBBBCBCBCCCCCCBAA,0x9888775544332111,
0x0000113355667789,0x9999AABBCCDDDDDC,0xBBBBBBBBBBBBBAA9,0x8777765443321111,
0x0000112345666788,0x88889AABCCDDDDCC,0xBBBCBCBBBBBBBA99,0x7766654433221110,
0x0000112345556677,0x878889ABCCDDDDDC,0xCBCCCBBBBBBAAA98,0x7665544332211110,
0x0000011345556667,0x777789ABCDDDDDCC,0xCCCCCCBBAAAAA987,0x6655543322111100,
0x0000011244555566,0x667788ABCDDDDCCB,0xCCCCCCBBBAAAA987,0x6655543221111000,
0x0000011234444556,0x666778ABCDCCCCBB,0xBCCCCCBBAA999877,0x6555443211110000,
0x0000001122334445,0x5566789BCCCBBBBB,0xCCCCCCBAA9999876,0x5444433111100000,
0x0000000111122345,0x5556689BCCCCBBBB,0xCCDCCCBA99988765,0x5433221111000000,
0x0000000011112344,0x5566789BCCCBBBBB,0xCCDCCBBA99888765,0x4432211110000000,
0x0000000001112334,0x4556789BBBBBBBAB,0xBCCCBBAA99887655,0x4432111100000000,
0x0000000000112234,0x4555779ABBAAAAAA,0xBCCBBAA998877654,0x4332111000000000,
0x0000000000011334,0x4445678AAAAA999A,0xACCBAA9988776544,0x3322111000000000,
0x0000000000011123,0x3344568999989889,0xABCBA99887665544,0x4331111000000000,
0x0000000000001112,0x2334467888777778,0x9BBB998876655444,0x4322111100000000,
0x0000000000000011,0x1233467777766677,0x9ABA987766554444,0x4332211100000000,
0x0000000000000001,0x1123456766565667,0x89AA876655544433,0x3322111100000000,
0x0000000000000001,0x1122456655555566,0x7898866554433332,0x2111111000000000,
0x0000000000000000,0x1112345544444555,0x6778765544332211,0x1111000000000000,
0x0000000000000000,0x0111344443434444,0x5566654443322111,0x1100000000000000,
0x0000000000000000,0x0011233323233334,0x4456553323221111,0x1000000000000000,
/*
0x0000000000000000,0x0011233323233334,0x4456553323221111,0x1000000000000000,
0x0000000000000000,0x0111344443434444,0x5566654443322111,0x1100000000000000,
0x0000000000000000,0x1112345544444555,0x6778765544332211,0x1111000000000000,
0x0000000000000001,0x1122456655555566,0x7898866554433332,0x2111111000000000,
0x0000000000000001,0x1123456766565667,0x89AA876655544433,0x3322111100000000,
0x0000000000000011,0x1233467777766677,0x9ABA987766554444,0x4332211100000000,
0x0000000000001112,0x2334467888777778,0x9BBB998876655444,0x4322111100000000,
0x0000000000011123,0x3344568999989889,0xABCBA99887665544,0x4331111000000000,
0x0000000000011334,0x4445678AAAAA999A,0xACCBAA9988776544,0x3322111000000000,
0x0000000000112234,0x4555779ABBAAAAAA,0xBCCBBAA998877654,0x4332111000000000,
0x0000000001112334,0x4556789BBBBBBBAB,0xBCCCBBAA99887655,0x4432111100000000,
0x0000000011112344,0x5566789BCCCBBBBB,0xCCDCCBBA99888765,0x4432211110000000,
0x0000000111122345,0x5556689BCCCCBBBB,0xCCDCCCBA99988765,0x5433221111000000,
0x0000001122334445,0x5566789BCCCBBBBB,0xCCCCCCBAA9999876,0x5444433111100000,
0x0000011234444556,0x666778ABCDCCCCBB,0xBCCCCCBBAA999877,0x6555443211110000,
0x0000011244555566,0x667788ABCDDDDCCB,0xCCCCCCBBBAAAA987,0x6655543221111000,
0x0000011345556667,0x777789ABCDDDDDCC,0xCCCCCCBBAAAAA987,0x6655543322111100,
0x0000112345556677,0x878889ABCCDDDDDC,0xCBCCCBBBBBBAAA98,0x7665544332211110,
0x0000112345666788,0x88889AABCCDDDDCC,0xBBBCBCBBBBBBBA99,0x7766654433221110,
0x0000113355667789,0x9999AABBCCDDDDDC,0xBBBBBBBBBBBBBAA9,0x8777765443321111,
0x0001122456677899,0x999AABBCCCCDDDCC,0xBBBCBCBCCCCCCBAA,0x9888775544332111,
0x000012345677889A,0xAAAABCCCCCDDDDDC,0xCCCCCCCCCCCCCBBA,0xA998876554433111,
0x000112345778899A,0xABABBCCCCDDDDEDD,0xCCCCCDCDDDCCCCBA,0xAAA9876654432211,
0x00112244678899AA,0xBBBBCCDDDDDDDDDD,0xDDDDDDDDDDCCCBBB,0xBAAA977665433211,
0x00112345688999AB,0xBBBCCDDDDDDDDDDD,0xDDDEDEEEDDCBBBBB,0xBBBA987765443311,
0x0011234578999AAB,0xCBBBCDDDDDDDDDDD,0xDDEEEEEEEDCBBABB,0xCCBA988765544221,
0x01112446789AAAAB,0xCCBCCDDDDDDDDDDD,0xDEEEEFEEEDCCBBBB,0xCCBA998776553321,
0x111234567899AABB,0xCCCCDDDDDDDDDDDD,0xDEEFFFFEEDDCCBCC,0xCCBAA98776544321,
0x11123456789AAABB,0xCCCCCDDEDDDDDDDD,0xDEEFFFFFEEDDDDCD,0xCCBA998877554321,
0x111234567899AABB,0xCCCCDDEDDDDCDDDD,0xDEEFFFFFFEEDDDDD,0xDCBAA99876654321,
0x111334567889ABBC,0xCCCCDDEEDDCCCCDD,0xDEEFFFFFFFEDDDDD,0xCCBBAA9877654321,
0x11223466789ABBCC,0xCCCCDDEEDDDCCCDD,0xEEFFFFFFFFEDDDDD,0xCCBBAA9877654321,
0x11133557789ABCCC,0xCCCDDEEEDDCCCCCD,0xDEEFFFFFFFEEDDDD,0xCCBBAA9877654321,
0x1123446688AACCDD,0xDCDDEEEEEDDCCCDD,0xEEEEFFFFFFEDDCCC,0xCBBBAA9887654321,
0x11234567899ABCDD,0xDDCDDEEEDDDDDDDE,0xEFEEEFFFFFEDCCBB,0xBBBBAA9877654321,
0x112345678899ABCD,0xDDDDDEEEEDDDDDEE,0xFFFEEEFFFEEDCBBB,0xBBBBA99887654321,
0x1123456789999BBD,0xDDDDDEEFEEDDDEEF,0xFFEEEEEFFEEDCCBB,0xBBBBA98877654321,
0x112345778899AACC,0xDDDDEEFFFEEEEEFF,0xFFEEEEEEFEEDCCBB,0xBBBAA98876654321,
0x1123466888899BCD,0xDDDDDEEFEFEEEFFF,0xFFEEEEEEEEDDCCCC,0xBBBA998877654321,
0x112355778899ABCD,0xDDDDEEFFFFFFFFFF,0xFFFEEDEEEEDCCBBB,0xCBBA998887654321,
0x11234678899AABCD,0xDDDDEFFFFFFFEEEF,0xFFFEDDEEEECCBBBC,0xCBBA998877654321,
0x112345788999BBDD,0xEDDDEEFFFFFEEEEF,0xFFFEEEEEEDDCBBBB,0xCBBAA99887654321,
0x11234678899AACDE,0xEDDDEFFFFFFEEEEF,0xFFFFFFFFEEDCBCCC,0xCBBAA99877654321,
0x1123557899AABCDE,0xEDDDEEFFFFFEEEEF,0xFFFFFFFFFEECCCCC,0xCCBAA99887654321,
0x1123467899ABBDDE,0xEEDEEEFFFFFFEEEF,0xFFFFFFFFFFEDCDCD,0xCCCBBA9877654321,
0x1123457899AABCDE,0xEEEEEEFFFFFFFEFF,0xFFFFFFFFFFEDDDDD,0xDCCCBAA887654321,
0x112346689AAABCDE,0xEFEFEFFFFFFFFFFF,0xFFFFFFFFFFEEDDDD,0xDDCCBBA987654321,
0x1123457899AABCDE,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFFEEEDDDD,0xDDCCCAA886654321,
0x112345788AAABCDE,0xEFFFFFFFFFFFFFFF,0xFFFFFFFFFFEEDDDD,0xDDCCBBA986554321,
0x1123457899AABCDE,0xEFFFFFFFFFFFFFFF,0xFFFFFFFFFEEEEDDD,0xDCCCBAA876544321,
0x11234568899AACDE,0xEFFFFFFFFFFFFFFF,0xFFFFFFFFFFEEEEDD,0xDCBBAA9876543321,
0x11234567899AABDD,0xEEFFFFFFFFFFFFFF,0xFFFFFFFFFEFFEEEE,0xDCCBA98775543221,
0x111334577899ABCD,0xEEEFFFFFFFFFFFFF,0xFFFFFFEEEEEEEEEE,0xDDCBA98765443211,
0x111234567889AACD,0xEEFFFFFFFFFEFEFF,0xFFFFFEEEEEEEEEEE,0xDDCBA98765433111,
0x1111244567789ABC,0xDDEEEFFFFFEEEEEF,0xFFFFFEDDDDDEDEDD,0xDCBB998865432111,
0x00112244557789AB,0xCCDDEEEEEEEDEEEE,0xFFFFFEDCDDDDDDDC,0xCBB9988775422110,
0x000112234456789A,0xABBCCDDDDDDDDDDE,0xEFEFEDCCCCCCCCCC,0xBB99887765321100,
0x0000112233456688,0x99AABBCBCCCBCCDD,0xDDEDDCBBBBBBBBBA,0xA997766553311000,
0x0000011113344567,0x7889999AABABABBC,0xBCCCBBAAAAAAAA99,0x8876554432110000,
0x0000000111234455,0x5677778898999999,0xA9A9999999988887,0x7655443221100000,
0x0000000001122334,0x4445566667777777,0x7878777777776655,0x4443321111000000,
0x0000000000111122,0x3343444454545555,0x5555555555544443,0x3322211110000000,
0x0000000000011111,0x1222233333333333,0x3333333333332322,0x1111110000000000,
0x0000000000000011,0x1111212122222122,0x2222222222212111,0x1011000000000000,
*/
};
void EmptyParticleCache(int MaxVerts)
{
int i;
if (WaterSprayNumVertsInCache==0)
return;
/* 32 vertices gives 8 polygons! */
if (WaterSprayNumVertsInCache>=MaxVerts) {
gSPVertex(N64GfxDlPtr++, &N64Vertices[draw_buffer][WaterSprayStartVertex], WaterSprayNumVertsInCache, 0);
for(i=0; i<WaterSprayNumVertsInCache; i+=4) {
gSP2Triangles(N64GfxDlPtr++, 0+i,1+i,2+i,0, 2+i,3+i,1+i,0);
}
WaterSprayNumVertsInCache = 0;
WaterSprayStartVertex = NumN64Vertices;
}
}
void waterspray_Draw( WorldSector_t **ppSectorList, u32 nSectorCount ) {
ParticleDef_t *pParticle;
u32 i, j, k=0, nPriorZbufState;
float fDx, fDz;
float Stash[4][4];
#ifdef DISABLE_WATERSPRAY
return;
#endif
_pCameraLastUsed = Xfm_pStackCam;
if( !_bDrawEnabled ) {
/* Spray drawing is disabled... */
return;
}
if( _nParticleCount && _pSprayTexDef ) {
/* CONSOLE("IN 0x%X 0x%X\n",_nParticleCount,_pSprayTexDef); */
/* Compare, but don't update zbuffer... */
nPriorZbufState = gutil_ZbufferSetState( GUTIL_ZBUFFER_STATE_COMPARE_ONLY );
/* Set graphics modes... */
material_PushState();
material_Defaults();
material_AlphaTestReferenceValue( 0 );
grDitherMode( GR_DITHER_2x2 );
/* Set texture... */
material_ColorCombine(
GR_COMBINE_FUNCTION_SCALE_OTHER,
GR_COMBINE_FACTOR_LOCAL,
GR_COMBINE_LOCAL_CONSTANT,
GR_COMBINE_OTHER_TEXTURE,
FXFALSE
);
material_AlphaCombine(
GR_COMBINE_FUNCTION_SCALE_OTHER,
GR_COMBINE_FACTOR_LOCAL,
GR_COMBINE_LOCAL_CONSTANT,
GR_COMBINE_OTHER_TEXTURE,
FXFALSE
);
tmem_Select( _pSprayTexDef );
tmem_SetTileMode( _pSprayTexDef, FALSE, FALSE );
/* Zer up render mode! */
gDPSetRenderMode(N64GfxDlPtr++,G_RM_ZB_XLU_SURF, G_RM_ZB_XLU_SURF2);
gSPClearGeometryMode(N64GfxDlPtr++,G_CULL_BOTH);
gSPSetGeometryMode(N64GfxDlPtr++,G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH);
gSPTexture(N64GfxDlPtr++,0x8000, 0x8000, 0, G_TX_RENDERTILE, G_ON);
gDPSetCombineMode(N64GfxDlPtr++, ASH_G_CC_BLENDPEDECALA, ASH_G_CC_BLENDPEDECALA);
gDPSetTextureLUT(N64GfxDlPtr++,G_TT_NONE);
gDPSetEnvColor(N64GfxDlPtr++, 196,196,196,0x0); /* Trans */
gDPSetPrimColor(N64GfxDlPtr++, 0,0, 255,255,255,0x20); /* Opaque */
/* Download alpha spray texture! */
gDPLoadTextureBlock_4b(N64GfxDlPtr++, WaterSpraySprite, G_IM_FMT_I, 64,64,0,
G_TX_CLAMP, G_TX_CLAMP, /* No mirror! */
6, 6, G_TX_NOLOD, G_TX_NOLOD);
/* Create scale+translation matrix */
Stash[0][0] =
Stash[1][1] =
Stash[2][2] = 1.0;
Stash[3][0] = N64CameraXPosFrac;
Stash[3][1] = -N64CameraYPosFrac;
Stash[3][2] = -N64CameraZPosFrac;
Stash[3][3] = 1.0;
Stash[1][0] = Stash[2][0] =
Stash[0][1] = Stash[2][1] =
Stash[0][2] = Stash[1][2] =
Stash[0][3] = Stash[1][3] = Stash[2][3] = 0.0;
guMtxF2L(Stash,&dynamicp->object_matrix[NumN64Objects]);
gSPMatrix(N64GfxDlPtr++,&dynamicp->object_matrix[NumN64Objects],G_MTX_MODELVIEW|G_MTX_LOAD|G_MTX_NOPUSH);
NumN64Objects++;
/* Store vertices and dump in chunks of 32 verts! */
WaterSprayNumVertsInCache = 0;
WaterSprayStartVertex = NumN64Vertices;
pParticle = _pParticleBuf;
for( i=0; i<_nParticleCount; i++, pParticle++ ) {
fDx = pParticle->HeadPoint.p[0] - _pCameraLastUsed->xfm.mr[3][0];
fDz = pParticle->HeadPoint.p[2] - _pCameraLastUsed->xfm.mr[3][2];
pParticle->fDistToCam2 = fDx*fDx + fDz*fDz;
if( !(pParticle->pWorldOb->nFlags & WORLDOB_FLAG_DONT_DRAW) ) {
for( j=0; j<nSectorCount; j++ ) {
if( pParticle->pSector == ppSectorList[j] ) {
/* Particle is in a sector that is being displayed... */
_PushParticleXfm( pParticle );
_DrawParticle( pParticle );
xfm_Pop();
break;
}
}
}
}
// Empty all cache
EmptyParticleCache(0);
/* Restore graphics state... */
grDitherMode( GR_DITHER_4x4 );
material_PopState(1);
/* Restore state and reset buffer index... */
gutil_ZbufferSetState( nPriorZbufState );
gDPSetTextureLUT(N64GfxDlPtr++,G_TT_RGBA16);
}
}
#endif /*ULTRA64*/
/* From Graphics Gems I, page 531.*/
static void _ComputeFlumeMtx( Mtx3_t *pRetFlumeMtx, Vec3_t *pW ) {
Vec3_t WP, WP90;
Mtx3_t TempMtx, TempMtx2;
static Mtx3_t QT = {
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f
};
static Mtx3_t R = {
0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f
};
static const Mtx3_t Rz90 = {
0.0f, 1.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f
};
vec3_CalcUnitCross( (Vec3_t *)(&QT.m[2][0]), (Vec3_t *)(&QT.m[0][0]), pW );
vec3_CalcUnitCross( (Vec3_t *)(&QT.m[1][0]), (Vec3_t *)(&QT.m[2][0]), (Vec3_t *)(&QT.m[0][0]) );
mtx3_MultiTranspose3x3TimesVec3( &WP, &QT, pW );
mtx3_Multi3x3TimesVec3( &WP90, &Rz90, &WP );
R.m[0][0] = WP.p[0];
R.m[0][1] = WP90.p[0];
R.m[1][0] = WP.p[1];
R.m[1][1] = WP90.p[1];
R.m[2][0] = WP.p[2];
R.m[2][1] = WP90.p[2];
mtx3_Multi3x3Times3x3( &TempMtx, &QT, &R );
mtx3_Multi3x3TimesTranspose3x3( &TempMtx2, &TempMtx, &QT );
pRetFlumeMtx->m[0][0] = TempMtx2.m[0][0];
pRetFlumeMtx->m[0][1] = TempMtx2.m[1][0];
pRetFlumeMtx->m[0][2] = TempMtx2.m[2][0];
pRetFlumeMtx->m[1][0] = TempMtx2.m[0][1];
pRetFlumeMtx->m[1][0] = TempMtx2.m[1][1];
pRetFlumeMtx->m[1][1] = TempMtx2.m[2][1];
pRetFlumeMtx->m[2][0] = TempMtx2.m[0][2];
pRetFlumeMtx->m[2][1] = TempMtx2.m[1][2];
pRetFlumeMtx->m[2][2] = TempMtx2.m[2][2];
}
static void _InitSprayObject( SprayDef_t *pSprayDef, WatersprayType_t nType,
u32 nJunctionCount, WatersprayJunctionDef_t *pJunctionList,
const float *pfSprayEmitterHeightAdjust,
const float *pfMistEmitterHeightAdjust,
const float fMeshScale ) {
u32 i, nSegmentCount, nEmitterIndex;
float fSegmentLength, fOneOverSegmentLength, fStepDistance, fUnitPercentageIntoSegment;
Vec3_t SegmentUnitVector, EmitterPoint, StartUnitNormal, EndUnitNormal;
EmitterDef_t *pEmitterDef;
nSegmentCount = nJunctionCount - 1;
nEmitterIndex = 0;
fStepDistance = 0.0f;
/* scale all of the modelspace points by the fMeshScale*/
for( i=0; i<nJunctionCount; i++ ) {
vec3_Scale1( &pJunctionList[i].ModelspacePoint, fMeshScale );
}
for( i=0; i<nSegmentCount; i++ ) {
/* Calculate segment length and unit vector...*/
vec3_CalcSub( &SegmentUnitVector, &pJunctionList[i+1].ModelspacePoint, &pJunctionList[i].ModelspacePoint );
fSegmentLength = vec3_CalcMag( &SegmentUnitVector );
fOneOverSegmentLength = 1.0f / fSegmentLength;
vec3_Unit( &SegmentUnitVector );
/* Calculate start unit normal...*/
if( i == 0 ) {
_CalcSegmentUnitNormal( &StartUnitNormal, &SegmentUnitVector );
} else {
vec3_Copy( &StartUnitNormal, &EndUnitNormal );
}
/* Calculate end unit normal...*/
if( i == (nSegmentCount-1) ) {
_CalcSegmentUnitNormal( &EndUnitNormal, &SegmentUnitVector );
} else {
_CalcCornerUnitNormal( &EndUnitNormal,
&pJunctionList[i].ModelspacePoint,
&pJunctionList[i+1].ModelspacePoint,
&pJunctionList[i+2].ModelspacePoint
);
}
while( fStepDistance <= fSegmentLength ) {
if( nEmitterIndex >= _MAX_EMITTERS_PER_OBJECT ) {
xprintf( "WARNING: Out of water spray emitters.\n" );
break;
}
pEmitterDef = &pSprayDef->aEmitters[nEmitterIndex];
fUnitPercentageIntoSegment = fStepDistance * fOneOverSegmentLength;
/* Compute emitter data...*/
vec3_CalcScale1( &EmitterPoint, &SegmentUnitVector, fStepDistance );
vec3_Add( &EmitterPoint, &pJunctionList[i].ModelspacePoint );
vec3_Copy( &pEmitterDef->ModelPoint, &EmitterPoint );
vec3_CalcInterpolate( &pEmitterDef->ModelUnitNormal,
&StartUnitNormal,
&EndUnitNormal,
fUnitPercentageIntoSegment
);
fStepDistance += _afDistanceBetweenEmitters[nType];
nEmitterIndex++;
}
fStepDistance -= fSegmentLength;
if( nEmitterIndex >= _MAX_EMITTERS_PER_OBJECT ) {
break;
}
}
if( pfSprayEmitterHeightAdjust == NULL ) {
pfSprayEmitterHeightAdjust = &_fZero;
}
if( pfMistEmitterHeightAdjust == NULL ) {
pfMistEmitterHeightAdjust = &_fOne;
}
pSprayDef->nType = nType;
pSprayDef->nEmitterCount = nEmitterIndex;
pSprayDef->nNextArrayIndex = 2;
pSprayDef->nEmitterLaunchTimer = _anGameloopsPerEmitterFire[nType];
pSprayDef->pfSprayEmitterHeightAdjust = pfSprayEmitterHeightAdjust;
pSprayDef->pfMistEmitterHeightAdjust = pfMistEmitterHeightAdjust;
}
static void _FindParticleFarthestFromCamera( void ) {
u32 i;
float fFarthestDistToCam2;
BOOL bFound;
_pParticleFarthestFromCamera = NULL;
if( _nParticleCount > 0 ) {
bFound = FALSE;
for( i=0; i<_nParticleCount; i++ ) {
if( !(_pParticleBuf[i].nControlFlags & WATERSPRAY_CONTROLFLAG_HIGH_PRIORITY) ) {
if( !bFound || (_pParticleBuf[i].fDistToCam2 > fFarthestDistToCam2) ) {
bFound = TRUE;
_pParticleFarthestFromCamera = &_pParticleBuf[i];
fFarthestDistToCam2 = _pParticleBuf[i].fDistToCam2;
}
}
}
} else {
/* No particles...*/
_pParticleFarthestFromCamera = NULL;
}
}
static ParticleDef_t *_FindNewParticleForEmitter( Vec2_t *pWorldPointXZ ) {
u32 i;
float fDx, fDz, fDistToCam2;
ParticleDef_t *pNewParticle;
pNewParticle = NULL;
if( _pCameraLastUsed ) {
fDx = pWorldPointXZ->p[0] - _pCameraLastUsed->xfm.mr[3][0];
fDz = pWorldPointXZ->p[1] - _pCameraLastUsed->xfm.mr[3][2];
fDistToCam2 = fDx*fDx + fDz*fDz;
if( _nParticleCount < _nMaxParticles ) {
/* Found a free particle...*/
pNewParticle = &_pParticleBuf[_nParticleCount++];
pNewParticle->fDistToCam2 = fDistToCam2;
pNewParticle->bCulledByFrustum = FALSE;
if( (_pParticleFarthestFromCamera==NULL) || (fDistToCam2 > _pParticleFarthestFromCamera->fDistToCam2) ) {
_pParticleFarthestFromCamera = pNewParticle;
}
return pNewParticle;
} else {
/* No more room for another particle...*/
for( i=0; i<_nParticleCount; i++ ) {
if( !(_pParticleBuf[i].nControlFlags & WATERSPRAY_CONTROLFLAG_HIGH_PRIORITY) && _pParticleBuf[i].bCulledByFrustum ) {
pNewParticle = &_pParticleBuf[i];
pNewParticle->fDistToCam2 = fDistToCam2;
pNewParticle->bCulledByFrustum = FALSE;
_FindParticleFarthestFromCamera();
return pNewParticle;
}
}
/* Replace furthest particle that's farther from camera than pWorldPointXZ is...*/
if( _pParticleFarthestFromCamera != NULL ) {
if( _pParticleFarthestFromCamera->fDistToCam2 > fDistToCam2 ) {
/* Farthest particle is also farther than this new particle.*/
/* Take it's slot!*/
pNewParticle = _pParticleFarthestFromCamera;
pNewParticle->fDistToCam2 = fDistToCam2;
pNewParticle->bCulledByFrustum = FALSE;
_FindParticleFarthestFromCamera();
return pNewParticle;
}
}
}
}
return NULL;
}
static void _CalcSegmentUnitNormal( Vec3_t *pUnitNormal, const Vec3_t *pSegmentVector ) {
vec3_Set( pUnitNormal, pSegmentVector->p[2], 0.0f, -pSegmentVector->p[0] );
vec3_Unit( pUnitNormal );
}
static void _CalcCornerUnitNormal( Vec3_t *pUnitNormal, const Vec3_t *pPrevPoint,
const Vec3_t *pCurPoint, const Vec3_t *pNextPoint ) {
Vec3_t PrevSegVec, NextSegVec, PrevNorm, NextNorm;
vec3_CalcSub( &PrevSegVec, pCurPoint, pPrevPoint );
vec3_CalcSub( &NextSegVec, pNextPoint, pCurPoint );
_CalcSegmentUnitNormal( &PrevNorm, &PrevSegVec );
_CalcSegmentUnitNormal( &NextNorm, &NextSegVec );
vec3_CalcMidpoint( pUnitNormal, &PrevNorm, &NextNorm );
vec3_Unit( pUnitNormal );
}
static void _PushParticleXfm( const ParticleDef_t *pParticle ) {
const Vec3_t *pHeadPoint, *pTailPoint;
float fScaleX;
Vec3_t ModelAxisInWorldspaceX, ModelAxisInWorldspaceY, ModelAxisInWorldspaceZ;
Vec3_t ModelToEyeVec, Origin;
pHeadPoint = &pParticle->HeadPoint;
pTailPoint = &pParticle->TailPoint;
fScaleX = pParticle->fScaleX;
Origin.p[0] = (pHeadPoint->p[0] + pTailPoint->p[0]) * 0.5f;
Origin.p[1] = (pHeadPoint->p[1] + pTailPoint->p[1]) * 0.5f;
Origin.p[2] = (pHeadPoint->p[2] + pTailPoint->p[2]) * 0.5f;
ModelAxisInWorldspaceY.p[0] = pHeadPoint->p[0] - pTailPoint->p[0];
ModelAxisInWorldspaceY.p[1] = pHeadPoint->p[1] - pTailPoint->p[1];
ModelAxisInWorldspaceY.p[2] = pHeadPoint->p[2] - pTailPoint->p[2];
ModelToEyeVec.p[0] = Xfm_pStackCam->pos.p[0] - pTailPoint->p[0];
ModelToEyeVec.p[1] = Xfm_pStackCam->pos.p[1] - pTailPoint->p[1];
ModelToEyeVec.p[2] = Xfm_pStackCam->pos.p[2] - pTailPoint->p[2];
vec3_CalcCross( &ModelAxisInWorldspaceX, &ModelAxisInWorldspaceY, &ModelToEyeVec );
vec3_CalcCross( &ModelAxisInWorldspaceZ, &ModelAxisInWorldspaceY, &ModelAxisInWorldspaceX );
vec3_Unit( &ModelAxisInWorldspaceX );
vec3_Scale1( &ModelAxisInWorldspaceX, fScaleX );
vec3_Unit( &ModelAxisInWorldspaceZ );
_ParticleXfm.mf[0][0] = _ParticleXfm.mr[0][0] = ModelAxisInWorldspaceX.p[0];
_ParticleXfm.mf[0][1] = _ParticleXfm.mr[1][0] = ModelAxisInWorldspaceX.p[1];
_ParticleXfm.mf[0][2] = _ParticleXfm.mr[2][0] = ModelAxisInWorldspaceX.p[2];
_ParticleXfm.mf[1][0] = _ParticleXfm.mr[0][1] = ModelAxisInWorldspaceY.p[0];
_ParticleXfm.mf[1][1] = _ParticleXfm.mr[1][1] = ModelAxisInWorldspaceY.p[1];
_ParticleXfm.mf[1][2] = _ParticleXfm.mr[2][1] = ModelAxisInWorldspaceY.p[2];
_ParticleXfm.mf[2][0] = _ParticleXfm.mr[0][2] = ModelAxisInWorldspaceZ.p[0];
_ParticleXfm.mf[2][1] = _ParticleXfm.mr[1][2] = ModelAxisInWorldspaceZ.p[1];
_ParticleXfm.mf[2][2] = _ParticleXfm.mr[2][2] = ModelAxisInWorldspaceZ.p[2];
_ParticleXfm.mf[3][0] = Origin.p[0];
_ParticleXfm.mf[3][1] = Origin.p[1];
_ParticleXfm.mf[3][2] = Origin.p[2];
_ParticleXfm.mr[3][0] = -(_ParticleXfm.mf[0][0]*Origin.p[0] + _ParticleXfm.mf[0][1]*Origin.p[1] + _ParticleXfm.mf[0][2]*Origin.p[2]);
_ParticleXfm.mr[3][1] = -(_ParticleXfm.mf[1][0]*Origin.p[0] + _ParticleXfm.mf[1][1]*Origin.p[1] + _ParticleXfm.mf[1][2]*Origin.p[2]);
_ParticleXfm.mr[3][2] = -(_ParticleXfm.mf[2][0]*Origin.p[0] + _ParticleXfm.mf[2][1]*Origin.p[1] + _ParticleXfm.mf[2][2]*Origin.p[2]);
xfm_PushSpecial( &_ParticleXfm );
}
#if TARGET!=ULTRA64
#if TARGET!=DREAMCAST
static void _DrawParticle( ParticleDef_t *pParticle ) {
u32 k, n;
float *pfCamspaceVtx, fOpacity, oow, q;
Vec3_t *pModelspaceVtx;
GrVertex *pScreenspaceVtx;
TileVtx_t *pTileVtx;
ARGBf_t SensedLight;
BOOL bLitParticle;
/* Transform vertices...*/
for( k=0; k<4; k++ ) {
pTileVtx = &_aParticleTileVtx[k];
pModelspaceVtx = &pTileVtx->ModelspaceVtx;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
pTileVtx->bScreenspaceVtxValid = FALSE;
pTileVtx->anOutsidePlaneCode[0]
= pTileVtx->anOutsidePlaneCode[1]
= pTileVtx->anOutsidePlaneCode[2]
= pTileVtx->anOutsidePlaneCode[3]
= _OUTCODE_UNKNOWN;
pModelspaceVtx->p[0] = _aParticleVtx[k].p[0];
pModelspaceVtx->p[1] = _aParticleVtx[k].p[1];
pModelspaceVtx->p[2] = 0.0f;
pfCamspaceVtx[_ITERATOR_X] = Xfm_pStackTop->mf[0][0] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][0] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][0];
pfCamspaceVtx[_ITERATOR_Y] = Xfm_pStackTop->mf[0][1] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][1] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][1];
pfCamspaceVtx[_ITERATOR_Z] = Xfm_pStackTop->mf[0][2] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][2] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][2];
pfCamspaceVtx[_ITERATOR_S] = _aParticleSt[k].p[0];
pfCamspaceVtx[_ITERATOR_T] = _aParticleSt[k].p[1];
}
/* Draw triangles...*/
pParticle->bCulledByFrustum = TRUE;
bLitParticle = FALSE;
for( k=0; k<2; k++ ) {
if( _TestTriAgainstFrustum( &_apParticleTileVtx[k] ) ) {
/* Triangle is at least partially visible...*/
pParticle->bCulledByFrustum = FALSE;
if( !bLitParticle ) {
bLitParticle = TRUE;
world_LightSensorPoint( pParticle->pSector, &pParticle->HeadPoint, &SensedLight );
SensedLight.argb[2] = (SensedLight.argb[2] + 1.0f) * 0.5f;
SensedLight.argb[1] = (SensedLight.argb[1] + 1.0f) * 0.5f;
SensedLight.argb[0] = (SensedLight.argb[0] + 1.0f) * 0.5f;
fOpacity = (pParticle->nType == WATERSPRAY_TYPE_IMPACT) ? pParticle->fOpacity : 0.10f;
material_ConstantColorValue( GFXDEFS_FLOATARGB_TO_GRCOLOR(
fOpacity,
SensedLight.argb[2],
SensedLight.argb[1],
SensedLight.argb[0]
) );
}
if( !_nTriCrossesPlanesMask ) {
/* Triangle doesn't require frustum clipping...*/
for( n=0; n<3; n++ ) {
pTileVtx = _apParticleTileVtx[n+k];
if( !pTileVtx->bScreenspaceVtxValid ) {
pTileVtx->bScreenspaceVtxValid = TRUE;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
oow = pScreenspaceVtx->oow = VIEWPORT_CALC_OOW( pfCamspaceVtx[2] );
q = VIEWPORT_CALC_Q( oow );
#if GFXDEFS_USE_ZBUFFER
pScreenspaceVtx->ooz = VIEWPORT_CALC_OOZ( pfCamspaceVtx[2] );
#endif
pScreenspaceVtx->x = GFXDEFS_SNAPVERT( q*pfCamspaceVtx[0] - Viewport_frustum_limit[0] );
pScreenspaceVtx->y = GFXDEFS_SNAPVERT( q*pfCamspaceVtx[1] - Viewport_frustum_limit[2] );
GUTIL_CLAMP_OOW( pScreenspaceVtx->oow );
pTileVtx->ScreenspaceVtx.tmuvtx[0].sow =
pTileVtx->ScreenspaceVtx.tmuvtx[1].sow =
pTileVtx->afCamspaceVtx[_ITERATOR_S]*pScreenspaceVtx->oow;
pTileVtx->ScreenspaceVtx.tmuvtx[0].tow =
pTileVtx->ScreenspaceVtx.tmuvtx[1].tow =
pTileVtx->afCamspaceVtx[_ITERATOR_T]*pScreenspaceVtx->oow;
}
}
gutil_ScreenCoverage_DrawTriangleIfUnderLimit(
&_apParticleTileVtx[k]->ScreenspaceVtx,
&_apParticleTileVtx[k+1]->ScreenspaceVtx,
&_apParticleTileVtx[k+2]->ScreenspaceVtx
);
} else {
/* Must clip triangle to frustum...*/
_ClipTriToFrustumAndDraw( &_apParticleTileVtx[k] );
}
}
}
}
/* Returns TRUE if triangle is at least partially visible.*/
static BOOL _TestTriAgainstFrustum( TileVtx_t **ppTileVtx ) {
u32 j, k, nWalkingBit, nOutsideCount;
Vec3_t *pNormVec;
float *pfCamspace, fDot;
_nTriCrossesPlanesMask = _nBoundCrossesPlanesMask;
if( _nBoundCrossesPlanesMask ) {
for( j=0; j<3; j++ ) {
if( ppTileVtx[j]->afCamspaceVtx[_ITERATOR_Z] >= 0.0f ) {
break;
}
}
if( j == 3 ) {
/* All vertices are behind the camera.*/
/* Cull this triangle...*/
return FALSE;
}
for( j=0, nWalkingBit=1; j<4; j++, nWalkingBit<<=1 ) {
if( _nBoundCrossesPlanesMask & nWalkingBit ) {
pNormVec = &Viewport_rect_norm[j];
nOutsideCount = 0;
for( k=0; k<3; k++ ) {
if( ppTileVtx[k]->anOutsidePlaneCode[j] == _OUTCODE_UNKNOWN ) {
pfCamspace = &ppTileVtx[k]->afCamspaceVtx[_ITERATOR_X];
fDot = pfCamspace[0]*pNormVec->p[0]
+ pfCamspace[1]*pNormVec->p[1]
+ pfCamspace[2]*pNormVec->p[2]
- GUTIL_CLIPPING_AVOID_DIVZERO_MARGIN;
if( fDot < 0.0f ) {
ppTileVtx[k]->anOutsidePlaneCode[j] = _OUTCODE_OUTSIDE;
nOutsideCount++;
} else {
ppTileVtx[k]->anOutsidePlaneCode[j] = _OUTCODE_INSIDE;
}
} else if( ppTileVtx[k]->anOutsidePlaneCode[j] == _OUTCODE_OUTSIDE ) {
nOutsideCount++;
}
}
if( nOutsideCount == 3 ) {
/* All vertices are outside this plane...*/
break;
}
if( nOutsideCount == 0 ) {
/* All vertices are inside this plane...*/
_nTriCrossesPlanesMask ^= nWalkingBit;
}
}
}
if( nOutsideCount == 3 ) {
/* Indicate that triangle is entirely outside frustum...*/
return FALSE;
}
}
return TRUE;
}
static void _ClipTriToFrustumAndDraw( TileVtx_t **ppTileVtx ) {
u32 i, j, k, nVtxInCount, nVtxStartOutIndex, nNumTris, nVtxInIndex, nVtxOutIndex;
float fPrevDot, fThisDot, fScale, x, y, w, q;
u32 nWalkingBit;
TileVtx_t *pVtxIn, *pPrevVtxIn, *pVtxBuf;
for( i=0; i<3; i++ ) {
ppTileVtx[i]->afCamspaceVtx[_ITERATOR_A] = ppTileVtx[i]->ScreenspaceVtx.a;
_apTileClippedVtx[i] = ppTileVtx[i];
}
pVtxBuf = _aTileClippedVtx;
nVtxInIndex = 0;
nVtxOutIndex = 3;
nVtxInCount = 3;
for( j=0, nWalkingBit=1; j<4; j++, nWalkingBit<<=1 ) {
if( _nTriCrossesPlanesMask & nWalkingBit ) {
nVtxStartOutIndex = nVtxOutIndex;
pVtxIn = _apTileClippedVtx[ nVtxInIndex + nVtxInCount - 1 ];
fPrevDot = pVtxIn->afCamspaceVtx[_ITERATOR_X]*Viewport_rect_norm[j].p[0]
+ pVtxIn->afCamspaceVtx[_ITERATOR_Y]*Viewport_rect_norm[j].p[1]
+ pVtxIn->afCamspaceVtx[_ITERATOR_Z]*Viewport_rect_norm[j].p[2]
- GUTIL_CLIPPING_AVOID_DIVZERO_MARGIN;
for( i=0; i<nVtxInCount; i++ ) {
pPrevVtxIn = pVtxIn;
if( fPrevDot>0.0f ) {
_apTileClippedVtx[nVtxOutIndex++] = pVtxIn;
}
pVtxIn = _apTileClippedVtx[nVtxInIndex++];
fThisDot = pVtxIn->afCamspaceVtx[_ITERATOR_X]*Viewport_rect_norm[j].p[0]
+ pVtxIn->afCamspaceVtx[_ITERATOR_Y]*Viewport_rect_norm[j].p[1]
+ pVtxIn->afCamspaceVtx[_ITERATOR_Z]*Viewport_rect_norm[j].p[2]
- GUTIL_CLIPPING_AVOID_DIVZERO_MARGIN;
if( fPrevDot*fThisDot < 0.0f ) {
/* vertices are on opposite sides of nearplane*/
fScale = fPrevDot / (fPrevDot-fThisDot);
for( k=0; k<_CAMSPACE_ITERATOR_COUNT; k++ ) {
pVtxBuf->afCamspaceVtx[k] = pPrevVtxIn->afCamspaceVtx[k]
+ (pVtxIn->afCamspaceVtx[k] - pPrevVtxIn->afCamspaceVtx[k] )*fScale;
}
_apTileClippedVtx[ nVtxOutIndex++ ] = pVtxBuf++;
}
fPrevDot = fThisDot;
}
nVtxInCount = nVtxOutIndex - nVtxStartOutIndex;
if( nVtxInCount==0 ) {
return;
}
}
}
for( i=0, j=nVtxInIndex; i<nVtxInCount; i++, j++ ) {
pVtxIn = _apTileClippedVtx[j];
w = pVtxIn->ScreenspaceVtx.oow = VIEWPORT_CALC_OOW( pVtxIn->afCamspaceVtx[_ITERATOR_Z] );
q = VIEWPORT_CALC_Q( w );
#if GFXDEFS_USE_ZBUFFER
pVtxIn->ScreenspaceVtx.ooz = VIEWPORT_CALC_OOZ( pVtxIn->afCamspaceVtx[_ITERATOR_Z] );
#endif
x = q*pVtxIn->afCamspaceVtx[_ITERATOR_X] - Viewport_frustum_limit[0];
y = q*pVtxIn->afCamspaceVtx[_ITERATOR_Y] - Viewport_frustum_limit[2];
pVtxIn->ScreenspaceVtx.x = GFXDEFS_SNAPVERT(x);
pVtxIn->ScreenspaceVtx.y = GFXDEFS_SNAPVERT(y);
pVtxIn->ScreenspaceVtx.a = pVtxIn->afCamspaceVtx[_ITERATOR_A];
GUTIL_CLAMP_OOW( pVtxIn->ScreenspaceVtx.oow );
}
nNumTris = nVtxInCount - 2;
/* Draw base water...*/
for( j=0; j<nVtxInCount; j++ ) {
pVtxIn = _apTileClippedVtx[j + nVtxInIndex];
w = pVtxIn->ScreenspaceVtx.oow;
pVtxIn->ScreenspaceVtx.tmuvtx[0].sow = pVtxIn->ScreenspaceVtx.tmuvtx[1].sow = pVtxIn->afCamspaceVtx[_ITERATOR_S] * w;
pVtxIn->ScreenspaceVtx.tmuvtx[0].tow = pVtxIn->ScreenspaceVtx.tmuvtx[1].tow = pVtxIn->afCamspaceVtx[_ITERATOR_T] * w;
}
for( i=0; i<nNumTris; i++ ) {
gutil_ScreenCoverage_DrawTriangleIfUnderLimit(
&_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx,
&_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx,
&_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx
);
}
}
#endif /*DREAMCAST */
#else /*ULTRA64*/
static void _DrawParticle( ParticleDef_t *pParticle ) {
float *pfCamspaceVtx;
TileVtx_t *pTileVtx;
Vec3_t *pModelspaceVtx;
Vtx *VtxPtr;
float Stash[4][4],Stash2[4][4];
int uvs[] = { 64,0, 64,64, 0,0, 0,64 }; // NOTE bow-tie
int i,k;
EmptyParticleCache(32); // Empty when 32 or more vertices
if (NumN64Vertices<(MAX_N64VERTICES-10)) {
// Create '4' vertices and add
VtxPtr = &N64Vertices[draw_buffer][NumN64Vertices];
for( k=0; k<4; k++ ) {
pTileVtx = &_aParticleTileVtx[k];
pModelspaceVtx = &pTileVtx->ModelspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
pModelspaceVtx->p[0] = _aParticleVtx[k].p[0];
pModelspaceVtx->p[1] = _aParticleVtx[k].p[1];
pModelspaceVtx->p[2] = 0.0f;
pfCamspaceVtx[_ITERATOR_X] = Xfm_pStackTop->mf[0][0] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][0] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][0];
pfCamspaceVtx[_ITERATOR_Y] = Xfm_pStackTop->mf[0][1] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][1] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][1];
pfCamspaceVtx[_ITERATOR_Z] = Xfm_pStackTop->mf[0][2] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][2] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][2];
VtxPtr->v.ob[0] = -(s16)(pfCamspaceVtx[_ITERATOR_X]);
VtxPtr->v.ob[1] = (s16)(pfCamspaceVtx[_ITERATOR_Y]);
VtxPtr->v.ob[2] = (s16)(pfCamspaceVtx[_ITERATOR_Z]);
VtxPtr->v.flag = 0;
VtxPtr->v.tc[0] = (s16)uvs[k*2]<<6;
VtxPtr->v.tc[1] = (s16)uvs[(k*2)+1]<<6;
VtxPtr->v.cn[0] = /*RGB */
VtxPtr->v.cn[1] =
VtxPtr->v.cn[2] = 0xff;
VtxPtr->v.cn[3] = 0xff; /*A */
VtxPtr++;
}
// And download/draw - Note TRI's is a bow-tie!!!
gSPVertex(N64GfxDlPtr++, &N64Vertices[draw_buffer][NumN64Vertices], 4, 0);
gSP2Triangles(N64GfxDlPtr++, 0,1,2,0, 2,3,1,0);
// WaterSprayNumVertsInCache += 4;
NumN64Vertices+=4;
}
/*
u32 k, n;
float *pfCamspaceVtx, fOpacity, oow, q;
Vec3_t *pModelspaceVtx;
GrVertex *pScreenspaceVtx;
TileVtx_t *pTileVtx;
ARGBf_t SensedLight;
BOOL bLitParticle;
// Transform vertices...
for( k=0; k<4; k++ ) {
pTileVtx = &_aParticleTileVtx[k];
pModelspaceVtx = &pTileVtx->ModelspaceVtx;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
pTileVtx->bScreenspaceVtxValid = FALSE;
pTileVtx->anOutsidePlaneCode[0]
= pTileVtx->anOutsidePlaneCode[1]
= pTileVtx->anOutsidePlaneCode[2]
= pTileVtx->anOutsidePlaneCode[3]
= _OUTCODE_UNKNOWN;
pModelspaceVtx->p[0] = _aParticleVtx[k].p[0];
pModelspaceVtx->p[1] = _aParticleVtx[k].p[1];
pModelspaceVtx->p[2] = 0.0f;
pfCamspaceVtx[_ITERATOR_X] = Xfm_pStackTop->mf[0][0] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][0] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][0];
pfCamspaceVtx[_ITERATOR_Y] = Xfm_pStackTop->mf[0][1] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][1] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][1];
pfCamspaceVtx[_ITERATOR_Z] = Xfm_pStackTop->mf[0][2] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][2] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][2];
pfCamspaceVtx[_ITERATOR_S] = _aParticleSt[k].p[0];
pfCamspaceVtx[_ITERATOR_T] = _aParticleSt[k].p[1];
}
// Draw triangles...
pParticle->bCulledByFrustum = TRUE;
bLitParticle = FALSE;
for( k=0; k<2; k++ ) {
if( _TestTriAgainstFrustum( &_apParticleTileVtx[k] ) ) {
// Triangle is at least partially visible...
pParticle->bCulledByFrustum = FALSE;
if( !bLitParticle ) {
bLitParticle = TRUE;
world_LightSensorPoint( pParticle->pSector, &pParticle->HeadPoint, &SensedLight );
SensedLight.argb[2] = (SensedLight.argb[2] + 1.0f) * 0.5f;
SensedLight.argb[1] = (SensedLight.argb[1] + 1.0f) * 0.5f;
SensedLight.argb[0] = (SensedLight.argb[0] + 1.0f) * 0.5f;
fOpacity = (pParticle->nType == WATERSPRAY_TYPE_IMPACT) ? pParticle->fOpacity : 0.10f;
material_ConstantColorValue( GFXDEFS_FLOATARGB_TO_GRCOLOR(
fOpacity,
SensedLight.argb[2],
SensedLight.argb[1],
SensedLight.argb[0]
) );
}
if( !_nTriCrossesPlanesMask ) {
// Triangle doesn't require frustum clipping...
for( n=0; n<3; n++ ) {
pTileVtx = _apParticleTileVtx[n+k];
if( !pTileVtx->bScreenspaceVtxValid ) {
pTileVtx->bScreenspaceVtxValid = TRUE;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
oow = pScreenspaceVtx->oow = VIEWPORT_CALC_OOW( pfCamspaceVtx[2] );
q = VIEWPORT_CALC_Q( oow );
#if GFXDEFS_USE_ZBUFFER
pScreenspaceVtx->ooz = VIEWPORT_CALC_OOZ( pfCamspaceVtx[2] );
#endif
pScreenspaceVtx->x = GFXDEFS_SNAPVERT( q*pfCamspaceVtx[0] - Viewport_frustum_limit[0] );
pScreenspaceVtx->y = GFXDEFS_SNAPVERT( q*pfCamspaceVtx[1] - Viewport_frustum_limit[2] );
GUTIL_CLAMP_OOW( pScreenspaceVtx->oow );
pTileVtx->ScreenspaceVtx.tmuvtx[0].sow =
pTileVtx->ScreenspaceVtx.tmuvtx[1].sow =
pTileVtx->afCamspaceVtx[_ITERATOR_S]*pScreenspaceVtx->oow;
pTileVtx->ScreenspaceVtx.tmuvtx[0].tow =
pTileVtx->ScreenspaceVtx.tmuvtx[1].tow =
pTileVtx->afCamspaceVtx[_ITERATOR_T]*pScreenspaceVtx->oow;
}
}
gutil_ScreenCoverage_DrawTriangleIfUnderLimit(
&_apParticleTileVtx[k]->ScreenspaceVtx,
&_apParticleTileVtx[k+1]->ScreenspaceVtx,
&_apParticleTileVtx[k+2]->ScreenspaceVtx
);
} else {
// Must clip triangle to frustum...
_ClipTriToFrustumAndDraw( &_apParticleTileVtx[k] );
}
}
}
*/
}
#endif /*ULTRA64*/
static void _KillParticle( ParticleDef_t *pParticle ) {
XASSERT( _nParticleCount > 0 );
if( --_nParticleCount ) {
*pParticle = _pParticleBuf[ _nParticleCount ];
}
}
static void _EmitNewParticle( const SprayDef_t *pSprayDef, const WorldOb_t *pWorldOb,
Vec2_t *pWorldPointXZ, u32 nEmitterIndex, float fEmitterY, float fWaterY ) {
const Orient_t *pOrient;
const Mtx3_t *pMtx;
const Vec3_t *pPos, *pModelUnitNormal;
const Vec2_t *pPrevFrameWorldPoint;
Vec3_t EmitterVelocity, WorldUnitNormal, ScaledEmitterVelocity;
WorldSector_t *pSector;
const EmitterDef_t *pEmitter;
float fTemp, fSpeed, fSpraySpeed, fEmitterDepthUnderWater;
WatersprayType_t nType;
ParticleDef_t *pParticle;
/* Set up pointers...*/
pEmitter = &pSprayDef->aEmitters[ nEmitterIndex ];
pModelUnitNormal = &pEmitter->ModelUnitNormal;
pPrevFrameWorldPoint = &pSprayDef->aWorldPoints[ 1-pSprayDef->nNextArrayIndex ][nEmitterIndex];
nType = pSprayDef->nType;
pOrient = &pWorldOb->Orient;
pSector = pWorldOb->Tws.pCenterSector;
pMtx = &pOrient->Rot;
pPos = &pOrient->Pos;
/* Calculate emitter unit normal in world space...*/
WorldUnitNormal.p[0] = pMtx->m[0][0] * pModelUnitNormal->p[0]
+ pMtx->m[2][0] * pModelUnitNormal->p[2];
WorldUnitNormal.p[1] = 0.0f;
WorldUnitNormal.p[2] = pMtx->m[0][2] * pModelUnitNormal->p[0]
+ pMtx->m[2][2] * pModelUnitNormal->p[2];
vec3_Unit( &WorldUnitNormal );
/* Calculate the emitter velocity in X/Z world space...*/
EmitterVelocity.p[0] = pWorldPointXZ->p[0] - pPrevFrameWorldPoint->p[0];
EmitterVelocity.p[1] = 0.0f;
EmitterVelocity.p[2] = pWorldPointXZ->p[1] - pPrevFrameWorldPoint->p[1];
/* Calculate speed of spray particle...*/
fSpeed = EmitterVelocity.p[0]*WorldUnitNormal.p[0] + EmitterVelocity.p[2]*WorldUnitNormal.p[2];
switch( nType ) {
case WATERSPRAY_TYPE_IMPACT:
if( fSpeed <= 0.2f ) {
/* Don't create particle...*/
return;
}
fSpeed *= 0.5f;
XMATH_CLAMP( fSpeed, 0.0f, 1.0f );
fSpraySpeed = fSpeed;
/* New particle...*/
pParticle = _FindNewParticleForEmitter( pWorldPointXZ );
if( pParticle == NULL ) {
return;
}
pParticle->nControlFlags = *pSprayDef->pnControlFlags;
fEmitterDepthUnderWater = fWaterY - fEmitterY;
XMATH_CLAMPMAX( fEmitterDepthUnderWater, 10.0f );
fTemp = fSpeed * (1.0f/6.0f);
pParticle->fOpacity = XMATH_FPOT( fTemp, 0.1f, 0.75f );
/* Set tail point and velocity...*/
pParticle->TailPoint.p[0] = pWorldPointXZ->p[0];
pParticle->TailPoint.p[1] = fWaterY + 2.0f;
pParticle->TailPoint.p[2] = pWorldPointXZ->p[1];
pParticle->TailVelocity.p[0] = WorldUnitNormal.p[0] * fSpraySpeed;
pParticle->TailVelocity.p[1] = fEmitterDepthUnderWater * fSpraySpeed * 0.075f;
pParticle->TailVelocity.p[2] = WorldUnitNormal.p[2] * fSpraySpeed;
vec3_Add( &pParticle->TailVelocity, &EmitterVelocity );
/* Set head point and velocity...*/
pParticle->HeadPoint.p[0] = pParticle->TailPoint.p[0];
pParticle->HeadPoint.p[1] = pParticle->TailPoint.p[1];
pParticle->HeadPoint.p[2] = pParticle->TailPoint.p[2];
vec3_Add( &pParticle->HeadPoint, &pParticle->TailVelocity );
#if 0
{
float fSpeed;
fSpeed = vec3_CalcMag( &pParticle->TailVelocity );
if( fSpeed > 50.0f ) {
int aasdf=5;
}
}
#endif
pParticle->HeadVelocity.p[0] = pParticle->TailVelocity.p[0];
pParticle->HeadVelocity.p[1] = pParticle->TailVelocity.p[1];
pParticle->HeadVelocity.p[2] = pParticle->TailVelocity.p[2];
vec3_Scale1( &pParticle->HeadVelocity, 1.2f );
pParticle->HeadVelocity.p[1] += 0.5f*_afGravity[nType]*fSpraySpeed;
/* Set fScaleX...*/
pParticle->fScaleX = 3.0f;
pParticle->fDeltaScaleX = 0.1f;
if( _pFlumeMtx ) {
Vec3_t TempVec;
TempVec = pParticle->HeadVelocity;
mtx3_Multi3x3TimesVec3( &pParticle->HeadVelocity, _pFlumeMtx, &TempVec );
TempVec = pParticle->TailVelocity;
mtx3_Multi3x3TimesVec3( &pParticle->TailVelocity, _pFlumeMtx, &TempVec );
}
break;
case WATERSPRAY_TYPE_DRAG:
if( fSpeed >= -0.5f ) {
/* Don't create particle...*/
return;
}
fSpeed *= (-0.5f * 0.268555f);
XMATH_CLAMP( fSpeed, 0.0f, 6.0f );
/* New particle...*/
pParticle = _FindNewParticleForEmitter( pWorldPointXZ );
if( pParticle == NULL ) {
return;
}
pParticle->nControlFlags = *pSprayDef->pnControlFlags;
vec3_CalcScale1( &ScaledEmitterVelocity, &EmitterVelocity, 1.0f );
fEmitterDepthUnderWater = fWaterY - fEmitterY;
XMATH_CLAMPMAX( fEmitterDepthUnderWater, 10.0f );
/* Set tail point and velocity...*/
pParticle->TailPoint.p[0] = pWorldPointXZ->p[0];
pParticle->TailPoint.p[1] = fWaterY + 1.0f;
pParticle->TailPoint.p[2] = pWorldPointXZ->p[1];
pParticle->TailVelocity.p[0] = WorldUnitNormal.p[0] * fSpeed;
pParticle->TailVelocity.p[1] = fEmitterDepthUnderWater * fSpeed * 0.15f;
pParticle->TailVelocity.p[2] = WorldUnitNormal.p[2] * fSpeed;
vec3_Add( &pParticle->TailVelocity, &ScaledEmitterVelocity );
/* Set head point and velocity...*/
pParticle->HeadPoint.p[0] = pParticle->TailPoint.p[0];
pParticle->HeadPoint.p[1] = pParticle->TailPoint.p[1];
pParticle->HeadPoint.p[2] = pParticle->TailPoint.p[2];
vec3_Add( &pParticle->HeadPoint, &pParticle->TailVelocity );
pParticle->HeadVelocity.p[0] = pParticle->TailVelocity.p[0];
pParticle->HeadVelocity.p[1] = pParticle->TailVelocity.p[1];
pParticle->HeadVelocity.p[2] = pParticle->TailVelocity.p[2];
vec3_Scale1( &pParticle->HeadVelocity, 1.2f );
pParticle->HeadVelocity.p[1] += 0.5f*_afGravity[nType]*fSpeed;
/* Set fScaleX...*/
pParticle->fScaleX = 3.0f;
pParticle->fDeltaScaleX = 0.1f;
if( _pFlumeMtx ) {
Vec3_t TempVec;
TempVec = pParticle->HeadVelocity;
mtx3_Multi3x3TimesVec3( &pParticle->HeadVelocity, _pFlumeMtx, &TempVec );
TempVec = pParticle->TailVelocity;
mtx3_Multi3x3TimesVec3( &pParticle->TailVelocity, _pFlumeMtx, &TempVec );
}
break;
default:
XASSERT_NOW;
}
pParticle->pSector = world_FindSectorContainingPoint(
pSector,
pParticle->HeadPoint.p[0],
pParticle->HeadPoint.p[2]
);
pParticle->pWorldOb = pWorldOb;
pParticle->nType = nType;
}
static void _BuildEmitterSegmentsFromTriList( WatersprayType_t nWatersprayType,
WatersprayControlFlags_t *pnControlFlags,
WorldOb_t *pWorldOb,
const float *pfSprayEmitterHeightAdjust,
const float *pfMistEmitterHeightAdjust ) {
u32 nAnchorTriIndex, nAnchorEdgeIndex;
for(;;) {
_nWatersprayJunctionCount = 0;
if( !_ReturnIndexOfUnvisitedTriWithUnsharedEdge( &nAnchorTriIndex, &nAnchorEdgeIndex ) ) {
break;
}
/* Found anchor triangle and the index of its unshared edge...*/
/* Add anchor to waterspray junction list...*/
_AddJunction( nAnchorTriIndex, nAnchorEdgeIndex );
/* Now recursively add all adjacent junctions...*/
_RecursivelyAddAdjacentJunctions( nAnchorTriIndex, !nAnchorEdgeIndex );
/* Make sure list of junctions is going the right way (counter-clockwise around origin)...*/
_OrderJunctionList();
/* Build the spray object...*/
waterspray_InitInstance(
nWatersprayType,
pnControlFlags,
pWorldOb,
_nWatersprayJunctionCount,
_aWatersprayJunctions,
pfSprayEmitterHeightAdjust,
pfMistEmitterHeightAdjust
);
}
}
static void _OrderJunctionList( void ) {
WatersprayJunctionDef_t TempJunction;
Vec2_t SprayEmitterLineXZ, SprayEmitterNormalXZ, TestVecXZ;
float fDot;
u32 i;
XASSERT( _nWatersprayJunctionCount >= 2 );
SprayEmitterLineXZ.p[0] = _aWatersprayJunctions[1].ModelspacePoint.p[0] - _aWatersprayJunctions[0].ModelspacePoint.p[0];
SprayEmitterLineXZ.p[1] = _aWatersprayJunctions[1].ModelspacePoint.p[2] - _aWatersprayJunctions[0].ModelspacePoint.p[2];
SprayEmitterNormalXZ.p[0] = -SprayEmitterLineXZ.p[1];
SprayEmitterNormalXZ.p[1] = SprayEmitterLineXZ.p[0];
TestVecXZ.p[0] = _aWatersprayJunctions[0].ModelspacePoint.p[0];
TestVecXZ.p[1] = _aWatersprayJunctions[0].ModelspacePoint.p[2];
fDot = VEC2DOT( SprayEmitterNormalXZ, TestVecXZ );
if( fDot > 0.0f ) {
/* Junction list is backwards, so let's reverse it...*/
for( i=0; i<(_nWatersprayJunctionCount>>1); i++ ) {
TempJunction = _aWatersprayJunctions[i];
_aWatersprayJunctions[i] = _aWatersprayJunctions[_nWatersprayJunctionCount-i-1];
_aWatersprayJunctions[_nWatersprayJunctionCount-i-1] = TempJunction;
}
}
}
static void _RecursivelyAddAdjacentJunctions( u32 nTriIndex, u32 nEdgeIndex ) {
u32 i, j;
for( i=0; i<_nTriListCount; i++ ) {
if( !_aTriList[i].bVisited ) {
/* Found unvisited triangle...*/
for( j=0; j<2; j++ ) {
if( _aTriList[i].apVtx[j] == _aTriList[nTriIndex].apVtx[nEdgeIndex] ) {
/* Found shared edge...*/
_aTriList[i].bVisited = TRUE;
_AddJunction( i, j );
_RecursivelyAddAdjacentJunctions( i, !j );
return;
}
}
}
}
/* Unvisited triangle with shared edge not found.*/
/* This triangle must be the end of the line...*/
_AddJunction( nTriIndex, nEdgeIndex );
}
static void _AddJunction( u32 nTriIndex, u32 nEdgeIndex ) {
XASSERT( _nWatersprayJunctionCount < _MAX_TRIS_FOR_SPRAY_TYPE*2 );
vec3_Copy(
&_aWatersprayJunctions[_nWatersprayJunctionCount].ModelspacePoint,
_aTriList[nTriIndex].apVtx[nEdgeIndex]
);
_nWatersprayJunctionCount++;
}
static BOOL _ReturnIndexOfUnvisitedTriWithUnsharedEdge( u32 *pnTriIndex, u32 *pnUnsharedEdgeIndex ) {
u32 i, j, k;
/* Find an unvisited triangle with a unshared edge...*/
for( i=0; i<_nTriListCount; i++ ) {
if( !_aTriList[i].bVisited ) {
/* Found anchor triangle...*/
for( j=0; j<2; j++ ) {
/* Each edge of anchor triangle...*/
for( k=0; k<_nTriListCount; k++ ) {
if( !_aTriList[k].bVisited && k!=i ) {
/* Found test triangle...*/
if( _aTriList[i].apVtx[j]==_aTriList[k].apVtx[0] || _aTriList[i].apVtx[j]==_aTriList[k].apVtx[1] ) {
/* Test triangle shares an edge...*/
break;
}
}
}
if( k == _nTriListCount ) {
/* Found non-shared edge of anchor triangle...*/
_aTriList[i].bVisited = TRUE;
if( pnTriIndex ) {
*pnTriIndex = i;
}
if( pnUnsharedEdgeIndex ) {
*pnUnsharedEdgeIndex = j;
}
return TRUE;
}
}
}
}
/* Non-shared edge not found...*/
return FALSE;
}
#if TARGET==DREAMCAST
static void _DrawParticle( ParticleDef_t *pParticle ) {
u32 k, n;
float *pfCamspaceVtx, fOpacity, ooz, q;
Vec3_t *pModelspaceVtx;
GrVertex *pScreenspaceVtx;
TileVtx_t *pTileVtx;
ARGBf_t SensedLight;
BOOL bLitParticle;
kmxxGetCurrentPtr(&VertexBufferDesc);
kmxxStartVertexStrip(&VertexBufferDesc);
kmxxReleaseCurrentPtr(&VertexBufferDesc);
/* Transform vertices... */
for( k=0; k<4; k++ ) {
pTileVtx = &_aParticleTileVtx[k];
pModelspaceVtx = &pTileVtx->ModelspaceVtx;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
pTileVtx->bScreenspaceVtxValid = FALSE;
pTileVtx->anOutsidePlaneCode[0]
= pTileVtx->anOutsidePlaneCode[1]
= pTileVtx->anOutsidePlaneCode[2]
= pTileVtx->anOutsidePlaneCode[3]
= _OUTCODE_UNKNOWN;
pModelspaceVtx->p[0] = _aParticleVtx[k].p[0];
pModelspaceVtx->p[1] = _aParticleVtx[k].p[1];
pModelspaceVtx->p[2] = 0.0f;
pfCamspaceVtx[_ITERATOR_X] = Xfm_pStackTop->mf[0][0] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][0] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][0];
pfCamspaceVtx[_ITERATOR_Y] = Xfm_pStackTop->mf[0][1] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][1] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][1];
pfCamspaceVtx[_ITERATOR_Z] = Xfm_pStackTop->mf[0][2] * pModelspaceVtx->p[0]
+ Xfm_pStackTop->mf[1][2] * pModelspaceVtx->p[1]
+ Xfm_pStackTop->mf[3][2];
pfCamspaceVtx[_ITERATOR_S] = _aParticleSt[k].p[0];
pfCamspaceVtx[_ITERATOR_T] = _aParticleSt[k].p[1];
}
/* Draw triangles... */
pParticle->bCulledByFrustum = TRUE;
for( k=0; k<2; k++ ) {
if( _TestTriAgainstFrustum( &_apParticleTileVtx[k] ) ) {
/* Triangle is at least partially visible... */
if( !_nTriCrossesPlanesMask ) {
/* Triangle doesn't require frustum clipping... */
for( n=0; n<3; n++ ) {
pTileVtx = _apParticleTileVtx[n+k];
if( !pTileVtx->bScreenspaceVtxValid ) {
pTileVtx->bScreenspaceVtxValid = TRUE;
pScreenspaceVtx = &pTileVtx->ScreenspaceVtx;
pfCamspaceVtx = pTileVtx->afCamspaceVtx;
ooz = 1.0f / pfCamspaceVtx[2];
q = Viewport_zscreen * ooz;
pScreenspaceVtx->x = 320.0f + q*pfCamspaceVtx[0];
pScreenspaceVtx->y = dcViewportYTrans - q*pfCamspaceVtx[1];
pScreenspaceVtx->ooz = ooz;
pTileVtx->ScreenspaceVtx.tmuvtx[0].sow =
pTileVtx->afCamspaceVtx[_ITERATOR_S] * (2.0f/256.0f);
pTileVtx->ScreenspaceVtx.tmuvtx[0].tow =
pTileVtx->afCamspaceVtx[_ITERATOR_T] * (1.0f/256.0f);
}
}
kmxxGetCurrentPtr(&VertexBufferDesc);
kmxxSetVertex_3(KM_VERTEXPARAM_NORMAL,
_apParticleTileVtx[k]->ScreenspaceVtx.x,
_apParticleTileVtx[k]->ScreenspaceVtx.y,
_apParticleTileVtx[k]->ScreenspaceVtx.ooz,
_apParticleTileVtx[k]->ScreenspaceVtx.tmuvtx[0].sow,
_apParticleTileVtx[k]->ScreenspaceVtx.tmuvtx[0].tow,
0x20ffffff,
0xff000000
);
kmxxSetVertex_3(KM_VERTEXPARAM_NORMAL,
_apParticleTileVtx[k+1]->ScreenspaceVtx.x,
_apParticleTileVtx[k+1]->ScreenspaceVtx.y,
_apParticleTileVtx[k+1]->ScreenspaceVtx.ooz,
_apParticleTileVtx[k+1]->ScreenspaceVtx.tmuvtx[0].sow,
_apParticleTileVtx[k+1]->ScreenspaceVtx.tmuvtx[0].tow,
0x20ffffff,
0xff000000
);
kmxxSetVertex_3(KM_VERTEXPARAM_ENDOFSTRIP,
_apParticleTileVtx[k+2]->ScreenspaceVtx.x,
_apParticleTileVtx[k+2]->ScreenspaceVtx.y,
_apParticleTileVtx[k+2]->ScreenspaceVtx.ooz,
_apParticleTileVtx[k+2]->ScreenspaceVtx.tmuvtx[0].sow,
_apParticleTileVtx[k+2]->ScreenspaceVtx.tmuvtx[0].tow,
0x20ffffff,
0xff000000
);
kmxxReleaseCurrentPtr(&VertexBufferDesc);
} else {
/* Must clip triangle to frustum... */
_ClipTriToFrustumAndDraw( &_apParticleTileVtx[k] );
}
}
}
}
void waterspray_Draw( WorldSector_t **ppSectorList, u32 nSectorCount ) {
ParticleDef_t *pParticle;
u32 i, j, nPriorZbufState, nTexture;
float fDx, fDz;
_pCameraLastUsed = Xfm_pStackCam;
if( !_bDrawEnabled ) {
/* Spray drawing is disabled... */
return;
}
if( _nParticleCount && _pSprayTexDef ) {
nTexture = _pSprayTexDef->aTMUTexInfo[0].nTmemStartAddress;
if(nTexture != -1){
dcSetCurrentVertexContext(&dcTextureTable[nTexture].AlphaBlendedTexVC);
}
pParticle = _pParticleBuf;
for( i=0; i<_nParticleCount; i++, pParticle++ ) {
fDx = pParticle->HeadPoint.p[0] - _pCameraLastUsed->xfm.mr[3][0];
fDz = pParticle->HeadPoint.p[2] - _pCameraLastUsed->xfm.mr[3][2];
pParticle->fDistToCam2 = fDx*fDx + fDz*fDz;
if( !(pParticle->pWorldOb->nFlags & WORLDOB_FLAG_DONT_DRAW) ) {
for( j=0; j<nSectorCount; j++ ) {
if( pParticle->pSector == MASK_SECTOR_ADDR(ppSectorList[j]) ) {
/* Particle is in a sector that is being displayed... */
_PushParticleXfm( pParticle );
_DrawParticle( pParticle );
xfm_Pop();
break;
}
}
}
}
}
}
/* Returns TRUE if triangle is at least partially visible. */
static BOOL _TestTriAgainstFrustum( TileVtx_t **ppTileVtx ) {
u32 j, k, nWalkingBit, nOutsideCount;
Vec3_t *pNormVec;
float *pfCamspace, fDot;
_nTriCrossesPlanesMask = _nBoundCrossesPlanesMask;
if( _nBoundCrossesPlanesMask ) {
for( j=0; j<3; j++ ) {
if( ppTileVtx[j]->afCamspaceVtx[_ITERATOR_Z] >= 0.0f ) {
break;
}
}
if( j == 3 ) {
/* All vertices are behind the camera. */
/* Cull this triangle... */
return FALSE;
}
for( j=0, nWalkingBit=1; j<4; j++, nWalkingBit<<=1 ) {
if( _nBoundCrossesPlanesMask & nWalkingBit ) {
pNormVec = &Viewport_rect_norm[j];
nOutsideCount = 0;
for( k=0; k<3; k++ ) {
if( ppTileVtx[k]->anOutsidePlaneCode[j] == _OUTCODE_UNKNOWN ) {
pfCamspace = &ppTileVtx[k]->afCamspaceVtx[_ITERATOR_X];
fDot = pfCamspace[0]*pNormVec->p[0]
+ pfCamspace[1]*pNormVec->p[1]
+ pfCamspace[2]*pNormVec->p[2]
- GUTIL_CLIPPING_AVOID_DIVZERO_MARGIN;
if( fDot < 0.0f ) {
ppTileVtx[k]->anOutsidePlaneCode[j] = _OUTCODE_OUTSIDE;
nOutsideCount++;
} else {
ppTileVtx[k]->anOutsidePlaneCode[j] = _OUTCODE_INSIDE;
}
} else if( ppTileVtx[k]->anOutsidePlaneCode[j] == _OUTCODE_OUTSIDE ) {
nOutsideCount++;
}
}
if( nOutsideCount == 3 ) {
/* All vertices are outside this plane... */
break;
}
if( nOutsideCount == 0 ) {
/* All vertices are inside this plane... */
_nTriCrossesPlanesMask ^= nWalkingBit;
}
}
}
if( nOutsideCount == 3 ) {
/* Indicate that triangle is entirely outside frustum... */
return FALSE;
}
}
return TRUE;
}
static void _ClipTriToFrustumAndDraw( TileVtx_t **ppTileVtx ) {
u32 i, j, k, nVtxInCount, nVtxStartOutIndex, nNumTris, nVtxInIndex, nVtxOutIndex;
float fPrevDot, fThisDot, fScale, x, y, w, q, ooz;
u32 nWalkingBit;
TileVtx_t *pVtxIn, *pPrevVtxIn, *pVtxBuf;
for( i=0; i<3; i++ ) {
ppTileVtx[i]->afCamspaceVtx[_ITERATOR_A] = ppTileVtx[i]->ScreenspaceVtx.a;
_apTileClippedVtx[i] = ppTileVtx[i];
}
pVtxBuf = _aTileClippedVtx;
nVtxInIndex = 0;
nVtxOutIndex = 3;
nVtxInCount = 3;
for( j=0, nWalkingBit=1; j<4; j++, nWalkingBit<<=1 ) {
if( _nTriCrossesPlanesMask & nWalkingBit ) {
nVtxStartOutIndex = nVtxOutIndex;
pVtxIn = _apTileClippedVtx[ nVtxInIndex + nVtxInCount - 1 ];
fPrevDot = pVtxIn->afCamspaceVtx[_ITERATOR_X]*Viewport_rect_norm[j].p[0]
+ pVtxIn->afCamspaceVtx[_ITERATOR_Y]*Viewport_rect_norm[j].p[1]
+ pVtxIn->afCamspaceVtx[_ITERATOR_Z]*Viewport_rect_norm[j].p[2]
- GUTIL_CLIPPING_AVOID_DIVZERO_MARGIN;
for( i=0; i<nVtxInCount; i++ ) {
pPrevVtxIn = pVtxIn;
if( fPrevDot>0.0f ) {
_apTileClippedVtx[nVtxOutIndex++] = pVtxIn;
}
pVtxIn = _apTileClippedVtx[nVtxInIndex++];
fThisDot = pVtxIn->afCamspaceVtx[_ITERATOR_X]*Viewport_rect_norm[j].p[0]
+ pVtxIn->afCamspaceVtx[_ITERATOR_Y]*Viewport_rect_norm[j].p[1]
+ pVtxIn->afCamspaceVtx[_ITERATOR_Z]*Viewport_rect_norm[j].p[2]
- GUTIL_CLIPPING_AVOID_DIVZERO_MARGIN;
if( fPrevDot*fThisDot < 0.0f ) {
/* vertices are on opposite sides of nearplane */
fScale = fPrevDot / (fPrevDot-fThisDot);
for( k=0; k<_CAMSPACE_ITERATOR_COUNT; k++ ) {
pVtxBuf->afCamspaceVtx[k] = pPrevVtxIn->afCamspaceVtx[k]
+ (pVtxIn->afCamspaceVtx[k] - pPrevVtxIn->afCamspaceVtx[k] )*fScale;
}
_apTileClippedVtx[ nVtxOutIndex++ ] = pVtxBuf++;
}
fPrevDot = fThisDot;
}
nVtxInCount = nVtxOutIndex - nVtxStartOutIndex;
if( nVtxInCount==0 ) {
return;
}
}
}
for( i=0, j=nVtxInIndex; i<nVtxInCount; i++, j++ ) {
pVtxIn = _apTileClippedVtx[j];
ooz = 1.0f / pVtxIn->afCamspaceVtx[_ITERATOR_Z];
q = Viewport_zscreen * ooz;
pVtxIn->ScreenspaceVtx.x = 320.0f + q*pVtxIn->afCamspaceVtx[_ITERATOR_X];
pVtxIn->ScreenspaceVtx.y = dcViewportYTrans - q*pVtxIn->afCamspaceVtx[_ITERATOR_Y];
pVtxIn->ScreenspaceVtx.ooz = ooz;
}
nNumTris = nVtxInCount - 2;
/* Draw base water... */
for( j=0; j<nVtxInCount; j++ ) {
pVtxIn = _apTileClippedVtx[j + nVtxInIndex];
pVtxIn->ScreenspaceVtx.tmuvtx[0].sow = pVtxIn->ScreenspaceVtx.tmuvtx[1].sow = pVtxIn->afCamspaceVtx[_ITERATOR_S] * (2.0f/256.0f);
pVtxIn->ScreenspaceVtx.tmuvtx[0].tow = pVtxIn->ScreenspaceVtx.tmuvtx[1].tow = pVtxIn->afCamspaceVtx[_ITERATOR_T] * (1.0f/256.0f);
}
for( i=0; i<nNumTris; i++ ) {
kmxxGetCurrentPtr(&VertexBufferDesc);
kmxxSetVertex_3(KM_VERTEXPARAM_NORMAL,
_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.x,
_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.y,
_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.ooz,
_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.tmuvtx[0].sow,
_apTileClippedVtx[nVtxInIndex]->ScreenspaceVtx.tmuvtx[0].tow,
0x20ffffff,
0xff000000
);
kmxxSetVertex_3(KM_VERTEXPARAM_NORMAL,
_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.x,
_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.y,
_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.ooz,
_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.tmuvtx[0].sow,
_apTileClippedVtx[nVtxInIndex+i+1]->ScreenspaceVtx.tmuvtx[0].tow,
0x20ffffff,
0xff000000
);
kmxxSetVertex_3(KM_VERTEXPARAM_ENDOFSTRIP,
_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.x,
_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.y,
_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.ooz,
_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.tmuvtx[0].sow,
_apTileClippedVtx[nVtxInIndex+i+2]->ScreenspaceVtx.tmuvtx[0].tow,
0x20ffffff,
0xff000000
);
kmxxReleaseCurrentPtr(&VertexBufferDesc);
}
}
#endif /* DREAMCAST */