This tutorial was created by David Rousal and demonstrates a way to add distance fog to the Genesis3D 1.0 engine. The changes are made to the source files world.c & user.c.

The changes are indicated in Yellow.

The global variables you can set to control the fog are:

SinusFog=0 Fog is turned off
SinusFog=1 Fog is turned on

SinusFogMin Start of Fog
SinusFogMax End of Fog

Here is a graphic example of what the values will do:

SinusFog=1; SinusFogMin=400; SinusFogMax=800;





Code Changes


In world.c close to the top:

//=====================================================================================
// Local Static Globals
//=====================================================================================
typedef struct
{
geCamera *Camera;
Frustum_Info *Frustum;
geWorld_SkyBoxTData *SkyTData;

} geWorld_RenderInfo;

//*************************************************************************************
//*************************************************************************************
// Begin of changes by SiNuS
//*************************************************************************************
//*************************************************************************************

geFloat SinusFogMin;
geFloat SinusFogMax;
int32 SinusFog,SinusDrawPoly;

//*************************************************************************************
//*************************************************************************************
//End of changes by SiNuS
//*************************************************************************************
//*************************************************************************************

static geEngine *CEngine = NULL;
static geWorld *CWorld = NULL;
static World_BSP *CBSP;
static GBSP_BSPData *BSPData = NULL; // This is in globals, but is also kept here for speed
static geBoolean CanDoMirrors;
static geWorld_DebugInfo *CDebugInfo;
static Frustum_Info *CFrustumInfo;

static Surf_SurfInfo *pSurfInfo;
static DRV_Driver *RDriver;



Then in the function RenderFace() at the beginning of the function:



//=====================================================================================
// RenderFace//=====================================================================================
static void RenderFace(int32 Face, const geWorld_RenderInfo *RenderInfo, int32 ClipFlags)
{
geVec3d Dest1[MAX_RENDERFACE_VERTS], Dest2[MAX_RENDERFACE_VERTS];
geVec3d *pDest1, *pDest2;
Surf_TexVert Tex1[MAX_RENDERFACE_VERTS], Tex2[MAX_RENDERFACE_VERTS];
Surf_TexVert *pTex1, *pTex2;
DRV_TLVertex Clipped1[MAX_RENDERFACE_VERTS];
geVec3d *pGFXVerts;
int32 Length1, Length2;
int32 i, p;
geFloat sinfog,sinfog2,sinfog3;
int32 *pIndex;
int32 TexFlags;
int32 NumVerts;




Then in the function RenderFace() towards the end of the function:





GlobalInfo.VecU = pTexInfo->Vecs[0];
GlobalInfo.VecV = pTexInfo->Vecs[1];
GlobalInfo.TexShiftX = pTexInfo->Shift[0];
GlobalInfo.TexShiftY = pTexInfo->Shift[1];

// Get the THandle from the bitmap
THandle = geBitmap_GetTHandle(pBitmap);
assert(THandle);

//*************************************************************************************
//*************************************************************************************
// Begin of changes by SiNuS
//*************************************************************************************
//*************************************************************************************

SinusDrawPoly=0;
if(SinusFog==1)
for(i=0;i<Length1;i++)
{
sinfog=Clipped1[i].z-SinusFogMin;
if(sinfog<0.0f)sinfog=0.0f;
sinfog2=SinusFogMax-SinusFogMin;
if(sinfog>sinfog2)sinfog=sinfog2;
sinfog3=1.0f-sinfog/sinfog2;
Clipped1[i].r=sinfog3*255.0f;
Clipped1[i].g=sinfog3*255.0f;
Clipped1[i].b=sinfog3*255.0f;
//if(Clipped1[i].r<0.0f)Clipped1[i].r=0.0f;
//if(Clipped1[i].g<0.0f)Clipped1[i].g=0.0f;
//if(Clipped1[i].b<0.0f)Clipped1[i].b=0.0f;
}



//*************************************************************************************
//*************************************************************************************
//End of changes by SiNuS
//*************************************************************************************
//*************************************************************************************



if (pTexInfo->Flags & TEXINFO_NO_LIGHTMAP)
{
RDriver->RenderWorldPoly(Clipped1, Length1, THandle, &DrvTexInfo, NULL, RenderFlags);
}
else
{
DRV_LInfo *pLInfo = &pSurfInfo[Face].LInfo;

// The camera is set up at the beginning of the world...
GlobalInfo.TexMinsX = pLInfo->MinU;
GlobalInfo.TexMinsY = pLInfo->MinV;
GlobalInfo.TexWidth = pLInfo->Width<<4;
GlobalInfo.TexHeight = pLInfo->Height<<4;

RDriver->RenderWorldPoly(Clipped1, Length1, THandle, &DrvTexInfo, &pSurfInfo[Face].LInfo, RenderFlags);
}
}


