MuckyFoot-UrbanChaos/fallen/Source/mist.cpp
2017-05-20 11:14:17 +10:00

522 lines
8.2 KiB
C++

//
// Mist.
//
#include "game.h"
#include <MFStdLib.h>
#ifndef PSX
#include <math.h>
#endif
#include "mist.h"
#include "pap.h"
//
// Messages drawn straight to the screen.
//
//void MSG_add(CBYTE *message, ...);
//
// This function returns the height of the floor at (x,z).
// It is defined in collide.cpp. We don't #include collide.h
// because then we would have to include thing.h and eventaully
// we'd have to include everything!
//
SLONG calc_height_at(SLONG x, SLONG z);
typedef struct
{
float u; // Offset from base u.
float v; // Offset from base v.
float du;
float dv;
} MIST_Point;
#ifdef PSX
#define MIST_MAX_POINTS 4096
#else
#define MIST_MAX_POINTS 4096
#endif
MIST_Point MIST_point[MIST_MAX_POINTS];
SLONG MIST_point_upto;
typedef struct
{
UBYTE type;
UBYTE detail;
UWORD p_index;
SLONG height;
SLONG x1, z1;
SLONG x2, z2;
} MIST_Mist;
#define MIST_MAX_MIST 8
MIST_Mist MIST_mist[MIST_MAX_MIST];
SLONG MIST_mist_upto;
void MIST_init()
{
//
// Initialise all the points and mist.
//
MIST_point_upto = 0;
MIST_mist_upto = 0;
}
void MIST_create(
SLONG detail, // The number of quads-per-row on the quad patch.
SLONG height, // Above the ground.
SLONG x1, SLONG z1,
SLONG x2, SLONG z2)
{
SLONG i;
static SLONG type_cycle = 0;
#ifdef PSX
return;
#endif
ASSERT(WITHIN(MIST_mist_upto, 0, MIST_MAX_MIST - 1));
MIST_Mist *mm = &MIST_mist[MIST_mist_upto++];
//
// No too detailed and not to simple.
//
SATURATE(detail, 3, 255);
//
// Do we have enough points?
//
ASSERT(MIST_point_upto + detail*detail <= MIST_MAX_POINTS);
//
// Initialise the new mist.
//
mm->type = type_cycle++ & 0x3;
mm->height = height;
mm->detail = detail;
mm->p_index = MIST_point_upto;
mm->x1 = x1;
mm->z1 = z1;
mm->x2 = x2;
mm->z2 = z2;
//
// Initialise all the points.
//
for (i = 0; i < detail * detail; i++)
{
MIST_point[MIST_point_upto].u = 0;
MIST_point[MIST_point_upto].v = 0;
MIST_point[MIST_point_upto].du = 0;
MIST_point[MIST_point_upto].dv = 0;
MIST_point_upto += 1;
}
}
void MIST_gust(
SLONG gx1, SLONG gz1,
SLONG gx2, SLONG gz2)
{
#ifndef PSX
SLONG i;
SLONG j;
SLONG sx;
SLONG sz;
SLONG sizex;
SLONG sizez;
SLONG x1, z1;
SLONG x2, z2;
SLONG x;
SLONG z;
SLONG wx;
SLONG wz;
SLONG dx;
SLONG dz;
SLONG dgx;
SLONG dgz;
SLONG dist;
SLONG push;
SLONG dprod;
SLONG strength;
SLONG p_index;
float ddu;
float ddv;
MIST_Mist *mm;
MIST_Point *mp;
//
// The stength (length) of the gust. Change the length
// of the gust depending on the TICK_RATIO, otherwise with
// fast frame rates, the mist never moves.
//
dgx = gx2 - gx1;
dgz = gz2 - gz1;
dgx = dgx * TICK_INV_RATIO >> TICK_SHIFT;
dgz = dgz * TICK_INV_RATIO >> TICK_SHIFT;
gx2 = gx1 + dgx;
gz2 = gz1 + dgz;
strength = QDIST2(abs(dgx), abs(dgz));
//
// Cars go too fast...
//
#define MIST_MAX_STRENGTH 48
if (strength > MIST_MAX_STRENGTH)
{
gx2 = gx1 + (dgx * MIST_MAX_STRENGTH) / strength;
gz2 = gz1 + (dgz * MIST_MAX_STRENGTH) / strength;
strength = MIST_MAX_STRENGTH;
}
strength <<= 4;
if (strength == 0)
{
//
// Ignore this gust.
//
}
for (i = 0; i < MIST_mist_upto; i++)
{
mm = &MIST_mist[i];
sizex = (mm->x2 - mm->x1) / (mm->detail - 1);
sizez = (mm->z2 - mm->z1) / (mm->detail - 1);
//
// Which square of the mist is the gust located in?
//
sx = (gx1 - mm->x1) / sizex;
sz = (gz2 - mm->z1) / sizez;
if (sx < -1 || sx >= mm->detail ||
sz < -1 || sz >= mm->detail)
{
//
// The gust is outside the mist.
//
}
else
{
//
// The bounding box of points to consider.
//
x1 = sx - 1;
z1 = sz - 1;
x2 = sx + 2;
z2 = sz + 2;
if (x1 < 0) {x1 = 0;}
if (z1 < 0) {z1 = 0;}
if (x2 > mm->detail - 1) {x2 = mm->detail - 1;}
if (z2 > mm->detail - 1) {z2 = mm->detail - 1;}
for (x = x1; x <= x2; x++)
for (z = z1; z <= z2; z++)
{
//
// The world position of this point.
//
wx = mm->x1 + x * sizex;
wz = mm->z1 + z * sizez;
//
// How far from the origin of the gust?
//
dx = wx - gx1;
dz = wz - gz1;
dist = QDIST2(abs(dx), abs(dz));
if (dist == 0)
{
//
// Ignore this point!
//
continue;
}
//
// Do we effect this point?
//
if (strength > dist)
{
//
// Yes we do! Depending on whether the point is in front of
// the gust or behind the gust, it moves towards or away from
// the origin.
//
dprod = dx * dgx + dz * dgz;
push = -dprod;
push /= dist;
push *= strength - dist;
push /= strength;
ddu = float(dx) * float(push) * 0.0000005F;
ddv = float(dz) * float(push) * 0.0000005F;
if (push > 0)
{
//
// Make it follow you more.
//
ddu += ddu;
ddv += ddv;
}
//
// Effect the point.
//
p_index = mm->p_index;
p_index += x + z * mm->detail;
ASSERT(WITHIN(p_index, 0, MIST_point_upto - 1));
mp = &MIST_point[p_index];
mp->du += ddu;
mp->dv += ddv;
}
}
}
}
#endif
}
void MIST_process()
{
SLONG i;
#ifndef PSX
MIST_Point *mp;
for (i = 0; i < MIST_point_upto; i++)
{
mp = &MIST_point[i];
mp->u += mp->du;
mp->v += mp->dv;
mp->dv -= mp->dv * 0.25F;
mp->du -= mp->du * 0.25F;
mp->du -= mp->u * 0.005F;
mp->dv -= mp->v * 0.005F;
}
#endif
}
SLONG MIST_get_upto;
SLONG MIST_get_dx;
SLONG MIST_get_dz;
float MIST_get_du;
float MIST_get_dv;
SLONG MIST_get_turn;
float MIST_get_base_u;
float MIST_get_base_v;
float MIST_off_u_odd;
float MIST_off_v_odd;
float MIST_off_u_even;
float MIST_off_v_even;
MIST_Mist *MIST_get_mist;
void MIST_get_start()
{
MIST_get_upto = 0;
MIST_get_turn += 1;
}
#ifndef PSX
SLONG MIST_get_detail()
{
float radius;
float yaw_odd;
float yaw_even;
MIST_Mist *mm;
if (!WITHIN(MIST_get_upto, 0, MIST_mist_upto - 1))
{
//
// No more mist left.
//
return NULL;
}
else
{
mm = &MIST_mist[MIST_get_upto++];
//
// Remember the layer of mist whose points we are getting.
//
MIST_get_mist = mm;
//
// Precalculate stuff to make returning the point info faster.
//
MIST_get_dx = (mm->x2 - mm->x1) / (mm->detail - 1);
MIST_get_dz = (mm->z2 - mm->z1) / (mm->detail - 1);
MIST_get_du = 0.5F / float(mm->detail - 1);
MIST_get_dv = 0.5F / float(mm->detail - 1);
MIST_get_base_u = float(mm->type & 1) * 0.5f;
MIST_get_base_v = float(mm->type >> 1) * 0.5f;
//
// The rotatey wibble on the even and odd points
//
yaw_odd = float(MIST_get_turn) / (float(mm->type + 1) * 15.0F);
yaw_odd += MIST_get_upto;
yaw_even = yaw_odd + (PI / 1.75F);
//
// The radius of the rotatey wibble.
//
radius = 0.1F / float(mm->detail - 1);
//
// The wibble offsets.
//
MIST_off_u_odd = sin(yaw_odd) * radius;
MIST_off_v_odd = cos(yaw_odd) * radius;
MIST_off_u_even = sin(yaw_even) * radius;
MIST_off_v_even = cos(yaw_even) * radius;
//
// The texture offsets.
//
return mm->detail;
}
}
void MIST_get_point(SLONG px, SLONG pz,
SLONG *x,
SLONG *y,
SLONG *z)
{
MIST_Mist *mm = MIST_get_mist;
ASSERT(WITHIN(mm, &MIST_mist[0], &MIST_mist[MIST_mist_upto - 1]));
ASSERT(WITHIN(px, 0, mm->detail - 1));
ASSERT(WITHIN(pz, 0, mm->detail - 1));
*x = mm->x1 + px * MIST_get_dx;
*z = mm->z1 + pz * MIST_get_dz;
*y = /*PAP_calc_height_at(*x,*z) +*/ mm->height;
}
void MIST_get_texture(SLONG px, SLONG pz,
float *u,
float *v)
{
SLONG p_index;
MIST_Point *mp;
MIST_Mist *mm = MIST_get_mist;
ASSERT(WITHIN(mm, &MIST_mist[0], &MIST_mist[MIST_mist_upto - 1]));
ASSERT(WITHIN(px, 0, mm->detail - 1));
ASSERT(WITHIN(pz, 0, mm->detail - 1));
*u = MIST_get_base_u;
*v = MIST_get_base_v;
*u += float(px) * MIST_get_du;
*v += float(pz) * MIST_get_dv;
if (px == 0 || px == mm->detail - 1 ||
pz == 0 || pz == mm->detail - 1)
{
//
// Dont wibble the outside points.
//
return;
}
//
// Odd or even?
//
if ((px + pz) & 0x1)
{
*u += MIST_off_u_odd;
*v += MIST_off_v_odd;
}
else
{
*u += MIST_off_u_even;
*v += MIST_off_v_even;
}
p_index = mm->p_index;
p_index += px + pz * mm->detail;
ASSERT(WITHIN(p_index, 0, MIST_point_upto - 1));
mp = &MIST_point[p_index];
*u += mp->u;
*v += mp->v;
}
#endif