//=====================================================================================
// RenderWorldModel
// Renders model 0 (the world model)//=====================================================================================
static geBoolean RenderWorldModel(geCamera *Camera, Frustum_Info *FrustumInfo, geWorld_SkyBoxTData *SkyTData)
{
geXForm3d OldXForm,NewXForm, CXForm;
geWorld_Model *Models;
Frustum_Info WorldSpaceFrustum;
uint32 StartClipFlags;




Then in the function RenderTransPoly() towards the end of the function:



//========================================================================================
// RenderTransPoly
//========================================================================================
static void RenderTransPoly(geCamera *Camera, World_TransPoly *pPoly)
{
geFloat sinfog,sinfog2,sinfog3; // *********** changes by Sinus!! ***********
int32 i; // ********** changes by Sinus!! ***********

int32 Face;
DRV_TLVertex *pTLVerts;
GFX_Face *pFace;
GFX_TexInfo *pTexInfo;
geBitmap *pBitmap;
int32 NumVerts;
Surf_SurfInfo *pSurfInfo2;
DRV_LInfo *pLInfo;
DRV_TexInfo DrvTexInfo;

Face = pPoly->Face;

pFace = &BSPData->GFXFaces[Face];

pTLVerts = pPoly->TLVerts;
NumVerts = pPoly->NumVerts;

pTexInfo = &BSPData->GFXTexInfo[pFace->TexInfo];
pSurfInfo2 = &pSurfInfo[Face];
pLInfo = &pSurfInfo2->LInfo;

// Get a pointer to the bitmap (texture)
pBitmap = geWBitmap_Pool_GetBitmapByIndex(CBSP->WBitmapPool, pTexInfo->Texture);

assert(geWorld_HasBitmap(CWorld, pBitmap));

pTLVerts->a = pTexInfo->Alpha;

DrvTexInfo.ShiftU = pSurfInfo2->ShiftU;
DrvTexInfo.ShiftV = pSurfInfo2->ShiftV;
//DrvTexInfo.ShiftU = pTexInfo->Shift[0];
//DrvTexInfo.ShiftV = pTexInfo->Shift[1];
DrvTexInfo.DrawScaleU = pTexInfo->DrawScale[0];
DrvTexInfo.DrawScaleV = pTexInfo->DrawScale[1];

// Setup the global info (pass this in the driver the right way!!!)
GlobalInfo.VecU = pTexInfo->Vecs[0];
GlobalInfo.VecV = pTexInfo->Vecs[1];
GlobalInfo.TexShiftX = pTexInfo->Shift[0];
GlobalInfo.TexShiftY = pTexInfo->Shift[1];

GlobalInfo.PlaneNormal = BSPData->GFXPlanes[pFace->PlaneNum].Normal;
GlobalInfo.PlaneDist = BSPData->GFXPlanes[pFace->PlaneNum].Dist;
geXForm3d_Rotate(geCamera_GetCameraSpaceXForm(Camera), &GlobalInfo.PlaneNormal, &GlobalInfo.RPlaneNormal);

//*************************************************************************************
//*************************************************************************************
// Begin of changes by SiNuS
//*************************************************************************************
//*************************************************************************************

SinusDrawPoly=0;
if(SinusFog==1)
for(i=0;i<NumVerts;i++)
{
sinfog=pTLVerts[i].z-SinusFogMin;
if(sinfog<0.0f)sinfog=0.0f;
sinfog2=SinusFogMax-SinusFogMin;
if(sinfog>sinfog2)sinfog=sinfog2;
sinfog3=1.0f-sinfog/sinfog2;
pTLVerts[i].r=sinfog3*255.0f;
pTLVerts[i].g=sinfog3*255.0f;
pTLVerts[i].b=sinfog3*255.0f;
//if(Clipped1[i].r<0.0f)Clipped1[i].r=0.0f;
//if(Clipped1[i].g<0.0f)Clipped1[i].g=0.0f;
//if(Clipped1[i].b<0.0f)Clipped1[i].b=0.0f;
}


//*************************************************************************************
//*************************************************************************************
//End of changes by SiNuS
//*************************************************************************************
//*************************************************************************************




if (pTexInfo->Flags & TEXINFO_NO_LIGHTMAP)
{
geRDriver_THandle *THandle;

THandle = geBitmap_GetTHandle(pBitmap);

assert(THandle);

RDriver->RenderWorldPoly(pTLVerts, NumVerts, THandle, &DrvTexInfo, NULL, DRV_RENDER_ALPHA | DRV_RENDER_FLUSH);
}
else
{
geRDriver_THandle *THandle;

// This global info only needs to be set up for faces that have lightmaps...
GlobalInfo.TexMinsX = pLInfo->MinU;
GlobalInfo.TexMinsY = pLInfo->MinV;
GlobalInfo.TexWidth = pLInfo->Width<<4;
GlobalInfo.TexHeight = pLInfo->Height<<4;

THandle = geBitmap_GetTHandle(pBitmap);

assert(THandle);

RDriver->RenderWorldPoly(pTLVerts, NumVerts, THandle, &DrvTexInfo, pLInfo, DRV_RENDER_ALPHA | DRV_RENDER_FLUSH);
}
}




In user.c:

In function RenderTexturedPoint() at the end of the fuction:


assert(geWorld_HasBitmap(gWorld, Bitmap));
assert(geBitmap_GetTHandle(Bitmap));

RDriver->RenderMiscTexturePoly((DRV_TLVertex*)ScreenPnts, 4, geBitmap_GetTHandle(Bitmap), RenderFlags);
}

return GE_TRUE;
}


//*************************************************************************************
//*************************************************************************************
// Begin of changes by SiNuS
//*************************************************************************************
//*************************************************************************************


extern geFloat SinusFogMin;
extern geFloat SinusFogMax;
extern int32 SinusFog;


//*************************************************************************************
//*************************************************************************************
// End of changes by SiNuS
//*************************************************************************************
//*************************************************************************************


//=====================================================================================
// RenderTexturedPoly
//=====================================================================================
static void RenderTexturedPoly(DRV_Driver *RDriver, gePoly *Poly, Frustum_Info *FInfo, geCamera *Camera)
{
geVec3d Dest1[30], Dest2[30], *pDest1, *pDest2, *pDest3, *pTempDest;
GE_LVertex *pLVert;





Then in the function RenderTexturedPoly() towards the end of the function:




//=====================================================================================
// RenderTexturedPoly
//=====================================================================================
static void RenderTexturedPoly(DRV_Driver *RDriver, gePoly *Poly, Frustum_Info *FInfo, geCamera *Camera)
{
geVec3d Dest1[30], Dest2[30], *pDest1, *pDest2, *pDest3, *pTempDest;
GE_LVertex *pLVert;
Surf_TexVert Tex1[30], Tex2[30];
Surf_TexVert *pTex1, *pTex2, *pTempTex;
DRV_TLVertex Clipped1[90];
int32 Length1, Length2;
geBitmap *pBitmap;
GFX_Plane *pFPlanes;
int32 i, p;
uint32 RenderFlags;
geFloat sinfog,sinfog2,sinfog3;

assert(geWorld_PolyIsValid(Poly));

pFPlanes = FInfo->Planes;

pDest1 = Dest1;
pTex1 = Tex1;
pLVert = Poly->Verts;

for (i=0; i< Poly->NumVerts; i++)
{
pDest1->X = pLVert->X;
pDest1->Y = pLVert->Y;
pDest1->Z = pLVert->Z;

pTex1->u = pLVert->u;
pTex1->v = pLVert->v;
pTex1->r = pLVert->r;
pTex1->g = pLVert->g;
pTex1->b = pLVert->b;

pDest1++;
pLVert++;
pTex1++;
}

pDest1 = Dest1;
pDest2 = Dest2;
pTex1 = Tex1;
pTex2 = Tex2;
Length1 = Poly->NumVerts;

for (p=0; p< FInfo->NumPlanes; p++, pFPlanes++)
{
if (!Frustum_ClipToPlaneUVRGB(pFPlanes, pDest1, pDest2, pTex1, pTex2, Length1, &Length2))
return;

// Swap them
pTempDest = pDest1;
pDest1 = pDest2;
pDest2 = pTempDest;

pTempTex = pTex1;
pTex1 = pTex2;
pTex2 = pTempTex;

Length1 = Length2;
}

if (Length1 < 3)
return;

pDest3 = pDest2;
for (i=0; i< Length1; i++)
{
//geXForm3d_Transform(&Camera->XForm, pDest1, pDest2);
geCamera_Transform(Camera,pDest1,pDest2);
pDest1++;
pDest2++;
}

Frustum_ProjectRGB(pDest3, pTex1, Clipped1, Length1, Camera);

pBitmap = Poly->Bitmap;

Clipped1[0].a = Poly->Verts[0].a;

if (Poly->RenderFlags & GE_RENDER_DO_NOT_OCCLUDE_OTHERS)
RenderFlags = DRV_RENDER_NO_ZWRITE;
else
RenderFlags = 0;

if (Poly->RenderFlags & GE_RENDER_DO_NOT_OCCLUDE_SELF)
RenderFlags |= DRV_RENDER_NO_ZMASK;

if (Clipped1[0].a != 255.0f)
RenderFlags |= DRV_RENDER_ALPHA;

if (Poly->RenderFlags & GE_RENDER_DEPTH_SORT_BF)
RenderFlags |= DRV_RENDER_FLUSH;

if (Poly->RenderFlags & GE_RENDER_CLAMP_UV)
RenderFlags |= DRV_RENDER_CLAMP_UV;

// Render it...
assert(geWorld_HasBitmap(gWorld, pBitmap));
assert(geBitmap_GetTHandle(pBitmap));

//*************************************************************************************
//*************************************************************************************
// Begin of changes by SiNuS
//*************************************************************************************
//*************************************************************************************


SinusDrawPoly=0;
if(SinusFog==1)
for(i=0;i<Length1;i++)
{
sinfog=Clipped1[i].z-SinusFogMin;
if(sinfog<0.0f)sinfog=0.0f;
sinfog2=SinusFogMax-SinusFogMin;
if(sinfog>sinfog2)sinfog=sinfog2;
sinfog3=1.0f-sinfog/sinfog2;
Clipped1[i].r=sinfog3*255.0f;
Clipped1[i].g=sinfog3*255.0f;
Clipped1[i].b=sinfog3*255.0f;
}


//*************************************************************************************
//*************************************************************************************
//End of changes by SiNuS
//*************************************************************************************
//*************************************************************************************



RDriver->RenderMiscTexturePoly(Clipped1, Length1, geBitmap_GetTHandle(pBitmap), RenderFlags);

}